Commit 5feeff7123

Vexu <git@vexu.eu>
2020-01-05 19:19:17
std-c improve error reporting and decl parsing
1 parent 795a503
Changed files (2)
lib/std/c/ast.zig
@@ -10,12 +10,11 @@ pub const Tree = struct {
     sources: SourceList,
     root_node: *Node.Root,
     arena_allocator: std.heap.ArenaAllocator,
-    errors: ErrorList,
-    warnings: ?ErrorList,
+    msgs: MsgList,
 
     pub const SourceList = SegmentedList(Source, 4);
     pub const TokenList = Source.TokenList;
-    pub const ErrorList = SegmentedList(Error, 0);
+    pub const MsgList = SegmentedList(Msg, 0);
 
     pub fn deinit(self: *Tree) void {
         // Here we copy the arena allocator into stack memory, because
@@ -26,6 +25,15 @@ pub const Tree = struct {
     }
 };
 
+pub const Msg = struct {
+    kind: enum {
+        Error,
+        Warning,
+        Note,
+    },
+    inner: Error,
+};
+
 pub const Error = union(enum) {
     InvalidToken: SingleTokenError("invalid token '{}'"),
     ExpectedToken: ExpectedToken,
@@ -268,7 +276,7 @@ pub const Node = struct {
 
     pub const FnDef = struct {
         base: Node = Node{ .id = .FnDef },
-        decl_spec: *DeclSpec,
+        decl_spec: DeclSpec,
         declarator: *Node,
         old_decls: OldDeclList,
         body: *CompoundStmt,
lib/std/c/parse.zig
@@ -70,11 +70,21 @@ const Parser = struct {
     arena: *Allocator,
     it: *TokenIterator,
     tree: *Tree,
-    typedefs: std.StringHashMap(void),
 
-    fn isTypedef(parser: *Parser, tok: TokenIndex) bool {
-        const token = parser.it.list.at(tok);
-        return parser.typedefs.contains(token.slice());
+    /// only used for scopes
+    arena_allocator: std.heap.ArenaAllocator,
+    // scopes: std.SegmentedLists(Scope),
+    warnings: bool = true,
+
+    // const Scope = struct {
+    //     types:
+    //     syms:
+    // };
+
+    fn getTypeDef(parser: *Parser, tok: TokenIndex) bool {
+        return false; // TODO
+        // const token = parser.it.list.at(tok);
+        // return parser.typedefs.contains(token.slice());
     }
 
     /// Root <- ExternalDeclaration* eof
@@ -84,7 +94,7 @@ const Parser = struct {
             .decls = Node.Root.DeclList.init(parser.arena),
             .eof = undefined,
         };
-        while (parser.externalDeclarations() catch |err| switch (err) {
+        while (parser.externalDeclarations() catch |e| switch (e) {
             error.OutOfMemory => return error.OutOfMemory,
             error.ParseError => return node,
         }) |decl| {
@@ -95,70 +105,99 @@ const Parser = struct {
     }
 
     /// ExternalDeclaration
-    ///     <- DeclSpec Declarator Declaration* CompoundStmt
+    ///     <- DeclSpec Declarator OldStyleDecl* CompoundStmt
     ///     / Declaration
+    /// OldStyleDecl <- DeclSpec Declarator (COMMA Declarator)* SEMICOLON
     fn externalDeclarations(parser: *Parser) !?*Node {
+        return parser.declarationExtra(false);
+    }
+
+    /// Declaration
+    ///     <- DeclSpec DeclInit SEMICOLON
+    ///     / StaticAssert
+    /// DeclInit <- Declarator (EQUAL Initializer)? (COMMA Declarator (EQUAL Initializer)?)*
+    fn declaration(parser: *Parser) !?*Node {
+        return parser.declarationExtra(true);
+    }
+
+    fn declarationExtra(parser: *Parser, local: bool) !?*Node {
         if (try parser.staticAssert()) |decl| return decl;
-        const ds = try parser.declSpec();
-        const dr = (try parser.declarator());
-        if (dr == null)
-            try parser.warning(.{
-                .ExpectedDeclarator = .{ .token = parser.it.index },
-            });
+        var ds = Node.DeclSpec{};
+        const got_ds = try parser.declSpec(&ds);
+        if (local and !got_ds) {
+            // not a declaration
+            return null;
+        }
+        var dr = try parser.declarator();
         // TODO disallow auto and register
         const next_tok = parser.it.peek().?;
+        if (next_tok.id == .Eof and !got_ds and dr == null) {
+            return null;
+        }
         switch (next_tok.id) {
             .Semicolon,
             .Equal,
             .Comma,
             .Eof,
-            => return parser.declarationExtra(ds, dr, false),
-            else => {},
-        }
-        var old_decls = Node.FnDef.OldDeclList.init(parser.arena);
-        while (try parser.declaration()) |decl| {
-            // validate declaration
-            try old_decls.push(decl);
-        }
-        const body = try parser.expect(compoundStmt, .{
-            .ExpectedFnBody = .{ .token = parser.it.index },
-        });
-
-        const node = try parser.arena.create(Node.FnDef);
-        node.* = .{
-            .decl_spec = ds,
-            .declarator = dr orelse return null,
-            .old_decls = old_decls,
-            .body = @fieldParentPtr(Node.CompoundStmt, "base", body),
-        };
-        return &node.base;
-    }
+            => {
+                while (dr != null) {
+                    if (parser.eatToken(.Equal)) |tok| {
+                        // TODO typedef
+                        // dr.?.init = try parser.expect(initializer, .{
+                        //     .ExpectedInitializer = .{ .token = parser.it.index },
+                        // });
+                    }
+                    if (parser.eatToken(.Comma) != null) break;
+                    dr = (try parser.declarator()) orelse return parser.err(.{
+                        .ExpectedDeclarator = .{ .token = parser.it.index },
+                    });
+                    // .push(dr);
+                }
+                const semicolon = try parser.expectToken(.Semicolon);
 
-    /// Declaration
-    ///     <- DeclSpec (Declarator (EQUAL Initializer)? COMMA)* SEMICOLON
-    ///     / StaticAssert
-    fn declaration(parser: *Parser) !?*Node {
-        if (try parser.staticAssert()) |decl| return decl;
-        const ds = try parser.declSpec();
-        const dr = (try parser.declarator());
-        if (dr == null)
-            try parser.warning(.{
-                .ExpectedDeclarator = .{ .token = parser.it.index },
-            });
-        // TODO disallow threadlocal without static or extern
-        return parser.declarationExtra(ds, dr, true);
-    }
+                // TODO VarDecl, TypeDecl, TypeDef
+                return null;
+            },
+            else => {
+                if (dr == null)
+                    return parser.err(.{
+                        .ExpectedDeclarator = .{ .token = parser.it.index },
+                    });
+                var old_decls = Node.FnDef.OldDeclList.init(parser.arena);
+                while (true) {
+                    var old_ds = Node.DeclSpec{};
+                    if (!(try parser.declSpec(&old_ds))) {
+                        // not old decl
+                        break;
+                    }
+                    var old_dr = (try parser.declarator());
+                    // if (old_dr == null)
+                    //     try parser.err(.{
+                    //         .NoParamName = .{ .token = parser.it.index },
+                    //     });
+                    // try old_decls.push(decl);
+                }
+                const body = (try parser.compoundStmt()) orelse return parser.err(.{
+                    .ExpectedFnBody = .{ .token = parser.it.index },
+                });
 
-    fn declarationExtra(parser: *Parser, ds: *Node.DeclSpec, dr: ?*Node, local: bool) !?*Node {
+                const node = try parser.arena.create(Node.FnDef);
+                node.* = .{
+                    .decl_spec = ds,
+                    .declarator = dr orelse return null,
+                    .old_decls = old_decls,
+                    .body = @fieldParentPtr(Node.CompoundStmt, "base", body),
+                };
+                return &node.base;
+            },
+        }
     }
 
     /// StaticAssert <- Keyword_static_assert LPAREN ConstExpr COMMA STRINGLITERAL RPAREN SEMICOLON
     fn staticAssert(parser: *Parser) !?*Node {
         const tok = parser.eatToken(.Keyword_static_assert) orelse return null;
         _ = try parser.expectToken(.LParen);
-        const const_expr = try parser.expect(constExpr, .{
-            .ExpectedExpr = .{ .token = parser.it.index },
-        });
+        const const_expr = try parser.constExpr();
         _ = try parser.expectToken(.Comma);
         const str = try parser.expectToken(.StringLiteral);
         _ = try parser.expectToken(.RParen);
@@ -173,11 +212,13 @@ const Parser = struct {
     }
 
     /// DeclSpec <- (StorageClassSpec / TypeSpec / FnSpec / AlignSpec)*
-    fn declSpec(parser: *Parser) !*Node.DeclSpec {
-        const ds = try parser.arena.create(Node.DeclSpec);
-        ds.* = .{};
-        while ((try parser.storageClassSpec(ds)) or (try parser.typeSpec(&ds.type_spec)) or (try parser.fnSpec(ds)) or (try parser.alignSpec(ds))) {}
-        return ds;
+    /// returns true if any tokens were consumed
+    fn declSpec(parser: *Parser, ds: *Node.DeclSpec) !bool {
+        var got = false;
+        while ((try parser.storageClassSpec(ds)) or (try parser.typeSpec(&ds.type_spec)) or (try parser.fnSpec(ds)) or (try parser.alignSpec(ds))) {
+            got = true;
+        }
+        return got;
     }
 
     /// StorageClassSpec
@@ -213,7 +254,7 @@ const Parser = struct {
             } else return false;
             return true;
         }
-        try parser.warning(.{
+        try parser.warn(.{
             .DuplicateSpecifier = .{ .token = parser.it.index },
         });
         return true;
@@ -420,7 +461,7 @@ const Parser = struct {
                 if (type_spec.spec != .None)
                     break :blk;
                 _ = try parser.expectToken(.LParen);
-                const name = try parser.expect(typeName, .{
+                const name = (try parser.typeName()) orelse return parser.err(.{
                     .ExpectedTypeName = .{ .token = parser.it.index },
                 });
                 type_spec.spec.Atomic = .{
@@ -440,7 +481,7 @@ const Parser = struct {
                 @panic("TODO record type");
                 // return true;
             } else if (parser.eatToken(.Identifier)) |tok| {
-                if (!parser.isTypedef(tok)) {
+                if (!parser.getTypeDef(tok)) {
                     parser.putBackToken(tok);
                     return false;
                 }
@@ -450,13 +491,12 @@ const Parser = struct {
                 return true;
             }
         }
-        try parser.tree.errors.push(.{
+        return parser.err(.{
             .InvalidTypeSpecifier = .{
                 .token = parser.it.index,
                 .type_spec = type_spec,
             },
         });
-        return error.ParseError;
     }
 
     /// TypeQual <- Keyword_const / Keyword_restrict / Keyword_volatile / Keyword_atomic
@@ -481,7 +521,7 @@ const Parser = struct {
             } else return false;
             return true;
         }
-        try parser.warning(.{
+        try parser.warn(.{
             .DuplicateQualifier = .{ .token = parser.it.index },
         });
         return true;
@@ -501,7 +541,7 @@ const Parser = struct {
             } else return false;
             return true;
         }
-        try parser.warning(.{
+        try parser.warn(.{
             .DuplicateSpecifier = .{ .token = parser.it.index },
         });
         return true;
@@ -511,11 +551,9 @@ const Parser = struct {
     fn alignSpec(parser: *Parser, ds: *Node.DeclSpec) !bool {
         if (parser.eatToken(.Keyword_alignas)) |tok| {
             _ = try parser.expectToken(.LParen);
-            const node = (try parser.typeName()) orelse (try parser.expect(constExpr, .{
-                .ExpectedExpr = .{ .token = parser.it.index },
-            }));
+            const node = (try parser.typeName()) orelse (try parser.constExpr());
             if (ds.align_spec != null) {
-                try parser.warning(.{
+                try parser.warn(.{
                     .DuplicateSpecifier = .{ .token = parser.it.index },
                 });
             }
@@ -594,7 +632,16 @@ const Parser = struct {
     fn assignmentExpr(parser: *Parser) !*Node {}
 
     /// ConstExpr <- ConditionalExpr
-    const constExpr = conditionalExpr;
+    fn constExpr(parser: *Parser) Error!*Node {
+        const start = parser.it.index;
+        const expression = try parser.conditionalExpr();
+        // TODO
+        // if (expression == nullor expression.?.value == null)
+        //     return parser.err(.{
+        //         .ConsExpr = start,
+        //     });
+        return expression.?;
+    }
 
     /// ConditionalExpr <- LogicalOrExpr (QUESTIONMARK Expr COLON ConditionalExpr)?
     fn conditionalExpr(parser: *Parser) !*Node {}
@@ -671,7 +718,7 @@ const Parser = struct {
     ///     / PERIOD IDENTIFIER
     fn designator(parser: *Parser) !*Node {}
 
-    /// CompoundStmt <- LBRACE (Stmt / Declaration)* RBRACE
+    /// CompoundStmt <- LBRACE (Declaration / Stmt)* RBRACE
     fn compoundStmt(parser: *Parser) Error!?*Node {
         const lbrace = parser.eatToken(.LBrace) orelse return null;
         const body_node = try parser.arena.create(Node.CompoundStmt);
@@ -680,7 +727,7 @@ const Parser = struct {
             .statements = Node.CompoundStmt.StmtList.init(parser.arena),
             .rbrace = undefined,
         };
-        while ((try parser.stmt()) orelse (try parser.declaration())) |node|
+        while ((try parser.declaration()) orelse (try parser.stmt())) |node|
             try body_node.statements.push(node);
         body_node.rbrace = try parser.expectToken(.RBrace);
         return &body_node.base;
@@ -708,7 +755,7 @@ const Parser = struct {
             _ = try parser.expectToken(.LParen);
             node.* = .{
                 .@"if" = tok,
-                .cond = try parser.expect(expr, .{
+                .cond = (try parser.expr()) orelse return parser.err(.{
                     .ExpectedExpr = .{ .token = parser.it.index },
                 }),
                 .@"else" = null,
@@ -717,7 +764,7 @@ const Parser = struct {
             if (parser.eatToken(.Keyword_else)) |else_tok| {
                 node.@"else" = .{
                     .tok = else_tok,
-                    .stmt = try parser.expect(stmt, .{
+                    .stmt = (try parser.stmt()) orelse return parser.err(.{
                         .ExpectedStmt = .{ .token = parser.it.index },
                     }),
                 };
@@ -797,7 +844,7 @@ const Parser = struct {
 
     fn eatToken(parser: *Parser, id: @TagType(Token.Id)) ?TokenIndex {
         while (true) {
-            switch (parser.it.next() orelse return null) {
+            switch ((parser.it.next() orelse return null).id) {
                 .LineComment, .MultiLineComment, .Nl => continue,
                 else => |next_id| if (next_id == id) {
                     return parser.it.index;
@@ -811,7 +858,7 @@ const Parser = struct {
 
     fn expectToken(parser: *Parser, id: @TagType(Token.Id)) Error!TokenIndex {
         while (true) {
-            switch (parser.it.next() orelse return null) {
+            switch ((parser.it.next() orelse return error.ParseError).id) {
                 .LineComment, .MultiLineComment, .Nl => continue,
                 else => |next_id| if (next_id != id) {
                     return parser.err(.{
@@ -826,9 +873,10 @@ const Parser = struct {
 
     fn putBackToken(parser: *Parser, putting_back: TokenIndex) void {
         while (true) {
-            switch (parser.it.next() orelse return null) {
+            const prev_tok = parser.it.next() orelse return;
+            switch (prev_tok.id) {
                 .LineComment, .MultiLineComment, .Nl => continue,
-                else => |next_id| {
+                else => {
                     assert(parser.it.list.at(putting_back) == prev_tok);
                     return;
                 },
@@ -836,23 +884,26 @@ const Parser = struct {
         }
     }
 
-    fn expect(
-        parser: *Parser,
-        parseFn: fn (*Parser) Error!?*Node,
-        err: ast.Error, // if parsing fails
-    ) Error!*Node {
-        return (try parseFn(parser)) orelse {
-            try parser.tree.errors.push(err);
-            return error.ParseError;
-        };
+    fn err(parser: *Parser, msg: ast.Error) Error {
+        try parser.tree.msgs.push(.{
+            .kind = .Error,
+            .inner = msg,
+        });
+        return error.ParseError;
     }
 
-    fn warning(parser: *Parser, err: ast.Error) Error!void {
-        if (parser.tree.warnings) |*w| {
-            try w.push(err);
-            return;
-        }
-        try parser.tree.errors.push(err);
-        return error.ParseError;
+    fn warn(parser: *Parser, msg: ast.Error) Error!void {
+        try parser.tree.msgs.push(.{
+            .kind = if (parser.warnings) .Warning else .Error,
+            .inner = msg,
+        });
+        if (!parser.warnings) return error.ParseError;
+    }
+
+    fn note(parser: *Parser, msg: ast.Error) Error!void {
+        try parser.tree.msgs.push(.{
+            .kind = .Note,
+            .inner = msg,
+        });
     }
 };