Commit dbc0457068

Vexu <git@vexu.eu>
2020-01-07 19:15:57
std-c declaration parsing
1 parent 4184d4c
Changed files (2)
lib/std/c/ast.zig
@@ -51,9 +51,14 @@ pub const Error = union(enum) {
     ExpectedEnumField: SingleTokenError("expected enum field, found '{}'"),
     ExpectedType: SingleTokenError("expected enum field, found '{}'"),
     InvalidTypeSpecifier: InvalidTypeSpecifier,
+    InvalidStorageClass: SingleTokenError("invalid storage class, found '{}'"),
+    InvalidDeclarator: SimpleError("invalid declarator"),
     DuplicateQualifier: SingleTokenError("duplicate type qualifier '{}'"),
     DuplicateSpecifier: SingleTokenError("duplicate declaration specifier '{}'"),
     MustUseKwToRefer: MustUseKwToRefer,
+    FnSpecOnNonFn: SingleTokenError("function specifier '{}' on non function"),
+    NothingDeclared: SimpleError("declaration doesn't declare anything"),
+    QualifierIgnored: SingleTokenError("qualifier '{}' ignored"),
 
     pub fn render(self: *const Error, tree: *Tree, stream: var) !void {
         switch (self.*) {
@@ -68,9 +73,14 @@ pub const Error = union(enum) {
             .ExpectedEnumField => |*x| return x.render(tree, stream),
             .ExpectedType => |*x| return x.render(tree, stream),
             .InvalidTypeSpecifier => |*x| return x.render(tree, stream),
+            .InvalidStorageClass => |*x| return x.render(tree, stream),
+            .InvalidDeclarator => |*x| return x.render(tree, stream),
             .DuplicateQualifier => |*x| return x.render(tree, stream),
             .DuplicateSpecifier => |*x| return x.render(tree, stream),
             .MustUseKwToRefer => |*x| return x.render(tree, stream),
+            .FnSpecOnNonFn => |*x| return x.render(tree, stream),
+            .NothingDeclared => |*x| return x.render(tree, stream),
+            .QualifierIgnored => |*x| return x.render(tree, stream),
         }
     }
 
@@ -87,9 +97,14 @@ pub const Error = union(enum) {
             .ExpectedEnumField => |x| return x.token,
             .ExpectedType => |*x| return x.token,
             .InvalidTypeSpecifier => |x| return x.token,
+            .InvalidStorageClass => |x| return x.token,
+            .InvalidDeclarator => |x| return x.token,
             .DuplicateQualifier => |x| return x.token,
             .DuplicateSpecifier => |x| return x.token,
             .MustUseKwToRefer => |*x| return x.name,
+            .FnSpecOnNonFn => |*x| return x.name,
+            .NothingDeclared => |*x| return x.name,
+            .QualifierIgnored => |*x| return x.name,
         }
     }
 
@@ -125,7 +140,7 @@ pub const Error = union(enum) {
         name: TokenIndex,
 
         pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {
-            return stream.print("must use '{}' tag to refer to type '{}'", .{tree.slice(kw), tree.slice(name)});
+            return stream.print("must use '{}' tag to refer to type '{}'", .{ tree.slice(kw), tree.slice(name) });
         }
     };
 
@@ -139,6 +154,18 @@ pub const Error = union(enum) {
             }
         };
     }
+
+    fn SimpleError(comptime msg: []const u8) type {
+        return struct {
+            const ThisError = @This();
+
+            token: TokenIndex,
+
+            pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {
+                return stream.write(msg);
+            }
+        };
+    }
 };
 
 pub const Type = struct {
@@ -194,9 +221,11 @@ pub const Node = struct {
         CompoundStmt,
         IfStmt,
         StaticAssert,
-        Fn,
+        Declarator,
+        Pointer,
+        FnDecl,
         Typedef,
-        Var,
+        VarDecl,
     };
 
     pub const Root = struct {
@@ -457,7 +486,7 @@ pub const Node = struct {
 
     pub const Declarator = struct {
         base: Node = Node{ .id = .Declarator },
-        pointer: *Pointer,
+        pointer: ?*Pointer,
         prefix: union(enum) {
             None,
             Identifer: TokenIndex,
@@ -482,7 +511,7 @@ pub const Node = struct {
     };
 
     pub const Array = struct {
-        rbracket: TokenIndex,
+        lbracket: TokenIndex,
         inner: union(enum) {
             Inferred,
             Unspecified: TokenIndex,
@@ -490,7 +519,7 @@ pub const Node = struct {
                 asterisk: ?TokenIndex,
                 static: ?TokenIndex,
                 qual: TypeQual,
-                expr: *Expr,
+                // expr: *Expr,
             },
         },
         rbracket: TokenIndex,
@@ -514,10 +543,10 @@ pub const Node = struct {
         },
     };
 
-    pub const Fn = struct {
-        base: Node = Node{ .id = .Fn },
+    pub const FnDecl = struct {
+        base: Node = Node{ .id = .FnDecl },
         decl_spec: DeclSpec,
-        declarator: *Node,
+        declarator: *Declarator,
         old_decls: OldDeclList,
         body: ?*CompoundStmt,
 
@@ -528,20 +557,23 @@ pub const Node = struct {
         base: Node = Node{ .id = .Typedef },
         decl_spec: DeclSpec,
         declarators: DeclaratorList,
+        semicolon: TokenIndex,
 
         pub const DeclaratorList = Root.DeclList;
     };
 
-    pub const Var = struct {
-        base: Node = Node{ .id = .Var },
+    pub const VarDecl = struct {
+        base: Node = Node{ .id = .VarDecl },
         decl_spec: DeclSpec,
         initializers: Initializers,
+        semicolon: TokenIndex,
 
-        pub const Initializers = std.SegmentedList(*Initialized, 2);
+        pub const Initializers = Root.DeclList;
     };
 
     pub const Initialized = struct {
-        declarator: *Node,
+        base: Node = Node{ .id = Initialized },
+        declarator: *Declarator,
         eq: TokenIndex,
         init: Initializer,
     };
lib/std/c/parse.zig
@@ -105,6 +105,10 @@ const Parser = struct {
         return null;
     }
 
+    fn declareSymbol(parser: *Parser, decl_spec: *Node.DeclSpec, dr: *Node.Declarator) Error!void {
+        return; // TODO
+    }
+
     /// Root <- ExternalDeclaration* eof
     fn root(parser: *Parser) Allocator.Error!*Node.Root {
         const node = try parser.arena.create(Node.Root);
@@ -140,77 +144,127 @@ const Parser = struct {
 
     fn declarationExtra(parser: *Parser, local: bool) !?*Node {
         if (try parser.staticAssert()) |decl| return decl;
+        const begin = parser.it.index + 1;
         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,
-            => {
-                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(.{
+        switch (ds.storage_class) {
+            .Auto, .Register => |tok| return parser.err(.{
+                .InvalidStorageClass = .{ .token = tok },
+            }),
+            .Typedef => {
+                const node = try parser.arena.create(Node.Typedef);
+                node.* = .{
+                    .decl_spec = ds,
+                    .declarators = Node.Typedef.DeclaratorList.init(parser.arena),
+                    .semicolon = undefined,
+                };
+                while (true) {
+                    const dr = @fieldParentPtr(Node.Declarator, "base", (try parser.declarator(.Must)) orelse return parser.err(.{
                         .ExpectedDeclarator = .{ .token = parser.it.index },
-                    });
-                    // .push(dr);
+                    }));
+                    try parser.declareSymbol(&ds, dr);
+                    try node.declarators.push(&dr.base);
+                    if (parser.eatToken(.Comma)) |_| {} else break;
                 }
-                const semicolon = try parser.expectToken(.Semicolon);
-
-                // TODO VarDecl, TypeDecl, TypeDef
-                return null;
+                return &node.base;
             },
-            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(.{
+            else => {},
+        }
+        var first_dr = try parser.declarator(.Must);
+        if (first_dr != null and declaratorIsFunction(first_dr.?)) {
+            const dr = @fieldParentPtr(Node.Declarator, "base", first_dr.?);
+            try parser.declareSymbol(&ds, dr);
+            var old_decls = Node.FnDecl.OldDeclList.init(parser.arena);
+            const body = if (parser.eatToken(.Semicolon)) |_|
+                null
+            else blk: {
+                // TODO first_dr.is_old
+                // while (true) {
+                //     var old_ds = Node.DeclSpec{};
+                //     if (!(try parser.declSpec(&old_ds))) {
+                //         // not old decl
+                //         break;
+                //     }
+                //     var old_dr = (try parser.declarator(.Must));
+                //     // if (old_dr == null)
+                //     //     try parser.err(.{
+                //     //         .NoParamName = .{ .token = parser.it.index },
+                //     //     });
+                //     // try old_decls.push(decl);
+                // }
+                const body_node = (try parser.compoundStmt()) orelse return parser.err(.{
                     .ExpectedFnBody = .{ .token = parser.it.index },
                 });
+                break :blk @fieldParentPtr(Node.CompoundStmt, "base", body_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),
+            const node = try parser.arena.create(Node.FnDecl);
+            node.* = .{
+                .decl_spec = ds,
+                .declarator = dr,
+                .old_decls = old_decls,
+                .body = body,
+            };
+            return &node.base;
+        } else {
+            switch (ds.fn_spec) {
+                .Inline, .Noreturn => |tok| return parser.err(.{
+                    .FnSpecOnNonFn = .{ .token = tok },
+                }),
+                else => {},
+            }
+            // TODO threadlocal without static or extern on local variable
+            const node = try parser.arena.create(Node.VarDecl);
+            node.* = .{
+                .decl_spec = ds,
+                .initializers = Node.VarDecl.Initializers.init(parser.arena),
+                .semicolon = undefined,
+            };
+            if (first_dr == null) {
+                node.semicolon = try parser.expectToken(.Semicolon);
+                const ok = switch (ds.type_spec.spec) {
+                    .Enum => |e| e.name != null,
+                    .Record => |r| r.name != null,
+                    else => false,
                 };
+                const q = ds.type_spec.qual;
+                if (!ok)
+                    try parser.warn(.{
+                        .NothingDeclared = .{ .token = begin },
+                    })
+                else if (q.@"const" orelse q.atomic orelse q.@"volatile" orelse q.restrict) |tok|
+                    try parser.warn(.{
+                        .QualifierIgnored = .{ .token = tok },
+                    });
                 return &node.base;
-            },
+            }
+            var dr = @fieldParentPtr(Node.Declarator, "base", first_dr.?);
+            while (true) {
+                try parser.declareSymbol(&ds, dr);
+                if (parser.eatToken(.Equal)) |tok| {
+                    try node.initializers.push((try parser.initializer(dr)) orelse return parser.err(.{
+                        .ExpectedInitializer = .{ .token = parser.it.index },
+                    }));
+                } else 
+                    try node.initializers.push(&dr.base);
+                if (parser.eatToken(.Comma) != null) break;
+                dr = @fieldParentPtr(Node.Declarator, "base", (try parser.declarator(.Must)) orelse return parser.err(.{
+                    .ExpectedDeclarator = .{ .token = parser.it.index },
+                }));
+            }
+            node.semicolon = try parser.expectToken(.Semicolon);
+            return &node.base;
         }
     }
 
+    fn declaratorIsFunction(dr: *Node) bool {
+        return false; // TODO
+    }
+
     /// 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;
@@ -733,7 +787,7 @@ const Parser = struct {
     fn recordDeclarator(parser: *Parser) !*Node {}
 
     /// Pointer <- ASTERISK TypeQual* Pointer?
-    fn pointer(parser: *Parser) Error!?*Node {
+    fn pointer(parser: *Parser) Error!?*Node.Pointer {
         const asterisk = parser.eatToken(.Asterisk) orelse return null;
         const node = try parser.arena.create(Node.Pointer);
         node.* = .{
@@ -743,7 +797,7 @@ const Parser = struct {
         };
         while (try parser.typeQual(&node.qual)) {}
         node.pointer = try parser.pointer();
-        return &node.base;
+        return node;
     }
 
     const Named = enum {
@@ -772,7 +826,7 @@ const Parser = struct {
             node.* = .{
                 .pointer = ptr,
                 .prefix = .{
-                    .Comples = .{
+                    .Complex = .{
                         .lparen = lparen,
                         .inner = inner,
                         .rparen = try parser.expectToken(.RParen),
@@ -785,7 +839,7 @@ const Parser = struct {
                 node = try parser.arena.create(Node.Declarator);
                 node.* = .{
                     .pointer = ptr,
-                    .prefix = .{ .Simple = tok },
+                    .prefix = .{ .Identifer = tok },
                     .suffix = .None,
                 };
             } else if (named == .Must) {
@@ -793,7 +847,9 @@ const Parser = struct {
                     .ExpectedToken = .{ .token = parser.it.index, .expected_id = .Identifier },
                 });
             } else {
-                return ptr;
+                if (ptr) |some|
+                    return &some.base;
+                return null;
             }
         } else {
             node = try parser.arena.create(Node.Declarator);
@@ -808,16 +864,16 @@ const Parser = struct {
             node.suffix = .{
                 .Fn = .{
                     .lparen = lparen,
-                    .params = .Node.Declarator.Params.init(parser.arena),
+                    .params = Node.Declarator.Params.init(parser.arena),
                     .rparen = undefined,
                 },
             };
-            try parser.ParamDecl(node);
+            try parser.paramDecl(node);
             node.suffix.Fn.rparen = try parser.expectToken(.RParen);
         } else {
-            while (parser.arrayDeclarator()) |arr| {
+            while (try parser.arrayDeclarator()) |arr| {
                 if (node.suffix == .None)
-                    node.suffix = .{ .Array = .Node.Declarator.Arrays.init(parser.arena) };
+                    node.suffix = .{ .Array = Node.Declarator.Arrays.init(parser.arena) };
                 try node.suffix.Array.push(arr);
             }
         }
@@ -825,7 +881,7 @@ const Parser = struct {
             return parser.err(.{
                 .InvalidDeclarator = .{ .token = tok },
             });
-        return node;
+        return &node.base;
     }
 
     /// ArrayDeclarator
@@ -834,11 +890,11 @@ const Parser = struct {
     ///     / TypeQual+ (ASTERISK / Keyword_static AssignmentExpr)
     ///     / TypeQual+ AssignmentExpr?
     ///     / AssignmentExpr
-    fn arrayDeclarator(parser: *Parser, dr: *Node.Declarator) !?*Node.Array {
+    fn arrayDeclarator(parser: *Parser) !?*Node.Array {
         const lbracket = parser.eatToken(.LBracket) orelse return null;
         const arr = try parser.arena.create(Node.Array);
         arr.* = .{
-            .lbracket = lbarcket,
+            .lbracket = lbracket,
             .inner = .Inferred,
             .rbracket = undefined,
         };
@@ -856,12 +912,12 @@ const Parser = struct {
     fn paramDecl(parser: *Parser, dr: *Node.Declarator) !void {
         var old_style = false;
         while (true) {
-            var ds = Node.DeclSpec;
+            var ds = Node.DeclSpec{};
             if (try parser.declSpec(&ds)) {
                 //TODO
-            } else if (parser.eatToken(.Identifier)) {
+            } else if (parser.eatToken(.Identifier)) |tok| {
                 old_style = true;
-            } else if (parser.eatToken(.Ellipsis)) {
+            } else if (parser.eatToken(.Ellipsis)) |tok| {
                 // TODO
             }
         }