| ... | ... | @@ -98,10 +98,15 @@ pub const Parser = struct { |
| 98 | 98 | ptr: &?&ast.Node, |
| 99 | 99 | }; |
| 100 | 100 | |
| 101 | | fn ListState(comptime T: type) type { |
| 101 | const ExprListCtx = struct { |
| 102 | list: &ArrayList(&ast.Node), |
| 103 | end: Token.Id, |
| 104 | ptr: &Token, |
| 105 | }; |
| 106 | |
| 107 | fn ListSave(comptime T: type) type { |
| 102 | 108 | return struct { |
| 103 | 109 | list: &ArrayList(T), |
| 104 | | end: Token.Id, |
| 105 | 110 | ptr: &Token, |
| 106 | 111 | }; |
| 107 | 112 | } |
| ... | ... | @@ -129,11 +134,16 @@ pub const Parser = struct { |
| 129 | 134 | FnDef: &ast.NodeFnProto, |
| 130 | 135 | Block: &ast.NodeBlock, |
| 131 | 136 | Statement: &ast.NodeBlock, |
| 132 | | ExprListItemOrEnd: ListState(&ast.Node), |
| 133 | | ExprListCommaOrEnd: ListState(&ast.Node), |
| 134 | | FieldInitListItemOrEnd: ListState(&ast.NodeFieldInitializer), |
| 135 | | FieldInitListCommaOrEnd: ListState(&ast.NodeFieldInitializer), |
| 137 | ExprListItemOrEnd: ExprListCtx, |
| 138 | ExprListCommaOrEnd: ExprListCtx, |
| 139 | FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer), |
| 140 | FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer), |
| 136 | 141 | FieldListCommaOrEnd: &ast.NodeContainerDecl, |
| 142 | SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase), |
| 143 | SwitchCaseCapture: &?ast.NodeSwitchCase.Capture, |
| 144 | SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase), |
| 145 | SwitchCaseItem: &ArrayList(&ast.Node), |
| 146 | SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), |
| 137 | 147 | |
| 138 | 148 | /// A state that can be appended before any other State. If an error occures, |
| 139 | 149 | /// the parser will first try looking for the closest optional state. If an |
| ... | ... | @@ -245,17 +255,8 @@ pub const Parser = struct { |
| 245 | 255 | Token.Id.Keyword_test => { |
| 246 | 256 | stack.append(State.TopLevel) catch unreachable; |
| 247 | 257 | |
| 248 | | const name_token = self.getNextToken(); |
| 249 | | if (name_token.id != Token.Id.StringLiteral) { |
| 250 | | try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.StringLiteral), @tagName(name_token.id)); |
| 251 | | continue; |
| 252 | | } |
| 253 | | |
| 254 | | const lbrace = self.getNextToken(); |
| 255 | | if (lbrace.id != Token.Id.LBrace) { |
| 256 | | try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.LBrace), @tagName(name_token.id)); |
| 257 | | continue; |
| 258 | | } |
| 258 | const name_token = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue; |
| 259 | const lbrace = (try self.eatToken(&stack, Token.Id.LBrace)) ?? continue; |
| 259 | 260 | |
| 260 | 261 | const name = try self.createStringLiteral(arena, name_token); |
| 261 | 262 | const block = try self.createBlock(arena, (?Token)(null), token); |
| ... | ... | @@ -974,9 +975,8 @@ pub const Parser = struct { |
| 974 | 975 | |
| 975 | 976 | stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable; |
| 976 | 977 | try stack.append(State { |
| 977 | | .FieldInitListItemOrEnd = ListState(&ast.NodeFieldInitializer) { |
| 978 | .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) { |
| 978 | 979 | .list = &node.op.StructInitializer, |
| 979 | | .end = Token.Id.RBrace, |
| 980 | 980 | .ptr = &node.rtoken, |
| 981 | 981 | } |
| 982 | 982 | }); |
| ... | ... | @@ -992,7 +992,7 @@ pub const Parser = struct { |
| 992 | 992 | |
| 993 | 993 | stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable; |
| 994 | 994 | try stack.append(State { |
| 995 | | .ExprListItemOrEnd = ListState(&ast.Node) { |
| 995 | .ExprListItemOrEnd = ExprListCtx { |
| 996 | 996 | .list = &node.op.ArrayInitializer, |
| 997 | 997 | .end = Token.Id.RBrace, |
| 998 | 998 | .ptr = &node.rtoken, |
| ... | ... | @@ -1084,7 +1084,7 @@ pub const Parser = struct { |
| 1084 | 1084 | |
| 1085 | 1085 | stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable; |
| 1086 | 1086 | try stack.append(State { |
| 1087 | | .ExprListItemOrEnd = ListState(&ast.Node) { |
| 1087 | .ExprListItemOrEnd = ExprListCtx { |
| 1088 | 1088 | .list = &node.op.Call.params, |
| 1089 | 1089 | .end = Token.Id.RParen, |
| 1090 | 1090 | .ptr = &node.rtoken, |
| ... | ... | @@ -1238,7 +1238,7 @@ pub const Parser = struct { |
| 1238 | 1238 | }; |
| 1239 | 1239 | dest_ptr.store(&node.base); |
| 1240 | 1240 | stack.append(State { |
| 1241 | | .ExprListItemOrEnd = ListState(&ast.Node) { |
| 1241 | .ExprListItemOrEnd = ExprListCtx { |
| 1242 | 1242 | .list = &node.params, |
| 1243 | 1243 | .end = Token.Id.RParen, |
| 1244 | 1244 | .ptr = &node.rparen_token, |
| ... | ... | @@ -1400,6 +1400,43 @@ pub const Parser = struct { |
| 1400 | 1400 | Token.Id.Keyword_asm => { |
| 1401 | 1401 | @panic("TODO: inline asm"); |
| 1402 | 1402 | }, |
| 1403 | Token.Id.Keyword_if => { |
| 1404 | @panic("TODO: inline if"); |
| 1405 | }, |
| 1406 | Token.Id.Keyword_while => { |
| 1407 | @panic("TODO: inline while"); |
| 1408 | }, |
| 1409 | Token.Id.Keyword_for => { |
| 1410 | @panic("TODO: inline for"); |
| 1411 | }, |
| 1412 | Token.Id.Keyword_switch => { |
| 1413 | const node = try arena.create(ast.NodeSwitch); |
| 1414 | *node = ast.NodeSwitch { |
| 1415 | .base = self.initNode(ast.Node.Id.Switch), |
| 1416 | .switch_token = token, |
| 1417 | .expr = undefined, |
| 1418 | .cases = ArrayList(&ast.NodeSwitchCase).init(arena), |
| 1419 | .rbrace = undefined, |
| 1420 | }; |
| 1421 | dest_ptr.store(&node.base); |
| 1422 | |
| 1423 | stack.append(State { |
| 1424 | .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) { |
| 1425 | .list = &node.cases, |
| 1426 | .ptr = &node.rbrace, |
| 1427 | }, |
| 1428 | }) catch unreachable; |
| 1429 | try stack.append(State { .ExpectToken = Token.Id.LBrace }); |
| 1430 | try stack.append(State { .ExpectToken = Token.Id.RParen }); |
| 1431 | try stack.append(State { .Expression = DestPtr { .Field = &node.expr } }); |
| 1432 | try stack.append(State { .ExpectToken = Token.Id.LParen }); |
| 1433 | }, |
| 1434 | Token.Id.Keyword_comptime => { |
| 1435 | @panic("TODO: inline comptime"); |
| 1436 | }, |
| 1437 | Token.Id.Keyword_suspend => { |
| 1438 | @panic("TODO: inline suspend"); |
| 1439 | }, |
| 1403 | 1440 | else => { |
| 1404 | 1441 | try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id)); |
| 1405 | 1442 | continue; |
| ... | ... | @@ -1463,8 +1500,7 @@ pub const Parser = struct { |
| 1463 | 1500 | State.FieldInitListItemOrEnd => |list_state| { |
| 1464 | 1501 | var token = self.getNextToken(); |
| 1465 | 1502 | |
| 1466 | | const IdTag = @TagType(Token.Id); |
| 1467 | | if (IdTag(list_state.end) == token.id){ |
| 1503 | if (token.id == Token.Id.RBrace){ |
| 1468 | 1504 | *list_state.ptr = token; |
| 1469 | 1505 | continue; |
| 1470 | 1506 | } |
| ... | ... | @@ -1497,13 +1533,82 @@ pub const Parser = struct { |
| 1497 | 1533 | }); |
| 1498 | 1534 | }, |
| 1499 | 1535 | |
| 1536 | State.SwitchCaseOrEnd => |list_state| { |
| 1537 | var token = self.getNextToken(); |
| 1538 | |
| 1539 | if (token.id == Token.Id.RBrace){ |
| 1540 | *list_state.ptr = token; |
| 1541 | continue; |
| 1542 | } |
| 1543 | |
| 1544 | self.putBackToken(token); |
| 1545 | |
| 1546 | const node = try arena.create(ast.NodeSwitchCase); |
| 1547 | *node = ast.NodeSwitchCase { |
| 1548 | .base = self.initNode(ast.Node.Id.SwitchCase), |
| 1549 | .items = ArrayList(&ast.Node).init(arena), |
| 1550 | .capture = null, |
| 1551 | .expr = undefined, |
| 1552 | }; |
| 1553 | try list_state.list.append(node); |
| 1554 | stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable; |
| 1555 | try stack.append(State { .Expression = DestPtr{ .Field = &node.expr } }); |
| 1556 | try stack.append(State { .SwitchCaseCapture = &node.capture }); |
| 1557 | |
| 1558 | const maybe_else = self.getNextToken(); |
| 1559 | if (maybe_else.id == Token.Id.Keyword_else) { |
| 1560 | const else_node = try arena.create(ast.NodeSwitchElse); |
| 1561 | *else_node = ast.NodeSwitchElse { |
| 1562 | .base = self.initNode(ast.Node.Id.SwitchElse), |
| 1563 | .token = maybe_else, |
| 1564 | }; |
| 1565 | try node.items.append(&else_node.base); |
| 1566 | try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); |
| 1567 | continue; |
| 1568 | } else { |
| 1569 | self.putBackToken(maybe_else); |
| 1570 | try stack.append(State { .SwitchCaseItem = &node.items }); |
| 1571 | continue; |
| 1572 | } |
| 1573 | }, |
| 1574 | |
| 1575 | State.SwitchCaseCapture => |capture| { |
| 1576 | const token = self.getNextToken(); |
| 1577 | if (token.id != Token.Id.Pipe) { |
| 1578 | self.putBackToken(token); |
| 1579 | continue; |
| 1580 | } |
| 1581 | |
| 1582 | const is_ptr = blk: { |
| 1583 | const asterik = self.getNextToken(); |
| 1584 | if (asterik.id == Token.Id.Asterisk) { |
| 1585 | break :blk true; |
| 1586 | } else { |
| 1587 | self.putBackToken(asterik); |
| 1588 | break :blk false; |
| 1589 | } |
| 1590 | }; |
| 1591 | |
| 1592 | const ident = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue; |
| 1593 | _ = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue; |
| 1594 | *capture = ast.NodeSwitchCase.Capture { |
| 1595 | .symbol = try self.createIdentifier(arena, ident), |
| 1596 | .is_ptr = is_ptr |
| 1597 | }; |
| 1598 | }, |
| 1599 | |
| 1600 | State.SwitchCaseItem => |case_items| { |
| 1601 | stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; |
| 1602 | try stack.append(State { .RangeExpressionBegin = DestPtr{ .Field = try case_items.addOne() } }); |
| 1603 | }, |
| 1604 | |
| 1500 | 1605 | State.ExprListCommaOrEnd => |list_state| { |
| 1501 | 1606 | try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state }); |
| 1502 | 1607 | continue; |
| 1503 | 1608 | }, |
| 1504 | 1609 | |
| 1505 | 1610 | State.FieldInitListCommaOrEnd => |list_state| { |
| 1506 | | try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .FieldInitListItemOrEnd = list_state }); |
| 1611 | try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state }); |
| 1507 | 1612 | continue; |
| 1508 | 1613 | }, |
| 1509 | 1614 | |
| ... | ... | @@ -1513,6 +1618,16 @@ pub const Parser = struct { |
| 1513 | 1618 | continue; |
| 1514 | 1619 | }, |
| 1515 | 1620 | |
| 1621 | State.SwitchCaseCommaOrEnd => |list_state| { |
| 1622 | try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state }); |
| 1623 | continue; |
| 1624 | }, |
| 1625 | |
| 1626 | State.SwitchCaseItemCommaOrEnd => |case_items| { |
| 1627 | try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items }); |
| 1628 | continue; |
| 1629 | }, |
| 1630 | |
| 1516 | 1631 | State.AddrOfModifiers => |addr_of_info| { |
| 1517 | 1632 | var token = self.getNextToken(); |
| 1518 | 1633 | switch (token.id) { |
| ... | ... | @@ -1741,6 +1856,11 @@ pub const Parser = struct { |
| 1741 | 1856 | stack.append(State { .Block = inner_block }) catch unreachable; |
| 1742 | 1857 | continue; |
| 1743 | 1858 | }, |
| 1859 | Token.Id.Keyword_switch => { |
| 1860 | self.putBackToken(next); |
| 1861 | stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } }) catch unreachable; |
| 1862 | continue; |
| 1863 | }, |
| 1744 | 1864 | else => { |
| 1745 | 1865 | self.putBackToken(next); |
| 1746 | 1866 | stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; |
| ... | ... | @@ -1754,7 +1874,7 @@ pub const Parser = struct { |
| 1754 | 1874 | } |
| 1755 | 1875 | } |
| 1756 | 1876 | |
| 1757 | | fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, ptr: &Token, state_after_comma: &const State) !void { |
| 1877 | fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void { |
| 1758 | 1878 | var token = self.getNextToken(); |
| 1759 | 1879 | switch (token.id) { |
| 1760 | 1880 | Token.Id.Comma => { |
| ... | ... | @@ -1763,7 +1883,9 @@ pub const Parser = struct { |
| 1763 | 1883 | else => { |
| 1764 | 1884 | const IdTag = @TagType(Token.Id); |
| 1765 | 1885 | if (IdTag(*end) == token.id) { |
| 1766 | | *ptr = token; |
| 1886 | if (maybe_ptr) |ptr| { |
| 1887 | *ptr = token; |
| 1888 | } |
| 1767 | 1889 | return; |
| 1768 | 1890 | } |
| 1769 | 1891 | |
| ... | ... | @@ -2498,7 +2620,8 @@ pub const Parser = struct { |
| 2498 | 2620 | ast.NodeInfixOp.InfixOp.Sub => " - ", |
| 2499 | 2621 | ast.NodeInfixOp.InfixOp.SubWrap => " -% ", |
| 2500 | 2622 | ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ", |
| 2501 | | else => unreachable, |
| 2623 | ast.NodeInfixOp.InfixOp.Range => " ... ", |
| 2624 | ast.NodeInfixOp.InfixOp.Catch => unreachable, |
| 2502 | 2625 | }; |
| 2503 | 2626 | |
| 2504 | 2627 | try stack.append(RenderState { .Text = text }); |
| ... | ... | @@ -2821,8 +2944,73 @@ pub const Parser = struct { |
| 2821 | 2944 | }, |
| 2822 | 2945 | ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"), |
| 2823 | 2946 | ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"), |
| 2824 | | ast.Node.Id.Switch => @panic("TODO switch"), |
| 2825 | | ast.Node.Id.SwitchCase => @panic("TODO switch case"), |
| 2947 | ast.Node.Id.Switch => { |
| 2948 | const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base); |
| 2949 | try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); |
| 2950 | |
| 2951 | try stack.append(RenderState { .Text = "}"}); |
| 2952 | try stack.append(RenderState.PrintIndent); |
| 2953 | try stack.append(RenderState { .Indent = indent }); |
| 2954 | try stack.append(RenderState { .Text = "\n"}); |
| 2955 | |
| 2956 | const cases = switch_node.cases.toSliceConst(); |
| 2957 | var i = cases.len; |
| 2958 | while (i != 0) { |
| 2959 | i -= 1; |
| 2960 | const node = cases[i]; |
| 2961 | try stack.append(RenderState { .Expression = &node.base}); |
| 2962 | try stack.append(RenderState.PrintIndent); |
| 2963 | try stack.append(RenderState { |
| 2964 | .Text = blk: { |
| 2965 | if (i != 0) { |
| 2966 | const prev_node = cases[i - 1]; |
| 2967 | const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); |
| 2968 | if (loc.line >= 2) { |
| 2969 | break :blk "\n\n"; |
| 2970 | } |
| 2971 | } |
| 2972 | break :blk "\n"; |
| 2973 | }, |
| 2974 | }); |
| 2975 | |
| 2976 | if (i != 0) { |
| 2977 | try stack.append(RenderState { .Text = "," }); |
| 2978 | } |
| 2979 | } |
| 2980 | try stack.append(RenderState { .Indent = indent + indent_delta}); |
| 2981 | try stack.append(RenderState { .Text = ") {"}); |
| 2982 | try stack.append(RenderState { .Expression = switch_node.expr }); |
| 2983 | }, |
| 2984 | ast.Node.Id.SwitchCase => { |
| 2985 | const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base); |
| 2986 | |
| 2987 | try stack.append(RenderState { .Expression = switch_case.expr }); |
| 2988 | if (switch_case.capture) |capture| { |
| 2989 | try stack.append(RenderState { .Text = "| "}); |
| 2990 | try stack.append(RenderState { .Expression = &capture.symbol.base }); |
| 2991 | |
| 2992 | if (capture.is_ptr) { |
| 2993 | try stack.append(RenderState { .Text = "*"}); |
| 2994 | } |
| 2995 | try stack.append(RenderState { .Text = "|"}); |
| 2996 | } |
| 2997 | try stack.append(RenderState { .Text = " => "}); |
| 2998 | |
| 2999 | const items = switch_case.items.toSliceConst(); |
| 3000 | var i = items.len; |
| 3001 | while (i != 0) { |
| 3002 | i -= 1; |
| 3003 | try stack.append(RenderState { .Expression = items[i] }); |
| 3004 | |
| 3005 | if (i != 0) { |
| 3006 | try stack.append(RenderState { .Text = ", " }); |
| 3007 | } |
| 3008 | } |
| 3009 | }, |
| 3010 | ast.Node.Id.SwitchElse => { |
| 3011 | const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base); |
| 3012 | try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token)); |
| 3013 | }, |
| 2826 | 3014 | |
| 2827 | 3015 | ast.Node.Id.StructField, |
| 2828 | 3016 | ast.Node.Id.UnionTag, |
| ... | ... | @@ -2867,7 +3055,7 @@ pub const Parser = struct { |
| 2867 | 3055 | const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base); |
| 2868 | 3056 | try stack.append(RenderState { .VarDecl = var_decl}); |
| 2869 | 3057 | }, |
| 2870 | | ast.Node.Id.Block => { |
| 3058 | ast.Node.Id.Block, ast.Node.Id.Switch => { |
| 2871 | 3059 | try stack.append(RenderState { .Expression = base}); |
| 2872 | 3060 | }, |
| 2873 | 3061 | else => { |
| ... | ... | @@ -3436,24 +3624,24 @@ test "zig fmt: switch" { |
| 3436 | 3624 | \\ else => { |
| 3437 | 3625 | \\ const a = 1; |
| 3438 | 3626 | \\ const b = a; |
| 3439 | | \\ }, |
| 3627 | \\ } |
| 3440 | 3628 | \\ } |
| 3441 | 3629 | \\ |
| 3442 | 3630 | \\ const res = switch (0) { |
| 3443 | 3631 | \\ 0 => 0, |
| 3444 | 3632 | \\ 1 => 2, |
| 3445 | | \\ else => 4, |
| 3633 | \\ else => 4 |
| 3446 | 3634 | \\ }; |
| 3447 | 3635 | \\ |
| 3448 | 3636 | \\ const Union = union(enum) { |
| 3449 | 3637 | \\ Int: i64, |
| 3450 | | \\ Float: f64, |
| 3638 | \\ Float: f64 |
| 3451 | 3639 | \\ }; |
| 3452 | 3640 | \\ |
| 3453 | | \\ const u = Union { .Int = 0 }; |
| 3641 | \\ const u = Union{ .Int = 0 }; |
| 3454 | 3642 | \\ switch (u) { |
| 3455 | 3643 | \\ Union.Int => |int| {}, |
| 3456 | | \\ Union.Float => |*float| unreachable, |
| 3644 | \\ Union.Float => |*float| unreachable |
| 3457 | 3645 | \\ } |
| 3458 | 3646 | \\} |
| 3459 | 3647 | \\ |