wip
This commit is contained in:
parent
0b1a070250
commit
a9c2c3a6b0
239
src/compiler.bbb
239
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
|
||||
|
@ -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<char* const> arguments, Slice<char* const> envp)
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
43
tests/field_parent_pointer.bbb
Normal file
43
tests/field_parent_pointer.bbb
Normal file
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user