From 1213be01286e271b6c77ff7342398ade178ced70 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sat, 14 Jun 2025 20:34:37 -0600 Subject: [PATCH] Implement for --- src/compiler.bbb | 309 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 304 insertions(+), 5 deletions(-) diff --git a/src/compiler.bbb b/src/compiler.bbb index 55d87df..030d6b3 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -2782,8 +2782,8 @@ StatementFor = struct { first_local: &Local, last_local: &Local, - kinds: ValueKind, - iteratables: &Value, + kinds: []ValueKind, + iteratables: []&Value, predicate: &Statement, scope: Scope, kind: StatementForKind, @@ -2811,6 +2811,12 @@ Statement = struct id: StatementId, } +scope_to_for = fn (scope: &Scope) &StatementFor +{ + assert(scope.kind == .for_each); + return #field_parent_pointer(scope, "scope"); +} + scope_to_block = fn (scope: &Scope) &Block { assert(scope.kind == .local); @@ -2852,7 +2858,17 @@ new_local = fn (module: &Module, scope: &Scope) &Local }, .for_each => { - #trap(); + >for_each = scope_to_for(scope); + if (for_each.last_local) + { + for_each.last_local.next = result; + } + else + { + for_each.first_local = result; + } + + for_each.last_local = result; } else => { @@ -4918,7 +4934,20 @@ reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: [ }, .for_each => { - #trap(); + assert(scope.parent != zero); + >for_each = scope_to_for(scope); + + >local = for_each.first_local; + while (local) + { + if (string_equal(identifier, local.variable.name)) + { + variable = &local.variable; + break; + } + + local = local.next; + } }, .macro_declaration => { @@ -6030,7 +6059,137 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement }, .for => { - #trap(); + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + >parent_scope = scope; + + statement.content = { + .for = { + .scope = { + .parent = parent_scope, + .line = statement_line, + .column = statement_column, + .kind = .for_each, + zero, + }, + zero, + }, + }; + statement.id = .for; + + >scope = &statement.content.for.scope; + >left_value_buffer: [64]ValueKind = undefined; + >left_value_count: u64 = 0; + + while (1) + { + skip_space(module); + + >is_left = module.content[module.offset] == '&'; + module.offset += #extend(is_left); + + >for_local_line = get_line(module); + >for_local_column = get_column(module); + + if (is_identifier_start(module.content[module.offset])) + { + >local_name = arena_duplicate_string(module.arena, parse_identifier(module)); + >local = new_local(module, scope); + local.& = { + .variable = { + .scope = scope, + .name = local_name, + .line = for_local_line, + .column = for_local_column, + zero, + }, + zero, + }; + + >kind: ValueKind = #select(is_left, .left, .right); + + left_value_buffer[left_value_count] = kind; + left_value_count += 1; + } + else + { + #trap(); + } + + skip_space(module); + + if (!consume_character_if_match(module, ',')) + { + expect_character(module, ':'); + break; + } + } + + skip_space(module); + + >right_value_buffer: [64]&Value = undefined; + >right_value_count: u64 = 0; + + skip_space(module); + + right_value_buffer[right_value_count] = parse_value(module, scope, zero); + right_value_count += 1; + + skip_space(module); + + >token = tokenize(module); + + >for_kind: StatementForKind = undefined; + + switch (token.id) + { + .double_dot => + { + if (left_value_count != 1) + { + report_error(); + } + + right_value_buffer[right_value_count] = parse_value(module, scope, zero); + right_value_count += 1; + + expect_character(module, right_parenthesis); + for_kind = .range; + }, + .right_parenthesis => + { + right_value_buffer[0].kind = .left; + for_kind = .slice; + }, + else => { report_error(); }, + } + + statement.content.for.kind = for_kind; + + if (for_kind == .slice and left_value_count != right_value_count) + { + report_error(); + } + + >left_values = arena_allocate_slice[ValueKind](module.arena, left_value_count); + memcpy(#pointer_cast(left_values.pointer), #pointer_cast(&left_value_buffer), left_value_count * #byte_size(ValueKind)); + + >right_values = arena_allocate_slice[&Value](module.arena, right_value_count); + memcpy(#pointer_cast(right_values.pointer), #pointer_cast(&right_value_buffer), right_value_count * #byte_size(&Value)); + + statement.content.for.kinds = left_values; + statement.content.for.iteratables = right_values; + + skip_space(module); + + >predicate = parse_statement(module, scope); + statement.content.for.predicate = predicate; + + skip_space(module); + + require_semicolon = 0; }, .while => { @@ -13471,6 +13630,144 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v LLVMClearInsertionPosition(module.llvm.builder); } }, + .for => + { + if (module.has_debug_info) + { + >lexical_block = LLVMDIBuilderCreateLexicalBlock(module.llvm.di_builder, statement.content.for.scope.parent.llvm, module.llvm.file, statement.content.for.scope.line, statement.content.for.scope.column); + statement.content.for.scope.llvm = lexical_block; + } + + >index_type = uint64(module); + resolve_type_in_place(module, index_type); + >index_zero = LLVMConstNull(index_type.llvm.abi); + + >entry_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.entry"); + >body_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.body"); + >continue_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.continue"); + >exit_block = LLVMAppendBasicBlockInContext(module.llvm.context, llvm_function, "for_each.exit"); + + >previous_continue_block = module.llvm.continue_block; + >previous_exit_block = module.llvm.exit_block; + + module.llvm.continue_block = continue_block; + module.llvm.exit_block = exit_block; + + >kinds = statement.content.for.kinds; + >iteratables = statement.content.for.iteratables; + + switch (statement.content.for.kind) + { + .slice => + { + #trap(); + }, + .range => + { + >local = statement.content.for.first_local; + assert(local != zero); + assert(!local.next); + assert(!local.variable.type); + + assert(kinds.length == 1); + + if (iteratables.length == 2) + { + >start = iteratables[0]; + >end = iteratables[1]; + + >local_type: &Type = zero; + + if (value_is_constant(start)) + { + #trap(); + } + else + { + analyze_binary_type(module, start, end, 0, 0, 0, 0); + assert(start.type == end.type); + local_type = start.type; + } + + assert(local_type != zero); + + for (iteratable: iteratables) + { + if (!iteratable.type) + { + analyze_type(module, iteratable, local_type, zero); + } + } + + local.variable.type = local_type; + emit_local(module, local); + emit_value(module, start, .memory, 0); + + >index_alloca = local.variable.storage.llvm; + + create_store(module, { + .source = start.llvm, + .destination = index_alloca, + .type = local_type, + zero, + }); + + LLVMBuildBr(module.llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module.llvm.builder, entry_block); + + >header_index_load = create_load(module, { + .type = local_type, + .pointer = index_alloca, + zero, + }); + + emit_value(module, end, .abi, 0); + + >length_value = end.llvm; + >index_compare = LLVMBuildICmp(module.llvm.builder, .ult, header_index_load, length_value, ""); + LLVMBuildCondBr(module.llvm.builder, index_compare, body_block, exit_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, body_block); + analyze_statement(module, &statement.content.for.scope, statement.content.for.predicate); + + if (LLVMGetInsertBlock(module.llvm.builder)) + { + LLVMBuildBr(module.llvm.builder, continue_block); + } + + LLVMPositionBuilderAtEnd(module.llvm.builder, continue_block); + + >continue_index_load = create_load(module, { + .type = local_type, + .pointer = index_alloca, + zero, + }); + + >inc = LLVMBuildAdd(module.llvm.builder, continue_index_load, LLVMConstInt(local_type.llvm.abi, 1, 0), ""); + + create_store(module, { + .source = inc, + .destination = index_alloca, + .type = local_type, + zero, + }); + + LLVMBuildBr(module.llvm.builder, entry_block); + + LLVMPositionBuilderAtEnd(module.llvm.builder, exit_block); + } + else + { + // TODO: case for reverse range + #trap(); + } + }, + } + + // END OF SCOPE + module.llvm.continue_block = previous_continue_block; + module.llvm.exit_block = previous_exit_block; + }, else => { #trap(); @@ -14918,6 +15215,8 @@ names: [_][]u8 = "else_if_complicated", "basic_shortcircuiting_if", "shortcircuiting_if", + "field_access_left_assign", + "for_each", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32