Commit a20c0b31de

Vexu <git@vexu.eu>
2020-01-04 20:26:43
std-c parser and ast organization
1 parent 73a53fa
Changed files (2)
lib/std/c/ast.zig
@@ -54,13 +54,47 @@ pub const Error = union(enum) {
     }
 };
 
-pub const Root = struct {
-    decls: DeclList,
-    eof_token: TokenIndex,
+pub const Node = struct {
+    id: Id,
 
-    pub const DeclList = SegmentedList(*Decl, 4);
-};
+    pub const Id = enum {
+        Root,
+        JumpStmt,
+        ExprStmt,
+        Label,
+    };
+
+    pub const Root = struct {
+        base: Node,
+        decls: DeclList,
+        eof: TokenIndex,
 
-pub const Decl = struct {
+        pub const DeclList = SegmentedList(*Node, 4);
+    };
 
-};
\ No newline at end of file
+    pub const JumpStmt = struct {
+        base: Node = Node{ .id = .JumpStmt},
+        ltoken: TokenIndex,
+        kind: Kind,
+        semicolon: TokenIndex,
+
+        pub const Kind = union(enum) {
+            Break,
+            Continue,
+            Return: ?*Node,
+            Goto: TokenIndex,
+        };
+    };
+
+    pub const ExprStmt = struct {
+        base: Node = Node{ .id = .ExprStmt},
+        expr: ?*Node,
+        semicolon: TokenIndex,
+    };
+
+    pub const Label = struct {
+        base: Node = Node{ .id = .Label},
+        identifier: TokenIndex,
+        colon: TokenIndex,
+    };
+};
lib/std/c/parse.zig
@@ -55,242 +55,356 @@ pub fn parse(allocator: *Allocator, source: []const u8) !*Tree {
         }
     }
 
-    tree.root_node = try parseRoot(arena, &it, tree);
+    var parser = Parser{
+        .arena = arena,
+        .it = &it,
+        .tree = tree,
+    };
+
+    tree.root_node = try parser.root();
     return tree;
 }
 
-/// Root <- ExternalDeclaration* eof
-fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!*Node {
-    const node = try arena.create(ast.Root);
-    node.* = .{
-        .decls = ast.Node.DeclList.init(arena),
-        .eof_token = undefined,
-    };
-    while (parseExternalDeclarations(arena, it, tree) catch |err| switch (err) {
-        error.OutOfMemory => return error.OutOfMemory,
-        error.ParseError => return node,
-    }) |decl| {
-        try node.decls.push(decl);
-    }
-    node.eof_token = eatToken(it, .Eof) orelse {
-        try tree.errors.push(.{
-            .ExpectedDecl = .{ .token = it.index },
-        });
+const Parser = struct {
+    arena: *Allocator,
+    it: *TokenIterator,
+    tree: *Tree,
+
+    /// Root <- ExternalDeclaration* eof
+    fn root(parser: *Parser) Allocator.Error!*Node {
+        const node = try arena.create(ast.Root);
+        node.* = .{
+            .decls = ast.Node.DeclList.init(arena),
+            .eof = undefined,
+        };
+        while (parser.externalDeclarations() catch |err| switch (err) {
+            error.OutOfMemory => return error.OutOfMemory,
+            error.ParseError => return node,
+        }) |decl| {
+            try node.decls.push(decl);
+        }
+        node.eof = eatToken(it, .Eof) orelse {
+            try tree.errors.push(.{
+                .ExpectedDecl = .{ .token = it.index },
+            });
+            return node;
+        };
         return node;
-    };
-    return node;
-}
+    }
 
-/// ExternalDeclaration
-///     <- Declaration
-///     / DeclarationSpecifiers Declarator Declaration* CompoundStmt
-fn parseExternalDeclarations(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
-    if (try parseDeclaration(arena, it, tree)) |decl| {}
-    return null;
-}
+    /// ExternalDeclaration
+    ///     <- Declaration
+    ///     / DeclarationSpecifiers Declarator Declaration* CompoundStmt
+    fn externalDeclarations(parser: *Parser) !?*Node {
+        if (try Declaration(parser)) |decl| {}
+        return null;
+    }
+
+    /// Declaration
+    ///     <- DeclarationSpecifiers (Declarator (EQUAL Initializer)?)* SEMICOLON
+    ///     \ StaticAssertDeclaration
+    fn declaration(parser: *Parser) !?*Node {}
+
+    /// StaticAssertDeclaration <- Keyword_static_assert LPAREN ConstExpr COMMA STRINGLITERAL RPAREN SEMICOLON
+    fn staticAssertDeclaration(parser: *Parser) !?*Node {}
+
+    /// DeclarationSpecifiers
+    ///     <- StorageClassSpecifier DeclarationSpecifiers?
+    ///     / TypeSpecifier DeclarationSpecifiers?
+    ///     / TypeQualifier DeclarationSpecifiers?
+    ///     / FunctionSpecifier DeclarationSpecifiers?
+    ///     / AlignmentSpecifier DeclarationSpecifiers?
+    fn declarationSpecifiers(parser: *Parser) !*Node {}
+
+    /// StorageClassSpecifier
+    ///     <- Keyword_typedef / Keyword_extern / Keyword_static / Keyword_thread_local / Keyword_auto / Keyword_register
+    fn storageClassSpecifier(parser: *Parser) !*Node {}
+
+    /// TypeSpecifier
+    ///     <- Keyword_void / Keyword_char / Keyword_short / Keyword_int / Keyword_long / Keyword_float / Keyword_double
+    ///     / Keyword_signed / Keyword_unsigned / Keyword_bool / Keyword_complex / Keyword_imaginary /
+    ///     / Keyword_atomic LPAREN TypeName RPAREN
+    ///     / EnumSpecifier
+    ///     / RecordSpecifier
+    ///     / IDENTIFIER // typedef name
+    fn typeSpecifier(parser: *Parser) !*Node {}
+
+    /// TypeQualifier <- Keyword_const / Keyword_restrict / Keyword_volatile / Keyword_atomic
+    fn typeQualifier(parser: *Parser) !*Node {}
+
+    /// FunctionSpecifier <- Keyword_inline / Keyword_noreturn
+    fn functionSpecifier(parser: *Parser) !*Node {}
 
-/// Declaration
-///     <- DeclarationSpecifiers (Declarator (EQUAL Initializer)?)* SEMICOLON
-///     \ StaticAssertDeclaration
-fn parseDeclaration(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {}
-
-/// StaticAssertDeclaration <- Keyword_static_assert LPAREN ConstExpr COMMA STRINGLITERAL RPAREN SEMICOLON
-fn parseStaticAssertDeclaration(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {}
-
-/// DeclarationSpecifiers
-///     <- StorageClassSpecifier DeclarationSpecifiers?
-///     / TypeSpecifier DeclarationSpecifiers?
-///     / TypeQualifier DeclarationSpecifiers?
-///     / FunctionSpecifier DeclarationSpecifiers?
-///     / AlignmentSpecifier DeclarationSpecifiers?
-fn parseDeclarationSpecifiers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// StorageClassSpecifier
-///     <- Keyword_typedef / Keyword_extern / Keyword_static / Keyword_thread_local / Keyword_auto / Keyword_register
-fn parseStorageClassSpecifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// TypeSpecifier
-///     <- Keyword_void / Keyword_char / Keyword_short / Keyword_int / Keyword_long / Keyword_float / Keyword_double
-///     / Keyword_signed / Keyword_unsigned / Keyword_bool / Keyword_complex / Keyword_imaginary /
-///     / Keyword_atomic LPAREN TypeName RPAREN
-///     / EnumSpecifier
-///     / RecordSpecifier
-///     / IDENTIFIER // typedef name
-fn parseTypeSpecifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
+    /// AlignmentSpecifier <- Keyword_alignas LPAREN (TypeName / ConstExpr) RPAREN
+    fn alignmentSpecifier(parser: *Parser) !*Node {}
 
-/// TypeQualifier <- Keyword_const / Keyword_restrict / Keyword_volatile / Keyword_atomic
-fn parseTypeQualifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
+    /// EnumSpecifier <- Keyword_enum IDENTIFIER? (LBRACE EnumField RBRACE)?
+    fn enumSpecifier(parser: *Parser) !*Node {}
 
-/// FunctionSpecifier <- Keyword_inline / Keyword_noreturn
-fn parseFunctionSpecifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
+    /// EnumField <- IDENTIFIER (EQUAL ConstExpr)? (COMMA EnumField) COMMA?
+    fn enumField(parser: *Parser) !*Node {}
 
-/// AlignmentSpecifier <- Keyword_alignas LPAREN (TypeName / ConstExpr) RPAREN
-fn parseAlignmentSpecifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
+    /// RecordSpecifier <- (Keyword_struct / Keyword_union) IDENTIFIER? (LBRACE RecordField+ RBRACE)?
+    fn recordSpecifier(parser: *Parser) !*Node {}
+
+    /// RecordField
+    ///     <- SpecifierQualifer (RecordDeclarator (COMMA RecordDeclarator))? SEMICOLON
+    ///     \ StaticAssertDeclaration
+    fn recordField(parser: *Parser) !*Node {}
+
+    /// TypeName
+    ///     <- SpecifierQualifer AbstractDeclarator?
+    fn typeName(parser: *Parser) !*Node {}
+
+    /// SpecifierQualifer
+    ///     <- TypeSpecifier SpecifierQualifer?
+    ///     / TypeQualifier SpecifierQualifer?
+    fn specifierQualifer(parser: *Parser) !*Node {}
+
+    /// RecordDeclarator <- Declarator? (COLON ConstExpr)?
+    fn recordDeclarator(parser: *Parser) !*Node {}
+
+    /// Declarator <- Pointer? DirectDeclarator
+    fn declarator(parser: *Parser) !*Node {}
+
+    /// Pointer <- ASTERISK TypeQualifier* Pointer?
+    fn pointer(parser: *Parser) !*Node {}
+
+    /// DirectDeclarator
+    ///     <- IDENTIFIER
+    ///     / LPAREN Declarator RPAREN
+    ///     / DirectDeclarator LBRACKET (ASTERISK / BracketDeclarator)? RBRACKET
+    ///     / DirectDeclarator LPAREN (ParamDecl (COMMA ParamDecl)* (COMMA ELLIPSIS)?)? RPAREN
+    fn directDeclarator(parser: *Parser) !*Node {}
+
+    /// BracketDeclarator
+    ///     <- Keyword_static TypeQualifier* AssignmentExpr
+    ///     / TypeQualifier+ (ASTERISK / Keyword_static AssignmentExpr)
+    ///     / TypeQualifier+ AssignmentExpr?
+    ///     / AssignmentExpr
+    fn bracketDeclarator(parser: *Parser) !*Node {}
+
+    /// ParamDecl <- DeclarationSpecifiers (Declarator / AbstractDeclarator)
+    fn paramDecl(parser: *Parser) !*Node {}
+
+    /// AbstractDeclarator <- Pointer? DirectAbstractDeclarator?
+    fn abstractDeclarator(parser: *Parser) !*Node {}
+
+    /// DirectAbstractDeclarator
+    ///     <- IDENTIFIER
+    ///     / LPAREN DirectAbstractDeclarator RPAREN
+    ///     / DirectAbstractDeclarator? LBRACKET (ASTERISK / BracketDeclarator)? RBRACKET
+    ///     / DirectAbstractDeclarator? LPAREN (ParamDecl (COMMA ParamDecl)* (COMMA ELLIPSIS)?)? RPAREN
+    fn directAbstractDeclarator(parser: *Parser) !*Node {}
+
+    /// Expr <- AssignmentExpr (COMMA Expr)*
+    fn expr(parser: *Parser) !*Node {}
+
+    /// AssignmentExpr
+    ///     <- ConditionalExpr // TODO recursive?
+    ///     / UnaryExpr (EQUAL / ASTERISKEQUAL / SLASHEQUAL / PERCENTEQUAL / PLUSEQUAL / MINUSEQUA /
+    ///     / ANGLEBRACKETANGLEBRACKETLEFTEQUAL / ANGLEBRACKETANGLEBRACKETRIGHTEQUAL /
+    ///     / AMPERSANDEQUAL / CARETEQUAL / PIPEEQUAL) AssignmentExpr
+    fn assignmentExpr(parser: *Parser) !*Node {}
+
+    /// ConstExpr <- ConditionalExpr
+    /// ConditionalExpr <- LogicalOrExpr (QUESTIONMARK Expr COLON ConditionalExpr)?
+    fn conditionalExpr(parser: *Parser) !*Node {}
+
+    /// LogicalOrExpr <- LogicalAndExpr (PIPEPIPE LogicalOrExpr)*
+    fn logicalOrExpr(parser: *Parser) !*Node {}
+
+    /// LogicalAndExpr <- BinOrExpr (AMPERSANDAMPERSAND LogicalAndExpr)*
+    fn logicalAndExpr(parser: *Parser) !*Node {}
+
+    /// BinOrExpr <- BinXorExpr (PIPE BinOrExpr)*
+    fn binOrExpr(parser: *Parser) !*Node {}
+
+    /// BinXorExpr <- BinAndExpr (CARET BinXorExpr)*
+    fn binXorExpr(parser: *Parser) !*Node {}
+
+    /// BinAndExpr <- EqualityExpr (AMPERSAND BinAndExpr)*
+    fn binAndExpr(parser: *Parser) !*Node {}
+
+    /// EqualityExpr <- ComparisionExpr ((EQUALEQUAL / BANGEQUAL) EqualityExpr)*
+    fn equalityExpr(parser: *Parser) !*Node {}
+
+    /// ComparisionExpr <- ShiftExpr (ANGLEBRACKETLEFT / ANGLEBRACKETLEFTEQUAL /ANGLEBRACKETRIGHT / ANGLEBRACKETRIGHTEQUAL) ComparisionExpr)*
+    fn comparisionExpr(parser: *Parser) !*Node {}
+
+    /// ShiftExpr <- AdditiveExpr (ANGLEBRACKETANGLEBRACKETLEFT / ANGLEBRACKETANGLEBRACKETRIGHT) ShiftExpr)*
+    fn shiftExpr(parser: *Parser) !*Node {}
+
+    /// AdditiveExpr <- MultiplicativeExpr (PLUS / MINUS) AdditiveExpr)*
+    fn additiveExpr(parser: *Parser) !*Node {}
+
+    /// MultiplicativeExpr <- UnaryExpr (ASTERISK / SLASH / PERCENT) MultiplicativeExpr)*
+    fn multiplicativeExpr(parser: *Parser) !*Node {}
+
+    /// UnaryExpr
+    ///     <- LPAREN TypeName RPAREN UnaryExpr
+    ///     / Keyword_sizeof LAPERN TypeName RPAREN
+    ///     / Keyword_sizeof UnaryExpr
+    ///     / Keyword_alignof LAPERN TypeName RPAREN
+    ///     / (AMPERSAND / ASTERISK / PLUS / PLUSPLUS / MINUS / MINUSMINUS / TILDE / BANG) UnaryExpr
+    ///     / PrimaryExpr PostFixExpr*
+    fn unaryExpr(parser: *Parser) !*Node {}
+
+    /// PrimaryExpr
+    ///     <- IDENTIFIER
+    ///     / INTEGERLITERAL / FLITERAL / STRINGLITERAL / CHARLITERAL
+    ///     / LPAREN Expr RPAREN
+    ///     / Keyword_generic LPAREN AssignmentExpr (COMMA Generic)+ RPAREN
+    fn primaryExpr(parser: *Parser) !*Node {}
+
+    /// Generic
+    ///     <- TypeName COLON AssignmentExpr
+    ///     / Keyword_default COLON AssignmentExpr
+    fn generic(parser: *Parser) !*Node {}
+
+    /// PostFixExpr
+    ///     <- LPAREN TypeName RPAREN LBRACE Initializers RBRACE
+    ///     / LBRACKET Expr RBRACKET
+    ///     / LPAREN (AssignmentExpr (COMMA AssignmentExpr)*)? RPAREN
+    ///     / (PERIOD / ARROW) IDENTIFIER
+    ///     / (PLUSPLUS / MINUSMINUS)
+    fn postFixExpr(parser: *Parser) !*Node {}
+
+    /// Initializers <- ((Designator+ EQUAL)? Initializer COMMA)* (Designator+ EQUAL)? Initializer COMMA?
+    fn initializers(parser: *Parser) !*Node {}
+
+    /// Initializer
+    ///     <- LBRACE Initializers RBRACE
+    ///     / AssignmentExpr
+    fn initializer(parser: *Parser) !*Node {}
+
+    /// Designator
+    ///     <- LBRACKET Initializers RBRACKET
+    ///     / PERIOD IDENTIFIER
+    fn designator(parser: *Parser) !*Node {}
+
+    /// CompoundStmt <- LBRACE (Declaration / Stmt)* RBRACE
+    fn compoundStmt(parser: *Parser) !?*Node {}
+
+    /// Stmt
+    ///     <- CompoundStmt
+    ///     / Keyword_if LPAREN Expr RPAREN Stmt (Keyword_ELSE Stmt)?
+    ///     / Keyword_switch LPAREN Expr RPAREN Stmt
+    ///     / Keyword_while LPAREN Expr RPAREN Stmt
+    ///     / Keyword_do statement Keyword_while LPAREN Expr RPAREN SEMICOLON
+    ///     / Keyword_for LPAREN (Declaration / ExprStmt) ExprStmt Expr? RPAREN Stmt
+    ///     / Keyword_default COLON Stmt
+    ///     / Keyword_case ConstExpr COLON Stmt
+    ///     / Keyword_goto IDENTIFIER SEMICOLON
+    ///     / Keyword_continue SEMICOLON
+    ///     / Keyword_break SEMICOLON
+    ///     / Keyword_return Expr? SEMICOLON
+    ///     / IDENTIFIER COLON Stmt
+    ///     / ExprStmt
+    fn stmt(parser: *Parser) !?*Node {
+        if (parser.compoundStmt()) |node| return node;
+        // if (parser.eatToken(.Keyword_if)) |tok| {}
+        // if (parser.eatToken(.Keyword_switch)) |tok| {}
+        // if (parser.eatToken(.Keyword_while)) |tok| {}
+        // if (parser.eatToken(.Keyword_do)) |tok| {}
+        // if (parser.eatToken(.Keyword_for)) |tok| {}
+        // if (parser.eatToken(.Keyword_default)) |tok| {}
+        // if (parser.eatToken(.Keyword_case)) |tok| {}
+        if (parser.eatToken(.Keyword_goto)) |tok| {
+            const node = try parser.arena.create(Node.JumpStmt);
+            node.* = .{
+                .ltoken = tok,
+                .kind = .Goto,
+                .semicolon = parser.expectToken(.Semicolon),
+            };
+            return &node.base;
+        }
+        if (parser.eatToken(.Keyword_continue)) |tok| {
+            const node = try parser.arena.create(Node.JumpStmt);
+            node.* = .{
+                .ltoken = tok,
+                .kind = .Continue,
+                .semicolon = parser.expectToken(.Semicolon),
+            };
+            return &node.base;
+        }
+        if (parser.eatToken(.Keyword_break)) |tok| {
+            const node = try parser.arena.create(Node.JumpStmt);
+            node.* = .{
+                .ltoken = tok,
+                .kind = .Break,
+                .semicolon = parser.expectToken(.Semicolon),
+            };
+            return &node.base;
+        }
+        if (parser.eatToken(.Keyword_return)) |tok| {
+            const node = try parser.arena.create(Node.JumpStmt);
+            node.* = .{
+                .ltoken = tok,
+                .kind = .{ .Return = try parser.expr() },
+                .semicolon = parser.expectToken(.Semicolon),
+            };
+            return &node.base;
+        }
+        if (parser.eatToken(.Identifier)) |tok| {
+            if (parser.eatToken(.Colon)) |col| {
+                const node = try parser.arena.create(Node.Label);
+                node.* = .{
+                    .identifier = tok,
+                    .semicolon = parser.expectToken(.Colon),
+                };
+                return &node.base;
+            }
+            putBackToken(tok);
+        }
+        if (parser.exprStmt()) |node| return node;
+        return null;
+    }
 
-/// EnumSpecifier <- Keyword_enum IDENTIFIER? (LBRACE EnumField RBRACE)?
-fn parseEnumSpecifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
+    /// ExprStmt <- Expr? SEMICOLON
+    fn exprStmt(parser: *Parser) !*Node {
+        const node = try parser.arena.create(Node.ExprStmt);
+        node.* = .{
+            .expr = try parser.expr(),
+            .semicolon = parser.expectToken(.Semicolon),
+        };
+        return &node.base;
+    }
 
-/// EnumField <- IDENTIFIER (EQUAL ConstExpr)? (COMMA EnumField) COMMA?
-fn parseEnumField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
+    fn eatToken(parser: *Parser, id: Token.Id) ?TokenIndex {
+        while (true) {
+            const next_tok = parser.it.next() orelse return null;
+            if (next_tok.id != .LineComment and next_tok.id != .MultiLineComment) {
+                if (next_tok.id == id) {
+                    return parser.it.index;
+                }
+                parser.it.prev();
+                return null;
+            }
+        }
+    }
 
-/// RecordSpecifier <- (Keyword_struct / Keyword_union) IDENTIFIER? (LBRACE RecordField+ RBRACE)?
-fn parseRecordSpecifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// RecordField
-///     <- SpecifierQualifer (RecordDeclarator (COMMA RecordDeclarator))? SEMICOLON
-///     \ StaticAssertDeclaration
-fn parseRecordField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// TypeName
-///     <- SpecifierQualifer AbstractDeclarator?
-fn parseTypeName(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// SpecifierQualifer
-///     <- TypeSpecifier SpecifierQualifer?
-///     / TypeQualifier SpecifierQualifer?
-fn parseSpecifierQualifer(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// RecordDeclarator <- Declarator? (COLON ConstExpr)?
-fn parseRecordDeclarator(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// Declarator <- Pointer? DirectDeclarator
-fn parseDeclarator(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// Pointer <- ASTERISK TypeQualifier* Pointer?
-fn parsePointer(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// DirectDeclarator
-///     <- IDENTIFIER
-///     / LPAREN Declarator RPAREN
-///     / DirectDeclarator LBRACKET (ASTERISK / BracketDeclarator)? RBRACKET
-///     / DirectDeclarator LPAREN (ParamDecl (COMMA ParamDecl)* (COMMA ELLIPSIS)?)? RPAREN
-fn parseDirectDeclarator(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// BracketDeclarator
-///     <- Keyword_static TypeQualifier* AssignmentExpr
-///     / TypeQualifier+ (ASTERISK / Keyword_static AssignmentExpr)
-///     / TypeQualifier+ AssignmentExpr?
-///     / AssignmentExpr
-fn parseBracketDeclarator(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// ParamDecl <- DeclarationSpecifiers (Declarator / AbstractDeclarator)
-fn parseParamDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// AbstractDeclarator <- Pointer? DirectAbstractDeclarator?
-fn parseAbstractDeclarator(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// DirectAbstractDeclarator
-///     <- IDENTIFIER
-///     / LPAREN DirectAbstractDeclarator RPAREN
-///     / DirectAbstractDeclarator? LBRACKET (ASTERISK / BracketDeclarator)? RBRACKET
-///     / DirectAbstractDeclarator? LPAREN (ParamDecl (COMMA ParamDecl)* (COMMA ELLIPSIS)?)? RPAREN
-fn parseDirectAbstractDeclarator(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// Expr <- AssignmentExpr (COMMA Expr)*
-fn parseExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// AssignmentExpr
-///     <- ConditionalExpr
-///     / UnaryExpr (EQUAL / ASTERISKEQUAL / SLASHEQUAL / PERCENTEQUAL / PLUSEQUAL / MINUSEQUA /
-///     / ANGLEBRACKETANGLEBRACKETLEFTEQUAL / ANGLEBRACKETANGLEBRACKETRIGHTEQUAL /
-///     / AMPERSANDEQUAL / CARETEQUAL / PIPEEQUAL) AssignmentExpr
-fn parseAssignmentExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// ConstExpr <- ConditionalExpr
-/// ConditionalExpr <- LogicalOrExpr (QUESTIONMARK Expr COLON ConditionalExpr)?
-fn parseConditionalExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// LogicalOrExpr <- LogicalAndExpr (PIPEPIPE LogicalOrExpr)*
-fn parseLogicalOrExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// LogicalAndExpr <- BinOrExpr (AMPERSANDAMPERSAND LogicalAndExpr)*
-fn parseLogicalAndExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// BinOrExpr <- BinXorExpr (PIPE BinOrExpr)*
-fn parseBinOrExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// BinXorExpr <- BinAndExpr (CARET BinXorExpr)*
-fn parseBinXorExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// BinAndExpr <- EqualityExpr (AMPERSAND BinAndExpr)*
-fn parseBinAndExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// EqualityExpr <- ComparisionExpr ((EQUALEQUAL / BANGEQUAL) EqualityExpr)*
-fn parseEqualityExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// ComparisionExpr <- ShiftExpr (ANGLEBRACKETLEFT / ANGLEBRACKETLEFTEQUAL /ANGLEBRACKETRIGHT / ANGLEBRACKETRIGHTEQUAL) ComparisionExpr)*
-fn parseComparisionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// ShiftExpr <- AdditiveExpr (ANGLEBRACKETANGLEBRACKETLEFT / ANGLEBRACKETANGLEBRACKETRIGHT) ShiftExpr)*
-fn parseShiftExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// AdditiveExpr <- MultiplicativeExpr (PLUS / MINUS) AdditiveExpr)*
-fn parseAdditiveExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// MultiplicativeExpr <- UnaryExpr (ASTERISK / SLASH / PERCENT) MultiplicativeExpr)*
-fn parseMultiplicativeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// UnaryExpr
-///     <- LPAREN TypeName RPAREN UnaryExpr
-///     / Keyword_sizeof LAPERN TypeName RPAREN
-///     / Keyword_sizeof UnaryExpr
-///     / Keyword_alignof LAPERN TypeName RPAREN
-///     / (AMPERSAND / ASTERISK / PLUS / PLUSPLUS / MINUS / MINUSMINUS / TILDE / BANG) UnaryExpr
-///     / PrimaryExpr PostFixExpr*
-fn parseUnaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// PrimaryExpr
-///     <- IDENTIFIER
-///     / INTEGERLITERAL / FLITERAL / STRINGLITERAL / CHARLITERAL
-///     / LPAREN Expr RPAREN
-///     / Keyword_generic LPAREN AssignmentExpr (COMMA Generic)+ RPAREN
-fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// Generic
-///     <- TypeName COLON AssignmentExpr
-///     / Keyword_default COLON AssignmentExpr
-fn parseGeneric(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// PostFixExpr
-///     <- LPAREN TypeName RPAREN LBRACE Initializers RBRACE
-///     / LBRACKET Expr RBRACKET
-///     / LPAREN (AssignmentExpr (COMMA AssignmentExpr)*)? RPAREN
-///     / (PERIOD / ARROW) IDENTIFIER
-///     / (PLUSPLUS / MINUSMINUS)
-fn parsePostFixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// Initializers <- ((Designator+ EQUAL)? Initializer COMMA)* (Designator+ EQUAL)? Initializer COMMA?
-fn parseInitializers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// Initializer
-///     <- LBRACE Initializers RBRACE
-///     / AssignmentExpr
-fn parseInitializer(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// Designator
-///     <- LBRACKET Initializers RBRACKET
-///     / PERIOD IDENTIFIER
-fn parseDesignator(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// CompoundStmt <- LBRACE (Declaration / Stmt)* RBRACE
-fn parseCompoundStmt(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// Stmt
-///     <- CompoundStmt
-///     / Keyword_if LPAREN Expr RPAREN Stmt (Keyword_ELSE Stmt)?
-///     / Keyword_switch LPAREN Expr RPAREN Stmt
-///     / Keyword_while LPAREN Expr RPAREN Stmt
-///     / Keyword_do statement Keyword_while LPAREN Expr RPAREN SEMICOLON
-///     / Keyword_for LPAREN (Declaration / ExprStmt) ExprStmt Expr? RPAREN Stmt
-///     / Keyword_default COLON Stmt
-///     / Keyword_case ConstExpr COLON Stmt
-///     / Keyword_goto IDENTIFIER SEMICOLON
-///     / Keyword_continue SEMICOLON
-///     / Keyword_break SEMICOLON
-///     / Keyword_return Expr? SEMICOLON
-///     / IDENTIFIER COLON Stmt
-///     / ExprStmt
-fn parseStmt(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
-
-/// ExprStmt <- Expr? SEMICOLON
-fn parseExprStmt(arena: *Allocator, it: *TokenIterator, tree: *Tree) !*Node {}
+    fn expectToken(parser: *Parser, id: Token.Id) Error!TokenIndex {
+        while (true) {
+            const next_tok = parser.it.next() orelse return error.ParseError;
+            if (next_tok.id != .LineComment and next_tok.id != .MultiLineComment) {
+                if (next_tok.id != id) {
+                    try tree.errors.push(.{
+                        .ExpectedToken = .{ .token = parser.it.index, .expected_id = id },
+                    });
+                    return error.ParseError;
+                }
+                return parser.it.index;
+            }
+        }
+    }
+
+    fn putBackToken(it: *TokenIterator, putting_back: TokenIndex) void {
+        while (true) {
+            const prev_tok = it.prev() orelse return;
+            if (next_tok.id == .LineComment or next_tok.id == .MultiLineComment) continue;
+            assert(it.list.at(putting_back) == prev_tok);
+            return;
+        }
+    }
+};