Commit 4184d4c66a

Vexu <git@vexu.eu>
2020-01-07 18:05:46
std-c parser record and enum specifiers
1 parent df12c13
Changed files (2)
lib/std/c/ast.zig
@@ -48,9 +48,12 @@ pub const Error = union(enum) {
     ExpectedFnBody: SingleTokenError("expected function body, found '{}'"),
     ExpectedDeclarator: SingleTokenError("expected declarator, found '{}'"),
     ExpectedInitializer: SingleTokenError("expected initializer, found '{}'"),
+    ExpectedEnumField: SingleTokenError("expected enum field, found '{}'"),
+    ExpectedType: SingleTokenError("expected enum field, found '{}'"),
     InvalidTypeSpecifier: InvalidTypeSpecifier,
     DuplicateQualifier: SingleTokenError("duplicate type qualifier '{}'"),
     DuplicateSpecifier: SingleTokenError("duplicate declaration specifier '{}'"),
+    MustUseKwToRefer: MustUseKwToRefer,
 
     pub fn render(self: *const Error, tree: *Tree, stream: var) !void {
         switch (self.*) {
@@ -62,9 +65,12 @@ pub const Error = union(enum) {
             .ExpectedDeclarator => |*x| return x.render(tree, stream),
             .ExpectedFnBody => |*x| return x.render(tree, stream),
             .ExpectedInitializer => |*x| return x.render(tree, stream),
+            .ExpectedEnumField => |*x| return x.render(tree, stream),
+            .ExpectedType => |*x| return x.render(tree, stream),
             .InvalidTypeSpecifier => |*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),
         }
     }
 
@@ -78,9 +84,12 @@ pub const Error = union(enum) {
             .ExpectedDeclarator => |x| return x.token,
             .ExpectedFnBody => |x| return x.token,
             .ExpectedInitializer => |x| return x.token,
+            .ExpectedEnumField => |x| return x.token,
+            .ExpectedType => |*x| return x.token,
             .InvalidTypeSpecifier => |x| return x.token,
             .DuplicateQualifier => |x| return x.token,
             .DuplicateSpecifier => |x| return x.token,
+            .MustUseKwToRefer => |*x| return x.name,
         }
     }
 
@@ -111,6 +120,15 @@ pub const Error = union(enum) {
         }
     };
 
+    pub const MustUseKwToRefer = struct {
+        kw: TokenIndex,
+        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)});
+        }
+    };
+
     fn SingleTokenError(comptime msg: []const u8) type {
         return struct {
             token: TokenIndex,
@@ -125,14 +143,13 @@ pub const Error = union(enum) {
 
 pub const Type = struct {
     pub const TypeList = std.SegmentedList(*Type, 4);
-    @"const": bool,
-    atomic: bool,
-    @"volatile": bool,
-    restrict: bool,
+    @"const": bool = false,
+    atomic: bool = false,
+    @"volatile": bool = false,
+    restrict: bool = false,
 
     id: union(enum) {
         Int: struct {
-            quals: Qualifiers,
             id: Id,
             is_signed: bool,
 
@@ -145,7 +162,6 @@ pub const Type = struct {
             };
         },
         Float: struct {
-            quals: Qualifiers,
             id: Id,
 
             pub const Id = enum {
@@ -154,10 +170,7 @@ pub const Type = struct {
                 LongDouble,
             };
         },
-        Pointer: struct {
-            quals: Qualifiers,
-            child_type: *Type,
-        },
+        Pointer: *Type,
         Function: struct {
             return_type: *Type,
             param_types: TypeList,
@@ -173,6 +186,8 @@ pub const Node = struct {
 
     pub const Id = enum {
         Root,
+        EnumField,
+        RecordField,
         JumpStmt,
         ExprStmt,
         Label,
@@ -350,15 +365,16 @@ pub const Node = struct {
     };
 
     pub const EnumField = struct {
-        base: Node = Node{ .id = EnumField },
+        base: Node = Node{ .id = .EnumField },
         name: TokenIndex,
         value: ?*Node,
     };
 
     pub const RecordType = struct {
-        kind: union(enum) {
-            Struct: TokenIndex,
-            Union: TokenIndex,
+        tok: TokenIndex,
+        kind: enum {
+            Struct,
+            Union,
         },
         name: ?TokenIndex,
         body: ?struct {
@@ -373,8 +389,12 @@ pub const Node = struct {
     };
 
     pub const RecordField = struct {
-        base: Node = Node{ .id = RecordField },
-        // TODO
+        base: Node = Node{ .id = .RecordField },
+        type_spec: TypeSpec,
+        declarators: DeclaratorList,
+        semicolon: TokenIndex,
+
+        pub const DeclaratorList = Root.DeclList;
     };
 
     pub const TypeQual = struct {
lib/std/c/parse.zig
@@ -94,12 +94,11 @@ const Parser = struct {
     }
 
     fn getSymbol(parser: *Parser, tok: TokenIndex) ?*Type {
-        const token = parser.it.list.at(tok);
-        const name = parser.tree.slice(token);
+        const name = parser.tree.slice(tok);
         const syms = parser.symbols.toSliceConst();
         var i = syms.len;
         while (i > 0) : (i -= 1) {
-            if (mem.eql(u8, name, syms[i].name)) {
+            if (std.mem.eql(u8, name, syms[i].name)) {
                 return syms[i].ty;
             }
         }
@@ -492,31 +491,29 @@ const Parser = struct {
             } else if (parser.eatToken(.Keyword_enum)) |tok| {
                 if (type_spec.spec != .None)
                     break :blk;
-                type_spec.Enum = try parser.enumSpec(tok);
+                type_spec.spec.Enum = try parser.enumSpec(tok);
                 return true;
             } else if (parser.eatToken(.Keyword_union) orelse parser.eatToken(.Keyword_struct)) |tok| {
                 if (type_spec.spec != .None)
                     break :blk;
-                type_spec.Record = try parser.recordSpec();
+                type_spec.spec.Record = try parser.recordSpec(tok);
                 return true;
             } else if (parser.eatToken(.Identifier)) |tok| {
                 const ty = parser.getSymbol(tok) orelse {
                     parser.putBackToken(tok);
                     return false;
                 };
-                switch (ty) {
+                switch (ty.id) {
                     .Enum => |e| {
                         return parser.err(.{
-                            .MustUseKwToRefer = .{ .kw = e.identifier, .sym = tok },
+                            .MustUseKwToRefer = .{ .kw = e.tok, .name = tok },
                         });
                     },
                     .Record => |r| {
                         return parser.err(.{
                             .MustUseKwToRefer = .{
-                                .kw = switch (r.kind) {
-                                    .Struct, .Union => |kw| kw,
-                                },
-                                .sym = tok,
+                                .kw = r.tok,
+                                .name = tok,
                             },
                         });
                     },
@@ -613,18 +610,121 @@ const Parser = struct {
     }
 
     /// EnumSpec <- Keyword_enum IDENTIFIER? (LBRACE EnumField RBRACE)?
-    fn enumSpecifier(parser: *Parser) !*Node {}
+    fn enumSpec(parser: *Parser, tok: TokenIndex) !*Node.EnumType {
+        const node = try parser.arena.create(Node.EnumType);
+        const name = parser.eatToken(.Identifier);
+        node.* = .{
+            .tok = tok,
+            .name = name,
+            .body = null,
+        };
+        const ty = try parser.arena.create(Type);
+        ty.* = .{
+            .id = .{
+                .Enum = node,
+            },
+        };
+        if (name) |some|
+            try parser.symbols.append(.{
+                .name = parser.tree.slice(some),
+                .ty = ty,
+            });
+        if (parser.eatToken(.LBrace)) |lbrace| {
+            var fields = Node.EnumType.FieldList.init(parser.arena);
+            try fields.push((try parser.enumField()) orelse return parser.err(.{
+                .ExpectedEnumField = .{ .token = parser.it.index },
+            }));
+            while (parser.eatToken(.Comma)) |_| {
+                try fields.push((try parser.enumField()) orelse break);
+            }
+            node.body = .{
+                .lbrace = lbrace,
+                .fields = fields,
+                .rbrace = try parser.expectToken(.RBrace),
+            };
+        }
+        return node;
+    }
 
     /// EnumField <- IDENTIFIER (EQUAL ConstExpr)? (COMMA EnumField) COMMA?
-    fn enumField(parser: *Parser) !*Node {}
+    fn enumField(parser: *Parser) !?*Node {
+        const name = parser.eatToken(.Identifier) orelse return null;
+        const node = try parser.arena.create(Node.EnumField);
+        node.* = .{
+            .name = name,
+            .value = null,
+        };
+        if (parser.eatToken(.Equal)) |eq| {
+            node.value = try parser.constExpr();
+        }
+        return &node.base;
+    }
 
     /// RecordSpec <- (Keyword_struct / Keyword_union) IDENTIFIER? (LBRACE RecordField+ RBRACE)?
-    fn recordSpecifier(parser: *Parser) !*Node {}
+    fn recordSpec(parser: *Parser, tok: TokenIndex) !*Node.RecordType {
+        const node = try parser.arena.create(Node.RecordType);
+        const name = parser.eatToken(.Identifier);
+        const is_struct = parser.tree.slice(tok)[0] == 's';
+        node.* = .{
+            .tok = tok,
+            .kind = if (is_struct) .Struct else .Union,
+            .name = name,
+            .body = null,
+        };
+        const ty = try parser.arena.create(Type);
+        ty.* = .{
+            .id = .{
+                .Record = node,
+            },
+        };
+        if (name) |some|
+            try parser.symbols.append(.{
+                .name = parser.tree.slice(some),
+                .ty = ty,
+            });
+        if (parser.eatToken(.LBrace)) |lbrace| {
+            var fields = Node.RecordType.FieldList.init(parser.arena);
+            while (true) {
+                if (parser.eatToken(.RBrace)) |rbrace| {
+                    node.body = .{
+                        .lbrace = lbrace,
+                        .fields = fields,
+                        .rbrace = rbrace,
+                    };
+                    break;
+                }
+                try fields.push(try parser.recordField());
+            }
+        }
+        return node;
+    }
 
     /// RecordField
     ///     <- TypeSpec* (RecordDeclarator (COMMA RecordDeclarator))? SEMICOLON
     ///     \ StaticAssert
-    fn recordField(parser: *Parser) !*Node {}
+    fn recordField(parser: *Parser) Error!*Node {
+        if (try parser.staticAssert()) |decl| return decl;
+        var got = false;
+        var type_spec = Node.TypeSpec{};
+        while (try parser.typeSpec(&type_spec)) got = true;
+        if (!got)
+            return parser.err(.{
+                .ExpectedType = .{ .token = parser.it.index },
+            });
+        const node = try parser.arena.create(Node.RecordField);
+        node.* = .{
+            .type_spec = type_spec,
+            .declarators = Node.RecordField.DeclaratorList.init(parser.arena),
+            .semicolon = undefined,
+        };
+        while (true) {
+            try node.declarators.push(try parser.recordDeclarator());
+            if (parser.eatToken(.Comma)) |_| {} else break;
+        }
+
+        node.semicolon = try parser.expectToken(.Semicolon);
+        return &node.base;
+    }
 
     /// TypeName <- TypeSpec* AbstractDeclarator?
     fn typeName(parser: *Parser) !*Node {