diff --git a/src/compiler.bbb b/src/compiler.bbb index 28f7798..2db3d91 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -2738,9 +2738,27 @@ StatementWhile = struct block: &Block, } +StatementSwitchDiscriminantId = enum +{ + single, + range, +} + +StatementSwitchDiscriminantContent = union +{ + single: &Value, + range: [2]&Value, +} + +StatementSwitchDiscriminant = struct +{ + content: StatementSwitchDiscriminantContent, + id: StatementSwitchDiscriminantId, +} + StatementSwitchClause = struct { - values: []&Value, + values: []StatementSwitchDiscriminant, block: &Block, } @@ -6014,7 +6032,148 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, .switch => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >discriminant = parse_value(module, scope, zero); + + skip_space(module); + expect_character(module, right_parenthesis); + + skip_space(module); + expect_character(module, left_brace); + + >clause_buffer: [64]StatementSwitchClause = undefined; + >clause_count: u64 = 0; + + while (1) + { + skip_space(module); + + >is_else: u1 = 0; + if (is_identifier_start(module.content[module.offset])) + { + >else_checkpoint = get_checkpoint(module); + >i = parse_identifier(module); + is_else = string_equal(i, "else"); + + if (!is_else) + { + set_checkpoint(module, else_checkpoint); + } + } + + >clause_values: []StatementSwitchDiscriminant = zero; + + if (is_else) + { + skip_space(module); + expect_character(module, '='); + expect_character(module, '>'); + } + else + { + >case_buffer: [64]StatementSwitchDiscriminant = undefined; + >case_count: u64 = 0; + + while (1) + { + >first_case_value = parse_value(module, scope, zero); + + skip_space(module); + + >checkpoint = get_checkpoint(module); + >token = tokenize(module); + + >clause_discriminant: StatementSwitchDiscriminant = undefined; + + switch (token.id) + { + .triple_dot => + { + >last_case_value = parse_value(module, scope, zero); + clause_discriminant = { + .content = { + .range = [ first_case_value, last_case_value ], + }, + .id = .range, + }; + }, + else => + { + if (token.id != .comma) + { + set_checkpoint(module, checkpoint); + } + + clause_discriminant = { + .content = { + .single = first_case_value, + }, + .id = .single, + }; + }, + } + + switch (clause_discriminant.id) + { + .single => { assert(clause_discriminant.content.single != zero); }, + .range => + { + assert(clause_discriminant.content.range[0] != zero); + assert(clause_discriminant.content.range[1] != zero); + }, + } + + case_buffer[case_count] = clause_discriminant; + case_count += 1; + + skip_space(module); + + if (consume_character_if_match(module, '=')) + { + expect_character(module, '>'); + break; + } + } + + clause_values = arena_allocate_slice[StatementSwitchDiscriminant](module.arena, case_count); + memcpy(#pointer_cast(clause_values.pointer), #pointer_cast(&case_buffer), case_count * #byte_size(StatementSwitchDiscriminant)); + } + + skip_space(module); + + >clause_block = parse_block(module, scope); + + clause_buffer[clause_count] = { + .values = clause_values, + .block = clause_block, + }; + clause_count += 1; + + consume_character_if_match(module, ','); + + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + } + + >clauses = arena_allocate_slice[StatementSwitchClause](module.arena, clause_count); + memcpy(#pointer_cast(clauses.pointer), #pointer_cast(&clause_buffer), clause_count * #byte_size(StatementSwitchClause)); + + require_semicolon = 0; + + statement.content = { + .switch = { + .discriminant = discriminant, + .clauses = clauses, + }, + }; + statement.id = .switch; }, .break => { @@ -13013,6 +13172,10 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v }, } }, + .switch => + { + #trap(); + }, else => { #trap(); @@ -14457,6 +14620,7 @@ names: [_][]u8 = "string_to_enum", "empty_if", "else_if", + "else_if_complicated", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32