diff --git a/src/compiler.bbb b/src/compiler.bbb index 5614547..6a59f5b 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -2264,6 +2264,28 @@ Statement = struct id: StatementId, } +scope_to_block = fn (scope: &Scope) &Block +{ + assert(scope.kind == .local); + >result: &Block = #field_parent_pointer(scope, "scope"); + return result; +} + +new_local = fn (module: &Module, scope: &Scope) &Local +{ + >result = arena_allocate[Local](module.arena, 1); + result.& = zero; + + switch (scope.kind) + { + .local => + { + >block = scope_to_block(scope); + #trap(); + }, + } +} + new_global = fn (module: &Module) &Global { >result = arena_allocate[Global](module.arena, 1); @@ -3632,7 +3654,43 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement { '>' => { - #trap(); + module.offset += 1; + skip_space(module); + + >local_name = parse_identifier(module); + skip_space(module); + + >local_type: &Type = zero; + + if (consume_character_if_match(module, ':')) + { + skip_space(module); + local_type = parse_type(module, scope); + skip_space(module); + } + + expect_character(module, '='); + + >initial_value = parse_value(module, scope, zero); + + >local = new_local(module, scope); + local.& = { + .variable = { + .type = local_type, + .scope = scope, + .name = local_name, + .line = statement_line, + .column = statement_column, + zero, + }, + .initial_value = initial_value, + zero, + }; + + statement.content = { + .local = local, + }; + statement.id = .local; }, '#' => { @@ -6480,7 +6538,16 @@ names: [_][]u8 = [ "constant_xor", "constant_shift_left", "constant_shift_right", - + "minimal_stack", + "minimal_stack_arithmetic", + "minimal_stack_arithmetic2", + "minimal_stack_arithmetic3", + "stack_negation", + "stack_add", + "stack_sub", + "extend", + "integer_max", + "integer_hex", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/compiler.cpp b/src/compiler.cpp index 3615b92..543d771 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -439,6 +439,7 @@ global_variable String names[] = string_literal("return_array"), string_literal("bool_pair"), string_literal("min_max"), + string_literal("field_parent_pointer"), }; void entry_point(Slice arguments, Slice envp) diff --git a/src/compiler.hpp b/src/compiler.hpp index 3cefaa2..83c45d2 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -842,6 +842,7 @@ enum class ValueId argument, build_mode, has_debug_info, + field_parent_pointer, }; struct ValueConstantInteger @@ -1055,6 +1056,12 @@ struct MacroInstantiation LLVMBasicBlockRef return_block; }; +struct FieldParentPointer +{ + Value* pointer; + String name; +}; + fn bool variable_is_constant(Value* value); struct Value @@ -1080,6 +1087,7 @@ struct Value ValueStringToEnum string_to_enum; MacroDeclaration* macro_reference; MacroInstantiation macro_instantiation; + FieldParentPointer field_parent_pointer; }; Type* type; ValueId id; diff --git a/src/emitter.cpp b/src/emitter.cpp index 9e9119d..2aba2c9 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -4445,6 +4445,51 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal value_type = uint1(module); typecheck(module, expected_type, value_type); } break; + case ValueId::field_parent_pointer: + { + auto field_pointer = value->field_parent_pointer.pointer; + auto field_name = value->field_parent_pointer.name; + + if (!expected_type) + { + report_error(); + } + + value_type = expected_type; + + if (value_type->id != TypeId::pointer) + { + report_error(); + } + + auto aggregate_type = value_type->pointer.element_type; + + Type* field_type = 0; + switch (aggregate_type->id) + { + case TypeId::structure: + { + auto fields = aggregate_type->structure.fields; + for (auto& field : fields) + { + if (field_name.equal(field.name)) + { + field_type = field.type; + break; + } + } + } break; + default: report_error(); + } + + if (!field_type) + { + report_error(); + } + + auto pointer_to_field = get_pointer_type(module, field_type); + analyze_type(module, field_pointer, pointer_to_field, {}); + } break; default: unreachable(); } @@ -7813,6 +7858,49 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect { llvm_value = LLVMConstInt(get_llvm_type(resolved_value_type, type_kind), module->has_debug_info, false); } break; + case ValueId::field_parent_pointer: + { + auto field_pointer = value->field_parent_pointer.pointer; + auto field_name = value->field_parent_pointer.name; + + emit_value(module, field_pointer, TypeKind::memory, false); + auto llvm_field_pointer = field_pointer->llvm; + assert(llvm_field_pointer); + + assert(resolved_value_type->id == TypeId::pointer); + auto aggregate_type = resolved_value_type->pointer.element_type; + + switch (aggregate_type->id) + { + case TypeId::structure: + { + auto fields = aggregate_type->structure.fields; + Field* result_field = 0; + for (auto& field: fields) + { + if (field_name.equal(field.name)) + { + result_field = &field; + break; + } + } + + assert(result_field); + auto offset = result_field->offset; + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + auto llvm_u64 = u64_type->llvm.abi; + auto address_int = LLVMBuildPtrToInt(module->llvm.builder, llvm_field_pointer, llvm_u64, ""); + + address_int = LLVMBuildSub(module->llvm.builder, address_int, LLVMConstInt(llvm_u64, offset, false), ""); + + auto address_pointer = LLVMBuildIntToPtr(module->llvm.builder, address_int, resolved_value_type->llvm.abi, ""); + llvm_value = address_pointer; + } break; + default: + report_error(); + } + } break; default: unreachable(); } diff --git a/src/parser.cpp b/src/parser.cpp index 36181cb..0687eb7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -9,6 +9,7 @@ enum class ValueIntrinsic enum_name, enum_values, extend, + field_parent_pointer, has_debug_info, integer_max, int_from_enum, @@ -1162,6 +1163,7 @@ fn Token tokenize(Module* module) string_literal("enum_name"), string_literal("enum_values"), string_literal("extend"), + string_literal("field_parent_pointer"), string_literal("has_debug_info"), string_literal("integer_max"), string_literal("int_from_enum"), @@ -1878,6 +1880,30 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) .id = ValueId::build_mode, }; } break; + case ValueIntrinsic::field_parent_pointer: + { + skip_space(module); + expect_character(module, left_parenthesis); + + auto field_pointer = parse_value(module, scope, {}); + + skip_space(module); + expect_character(module, ','); + skip_space(module); + + auto field_name = parse_string_literal(module); + + skip_space(module); + expect_character(module, right_parenthesis); + + *result = { + .field_parent_pointer = { + .pointer = field_pointer, + .name = field_name, + }, + .id = ValueId::field_parent_pointer, + }; + } break; case ValueIntrinsic::count: unreachable(); } } break; diff --git a/tests/field_parent_pointer.bbb b/tests/field_parent_pointer.bbb new file mode 100644 index 0000000..4e10524 --- /dev/null +++ b/tests/field_parent_pointer.bbb @@ -0,0 +1,43 @@ +S = struct +{ + a: u8, + b: u32, + c: u8, +} + +assert = fn (ok: u1) void +{ + if (!ok) #trap(); +} + +[export] main = fn [cc(c)] () s32 +{ + >s: S = { + .a = 241, + .b = 12356, + .c = 128, + }; + + >p_a = &s.a; + >p_a_struct: &S = #field_parent_pointer(p_a, "a"); + assert(p_a_struct == &s); + assert(p_a_struct.a == s.a); + assert(p_a_struct.b == s.b); + assert(p_a_struct.c == s.c); + + >p_b = &s.b; + >p_b_struct: &S = #field_parent_pointer(p_b, "b"); + assert(p_b_struct == &s); + assert(p_b_struct.a == s.a); + assert(p_b_struct.b == s.b); + assert(p_b_struct.c == s.c); + + >p_c = &s.c; + >p_c_struct: &S = #field_parent_pointer(p_c, "c"); + assert(p_c_struct == &s); + assert(p_c_struct.a == s.a); + assert(p_c_struct.b == s.b); + assert(p_c_struct.c == s.c); + + return 0; +}