Commit 820de1716b

Jimmi Holst Christensen <jhc@liab.dk>
2018-04-06 15:37:49
std.zig.parser now parses labeled blocks. * There is also some code for switch range parsing
1 parent f667744
Changed files (2)
std/zig/ast.zig
@@ -20,6 +20,8 @@ pub const Node = struct {
         FnProto,
         ParamDecl,
         Block,
+        Switch,
+        SwitchCase,
         InfixOp,
         PrefixOp,
         SuffixOp,
@@ -55,6 +57,8 @@ pub const Node = struct {
             Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
             Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
             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.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
             Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
             Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index),
@@ -91,6 +95,8 @@ pub const Node = struct {
             Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
             Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
             Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
+            Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
+            Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).firstToken(),
             Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
             Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
             Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
@@ -127,6 +133,8 @@ pub const Node = struct {
             Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
             Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
             Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
+            Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
+            Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
             Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
             Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
             Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
@@ -506,9 +514,10 @@ pub const NodeParamDecl = struct {
 
 pub const NodeBlock = struct {
     base: Node,
-    begin_token: Token,
-    end_token: Token,
+    label: ?Token,
+    lbrace: Token,
     statements: ArrayList(&Node),
+    rbrace: Token,
 
     pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
         var i = index;
@@ -520,11 +529,80 @@ pub const NodeBlock = struct {
     }
 
     pub fn firstToken(self: &NodeBlock) Token {
-        return self.begin_token;
+        if (self.label) |label| {
+            return label;
+        }
+
+        return self.lbrace;
     }
 
     pub fn lastToken(self: &NodeBlock) Token {
-        return self.end_token;
+        return self.rbrace;
+    }
+};
+
+pub const NodeSwitch = struct {
+    base: Node,
+    switch_token: Token,
+    expr: &Node,
+    cases: ArrayList(&NodeSwitchCase),
+    rbrace: Token,
+
+    pub fn iterate(self: &NodeSwitch, index: usize) ?&Node {
+        var i = index;
+
+        if (i < 1) return self.expr;
+        i -= 1;
+
+        if (i < self.cases.len) return self.cases.at(i);
+        i -= self.cases.len;
+
+        return null;
+    }
+
+    pub fn firstToken(self: &NodeSwitch) Token {
+        return self.switch_token;
+    }
+
+    pub fn lastToken(self: &NodeSwitch) Token {
+        return self.rbrace;
+    }
+};
+
+pub const NodeSwitchCase = struct {
+    base: Node,
+    items: ArrayList(&Node),
+    capture: ?Capture,
+    expr: &Node,
+
+    const Capture = struct {
+        symbol: &NodeIdentifier,
+        is_ptr: bool,
+    };
+
+    pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
+        var i = index;
+
+        if (i < self.items.len) return self.items.at(i);
+        i -= self.items.len;
+
+        if (self.capture) |capture| {
+            if (i < 1) return &capture.base;
+            i -= 1;
+        }
+
+        if (i < 1) return self.expr;
+        i -= 1;
+
+        return null;
+    }
+
+    pub fn firstToken(self: &NodeSwitchCase) Token {
+        return self.items.at(0).firstToken();
+    }
+
+    pub fn lastToken(self: &NodeSwitchCase) Token {
+        return self.expr.lastToken();
     }
 };
 
@@ -575,6 +653,7 @@ pub const NodeInfixOp = struct {
         Mult,
         MultWrap,
         Period,
+        Range,
         Sub,
         SubWrap,
         UnwrapMaybe,
@@ -625,6 +704,7 @@ pub const NodeInfixOp = struct {
             InfixOp.Mult,
             InfixOp.MultWrap,
             InfixOp.Period,
+            InfixOp.Range,
             InfixOp.Sub,
             InfixOp.SubWrap,
             InfixOp.UnwrapMaybe => {},
std/zig/parser.zig
@@ -146,6 +146,8 @@ pub const Parser = struct {
         Required,
 
         Expression: DestPtr,
+        RangeExpressionBegin: DestPtr,
+        RangeExpressionEnd: DestPtr,
         AssignmentExpressionBegin: DestPtr,
         AssignmentExpressionEnd: DestPtr,
         UnwrapExpressionBegin: DestPtr,
@@ -256,7 +258,7 @@ pub const Parser = struct {
                             }
 
                             const name = try self.createStringLiteral(arena, name_token);
-                            const block = try self.createBlock(arena, token);
+                            const block = try self.createBlock(arena, (?Token)(null), token);
                             const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, &name.base, block);
                             stack.append(State { .Block = block }) catch unreachable;
                             continue;
@@ -643,6 +645,27 @@ pub const Parser = struct {
                     }
                 },
 
+                State.RangeExpressionBegin => |dest_ptr| {
+                    stack.append(State { .RangeExpressionEnd = dest_ptr }) catch unreachable;
+                    try stack.append(State { .Expression = dest_ptr });
+                    continue;
+                },
+
+                State.RangeExpressionEnd => |dest_ptr| {
+                    const token = self.getNextToken();
+                    if (token.id == Token.Id.Ellipsis3) {
+                        const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Range);
+                        node.lhs = dest_ptr.get();
+                        dest_ptr.store(&node.base);
+
+                        stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+                        continue;
+                    } else {
+                        self.putBackToken(token);
+                        continue;
+                    }
+                },
+
                 State.AssignmentExpressionBegin => |dest_ptr| {
                     stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
                     try stack.append(State { .UnwrapExpressionBegin = dest_ptr });
@@ -1205,10 +1228,6 @@ pub const Parser = struct {
                             try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
                             continue;
                         },
-                        Token.Id.Identifier => {
-                            dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
-                            continue;
-                        },
                         Token.Id.Builtin => {
                             const node = try arena.create(ast.NodeBuiltinCall);
                             *node = ast.NodeBuiltinCall {
@@ -1348,8 +1367,32 @@ pub const Parser = struct {
                                 },
                             }) catch unreachable;
                         },
+                        Token.Id.Identifier => {
+                            const next = self.getNextToken();
+                            if (next.id != Token.Id.Colon) {
+                                self.putBackToken(next);
+                                dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
+                                continue;
+                            }
+
+                            const block = try self.createBlock(arena, (?Token)(token), Token(undefined));
+                            dest_ptr.store(&block.base);
+
+                            stack.append(State { .Block = block }) catch unreachable;
+                            try stack.append(State {
+                                .ExpectTokenSave = ExpectTokenSave {
+                                    .id = Token.Id.LBrace,
+                                    .ptr = &block.lbrace,
+                                }
+                            });
+                            continue;
+                        },
                         Token.Id.LBrace => {
-                            @panic("TODO: Block expr");
+                            const block = try self.createBlock(arena, (?Token)(null), token);
+                            dest_ptr.store(&block.base);
+
+                            stack.append(State { .Block = block }) catch unreachable;
+                            continue;
                         },
                         Token.Id.Keyword_fn => {
                             @panic("TODO: fn proto");
@@ -1618,7 +1661,7 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch(token.id) {
                         Token.Id.LBrace => {
-                            const block = try self.createBlock(arena, token);
+                            const block = try self.createBlock(arena, (?Token)(null), token);
                             fn_proto.body_node = &block.base;
                             stack.append(State { .Block = block }) catch unreachable;
                             continue;
@@ -1635,7 +1678,7 @@ pub const Parser = struct {
                     const token = self.getNextToken();
                     switch (token.id) {
                         Token.Id.RBrace => {
-                            block.end_token = token;
+                            block.rbrace = token;
                             continue;
                         },
                         else => {
@@ -1648,38 +1691,64 @@ pub const Parser = struct {
                 },
 
                 State.Statement => |block| {
-                    {
-                        // Look for comptime var, comptime const
-                        const comptime_token = self.getNextToken();
-                        if (comptime_token.id == Token.Id.Keyword_comptime) {
+                    const next = self.getNextToken();
+                    switch (next.id) {
+                        Token.Id.Keyword_comptime => {
                             const mut_token = self.getNextToken();
                             if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
                                 // TODO shouldn't need these casts
                                 const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
-                                    mut_token, (?Token)(comptime_token), (?Token)(null), null);
+                                    mut_token, (?Token)(next), (?Token)(null), null);
                                 stack.append(State { .VarDecl = var_decl }) catch unreachable;
                                 continue;
+                            } else {
+                                self.putBackToken(mut_token);
+                                @panic("TODO: comptime block");
                             }
-                            self.putBackToken(mut_token);
-                        }
-                        self.putBackToken(comptime_token);
-                    }
-                    {
-                        // Look for const, var
-                        const mut_token = self.getNextToken();
-                        if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
-                            // TODO shouldn't need these casts
+                        },
+                        Token.Id.Keyword_var, Token.Id.Keyword_const => {
                             const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
-                                mut_token, (?Token)(null), (?Token)(null), null);
+                                next, (?Token)(null), (?Token)(null), null);
                             stack.append(State { .VarDecl = var_decl }) catch unreachable;
                             continue;
+                        },
+                        Token.Id.Identifier => {
+                            const maybe_colon = self.getNextToken();
+                            if (maybe_colon.id != Token.Id.Colon) {
+                                self.putBackToken(maybe_colon);
+                                self.putBackToken(next);
+                                stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+                                try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
+                                continue;
+                            }
+
+                            const inner_block = try self.createBlock(arena, (?Token)(next), Token(undefined));
+                            try block.statements.append(&inner_block.base);
+
+                            stack.append(State { .Block = inner_block }) catch unreachable;
+                            try stack.append(State {
+                                .ExpectTokenSave = ExpectTokenSave {
+                                    .id = Token.Id.LBrace,
+                                    .ptr = &inner_block.lbrace,
+                                }
+                            });
+                            continue;
+                        },
+                        Token.Id.LBrace => {
+                            const inner_block = try self.createBlock(arena, (?Token)(null), next);
+                            try block.statements.append(&inner_block.base);
+
+                            stack.append(State { .Block = inner_block }) catch unreachable;
+                            continue;
+                        },
+                        else => {
+                            self.putBackToken(next);
+                            stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+                            try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
+                            continue;
                         }
-                        self.putBackToken(mut_token);
                     }
 
-                    stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
-                    try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
-                    continue;
                 },
             }
         }
@@ -1905,14 +1974,15 @@ pub const Parser = struct {
         return node;
     }
 
-    fn createBlock(self: &Parser, arena: &mem.Allocator, begin_token: &const Token) !&ast.NodeBlock {
+    fn createBlock(self: &Parser, arena: &mem.Allocator, label: &const ?Token, lbrace: &const Token) !&ast.NodeBlock {
         const node = try arena.create(ast.NodeBlock);
 
         *node = ast.NodeBlock {
             .base = self.initNode(ast.Node.Id.Block),
-            .begin_token = *begin_token,
-            .end_token = undefined,
+            .label = *label,
+            .lbrace = *lbrace,
             .statements = ArrayList(&ast.Node).init(arena),
+            .rbrace = undefined,
         };
         return node;
     }
@@ -2340,6 +2410,10 @@ pub const Parser = struct {
                     },
                     ast.Node.Id.Block => {
                         const block = @fieldParentPtr(ast.NodeBlock, "base", base);
+                        if (block.label) |label| {
+                            try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+                        }
+
                         if (block.statements.len == 0) {
                             try stream.write("{}");
                         } else {
@@ -2747,6 +2821,8 @@ 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.StructField,
                     ast.Node.Id.UnionTag,
@@ -2791,6 +2867,9 @@ pub const Parser = struct {
                             const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
                             try stack.append(RenderState { .VarDecl = var_decl});
                         },
+                        ast.Node.Id.Block => {
+                            try stack.append(RenderState { .Expression = base});
+                        },
                         else => {
                             try stack.append(RenderState { .Text = ";"});
                             try stack.append(RenderState { .Expression = base});
@@ -3323,6 +3402,28 @@ test "zig fmt: catch" {
     );
 }
 
+test "zig fmt: blocks" {
+    try testCanonical(
+        \\test "blocks" {
+        \\    {
+        \\        const a = 0;
+        \\        const b = 0;
+        \\    }
+        \\
+        \\    blk: {
+        \\        const a = 0;
+        \\        const b = 0;
+        \\    }
+        \\
+        \\    const r = blk: {
+        \\        const a = 0;
+        \\        const b = 0;
+        \\    };
+        \\}
+        \\
+    );
+}
+
 test "zig fmt: switch" {
     try testCanonical(
         \\test "switch" {