From bdff5bfa3e9f6ab490771b54109cb200b180b4da Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 7 Apr 2018 01:38:38 +0200 Subject: [PATCH] std.zig.parser now parses switch --- std/zig/ast.zig | 21 ++++ std/zig/parser.zig | 260 ++++++++++++++++++++++++++++++++++++------ std/zig/tokenizer.zig | 6 + 3 files changed, 251 insertions(+), 36 deletions(-) diff --git a/std/zig/ast.zig b/std/zig/ast.zig index bc066a9922..ca384efedd 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -22,6 +22,7 @@ pub const Node = struct { Block, Switch, SwitchCase, + SwitchElse, InfixOp, PrefixOp, SuffixOp, @@ -59,6 +60,7 @@ pub const Node = struct { Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index), Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).iterate(index), Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).iterate(index), + Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).iterate(index), Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index), Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index), Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index), @@ -97,6 +99,7 @@ pub const Node = struct { Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(), Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).firstToken(), Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).firstToken(), + Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(), Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(), Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(), Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(), @@ -135,6 +138,7 @@ pub const Node = struct { Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(), Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(), Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(), + Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(), Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(), Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(), Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(), @@ -606,6 +610,23 @@ pub const NodeSwitchCase = struct { } }; +pub const NodeSwitchElse = struct { + base: Node, + token: Token, + + pub fn iterate(self: &NodeSwitchElse, index: usize) ?&Node { + return null; + } + + pub fn firstToken(self: &NodeSwitchElse) Token { + return self.token; + } + + pub fn lastToken(self: &NodeSwitchElse) Token { + return self.token; + } +}; + pub const NodeInfixOp = struct { base: Node, op_token: Token, diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 3337968d69..09f7827505 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -98,10 +98,15 @@ pub const Parser = struct { ptr: &?&ast.Node, }; - fn ListState(comptime T: type) type { + const ExprListCtx = struct { + list: &ArrayList(&ast.Node), + end: Token.Id, + ptr: &Token, + }; + + fn ListSave(comptime T: type) type { return struct { list: &ArrayList(T), - end: Token.Id, ptr: &Token, }; } @@ -129,11 +134,16 @@ pub const Parser = struct { FnDef: &ast.NodeFnProto, Block: &ast.NodeBlock, Statement: &ast.NodeBlock, - ExprListItemOrEnd: ListState(&ast.Node), - ExprListCommaOrEnd: ListState(&ast.Node), - FieldInitListItemOrEnd: ListState(&ast.NodeFieldInitializer), - FieldInitListCommaOrEnd: ListState(&ast.NodeFieldInitializer), + ExprListItemOrEnd: ExprListCtx, + ExprListCommaOrEnd: ExprListCtx, + FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer), + FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer), FieldListCommaOrEnd: &ast.NodeContainerDecl, + SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase), + SwitchCaseCapture: &?ast.NodeSwitchCase.Capture, + SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase), + SwitchCaseItem: &ArrayList(&ast.Node), + SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), /// A state that can be appended before any other State. If an error occures, /// the parser will first try looking for the closest optional state. If an @@ -245,17 +255,8 @@ pub const Parser = struct { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const name_token = self.getNextToken(); - if (name_token.id != Token.Id.StringLiteral) { - try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.StringLiteral), @tagName(name_token.id)); - continue; - } - - const lbrace = self.getNextToken(); - if (lbrace.id != Token.Id.LBrace) { - try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.LBrace), @tagName(name_token.id)); - continue; - } + const name_token = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue; + const lbrace = (try self.eatToken(&stack, Token.Id.LBrace)) ?? continue; const name = try self.createStringLiteral(arena, name_token); const block = try self.createBlock(arena, (?Token)(null), token); @@ -974,9 +975,8 @@ pub const Parser = struct { stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable; try stack.append(State { - .FieldInitListItemOrEnd = ListState(&ast.NodeFieldInitializer) { + .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) { .list = &node.op.StructInitializer, - .end = Token.Id.RBrace, .ptr = &node.rtoken, } }); @@ -992,7 +992,7 @@ pub const Parser = struct { stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable; try stack.append(State { - .ExprListItemOrEnd = ListState(&ast.Node) { + .ExprListItemOrEnd = ExprListCtx { .list = &node.op.ArrayInitializer, .end = Token.Id.RBrace, .ptr = &node.rtoken, @@ -1084,7 +1084,7 @@ pub const Parser = struct { stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable; try stack.append(State { - .ExprListItemOrEnd = ListState(&ast.Node) { + .ExprListItemOrEnd = ExprListCtx { .list = &node.op.Call.params, .end = Token.Id.RParen, .ptr = &node.rtoken, @@ -1238,7 +1238,7 @@ pub const Parser = struct { }; dest_ptr.store(&node.base); stack.append(State { - .ExprListItemOrEnd = ListState(&ast.Node) { + .ExprListItemOrEnd = ExprListCtx { .list = &node.params, .end = Token.Id.RParen, .ptr = &node.rparen_token, @@ -1400,6 +1400,43 @@ pub const Parser = struct { Token.Id.Keyword_asm => { @panic("TODO: inline asm"); }, + Token.Id.Keyword_if => { + @panic("TODO: inline if"); + }, + Token.Id.Keyword_while => { + @panic("TODO: inline while"); + }, + Token.Id.Keyword_for => { + @panic("TODO: inline for"); + }, + Token.Id.Keyword_switch => { + const node = try arena.create(ast.NodeSwitch); + *node = ast.NodeSwitch { + .base = self.initNode(ast.Node.Id.Switch), + .switch_token = token, + .expr = undefined, + .cases = ArrayList(&ast.NodeSwitchCase).init(arena), + .rbrace = undefined, + }; + dest_ptr.store(&node.base); + + stack.append(State { + .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) { + .list = &node.cases, + .ptr = &node.rbrace, + }, + }) catch unreachable; + try stack.append(State { .ExpectToken = Token.Id.LBrace }); + try stack.append(State { .ExpectToken = Token.Id.RParen }); + try stack.append(State { .Expression = DestPtr { .Field = &node.expr } }); + try stack.append(State { .ExpectToken = Token.Id.LParen }); + }, + Token.Id.Keyword_comptime => { + @panic("TODO: inline comptime"); + }, + Token.Id.Keyword_suspend => { + @panic("TODO: inline suspend"); + }, else => { try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id)); continue; @@ -1463,8 +1500,7 @@ pub const Parser = struct { State.FieldInitListItemOrEnd => |list_state| { var token = self.getNextToken(); - const IdTag = @TagType(Token.Id); - if (IdTag(list_state.end) == token.id){ + if (token.id == Token.Id.RBrace){ *list_state.ptr = token; continue; } @@ -1497,13 +1533,82 @@ pub const Parser = struct { }); }, + State.SwitchCaseOrEnd => |list_state| { + var token = self.getNextToken(); + + if (token.id == Token.Id.RBrace){ + *list_state.ptr = token; + continue; + } + + self.putBackToken(token); + + const node = try arena.create(ast.NodeSwitchCase); + *node = ast.NodeSwitchCase { + .base = self.initNode(ast.Node.Id.SwitchCase), + .items = ArrayList(&ast.Node).init(arena), + .capture = null, + .expr = undefined, + }; + try list_state.list.append(node); + stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable; + try stack.append(State { .Expression = DestPtr{ .Field = &node.expr } }); + try stack.append(State { .SwitchCaseCapture = &node.capture }); + + const maybe_else = self.getNextToken(); + if (maybe_else.id == Token.Id.Keyword_else) { + const else_node = try arena.create(ast.NodeSwitchElse); + *else_node = ast.NodeSwitchElse { + .base = self.initNode(ast.Node.Id.SwitchElse), + .token = maybe_else, + }; + try node.items.append(&else_node.base); + try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + continue; + } else { + self.putBackToken(maybe_else); + try stack.append(State { .SwitchCaseItem = &node.items }); + continue; + } + }, + + State.SwitchCaseCapture => |capture| { + const token = self.getNextToken(); + if (token.id != Token.Id.Pipe) { + self.putBackToken(token); + continue; + } + + const is_ptr = blk: { + const asterik = self.getNextToken(); + if (asterik.id == Token.Id.Asterisk) { + break :blk true; + } else { + self.putBackToken(asterik); + break :blk false; + } + }; + + const ident = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue; + _ = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue; + *capture = ast.NodeSwitchCase.Capture { + .symbol = try self.createIdentifier(arena, ident), + .is_ptr = is_ptr + }; + }, + + State.SwitchCaseItem => |case_items| { + stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; + try stack.append(State { .RangeExpressionBegin = DestPtr{ .Field = try case_items.addOne() } }); + }, + State.ExprListCommaOrEnd => |list_state| { try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state }); continue; }, State.FieldInitListCommaOrEnd => |list_state| { - try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .FieldInitListItemOrEnd = list_state }); + try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state }); continue; }, @@ -1513,6 +1618,16 @@ pub const Parser = struct { continue; }, + State.SwitchCaseCommaOrEnd => |list_state| { + try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state }); + continue; + }, + + State.SwitchCaseItemCommaOrEnd => |case_items| { + try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items }); + continue; + }, + State.AddrOfModifiers => |addr_of_info| { var token = self.getNextToken(); switch (token.id) { @@ -1741,6 +1856,11 @@ pub const Parser = struct { stack.append(State { .Block = inner_block }) catch unreachable; continue; }, + Token.Id.Keyword_switch => { + self.putBackToken(next); + stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } }) catch unreachable; + continue; + }, else => { self.putBackToken(next); stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; @@ -1754,7 +1874,7 @@ pub const Parser = struct { } } - fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, ptr: &Token, state_after_comma: &const State) !void { + fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void { var token = self.getNextToken(); switch (token.id) { Token.Id.Comma => { @@ -1763,7 +1883,9 @@ pub const Parser = struct { else => { const IdTag = @TagType(Token.Id); if (IdTag(*end) == token.id) { - *ptr = token; + if (maybe_ptr) |ptr| { + *ptr = token; + } return; } @@ -2498,7 +2620,8 @@ pub const Parser = struct { ast.NodeInfixOp.InfixOp.Sub => " - ", ast.NodeInfixOp.InfixOp.SubWrap => " -% ", ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ", - else => unreachable, + ast.NodeInfixOp.InfixOp.Range => " ... ", + ast.NodeInfixOp.InfixOp.Catch => unreachable, }; try stack.append(RenderState { .Text = text }); @@ -2821,8 +2944,73 @@ pub const Parser = struct { }, ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"), ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"), - ast.Node.Id.Switch => @panic("TODO switch"), - ast.Node.Id.SwitchCase => @panic("TODO switch case"), + ast.Node.Id.Switch => { + const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base); + try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); + + try stack.append(RenderState { .Text = "}"}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { .Indent = indent }); + try stack.append(RenderState { .Text = "\n"}); + + const cases = switch_node.cases.toSliceConst(); + var i = cases.len; + while (i != 0) { + i -= 1; + const node = cases[i]; + try stack.append(RenderState { .Expression = &node.base}); + try stack.append(RenderState.PrintIndent); + try stack.append(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = cases[i - 1]; + const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); + + if (i != 0) { + try stack.append(RenderState { .Text = "," }); + } + } + try stack.append(RenderState { .Indent = indent + indent_delta}); + try stack.append(RenderState { .Text = ") {"}); + try stack.append(RenderState { .Expression = switch_node.expr }); + }, + ast.Node.Id.SwitchCase => { + const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base); + + try stack.append(RenderState { .Expression = switch_case.expr }); + if (switch_case.capture) |capture| { + try stack.append(RenderState { .Text = "| "}); + try stack.append(RenderState { .Expression = &capture.symbol.base }); + + if (capture.is_ptr) { + try stack.append(RenderState { .Text = "*"}); + } + try stack.append(RenderState { .Text = "|"}); + } + try stack.append(RenderState { .Text = " => "}); + + const items = switch_case.items.toSliceConst(); + var i = items.len; + while (i != 0) { + i -= 1; + try stack.append(RenderState { .Expression = items[i] }); + + if (i != 0) { + try stack.append(RenderState { .Text = ", " }); + } + } + }, + ast.Node.Id.SwitchElse => { + const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base); + try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token)); + }, ast.Node.Id.StructField, ast.Node.Id.UnionTag, @@ -2867,7 +3055,7 @@ pub const Parser = struct { const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base); try stack.append(RenderState { .VarDecl = var_decl}); }, - ast.Node.Id.Block => { + ast.Node.Id.Block, ast.Node.Id.Switch => { try stack.append(RenderState { .Expression = base}); }, else => { @@ -3436,24 +3624,24 @@ test "zig fmt: switch" { \\ else => { \\ const a = 1; \\ const b = a; - \\ }, + \\ } \\ } \\ \\ const res = switch (0) { \\ 0 => 0, \\ 1 => 2, - \\ else => 4, + \\ else => 4 \\ }; \\ \\ const Union = union(enum) { \\ Int: i64, - \\ Float: f64, + \\ Float: f64 \\ }; \\ - \\ const u = Union { .Int = 0 }; + \\ const u = Union{ .Int = 0 }; \\ switch (u) { \\ Union.Int => |int| {}, - \\ Union.Float => |*float| unreachable, + \\ Union.Float => |*float| unreachable \\ } \\} \\ diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 87597adff1..422aa20629 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -86,6 +86,7 @@ pub const Token = struct { PipeEqual, Equal, EqualEqual, + EqualAngleBracketRight, BangEqual, LParen, RParen, @@ -688,6 +689,11 @@ pub const Tokenizer = struct { self.index += 1; break; }, + '>' => { + result.id = Token.Id.EqualAngleBracketRight; + self.index += 1; + break; + }, else => { result.id = Token.Id.Equal; break;