From 4184d4c66a26ba10fbc78cc21f2c73db8f0cfcb2 Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 7 Jan 2020 19:05:46 +0200 Subject: [PATCH] std-c parser record and enum specifiers --- lib/std/c/ast.zig | 52 ++++++++++++------ lib/std/c/parse.zig | 130 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 151 insertions(+), 31 deletions(-) diff --git a/lib/std/c/ast.zig b/lib/std/c/ast.zig index 41315466e..13c9699e7 100644 --- a/lib/std/c/ast.zig +++ b/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 { diff --git a/lib/std/c/parse.zig b/lib/std/c/parse.zig index 736c25133..3b30fc8a4 100644 --- a/lib/std/c/parse.zig +++ b/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 {