Commit b4d81857f3

Veikka Tuominen <git@vexu.eu>
2022-02-13 17:23:35
stage1+2: parse inline switch cases
1 parent 8849792
Changed files (6)
lib/std/zig/Ast.zig
@@ -643,11 +643,23 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
                 n = datas[n].lhs;
             }
         },
+        .switch_case_inline_one => {
+            if (datas[n].lhs == 0) {
+                return main_tokens[n] - 2 - end_offset; // else token
+            } else {
+                return firstToken(tree, datas[n].lhs) - 1;
+            }
+        },
         .switch_case => {
             const extra = tree.extraData(datas[n].lhs, Node.SubRange);
             assert(extra.end - extra.start > 0);
             n = tree.extra_data[extra.start];
         },
+        .switch_case_inline => {
+            const extra = tree.extraData(datas[n].lhs, Node.SubRange);
+            assert(extra.end - extra.start > 0);
+            return firstToken(tree, tree.extra_data[extra.start]) - 1;
+        },
 
         .asm_output, .asm_input => {
             assert(token_tags[main_tokens[n] - 1] == .l_bracket);
@@ -763,7 +775,9 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
         .ptr_type_bit_range,
         .array_type,
         .switch_case_one,
+        .switch_case_inline_one,
         .switch_case,
+        .switch_case_inline,
         .switch_range,
         => n = datas[n].rhs,
 
@@ -1755,7 +1769,7 @@ pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
         .values = if (data.lhs == 0) values[0..0] else values[0..1],
         .arrow_token = tree.nodes.items(.main_token)[node],
         .target_expr = data.rhs,
-    });
+    }, node);
 }
 
 pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
@@ -1765,7 +1779,7 @@ pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
         .values = tree.extra_data[extra.start..extra.end],
         .arrow_token = tree.nodes.items(.main_token)[node],
         .target_expr = data.rhs,
-    });
+    }, node);
 }
 
 pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {
@@ -2038,15 +2052,21 @@ fn fullContainerDecl(tree: Ast, info: full.ContainerDecl.Components) full.Contai
     return result;
 }
 
-fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components) full.SwitchCase {
+fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase {
     const token_tags = tree.tokens.items(.tag);
+    const node_tags = tree.nodes.items(.tag);
     var result: full.SwitchCase = .{
         .ast = info,
         .payload_token = null,
+        .inline_token = null,
     };
     if (token_tags[info.arrow_token + 1] == .pipe) {
         result.payload_token = info.arrow_token + 2;
     }
+    switch (node_tags[node]) {
+        .switch_case_inline, .switch_case_inline_one => result.inline_token = firstToken(tree, node),
+        else => {},
+    }
     return result;
 }
 
@@ -2454,6 +2474,7 @@ pub const full = struct {
     };
 
     pub const SwitchCase = struct {
+        inline_token: ?TokenIndex,
         /// Points to the first token after the `|`. Will either be an identifier or
         /// a `*` (with an identifier immediately after it).
         payload_token: ?TokenIndex,
@@ -2847,9 +2868,13 @@ pub const Node = struct {
         /// `lhs => rhs`. If lhs is omitted it means `else`.
         /// main_token is the `=>`
         switch_case_one,
+        /// Same ast `switch_case_one` but the case is inline
+        switch_case_inline_one,
         /// `a, b, c => rhs`. `SubRange[lhs]`.
         /// main_token is the `=>`
         switch_case,
+        /// Same ast `switch_case` but the case is inline
+        switch_case_inline,
         /// `lhs...rhs`.
         switch_range,
         /// `while (lhs) rhs`.
lib/std/zig/parse.zig
@@ -3100,7 +3100,7 @@ const Parser = struct {
         return identifier;
     }
 
-    /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
+    /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrPayload? AssignExpr
     /// SwitchCase
     ///     <- SwitchItem (COMMA SwitchItem)* COMMA?
     ///      / KEYWORD_else
@@ -3108,6 +3108,8 @@ const Parser = struct {
         const scratch_top = p.scratch.items.len;
         defer p.scratch.shrinkRetainingCapacity(scratch_top);
 
+        const is_inline = p.eatToken(.keyword_inline) != null;
+
         if (p.eatToken(.keyword_else) == null) {
             while (true) {
                 const item = try p.parseSwitchItem();
@@ -3115,7 +3117,10 @@ const Parser = struct {
                 try p.scratch.append(p.gpa, item);
                 if (p.eatToken(.comma) == null) break;
             }
-            if (scratch_top == p.scratch.items.len) return null_node;
+            if (scratch_top == p.scratch.items.len) {
+                if (is_inline) p.tok_i -= 1;
+                return null_node;
+            }
         }
         const arrow_token = try p.expectToken(.equal_angle_bracket_right);
         _ = try p.parsePtrPayload();
@@ -3123,7 +3128,7 @@ const Parser = struct {
         const items = p.scratch.items[scratch_top..];
         switch (items.len) {
             0 => return p.addNode(.{
-                .tag = .switch_case_one,
+                .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
                 .main_token = arrow_token,
                 .data = .{
                     .lhs = 0,
@@ -3131,7 +3136,7 @@ const Parser = struct {
                 },
             }),
             1 => return p.addNode(.{
-                .tag = .switch_case_one,
+                .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
                 .main_token = arrow_token,
                 .data = .{
                     .lhs = items[0],
@@ -3139,7 +3144,7 @@ const Parser = struct {
                 },
             }),
             else => return p.addNode(.{
-                .tag = .switch_case,
+                .tag = if (is_inline) .switch_case_inline else .switch_case,
                 .main_token = arrow_token,
                 .data = .{
                     .lhs = try p.addExtra(try p.listToSpan(items)),
lib/std/zig/render.zig
@@ -685,8 +685,8 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
             return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
         },
 
-        .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
-        .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
+        .switch_case_one, .switch_case_inline_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
+        .switch_case, .switch_case_inline => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
 
         .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
         .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),
@@ -1509,6 +1509,11 @@ fn renderSwitchCase(
         break :blk hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token);
     };
 
+    // render inline keyword
+    if (switch_case.inline_token) |some| {
+        try renderToken(ais, tree, some, .space);
+    }
+
     // Render everything before the arrow
     if (switch_case.ast.values.len == 0) {
         try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword
src/stage1/all_types.hpp
@@ -1039,6 +1039,7 @@ struct AstNodeSwitchProng {
     AstNode *expr;
     bool var_is_ptr;
     bool any_items_are_range;
+    bool is_inline;
 };
 
 struct AstNodeSwitchRange {
src/stage1/parser.cpp
@@ -2306,7 +2306,7 @@ static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {
     return Optional<PtrIndexPayload>::some(res);
 }
 
-// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
+// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrPayload? AssignExpr
 static AstNode *ast_parse_switch_prong(ParseContext *pc) {
     AstNode *res = ast_parse_switch_case(pc);
     if (res == nullptr)
@@ -2331,9 +2331,11 @@ static AstNode *ast_parse_switch_prong(ParseContext *pc) {
 //     <- SwitchItem (COMMA SwitchItem)* COMMA?
 //      / KEYWORD_else
 static AstNode *ast_parse_switch_case(ParseContext *pc) {
+    bool is_inline = eat_token_if(pc, TokenIdKeywordInline) != 0;
     AstNode *first = ast_parse_switch_item(pc);
     if (first != nullptr) {
         AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first);
+        res->data.switch_prong.is_inline = is_inline;
         res->data.switch_prong.items.append(first);
         res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange;
 
@@ -2350,9 +2352,13 @@ static AstNode *ast_parse_switch_case(ParseContext *pc) {
     }
 
     TokenIndex else_token = eat_token_if(pc, TokenIdKeywordElse);
-    if (else_token != 0)
-        return ast_create_node(pc, NodeTypeSwitchProng, else_token);
+    if (else_token != 0) {
+        AstNode *res = ast_create_node(pc, NodeTypeSwitchProng, else_token);
+        res->data.switch_prong.is_inline = is_inline;
+        return res;
+    }
 
+    if (is_inline) pc->current_token -= 1;
     return nullptr;
 }
 
src/AstGen.zig
@@ -386,7 +386,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
         .simple_var_decl => unreachable,
         .aligned_var_decl => unreachable,
         .switch_case => unreachable,
+        .switch_case_inline => unreachable,
         .switch_case_one => unreachable,
+        .switch_case_inline_one => unreachable,
         .container_field_init => unreachable,
         .container_field_align => unreachable,
         .container_field => unreachable,
@@ -600,7 +602,9 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
         .@"errdefer" => unreachable, // Handled in `blockExpr`.
 
         .switch_case => unreachable, // Handled in `switchExpr`.
+        .switch_case_inline => unreachable, // Handled in `switchExpr`.
         .switch_case_one => unreachable, // Handled in `switchExpr`.
+        .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
         .switch_range => unreachable, // Handled in `switchExpr`.
 
         .asm_output => unreachable, // Handled in `asmExpr`.
@@ -6216,14 +6220,15 @@ fn switchExpr(
     var any_payload_is_ref = false;
     var scalar_cases_len: u32 = 0;
     var multi_cases_len: u32 = 0;
+    var inline_cases_len: u32 = 0;
     var special_prong: Zir.SpecialProng = .none;
     var special_node: Ast.Node.Index = 0;
     var else_src: ?Ast.TokenIndex = null;
     var underscore_src: ?Ast.TokenIndex = null;
     for (case_nodes) |case_node| {
         const case = switch (node_tags[case_node]) {
-            .switch_case_one => tree.switchCaseOne(case_node),
-            .switch_case => tree.switchCase(case_node),
+            .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
+            .switch_case, .switch_case_inline => tree.switchCase(case_node),
             else => unreachable,
         };
         if (case.payload_token) |payload_token| {
@@ -6318,6 +6323,9 @@ fn switchExpr(
         } else {
             multi_cases_len += 1;
         }
+        if (case.inline_token != null) {
+            inline_cases_len += 1;
+        }
     }
 
     const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
@@ -8436,7 +8444,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_
             .@"usingnamespace",
             .test_decl,
             .switch_case,
+            .switch_case_inline,
             .switch_case_one,
+            .switch_case_inline_one,
             .container_field_init,
             .container_field_align,
             .container_field,
@@ -8668,7 +8678,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
             .@"usingnamespace",
             .test_decl,
             .switch_case,
+            .switch_case_inline,
             .switch_case_one,
+            .switch_case_inline_one,
             .container_field_init,
             .container_field_align,
             .container_field,
@@ -8879,7 +8891,9 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
             .@"usingnamespace",
             .test_decl,
             .switch_case,
+            .switch_case_inline,
             .switch_case_one,
+            .switch_case_inline_one,
             .container_field_init,
             .container_field_align,
             .container_field,