parse switch

This commit is contained in:
David Gonzalez Martin 2025-04-15 09:01:17 -06:00
parent 03484afd75
commit 8a6eb3b714
2 changed files with 177 additions and 67 deletions

View File

@ -574,15 +574,9 @@ pub const Statement = struct {
@"return": ?*Value, @"return": ?*Value,
assignment: Assignment, assignment: Assignment,
expression: *Value, expression: *Value,
@"if": struct { @"if": If,
condition: *Value, @"while": While,
if_block: *LexicalBlock, @"switch": Switch,
else_block: ?*LexicalBlock,
},
@"while": struct {
condition: *Value,
block: *LexicalBlock,
},
}, },
line: u32, line: u32,
column: 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 { const Unary = struct {
@ -1561,6 +1576,7 @@ pub const Module = struct {
@"if", @"if",
// TODO: make `unreachable` a statement start keyword? // TODO: make `unreachable` a statement start keyword?
@"while", @"while",
@"switch",
}; };
const rules = blk: { const rules = blk: {
@ -2245,77 +2261,146 @@ pub const Module = struct {
'A'...'Z', 'a'...'z' => blk: { 'A'...'Z', 'a'...'z' => blk: {
const statement_start_identifier = module.parse_identifier(); const statement_start_identifier = module.parse_identifier();
if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| { if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| switch (statement_start_keyword) {
switch (statement_start_keyword) { ._ => @trap(),
._ => @trap(), .@"return" => break :blk .{
.@"return" => break :blk .{ .@"return" = module.parse_value(function, .{}),
.@"return" = module.parse_value(function, .{}), },
}, .@"if" => {
.@"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.skip_space();
module.expect_character(left_parenthesis); var case_buffer: [64]*Value = undefined;
module.skip_space(); 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.consume_character_if_match(',');
module.expect_character(right_parenthesis);
module.skip_space(); module.skip_space();
const if_block = module.parse_block(function); if (module.consume_character_if_match('=')) {
module.expect_character('>');
module.skip_space(); break;
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; module.skip_space();
require_semicolon = false; const clause_block = module.parse_block(function);
break :blk .{ const clause_values = module.arena.allocate(*Value, case_count);
.@"if" = .{ @memcpy(clause_values, case_buffer[0..case_count]);
.condition = condition,
.if_block = if_block, clause_buffer[clause_count] = .{
.else_block = else_block, .values = clause_values,
}, .block = clause_block,
}; };
}, clause_count += 1;
.@"while" => {
module.skip_space();
module.expect_character(left_parenthesis); _ = module.consume_character_if_match(',');
module.skip_space();
const condition = module.parse_value(function, .{});
module.skip_space();
module.expect_character(right_parenthesis);
module.skip_space(); module.skip_space();
const while_block = module.parse_block(function); if (module.consume_character_if_match(right_brace)) {
break;
}
}
require_semicolon = false; const clauses = module.arena.allocate(Statement.Switch.Clause, clause_count);
@memcpy(clauses, clause_buffer[0..clause_count]);
break :blk .{ require_semicolon = false;
.@"while" = .{
.condition = condition, break :blk .{
.block = while_block, .@"switch" = .{
}, .discriminant = discriminant,
}; .clauses = clauses,
}, },
} };
},
} else { } else {
module.offset -= statement_start_identifier.len; module.offset -= statement_start_identifier.len;
@ -3356,14 +3441,12 @@ pub const Module = struct {
const field_index = field_count; const field_index = field_count;
const field_name = module.parse_identifier(); const field_name = module.parse_identifier();
module.skip_space(); module.skip_space();
const has_explicit_value = module.consume_character_if_match('=');
const field_value = if (module.consume_character_if_match('=')) blk: { const field_value = if (has_explicit_value) blk: {
module.skip_space(); module.skip_space();
const field_value = module.parse_integer_value(false); const field_value = module.parse_integer_value(false);
break :blk field_value; break :blk field_value;
} else { } else field_index;
@trap();
};
field_buffer[field_index] = .{ field_buffer[field_index] = .{
.name = field_name, .name = field_name,
@ -6448,6 +6531,7 @@ pub const Module = struct {
module.llvm.builder.position_at_end(loop_end_block); module.llvm.builder.position_at_end(loop_end_block);
}, },
.@"switch" => @trap(),
} }
} }
} }

26
tests/switch.bbb Normal file
View File

@ -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;
},
}
}