Commit ab44939941

Josh Wolfe <thejoshwolfe@gmail.com>
2017-12-13 06:41:21
roughly parsing infix operators
1 parent 39e96d9
Changed files (3)
src-self-hosted/ast.zig
@@ -13,9 +13,9 @@ pub const Node = struct {
         Identifier,
         FnProto,
         ParamDecl,
-        AddrOfExpr,
         Block,
-        Return,
+        InfixOp,
+        PrefixOp,
         IntegerLiteral,
         FloatLiteral,
     };
@@ -27,9 +27,9 @@ pub const Node = struct {
             Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
             Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
             Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
-            Id.AddrOfExpr => @fieldParentPtr(NodeAddrOfExpr, "base", base).iterate(index),
             Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
-            Id.Return => @fieldParentPtr(NodeReturn, "base", base).iterate(index),
+            Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
+            Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
             Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
             Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
         };
@@ -42,9 +42,9 @@ pub const Node = struct {
             Id.Identifier => allocator.destroy(@fieldParentPtr(NodeIdentifier, "base", base)),
             Id.FnProto => allocator.destroy(@fieldParentPtr(NodeFnProto, "base", base)),
             Id.ParamDecl => allocator.destroy(@fieldParentPtr(NodeParamDecl, "base", base)),
-            Id.AddrOfExpr => allocator.destroy(@fieldParentPtr(NodeAddrOfExpr, "base", base)),
             Id.Block => allocator.destroy(@fieldParentPtr(NodeBlock, "base", base)),
-            Id.Return => allocator.destroy(@fieldParentPtr(NodeReturn, "base", base)),
+            Id.InfixOp => allocator.destroy(@fieldParentPtr(NodeInfixOp, "base", base)),
+            Id.PrefixOp => allocator.destroy(@fieldParentPtr(NodePrefixOp, "base", base)),
             Id.IntegerLiteral => allocator.destroy(@fieldParentPtr(NodeIntegerLiteral, "base", base)),
             Id.FloatLiteral => allocator.destroy(@fieldParentPtr(NodeFloatLiteral, "base", base)),
         };
@@ -170,56 +170,84 @@ pub const NodeParamDecl = struct {
     }
 };
 
-pub const NodeAddrOfExpr = struct {
+pub const NodeBlock = struct {
     base: Node,
-    op_token: Token,
-    align_expr: ?&Node,
-    bit_offset_start_token: ?Token,
-    bit_offset_end_token: ?Token,
-    const_token: ?Token,
-    volatile_token: ?Token,
-    op_expr: &Node,
-
-    pub fn iterate(self: &NodeAddrOfExpr, index: usize) -> ?&Node {
-        var i = index;
+    begin_token: Token,
+    end_token: Token,
+    statements: ArrayList(&Node),
 
-        if (self.align_expr) |align_expr| {
-            if (i < 1) return align_expr;
-            i -= 1;
-        }
+    pub fn iterate(self: &NodeBlock, index: usize) -> ?&Node {
+        var i = index;
 
-        if (i < 1) return self.op_expr;
-        i -= 1;
+        if (i < self.statements.len) return self.statements.items[i];
+        i -= self.statements.len;
 
         return null;
     }
 };
 
-pub const NodeBlock = struct {
+pub const NodeInfixOp = struct {
     base: Node,
-    begin_token: Token,
-    end_token: Token,
-    statements: ArrayList(&Node),
+    op_token: Token,
+    lhs: &Node,
+    op: InfixOp,
+    rhs: &Node,
 
-    pub fn iterate(self: &NodeBlock, index: usize) -> ?&Node {
+    const InfixOp = enum {
+        EqualEqual,
+        BangEqual,
+    };
+
+    pub fn iterate(self: &NodeInfixOp, index: usize) -> ?&Node {
         var i = index;
 
-        if (i < self.statements.len) return self.statements.items[i];
-        i -= self.statements.len;
+        if (i < 1) return self.lhs;
+        i -= 1;
+
+        switch (self.op) {
+            InfixOp.EqualEqual => {},
+            InfixOp.BangEqual => {},
+        }
+
+        if (i < 1) return self.rhs;
+        i -= 1;
 
         return null;
     }
 };
 
-pub const NodeReturn = struct {
+pub const NodePrefixOp = struct {
     base: Node,
-    return_token: Token,
-    expr: &Node,
+    op_token: Token,
+    op: PrefixOp,
+    rhs: &Node,
 
-    pub fn iterate(self: &NodeReturn, index: usize) -> ?&Node {
+    const PrefixOp = union(enum) {
+        Return,
+        AddrOf: AddrOfInfo,
+    };
+    const AddrOfInfo = struct {
+        align_expr: ?&Node,
+        bit_offset_start_token: ?Token,
+        bit_offset_end_token: ?Token,
+        const_token: ?Token,
+        volatile_token: ?Token,
+    };
+
+    pub fn iterate(self: &NodePrefixOp, index: usize) -> ?&Node {
         var i = index;
 
-        if (i < 1) return self.expr;
+        switch (self.op) {
+            PrefixOp.Return => {},
+            PrefixOp.AddrOf => |addr_of_info| {
+                if (addr_of_info.align_expr) |align_expr| {
+                    if (i < 1) return align_expr;
+                    i -= 1;
+                }
+            },
+        }
+
+        if (i < 1) return self.rhs;
         i -= 1;
 
         return null;
src-self-hosted/parser.zig
@@ -68,7 +68,12 @@ pub const Parser = struct {
         TopLevelExtern: ?Token,
         TopLevelDecl: TopLevelDeclCtx,
         Expression: DestPtr,
-        AddrOfModifiers: &ast.NodeAddrOfExpr,
+        ExpectOperand,
+        Operand: &ast.Node,
+        AfterOperand,
+        InfixOp: &ast.NodeInfixOp,
+        PrefixOp: &ast.NodePrefixOp,
+        AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
         TypeExpr: DestPtr,
         VarDecl: &ast.NodeVarDecl,
         VarDeclAlign: &ast.NodeVarDecl,
@@ -265,63 +270,140 @@ pub const Parser = struct {
                     _ = %return self.eatToken(token_id);
                     continue;
                 },
+
                 State.Expression => |dest_ptr| {
+                    // save the dest_ptr for later
+                    stack.append(state) %% unreachable;
+                    %return stack.append(State.ExpectOperand);
+                    continue;
+                },
+                State.ExpectOperand => {
+                    // we'll either get an operand (like 1 or x),
+                    // or a prefix operator (like ~ or return).
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Keyword_return => {
-                            const return_node = %return self.createAttachReturn(dest_ptr, token);
-                            stack.append(State {.Expression = DestPtr {.Field = &return_node.expr} }) %% unreachable;
+                            %return stack.append(State { .PrefixOp = %return self.createPrefixOp(token,
+                                ast.NodePrefixOp.PrefixOp.Return) });
+                            %return stack.append(State.ExpectOperand);
+                            continue;
+                        },
+                        Token.Id.Ampersand => {
+                            const prefix_op = %return self.createPrefixOp(token, ast.NodePrefixOp.PrefixOp{
+                                .AddrOf = ast.NodePrefixOp.AddrOfInfo {
+                                    .align_expr = null,
+                                    .bit_offset_start_token = null,
+                                    .bit_offset_end_token = null,
+                                    .const_token = null,
+                                    .volatile_token = null,
+                                }
+                            });
+                            %return stack.append(State { .PrefixOp = prefix_op });
+                            %return stack.append(State.ExpectOperand);
+                            %return stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
                             continue;
                         },
                         Token.Id.Identifier => {
-                            _ = %return self.createAttachIdentifier(dest_ptr, token);
+                            %return stack.append(State {
+                                .Operand = &(%return self.createIdentifier(token)).base
+                            });
+                            %return stack.append(State.AfterOperand);
                             continue;
                         },
                         Token.Id.IntegerLiteral => {
-                            _ = %return self.createAttachIntegerLiteral(dest_ptr, token);
+                            %return stack.append(State {
+                                .Operand = &(%return self.createIntegerLiteral(token)).base
+                            });
+                            %return stack.append(State.AfterOperand);
                             continue;
                         },
                         Token.Id.FloatLiteral => {
-                            _ = %return self.createAttachFloatLiteral(dest_ptr, token);
+                            %return stack.append(State {
+                                .Operand = &(%return self.createFloatLiteral(token)).base
+                            });
+                            %return stack.append(State.AfterOperand);
                             continue;
                         },
-                        Token.Id.Ampersand => {
-                            const addr_of_expr = %return self.createAttachAddrOfExpr(dest_ptr, token);
-                            stack.append(State { .AddrOfModifiers = addr_of_expr }) %% unreachable;
+                        else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
+                    }
+                },
+
+                State.AfterOperand => {
+                    // we'll either get an infix operator (like != or ^),
+                    // or a postfix operator (like () or {}),
+                    // otherwise this expression is done (like on a ; or else).
+                    var token = self.getNextToken();
+                    switch (token.id) {
+                        Token.Id.EqualEqual => {
+                            %return stack.append(State {
+                                .InfixOp = %return self.createInfixOp(token, ast.NodeInfixOp.InfixOp.EqualEqual)
+                            });
+                            %return stack.append(State.ExpectOperand);
+                            continue;
+                        },
+                        Token.Id.BangEqual => {
+                            %return stack.append(State {
+                                .InfixOp = %return self.createInfixOp(token, ast.NodeInfixOp.InfixOp.BangEqual)
+                            });
+                            %return stack.append(State.ExpectOperand);
+                            continue;
+                        },
+                        else => {
+                            // no postfix/infix operator after this operand.
+                            self.putBackToken(token);
+                            // reduce the stack
+                            var expression: &ast.Node = stack.pop().Operand;
+                            while (true) {
+                                switch (stack.pop()) {
+                                    State.Expression => |dest_ptr| {
+                                        // we're done
+                                        %return dest_ptr.store(expression);
+                                        break;
+                                    },
+                                    State.InfixOp => |infix_op| {
+                                        infix_op.rhs = expression;
+                                        infix_op.lhs = stack.pop().Operand;
+                                        expression = &infix_op.base;
+                                        continue;
+                                    },
+                                    State.PrefixOp => |prefix_op| {
+                                        prefix_op.rhs = expression;
+                                        expression = &prefix_op.base;
+                                        continue;
+                                    },
+                                    else => unreachable,
+                                }
+                            }
                             continue;
                         },
-                        else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
                     }
                 },
 
-                State.AddrOfModifiers => |addr_of_expr| {
+                State.AddrOfModifiers => |addr_of_info| {
                     var token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.Keyword_align => {
-                            stack.append(State { .AddrOfModifiers = addr_of_expr }) %% unreachable;
-                            if (addr_of_expr.align_expr != null) return self.parseError(token, "multiple align qualifiers");
+                            stack.append(state) %% unreachable;
+                            if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
                             _ = %return self.eatToken(Token.Id.LParen);
                             %return stack.append(State { .ExpectToken = Token.Id.RParen });
-                            %return stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_expr.align_expr} });
+                            %return stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
                             continue;
                         },
                         Token.Id.Keyword_const => {
-                            if (addr_of_expr.const_token != null) return self.parseError(token, "duplicate qualifier: const");
-                            addr_of_expr.const_token = token;
-                            stack.append(State { .AddrOfModifiers = addr_of_expr }) %% unreachable;
+                            stack.append(state) %% unreachable;
+                            if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const");
+                            addr_of_info.const_token = token;
                             continue;
                         },
                         Token.Id.Keyword_volatile => {
-                            if (addr_of_expr.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
-                            addr_of_expr.volatile_token = token;
-                            stack.append(State { .AddrOfModifiers = addr_of_expr }) %% unreachable;
+                            stack.append(state) %% unreachable;
+                            if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
+                            addr_of_info.volatile_token = token;
                             continue;
                         },
                         else => {
                             self.putBackToken(token);
-                            stack.append(State {
-                                .Expression = DestPtr { .Field = &addr_of_expr.op_expr},
-                            }) %% unreachable;
                             continue;
                         },
                     }
@@ -482,8 +564,14 @@ pub const Parser = struct {
                     %return stack.append(State { .Expression = DestPtr{.List = &block.statements} });
                     continue;
                 },
+
+                // These are data, not control flow.
+                State.InfixOp => unreachable,
+                State.PrefixOp => unreachable,
+                State.Operand => unreachable,
             }
-            unreachable;
+            @import("std").debug.panic("{}", @tagName(state));
+            //unreachable;
         }
     }
 
@@ -560,23 +648,6 @@ pub const Parser = struct {
         return node;
     }
 
-    fn createAddrOfExpr(self: &Parser, op_token: &const Token) -> %&ast.NodeAddrOfExpr {
-        const node = %return self.allocator.create(ast.NodeAddrOfExpr);
-        %defer self.allocator.destroy(node);
-
-        *node = ast.NodeAddrOfExpr {
-            .base = ast.Node {.id = ast.Node.Id.AddrOfExpr},
-            .align_expr = null,
-            .op_token = *op_token,
-            .bit_offset_start_token = null,
-            .bit_offset_end_token = null,
-            .const_token = null,
-            .volatile_token = null,
-            .op_expr = undefined,
-        };
-        return node;
-    }
-
     fn createBlock(self: &Parser, begin_token: &const Token) -> %&ast.NodeBlock {
         const node = %return self.allocator.create(ast.NodeBlock);
         %defer self.allocator.destroy(node);
@@ -590,14 +661,29 @@ pub const Parser = struct {
         return node;
     }
 
-    fn createReturn(self: &Parser, return_token: &const Token) -> %&ast.NodeReturn {
-        const node = %return self.allocator.create(ast.NodeReturn);
+    fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) -> %&ast.NodeInfixOp {
+        const node = %return self.allocator.create(ast.NodeInfixOp);
+        %defer self.allocator.destroy(node);
+
+        *node = ast.NodeInfixOp {
+            .base = ast.Node {.id = ast.Node.Id.InfixOp},
+            .op_token = *op_token,
+            .lhs = undefined,
+            .op = *op,
+            .rhs = undefined,
+        };
+        return node;
+    }
+
+    fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) -> %&ast.NodePrefixOp {
+        const node = %return self.allocator.create(ast.NodePrefixOp);
         %defer self.allocator.destroy(node);
 
-        *node = ast.NodeReturn {
-            .base = ast.Node {.id = ast.Node.Id.Return},
-            .return_token = *return_token,
-            .expr = undefined,
+        *node = ast.NodePrefixOp {
+            .base = ast.Node {.id = ast.Node.Id.PrefixOp},
+            .op_token = *op_token,
+            .op = *op,
+            .rhs = undefined,
         };
         return node;
     }
@@ -635,20 +721,6 @@ pub const Parser = struct {
         return node;
     }
 
-    fn createAttachFloatLiteral(self: &Parser, dest_ptr: &const DestPtr, token: &const Token) -> %&ast.NodeFloatLiteral {
-        const node = %return self.createFloatLiteral(token);
-        %defer self.allocator.destroy(node);
-        %return dest_ptr.store(&node.base);
-        return node;
-    }
-
-    fn createAttachIntegerLiteral(self: &Parser, dest_ptr: &const DestPtr, token: &const Token) -> %&ast.NodeIntegerLiteral {
-        const node = %return self.createIntegerLiteral(token);
-        %defer self.allocator.destroy(node);
-        %return dest_ptr.store(&node.base);
-        return node;
-    }
-
     fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) -> %&ast.NodeIdentifier {
         const node = %return self.createIdentifier(name_token);
         %defer self.allocator.destroy(node);
@@ -656,20 +728,6 @@ pub const Parser = struct {
         return node;
     }
 
-    fn createAttachReturn(self: &Parser, dest_ptr: &const DestPtr, return_token: &const Token) -> %&ast.NodeReturn {
-        const node = %return self.createReturn(return_token);
-        %defer self.allocator.destroy(node);
-        %return dest_ptr.store(&node.base);
-        return node;
-    }
-
-    fn createAttachAddrOfExpr(self: &Parser, dest_ptr: &const DestPtr, op_token: &const Token) -> %&ast.NodeAddrOfExpr {
-        const node = %return self.createAddrOfExpr(op_token);
-        %defer self.allocator.destroy(node);
-        %return dest_ptr.store(&node.base);
-        return node;
-    }
-
     fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) -> %&ast.NodeParamDecl {
         const node = %return self.createParamDecl();
         %defer self.allocator.destroy(node);
@@ -783,7 +841,6 @@ pub const Parser = struct {
         ParamDecl: &ast.Node,
         Text: []const u8,
         Expression: &ast.Node,
-        AddrOfExprBit: &ast.NodeAddrOfExpr,
         VarDecl: &ast.NodeVarDecl,
         Statement: &ast.Node,
         PrintIndent,
@@ -912,17 +969,6 @@ pub const Parser = struct {
                         const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
                         %return stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
                     },
-                    ast.Node.Id.AddrOfExpr => {
-                        const addr_of_expr = @fieldParentPtr(ast.NodeAddrOfExpr, "base", base);
-                        %return stream.print("{}", self.tokenizer.getTokenSlice(addr_of_expr.op_token));
-                        %return stack.append(RenderState { .AddrOfExprBit = addr_of_expr});
-
-                        if (addr_of_expr.align_expr) |align_expr| {
-                            %return stream.print("align(");
-                            %return stack.append(RenderState { .Text = ") "});
-                            %return stack.append(RenderState { .Expression = align_expr});
-                        }
-                    },
                     ast.Node.Id.Block => {
                         const block = @fieldParentPtr(ast.NodeBlock, "base", base);
                         %return stream.write("{");
@@ -940,10 +986,43 @@ pub const Parser = struct {
                             %return stack.append(RenderState { .Text = "\n" });
                         }
                     },
-                    ast.Node.Id.Return => {
-                        const return_node = @fieldParentPtr(ast.NodeReturn, "base", base);
-                        %return stream.write("return ");
-                        %return stack.append(RenderState { .Expression = return_node.expr });
+                    ast.Node.Id.InfixOp => {
+                        const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
+                        %return stack.append(RenderState { .Expression = prefix_op_node.rhs });
+                        switch (prefix_op_node.op) {
+                            ast.NodeInfixOp.InfixOp.EqualEqual => {
+                                %return stack.append(RenderState { .Text = " == "});
+                            },
+                            ast.NodeInfixOp.InfixOp.BangEqual => {
+                                %return stack.append(RenderState { .Text = " != "});
+                            },
+                            else => unreachable,
+                        }
+                        %return stack.append(RenderState { .Expression = prefix_op_node.lhs });
+                    },
+                    ast.Node.Id.PrefixOp => {
+                        const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
+                        %return stack.append(RenderState { .Expression = prefix_op_node.rhs });
+                        switch (prefix_op_node.op) {
+                            ast.NodePrefixOp.PrefixOp.Return => {
+                                %return stream.write("return ");
+                            },
+                            ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
+                                %return stream.write("&");
+                                if (addr_of_info.volatile_token != null) {
+                                    %return stack.append(RenderState { .Text = "volatile "});
+                                }
+                                if (addr_of_info.const_token != null) {
+                                    %return stack.append(RenderState { .Text = "const "});
+                                }
+                                if (addr_of_info.align_expr) |align_expr| {
+                                    %return stream.print("align(");
+                                    %return stack.append(RenderState { .Text = ") "});
+                                    %return stack.append(RenderState { .Expression = align_expr});
+                                }
+                            },
+                            else => unreachable,
+                        }
                     },
                     ast.Node.Id.IntegerLiteral => {
                         const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
@@ -955,21 +1034,6 @@ pub const Parser = struct {
                     },
                     else => unreachable,
                 },
-                RenderState.AddrOfExprBit => |addr_of_expr| {
-                    if (addr_of_expr.bit_offset_start_token) |bit_offset_start_token| {
-                        %return stream.print("{} ", self.tokenizer.getTokenSlice(bit_offset_start_token));
-                    }
-                    if (addr_of_expr.bit_offset_end_token) |bit_offset_end_token| {
-                        %return stream.print("{} ", self.tokenizer.getTokenSlice(bit_offset_end_token));
-                    }
-                    if (addr_of_expr.const_token) |const_token| {
-                        %return stream.print("{} ", self.tokenizer.getTokenSlice(const_token));
-                    }
-                    if (addr_of_expr.volatile_token) |volatile_token| {
-                        %return stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token));
-                    }
-                    %return stack.append(RenderState { .Expression = addr_of_expr.op_expr});
-                },
                 RenderState.FnProtoRParen => |fn_proto| {
                     %return stream.print(")");
                     if (fn_proto.align_expr != null) {
@@ -1128,4 +1192,12 @@ test "zig fmt" {
         \\extern fn f3(s: &align(1) const volatile u8) -> c_int;
         \\
     );
+
+    testCanonical(
+        \\fn f1(a: bool, b: bool) -> bool {
+        \\    a != b;
+        \\    return a == b;
+        \\}
+        \\
+    );
 }
src-self-hosted/tokenizer.zig
@@ -71,7 +71,10 @@ pub const Token = struct {
         StringLiteral: StrLitKind,
         Eof,
         Builtin,
+        Bang,
         Equal,
+        EqualEqual,
+        BangEqual,
         LParen,
         RParen,
         Semicolon,
@@ -187,6 +190,8 @@ pub const Tokenizer = struct {
         C,
         StringLiteral,
         StringLiteralBackslash,
+        Equal,
+        Bang,
         Minus,
         Slash,
         LineComment,
@@ -232,9 +237,10 @@ pub const Tokenizer = struct {
                         result.id = Token.Id.Builtin;
                     },
                     '=' => {
-                        result.id = Token.Id.Equal;
-                        self.index += 1;
-                        break;
+                        state = State.Equal;
+                    },
+                    '!' => {
+                        state = State.Bang;
                     },
                     '(' => {
                         result.id = Token.Id.LParen;
@@ -356,6 +362,30 @@ pub const Tokenizer = struct {
                     },
                 },
 
+                State.Bang => switch (c) {
+                    '=' => {
+                        result.id = Token.Id.BangEqual;
+                        self.index += 1;
+                        break;
+                    },
+                    else => {
+                        result.id = Token.Id.Bang;
+                        break;
+                    },
+                },
+
+                State.Equal => switch (c) {
+                    '=' => {
+                        result.id = Token.Id.EqualEqual;
+                        self.index += 1;
+                        break;
+                    },
+                    else => {
+                        result.id = Token.Id.Equal;
+                        break;
+                    },
+                },
+
                 State.Minus => switch (c) {
                     '>' => {
                         result.id = Token.Id.Arrow;