This commit is contained in:
David Gonzalez Martin 2025-06-06 21:32:57 -06:00
parent 09c92d666c
commit 63d79332ae
2 changed files with 559 additions and 3 deletions

View File

@ -1487,6 +1487,9 @@ ValueId = enum
function,
constant_integer,
global,
unary,
binary,
string_literal,
}
ValueConstantInteger = struct
@ -1510,10 +1513,134 @@ ValueFunction = struct
attributes: FunctionAttributes,
}
UnaryId = enum
{
minus,
plus,
ampersand,
exclamation,
tilde,
enum_name,
extend,
truncate,
pointer_cast,
int_from_enum,
int_from_pointer,
va_end,
bitwise_not,
dereference,
pointer_from_int,
enum_from_int,
}
unary_is_boolean = fn (id: UnaryId) u1
{
switch (id)
{
.exclamation =>
{
return 1;
},
else =>
{
return 0;
}
}
}
ValueUnary = struct
{
value: &Value,
id: UnaryId,
}
BinaryId = enum
{
add,
sub,
mul,
div,
rem,
bitwise_and,
bitwise_or,
bitwise_xor,
shift_left,
shift_right,
compare_equal,
compare_not_equal,
compare_greater,
compare_less,
compare_greater_equal,
compare_less_equal,
logical_and,
logical_or,
logical_and_shortcircuit,
logical_or_shortcircuit,
max,
min,
}
binary_is_boolean = fn (id: BinaryId) u1
{
switch (id)
{
.add,
.sub,
.mul,
.div,
.rem,
.bitwise_and,
.bitwise_or,
.bitwise_xor,
.shift_left,
.shift_right,
.max,
.min,
=>
{
return 0;
},
.compare_equal,
.compare_not_equal,
.compare_less,
.compare_less_equal,
.compare_greater,
.compare_greater_equal,
.logical_and,
.logical_or,
.logical_and_shortcircuit,
.logical_or_shortcircuit,
=>
{
return 1;
},
}
}
binary_is_shortcircuiting = fn (id: BinaryId) u1
{
switch (id)
{
.logical_and_shortcircuit,
.logical_or_shortcircuit,
=> { return 1; },
else => { return 0; },
}
}
ValueBinary = struct
{
left: &Value,
right: &Value,
id: BinaryId,
}
ValueContent = union
{
constant_integer: ValueConstantInteger,
function: ValueFunction,
unary: ValueUnary,
binary: ValueBinary,
}
ValueKind = enum
@ -1531,6 +1658,25 @@ Value = struct
llvm: &LLVMValue,
}
value_is_constant = fn (value: &Value) u1
{
switch (value.id)
{
.constant_integer =>
{
return 1;
},
.unary =>
{
return 0;
},
else =>
{
#trap();
}
}
}
i128_offset: u64 = 64 * 2;
void_offset: u64 = i128_offset + 2;
@ -2139,6 +2285,11 @@ integer_type = fn (module: &Module, integer: TypeInteger) &Type
return result;
}
uint1 = fn (module: &Module) &Type
{
return integer_type(module, { .bit_count = 1, .signed = 0 });
}
uint64 = fn (module: &Module) &Type
{
return integer_type(module, { .bit_count = 64, .signed = 0 });
@ -2819,7 +2970,7 @@ tokenize = fn (module: &Module) Token
>start_character = module.content[start_index];
>token: Token = undefined;
>token: Token = zero;
switch (start_character)
{
@ -2940,7 +3091,45 @@ tokenize = fn (module: &Module) Token
'!',
=>
{
#trap();
>next_ch = module.content[start_index + 1];
>id: TokenId = undefined;
if (next_ch == '=')
{
switch (start_character)
{
'+' => { id = .assign_plus; },
'-' => { id = .assign_dash; },
'*' => { id = .assign_asterisk; },
'/' => { id = .assign_forward_slash; },
'%' => { id = .assign_percentage; },
'&' => { id = .assign_ampersand; },
'|' => { id = .assign_bar; },
'^' => { id = .assign_caret; },
'!' => { id = .compare_not_equal; },
else => { unreachable; }
}
}
else
{
switch (start_character)
{
'+' => { id = .plus; },
'-' => { id = .dash; },
'*' => { id = .asterisk; },
'/' => { id = .forward_slash; },
'%' => { id = .percentage; },
'&' => { id = .ampersand; },
'|' => { id = .bar; },
'^' => { id = .caret; },
'!' => { id = .exclamation; },
else => { unreachable; }
}
}
token.id = id;
module.offset += #extend(next_ch == '=') + 1;
},
else =>
{
@ -2984,6 +3173,8 @@ ValueBuilder = struct
allow_assignment_operators: u1,
}
parse_precedence = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value;
parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value
{
>token = builder.token;
@ -3007,6 +3198,72 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value
zero,
};
},
.dash, .ampersand, .exclamation, .tilde =>
{
assert(!builder.left);
>id: UnaryId = undefined;
switch (token.id)
{
.dash => { id = .minus; },
.ampersand => { id = .ampersand; },
.exclamation => { id = .exclamation; },
.tilde => { id = .bitwise_not; },
else => { unreachable; },
}
>unary_builder = builder;
unary_builder.precedence = .prefix;
unary_builder.token = zero;
unary_builder.kind = #select(token.id == .ampersand, .left, builder.kind);
>unary_value = parse_precedence(module, scope, unary_builder);
result = new_value(module);
result.& = {
.content = {
.unary = {
.value = unary_value,
.id = id,
},
},
.id = .unary,
.kind = .right,
zero,
};
},
.identifier =>
{
#trap();
},
.value_intrinsic =>
{
#trap();
},
.left_bracket =>
{
#trap();
},
.dot =>
{
#trap();
},
.left_parenthesis =>
{
#trap();
},
.string_literal =>
{
#trap();
},
.left_brace =>
{
#trap();
},
.value_keyword =>
{
#trap();
},
else =>
{
report_error();
@ -3102,7 +3359,109 @@ get_token_precedence = fn (token: Token) Precedence
parse_right = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value
{
#trap();
>left = builder.left;
assert(left != zero);
>token = builder.token;
>result: &Value = zero;
switch (token.id)
{
.plus,
.dash,
.asterisk,
.forward_slash,
.percentage,
.ampersand,
.bar,
.caret,
.shift_left,
.shift_right,
.compare_equal,
.compare_not_equal,
.compare_less,
.compare_less_equal,
.compare_greater,
.compare_greater_equal,
.operator_keyword,
=>
{
>precedence = get_token_precedence(token);
assert(precedence != .assignment);
>id: BinaryId = undefined;
switch (token.id)
{
.operator_keyword =>
{
#trap();
},
.plus => { id = .add; },
.dash => { id = .sub; },
.asterisk => { id = .mul; },
.forward_slash => { id = .div; },
.percentage => { id = .rem; },
.ampersand => { id = .bitwise_and; },
.bar => { id = .bitwise_or; },
.caret => { id = .bitwise_xor; },
.shift_left => { id = .shift_left; },
.shift_right => { id = .shift_right; },
.compare_equal => { id = .compare_equal; },
.compare_not_equal => { id = .compare_not_equal; },
.compare_less => { id = .compare_less; },
.compare_less_equal => { id = .compare_less_equal; },
.compare_greater => { id = .compare_greater; },
.compare_greater_equal => { id = .compare_greater_equal; },
else => { unreachable; },
}
>right_precedence: Precedence = #enum_from_int(#int_from_enum(precedence) + 1);
>right_builder = builder;
right_builder.precedence = right_precedence;
right_builder.token = zero;
right_builder.left = zero;
>right = parse_precedence(module, scope, right_builder);
result = new_value(module);
result.& = {
.content = {
.binary = {
.left = left,
.right = right,
.id = id,
},
},
.id = .binary,
.kind = .right,
zero,
};
},
.pointer_dereference =>
{
#trap();
},
.left_parenthesis =>
{
#trap();
},
.left_bracket =>
{
#trap();
},
.dot =>
{
#trap();
},
else =>
{
report_error();
},
}
assert(result != zero);
return result;
}
parse_right_with_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value
@ -4452,6 +4811,59 @@ TypeAnalysis = struct
indexing_type: &Type,
must_be_constant: u1,
}
analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void;
analyze_binary_type = fn (module: &Module, left: &Value, right: &Value, is_boolean: u1, expected_type: &Type, must_be_constant: u1) void
{
>left_constant = value_is_constant(left);
>right_constant = value_is_constant(right);
if (!expected_type)
{
if (left_constant != zero and right_constant)
{
if (left.type == zero and right.type == zero)
{
>string_literal = left.id == .string_literal and right.id == .string_literal;
if (string_literal)
{
#trap();
}
else
{
report_error();
}
}
}
}
if (is_boolean or expected_type == zero)
{
if (left_constant)
{
analyze_type(module, right, zero, { .must_be_constant = must_be_constant, zero });
analyze_type(module, left, right.type, { .must_be_constant = must_be_constant, zero });
}
else
{
analyze_type(module, left, zero, { .must_be_constant = must_be_constant, zero });
analyze_type(module, right, left.type, { .must_be_constant = must_be_constant, zero });
}
}
else if (!is_boolean and expected_type != zero)
{
analyze_type(module, left, expected_type, { .must_be_constant = must_be_constant, zero });
analyze_type(module, right, expected_type, { .must_be_constant = must_be_constant, zero });
}
else
{
report_error();
}
assert(left.type != zero);
assert(right.type != zero);
}
analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysis: TypeAnalysis) void
{
@ -4527,6 +4939,87 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi
typecheck(module, expected_type, value_type);
},
.binary =>
{
>left = value.content.binary.left;
>right = value.content.binary.right;
>id = value.content.binary.id;
>is_boolean = binary_is_boolean(id);
analyze_binary_type(module, left, right, is_boolean, expected_type, analysis.must_be_constant);
check_types(module, left.type, right.type);
value_type = #select(is_boolean, uint1(module), left.type);
},
.unary =>
{
>unary_id = value.content.unary.id;
>unary_value = value.content.unary.value;
switch (unary_id)
{
.extend =>
{
#trap();
},
.truncate =>
{
#trap();
},
.dereference =>
{
#trap();
},
.int_from_enum =>
{
#trap();
},
.int_from_pointer =>
{
#trap();
},
.pointer_cast =>
{
#trap();
},
.enum_name =>
{
#trap();
},
.pointer_from_int =>
{
#trap();
},
.enum_from_int =>
{
#trap();
},
// Generic case
.minus,
=>
{
>is_boolean = unary_is_boolean(unary_id);
if (is_boolean)
{
analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero });
value_type = uint1(module);
}
else
{
analyze_type(module, unary_value, expected_type, { .must_be_constant = analysis.must_be_constant, zero });
value_type = unary_value.type;
}
typecheck(module, expected_type, value_type);
},
else =>
{
// TODO
#trap();
},
}
},
else =>
{
#trap();
@ -4552,6 +5045,25 @@ get_llvm_type = fn (type: &Type, kind: TypeKind) &LLVMType
}
}
emit_binary = fn (module: &Module, left: &LLVMValue, left_type: &Type, right: &LLVMValue, right_type: &Type, id: BinaryId, resolved_value_type: &Type) &LLVMValue
{
switch (resolved_value_type.id)
{
.integer =>
{
#trap();
},
.pointer =>
{
#trap();
}
else =>
{
report_error();
}
}
}
emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_constant: u1) void
{
assert(value.type != zero);
@ -4571,6 +5083,32 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con
>llvm_integer_type = get_llvm_type(resolved_value_type, type_kind);
llvm_value = LLVMConstInt(llvm_integer_type, value.content.constant_integer.value, #extend(value.content.constant_integer.signed));
},
.binary =>
{
>binary_id = value.content.binary.id;
>is_shortcircuiting = binary_is_shortcircuiting(binary_id);
>left = value.content.binary.left;
>right = value.content.binary.right;
>values = [ left, right ];
if (is_shortcircuiting)
{
#trap();
}
else
{
>llvm_values: [2]&LLVMValue = undefined;
for (i: 0..llvm_values.length)
{
>binary_value = values[i];
emit_value(module, binary_value, .abi, must_be_constant);
llvm_values[i] = binary_value.llvm;
}
llvm_value = emit_binary(module, llvm_values[0], values[0].type, llvm_values[1], values[1].type, binary_id, resolved_value_type);
}
},
else =>
{
#trap();
@ -5738,6 +6276,18 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile) []u8
names: [_][]u8 = [
"minimal",
"comments",
"constant_add",
"constant_and",
"constant_div",
"constant_mul",
"constant_rem",
"constant_or",
"constant_sub",
"constant_xor",
"constant_shift_left",
"constant_shift_right",
];
[export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32

View File

@ -3509,6 +3509,10 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal
auto backing_type = enum_type->enumerator.backing_type;
value_type = backing_type;
}
else if (resolved_aggregate_type->id == TypeId::array)
{
value_type = uint64(module);
}
else
{
report_error();
@ -5983,6 +5987,7 @@ fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef lef
auto load = create_load(module, {
.type = field_access.type,
.pointer = gep,
.kind = type_kind,
});
return load;
} break;
@ -7704,6 +7709,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect
auto condition = value->select.condition;
auto true_value = value->select.true_value;
auto false_value = value->select.false_value;
emit_value(module, condition, TypeKind::abi, must_be_constant);
LLVMValueRef llvm_condition = condition->llvm;
auto condition_type = condition->type;