diff --git a/lib/std/c/ast.zig b/lib/std/c/ast.zig index 7a9d06af32..936964fddf 100644 --- a/lib/std/c/ast.zig +++ b/lib/std/c/ast.zig @@ -26,21 +26,43 @@ pub const Tree = struct { }; pub const Error = union(enum) { - InvalidToken: InvalidToken, + InvalidToken: SingleTokenError("Invalid token '{}'"), + ExpectedToken: ExpectedToken, + ExpectedExpr: SingleTokenError("Expected expression, found '{}'"), + ExpectedStmt: SingleTokenError("Expected statement, found '{}'"), pub fn render(self: *const Error, tokens: *Tree.TokenList, stream: var) !void { switch (self.*) { .InvalidToken => |*x| return x.render(tokens, stream), + .ExpectedToken => |*x| return x.render(tokens, stream), + .ExpectedExpr => |*x| return x.render(tokens, stream), + .ExpectedStmt => |*x| return x.render(tokens, stream), } } pub fn loc(self: *const Error) TokenIndex { switch (self.*) { .InvalidToken => |x| return x.token, + .ExpectedToken => |x| return x.token, + .ExpectedExpr => |x| return x.token, + .ExpectedStmt => |x| return x.token, } } - pub const InvalidToken = SingleTokenError("Invalid token '{}'"); + pub const ExpectedToken = struct { + token: TokenIndex, + expected_id: @TagType(Token.Id), + + pub fn render(self: *const ExpectedToken, tokens: *Tree.TokenList, stream: var) !void { + const found_token = tokens.at(self.token); + if (found_token.id == .Invalid) { + return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()}); + } else { + const token_name = found_token.id.symbol(); + return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name }); + } + } + }; fn SingleTokenError(comptime msg: []const u8) type { return struct { @@ -62,6 +84,8 @@ pub const Node = struct { JumpStmt, ExprStmt, Label, + CompoundStmt, + IfStmt, }; pub const Root = struct { @@ -73,7 +97,7 @@ pub const Node = struct { }; pub const JumpStmt = struct { - base: Node = Node{ .id = .JumpStmt}, + base: Node = Node{ .id = .JumpStmt }, ltoken: TokenIndex, kind: Kind, semicolon: TokenIndex, @@ -87,14 +111,33 @@ pub const Node = struct { }; pub const ExprStmt = struct { - base: Node = Node{ .id = .ExprStmt}, + base: Node = Node{ .id = .ExprStmt }, expr: ?*Node, semicolon: TokenIndex, }; pub const Label = struct { - base: Node = Node{ .id = .Label}, + base: Node = Node{ .id = .Label }, identifier: TokenIndex, colon: TokenIndex, }; + + pub const CompoundStmt = struct { + base: Node = Node{ .id = .CompoundStmt }, + lbrace: TokenIndex, + statements: StmtList, + rbrace: TokenIndex, + + pub const StmtList = Root.DeclList; + }; + + pub const IfStmt = struct { + base: Node = Node{ .id = .IfStmt }, + @"if": TokenIndex, + cond: *Node, + @"else": ?struct { + tok: TokenIndex, + stmt: *Node, + }, + }; }; diff --git a/lib/std/c/parse.zig b/lib/std/c/parse.zig index 527e03a48b..3fcfeaec52 100644 --- a/lib/std/c/parse.zig +++ b/lib/std/c/parse.zig @@ -284,7 +284,19 @@ const Parser = struct { fn designator(parser: *Parser) !*Node {} /// CompoundStmt <- LBRACE (Declaration / Stmt)* RBRACE - fn compoundStmt(parser: *Parser) !?*Node {} + fn compoundStmt(parser: *Parser) !?*Node { + const lbrace = parser.eatToken(.LBrace) orelse return null; + const node = try parser.arena.create(Node.CompoundStmt); + node.* = .{ + .lbrace = lbrace, + .statements = Node.JumpStmt.StmtList.init(parser.arena), + .rbrace = undefined, + }; + while (parser.declaration() orelse parser.stmt()) |node| + try node.statements.push(node); + node.rbrace = try parser.expectToken(.RBrace); + return &node.base; + } /// Stmt /// <- CompoundStmt @@ -303,7 +315,27 @@ const Parser = struct { /// / ExprStmt fn stmt(parser: *Parser) !?*Node { if (parser.compoundStmt()) |node| return node; - // if (parser.eatToken(.Keyword_if)) |tok| {} + if (parser.eatToken(.Keyword_if)) |tok| { + const node = try parser.arena.create(Node.IfStmt); + _ = try parser.expectToken(.LParen); + node.* = .{ + .@"if" = tok, + .cond = try parser.expect(expr, .{ + .ExpectedExpr = .{ .token = it.index }, + }), + .@"else" = null, + }; + _ = try parser.expectToken(.RParen); + if (parser.eatToken(.Keyword_else)) |else_tok| { + node.@"else" = .{ + .tok = else_tok, + .stmt = try parser.stmt(expr, .{ + .ExpectedStmt = .{ .token = it.index }, + }), + }; + } + return &node.base; + } // if (parser.eatToken(.Keyword_switch)) |tok| {} // if (parser.eatToken(.Keyword_while)) |tok| {} // if (parser.eatToken(.Keyword_do)) |tok| {} @@ -407,4 +439,15 @@ const Parser = struct { return; } } + + fn expect( + parser: *Parser, + parseFn: fn (*Parser) Error!?*Node, + err: ast.Error, // if parsing fails + ) Error!*Node { + return (try parseFn(arena, it, tree)) orelse { + try parser.tree.errors.push(err); + return error.ParseError; + }; + } }; diff --git a/lib/std/c/tokenizer.zig b/lib/std/c/tokenizer.zig index b8e515bec9..f7732b644e 100644 --- a/lib/std/c/tokenizer.zig +++ b/lib/std/c/tokenizer.zig @@ -6,7 +6,7 @@ pub const Source = struct { file_name: []const u8, tokens: TokenList, - pub const TokenList = SegmentedList(Token, 64); + pub const TokenList = std.SegmentedList(Token, 64); }; pub const Token = struct { @@ -134,6 +134,121 @@ pub const Token = struct { Keyword_ifndef, Keyword_error, Keyword_pragma, + + pub fn symbol(tok: Token) []const u8 { + return switch (tok.id) { + .Invalid => "Invalid", + .Eof => "Eof", + .Nl => "NewLine", + .Identifier => "Identifier", + .MacroString => "MacroString", + .StringLiteral => "StringLiteral", + .CharLiteral => "CharLiteral", + .IntegerLiteral => "IntegerLiteral", + .FloatLiteral => "FloatLiteral", + .LineComment => "LineComment", + .MultiLineComment => "MultiLineComment", + + .Bang => "!", + .BangEqual => "!=", + .Pipe => "|", + .PipePipe => "||", + .PipeEqual => "|=", + .Equal => "=", + .EqualEqual => "==", + .LParen => "(", + .RParen => ")", + .LBrace => "{", + .RBrace => "}", + .LBracket => "[", + .RBracket => "]", + .Period => ".", + .Ellipsis => "...", + .Caret => "^", + .CaretEqual => "^=", + .Plus => "+", + .PlusPlus => "++", + .PlusEqual => "+=", + .Minus => "-", + .MinusMinus => "--", + .MinusEqual => "-=", + .Asterisk => "*", + .AsteriskEqual => "*=", + .Percent => "%", + .PercentEqual => "%=", + .Arrow => "->", + .Colon => ":", + .Semicolon => ";", + .Slash => "/", + .SlashEqual => "/=", + .Comma => ",", + .Ampersand => "&", + .AmpersandAmpersand => "&&", + .AmpersandEqual => "&=", + .QuestionMark => "?", + .AngleBracketLeft => "<", + .AngleBracketLeftEqual => "<=", + .AngleBracketAngleBracketLeft => "<<", + .AngleBracketAngleBracketLeftEqual => "<<=", + .AngleBracketRight => ">", + .AngleBracketRightEqual => ">=", + .AngleBracketAngleBracketRight => ">>", + .AngleBracketAngleBracketRightEqual => ">>=", + .Tilde => "~", + .Hash => "#", + .HashHash => "##", + .Keyword_auto => "auto", + .Keyword_break => "break", + .Keyword_case => "case", + .Keyword_char => "char", + .Keyword_const => "const", + .Keyword_continue => "continue", + .Keyword_default => "default", + .Keyword_do => "do", + .Keyword_double => "double", + .Keyword_else => "else", + .Keyword_enum => "enum", + .Keyword_extern => "extern", + .Keyword_float => "float", + .Keyword_for => "for", + .Keyword_goto => "goto", + .Keyword_if => "if", + .Keyword_int => "int", + .Keyword_long => "long", + .Keyword_register => "register", + .Keyword_return => "return", + .Keyword_short => "short", + .Keyword_signed => "signed", + .Keyword_sizeof => "sizeof", + .Keyword_static => "static", + .Keyword_struct => "struct", + .Keyword_switch => "switch", + .Keyword_typedef => "typedef", + .Keyword_union => "union", + .Keyword_unsigned => "unsigned", + .Keyword_void => "void", + .Keyword_volatile => "volatile", + .Keyword_while => "while", + .Keyword_bool => "_Bool", + .Keyword_complex => "_Complex", + .Keyword_imaginary => "_Imaginary", + .Keyword_inline => "inline", + .Keyword_restrict => "restrict", + .Keyword_alignas => "_Alignas", + .Keyword_alignof => "_Alignof", + .Keyword_atomic => "_Atomic", + .Keyword_generic => "_Generic", + .Keyword_noreturn => "_Noreturn", + .Keyword_static_assert => "_Static_assert", + .Keyword_thread_local => "_Thread_local", + .Keyword_include => "include", + .Keyword_define => "define", + .Keyword_ifdef => "ifdef", + .Keyword_ifndef => "ifndef", + .Keyword_error => "error", + .Keyword_pragma => "pragma", + }; + } }; pub const Keyword = struct { @@ -1121,8 +1236,7 @@ pub const Tokenizer = struct { } } else if (self.index == self.source.buffer.len) { switch (state) { - .AfterStringLiteral, - .Start => {}, + .AfterStringLiteral, .Start => {}, .u, .u8, .U, .L, .Identifier => { result.id = Token.getKeyword(self.source.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier; }, @@ -1416,6 +1530,7 @@ fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void { .source = &Source{ .buffer = source, .file_name = undefined, + .tokens = undefined, }, }; for (expected_tokens) |expected_token_id| {