From a9c2c3a6b0abb6b14aceaa1467c0e5f10979c9c1 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sat, 7 Jun 2025 09:14:54 -0600 Subject: [PATCH] wip --- src/compiler.bbb | 239 +++++++++++++++++++++++++++++++-- src/compiler.cpp | 1 + src/compiler.hpp | 8 ++ src/emitter.cpp | 88 ++++++++++++ src/parser.cpp | 29 ++++ tests/field_parent_pointer.bbb | 43 ++++++ 6 files changed, 399 insertions(+), 9 deletions(-) create mode 100644 tests/field_parent_pointer.bbb diff --git a/src/compiler.bbb b/src/compiler.bbb index 5614547..ce967fd 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1,3 +1,8 @@ +report_error = fn () noreturn +{ + #trap(); +} + mode_t = typealias u64; int = typealias s32; usize = typealias u64; @@ -1490,6 +1495,7 @@ ValueId = enum unary, binary, string_literal, + variable, } ValueConstantInteger = struct @@ -1641,6 +1647,7 @@ ValueContent = union function: ValueFunction, unary: ValueUnary, binary: ValueBinary, + variable: &Variable, } ValueKind = enum @@ -2264,6 +2271,47 @@ 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); + if (block.last_local) + { + block.last_local.next = result; + block.last_local = result; + } + else + { + block.first_local = result; + block.last_local = result; + } + }, + .for_each => + { + #trap(); + } + else => + { + report_error(); + } + } + + return result; +} + new_global = fn (module: &Module) &Global { >result = arena_allocate[Global](module.arena, 1); @@ -2437,11 +2485,6 @@ is_identifier = fn (ch: u8) u1 return is_identifier_start(ch) or is_decimal(ch); } -report_error = fn () noreturn -{ - #trap(); -} - get_line = fn (module: &Module) u32 { >line = module.line_offset + 1; @@ -2987,6 +3030,7 @@ TokenContent = union identifier: []u8, string_literal: []u8, } + Token = struct { content: TokenContent, @@ -3216,7 +3260,50 @@ tokenize = fn (module: &Module) Token { if (is_identifier_start(start_character)) { - #trap(); + >identifier = parse_identifier(module); + >value_keyword_s2e = #string_to_enum(ValueKeyword, identifier); + if (value_keyword_s2e.is_valid) + { + >value_keyword = value_keyword_s2e.enum_value; + token = { + .content = { + .value_keyword = value_keyword, + }, + .id = .value_keyword, + }; + } + else + { + >advance = identifier.pointer[identifier.length] == '?'; + identifier.length += #extend(advance); + module.offset += #extend(advance); + + >operator_keyword_s2e = #string_to_enum(OperatorKeyword, identifier); + + if (operator_keyword_s2e.is_valid) + { + >operator_keyword = operator_keyword_s2e.enum_value; + + token = { + .content = { + .operator_keyword = operator_keyword, + }, + .id = .operator_keyword, + }; + } + else + { + identifier.length -= #extend(advance); + module.offset -= #extend(advance); + + token = { + .content = { + .identifier = identifier, + }, + .id = .identifier, + }; + } + } } else { @@ -3256,6 +3343,83 @@ ValueBuilder = struct parse_precedence = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value; +reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: []u8, kind: ValueKind) &Value +{ + assert(!string_equal(identifier, "")); + assert(!string_equal(identifier, "_")); + + >variable: &Variable = zero; + >scope = current_scope; + + while (scope != zero and variable == zero) + { + switch (scope.kind) + { + .global => + { + #trap(); + }, + .function => + { + #trap(); + }, + .local => + { + assert(scope.parent != zero); + assert(scope.parent.kind != .global); + + >block = scope_to_block(scope); + >local = block.first_local; + + while (local != zero) + { + assert(local.next == zero or block.last_local != local); + + if (string_equal(identifier, local.variable.name)) + { + variable = &local.variable; + break; + } + + local = local.next; + } + }, + .for_each => + { + #trap(); + }, + .macro_declaration => + { + #trap(); + }, + .macro_instantiation => + { + #trap(); + }, + } + + scope = scope.parent; + } + + if (variable != zero) + { + >result = new_value(module); + result.& = { + .content = { + .variable = variable, + }, + .id = .variable, + .kind = kind, + zero, + }; + return result; + } + else + { + report_error(); + } +} + parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value { >token = builder.token; @@ -3315,7 +3479,7 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .identifier => { - #trap(); + result = reference_identifier(module, scope, token.content.identifier, builder.kind); }, .value_intrinsic => { @@ -3632,7 +3796,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; }, '#' => { @@ -5445,6 +5645,18 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v report_error(); } }, + .local => + { + >local = statement.content.local; + >expected_type = local.variable.type; + assert(!local.variable.storage); + + analyze_type(module, local.initial_value, expected_type, zero); + local.variable.type = #select(expected_type != zero, expected_type, local.initial_value.type); + assert(local.variable.type != zero); + + #trap(); + }, else => { #trap(); @@ -6480,7 +6692,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..3a4a7d7 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"), @@ -1494,6 +1496,9 @@ fn Token tokenize(Module* module) auto operator_keyword = (OperatorKeyword)i; if (operator_keyword == OperatorKeyword::count) { + identifier.length -= advance; + module->offset -= advance; + token = { .identifier = identifier, .id = TokenId::identifier, @@ -1878,6 +1883,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; +}