diff --git a/src/bootstrap.zig b/src/bootstrap.zig index ff6bab8..c9dbc6a 100644 --- a/src/bootstrap.zig +++ b/src/bootstrap.zig @@ -574,15 +574,9 @@ pub const Statement = struct { @"return": ?*Value, assignment: Assignment, expression: *Value, - @"if": struct { - condition: *Value, - if_block: *LexicalBlock, - else_block: ?*LexicalBlock, - }, - @"while": struct { - condition: *Value, - block: *LexicalBlock, - }, + @"if": If, + @"while": While, + @"switch": Switch, }, line: u32, column: u32, @@ -606,6 +600,27 @@ pub const Statement = struct { @"^=", }; }; + + const If = struct { + condition: *Value, + if_block: *LexicalBlock, + else_block: ?*LexicalBlock, + }; + + const While = struct { + condition: *Value, + block: *LexicalBlock, + }; + + const Switch = struct { + discriminant: *Value, + clauses: []const Clause, + + const Clause = struct { + values: []const *Value, + block: *LexicalBlock, + }; + }; }; const Unary = struct { @@ -1561,6 +1576,7 @@ pub const Module = struct { @"if", // TODO: make `unreachable` a statement start keyword? @"while", + @"switch", }; const rules = blk: { @@ -2245,77 +2261,146 @@ pub const Module = struct { 'A'...'Z', 'a'...'z' => blk: { const statement_start_identifier = module.parse_identifier(); - if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| { - switch (statement_start_keyword) { - ._ => @trap(), - .@"return" => break :blk .{ - .@"return" = module.parse_value(function, .{}), - }, - .@"if" => { + if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| switch (statement_start_keyword) { + ._ => @trap(), + .@"return" => break :blk .{ + .@"return" = module.parse_value(function, .{}), + }, + .@"if" => { + module.skip_space(); + + module.expect_character(left_parenthesis); + module.skip_space(); + + const condition = module.parse_value(function, .{}); + + module.skip_space(); + module.expect_character(right_parenthesis); + + module.skip_space(); + + const if_block = module.parse_block(function); + + module.skip_space(); + + var is_else = false; + if (is_identifier_start_ch(module.content[module.offset])) { + const identifier = module.parse_identifier(); + is_else = lib.string.equal(identifier, "else"); + if (!is_else) { + module.offset -= identifier.len; + } else { + module.skip_space(); + } + } + + const else_block = if (is_else) module.parse_block(function) else null; + + require_semicolon = false; + + break :blk .{ + .@"if" = .{ + .condition = condition, + .if_block = if_block, + .else_block = else_block, + }, + }; + }, + .@"while" => { + module.skip_space(); + + module.expect_character(left_parenthesis); + module.skip_space(); + + const condition = module.parse_value(function, .{}); + + module.skip_space(); + module.expect_character(right_parenthesis); + + module.skip_space(); + + const while_block = module.parse_block(function); + + require_semicolon = false; + + break :blk .{ + .@"while" = .{ + .condition = condition, + .block = while_block, + }, + }; + }, + .@"switch" => { + module.skip_space(); + module.expect_character(left_parenthesis); + module.skip_space(); + + const discriminant = module.parse_value(function, .{}); + + module.skip_space(); + module.expect_character(right_parenthesis); + + module.skip_space(); + module.expect_character(left_brace); + + var clause_buffer: [64]Statement.Switch.Clause = undefined; + var clause_count: u64 = 0; + + while (true) { module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); + var case_buffer: [64]*Value = undefined; + var case_count: u64 = 0; - const condition = module.parse_value(function, .{}); + while (true) { + const case_value = module.parse_value(function, .{}); + case_buffer[case_count] = case_value; + case_count += 1; - module.skip_space(); - module.expect_character(right_parenthesis); + _ = module.consume_character_if_match(','); - module.skip_space(); + module.skip_space(); - const if_block = module.parse_block(function); - - module.skip_space(); - - var is_else = false; - if (is_identifier_start_ch(module.content[module.offset])) { - const identifier = module.parse_identifier(); - is_else = lib.string.equal(identifier, "else"); - if (!is_else) { - module.offset -= identifier.len; - } else { - module.skip_space(); + if (module.consume_character_if_match('=')) { + module.expect_character('>'); + break; } } - const else_block = if (is_else) module.parse_block(function) else null; + module.skip_space(); - require_semicolon = false; + const clause_block = module.parse_block(function); - break :blk .{ - .@"if" = .{ - .condition = condition, - .if_block = if_block, - .else_block = else_block, - }, + const clause_values = module.arena.allocate(*Value, case_count); + @memcpy(clause_values, case_buffer[0..case_count]); + + clause_buffer[clause_count] = .{ + .values = clause_values, + .block = clause_block, }; - }, - .@"while" => { - module.skip_space(); + clause_count += 1; - module.expect_character(left_parenthesis); - module.skip_space(); - - const condition = module.parse_value(function, .{}); + _ = module.consume_character_if_match(','); module.skip_space(); - module.expect_character(right_parenthesis); + + if (module.consume_character_if_match(right_brace)) { + break; + } + } - module.skip_space(); + const clauses = module.arena.allocate(Statement.Switch.Clause, clause_count); + @memcpy(clauses, clause_buffer[0..clause_count]); - const while_block = module.parse_block(function); + require_semicolon = false; - require_semicolon = false; - - break :blk .{ - .@"while" = .{ - .condition = condition, - .block = while_block, - }, - }; - }, - } + break :blk .{ + .@"switch" = .{ + .discriminant = discriminant, + .clauses = clauses, + }, + }; + }, } else { module.offset -= statement_start_identifier.len; @@ -3356,14 +3441,12 @@ pub const Module = struct { const field_index = field_count; const field_name = module.parse_identifier(); module.skip_space(); - - const field_value = if (module.consume_character_if_match('=')) blk: { + const has_explicit_value = module.consume_character_if_match('='); + const field_value = if (has_explicit_value) blk: { module.skip_space(); const field_value = module.parse_integer_value(false); break :blk field_value; - } else { - @trap(); - }; + } else field_index; field_buffer[field_index] = .{ .name = field_name, @@ -6448,6 +6531,7 @@ pub const Module = struct { module.llvm.builder.position_at_end(loop_end_block); }, + .@"switch" => @trap(), } } } diff --git a/tests/switch.bbb b/tests/switch.bbb new file mode 100644 index 0000000..075439e --- /dev/null +++ b/tests/switch.bbb @@ -0,0 +1,26 @@ +E = enum +{ + a, + b, + c, +} + +[export] main = fn [cc(c)] () s32 +{ + >some_enum: E = .a; + switch (some_enum) + { + .a => + { + return 0; + }, + .b => + { + return 1; + }, + .c => + { + return 1; + }, + } +}