Forward declared type
This commit is contained in:
parent
5de4ba76f5
commit
c804840a12
797
src/compiler.bbb
797
src/compiler.bbb
@ -533,8 +533,7 @@ CompilerCommand = enum
|
||||
BuildMode = enum
|
||||
{
|
||||
debug_none,
|
||||
debug_fast,
|
||||
debug_size,
|
||||
debug,
|
||||
soft_optimize,
|
||||
optimize_for_speed,
|
||||
optimize_for_size,
|
||||
@ -580,17 +579,53 @@ CompileOptions = struct
|
||||
executable: []u8,
|
||||
name: []u8,
|
||||
objects: [][]u8,
|
||||
libraries: [][]u8,
|
||||
target: Target,
|
||||
build_mode: BuildMode,
|
||||
has_debug_info: u1,
|
||||
silent: u1,
|
||||
}
|
||||
|
||||
CallingConvention = enum
|
||||
{
|
||||
c,
|
||||
}
|
||||
|
||||
InlineBehavior = enum
|
||||
{
|
||||
default = 0,
|
||||
always_inline = 1,
|
||||
no_inline = 2,
|
||||
inline_hint = 3,
|
||||
}
|
||||
|
||||
FunctionAttributes = struct
|
||||
{
|
||||
inline_behavior: InlineBehavior,
|
||||
naked: u1,
|
||||
}
|
||||
|
||||
Type = struct;
|
||||
|
||||
TypeId = enum
|
||||
{
|
||||
void,
|
||||
noreturn,
|
||||
forward_declaration,
|
||||
integer,
|
||||
function,
|
||||
pointer,
|
||||
array,
|
||||
enum,
|
||||
struct,
|
||||
bits,
|
||||
alias,
|
||||
union,
|
||||
unresolved,
|
||||
vector,
|
||||
floating_point,
|
||||
enum_array,
|
||||
opaque,
|
||||
}
|
||||
|
||||
TypeInteger = struct
|
||||
@ -599,9 +634,40 @@ TypeInteger = struct
|
||||
signed: u1,
|
||||
}
|
||||
|
||||
AbiRegisterCountSystemV = struct
|
||||
{
|
||||
gpr: u32,
|
||||
sse: u32,
|
||||
};
|
||||
|
||||
AbiRegisterCount = union
|
||||
{
|
||||
system_v: AbiRegisterCountSystemV,
|
||||
};
|
||||
|
||||
AbiInformation = struct
|
||||
{
|
||||
foo: u32,
|
||||
}
|
||||
|
||||
TypeFunction = struct
|
||||
{
|
||||
semantic_return_type: &Type,
|
||||
semantic_argument_types: []&Type,
|
||||
calling_convention: CallingConvention,
|
||||
is_variable_arguments: u1,
|
||||
|
||||
abi_argument_types: []&Type,
|
||||
abi_return_type: &Type,
|
||||
available_registers: AbiRegisterCount,
|
||||
argument_abis: []AbiInformation,
|
||||
return_abi: AbiInformation,
|
||||
}
|
||||
|
||||
TypeContent = union
|
||||
{
|
||||
integer: TypeInteger,
|
||||
function: TypeFunction,
|
||||
}
|
||||
|
||||
Type = struct
|
||||
@ -609,6 +675,7 @@ Type = struct
|
||||
content: TypeContent,
|
||||
id: TypeId,
|
||||
name: []u8,
|
||||
next: &Type,
|
||||
}
|
||||
|
||||
ValueId = enum
|
||||
@ -625,16 +692,148 @@ Value = struct
|
||||
i128_offset: u64 = 64 * 2;
|
||||
void_offset: u64 = i128_offset + 2;
|
||||
|
||||
ScopeKind = enum
|
||||
{
|
||||
global,
|
||||
function,
|
||||
local,
|
||||
for_each,
|
||||
macro_declaration,
|
||||
macro_instantiation,
|
||||
}
|
||||
|
||||
Scope = struct
|
||||
{
|
||||
parent: &Scope,
|
||||
line: u32,
|
||||
column: u32,
|
||||
kind: ScopeKind,
|
||||
}
|
||||
|
||||
Variable = struct
|
||||
{
|
||||
storage: &Value,
|
||||
type: &Type,
|
||||
scope: &Scope,
|
||||
name: []u8,
|
||||
line: u32,
|
||||
column: u32,
|
||||
}
|
||||
|
||||
Linkage = enum
|
||||
{
|
||||
internal,
|
||||
external,
|
||||
}
|
||||
|
||||
Global = struct
|
||||
{
|
||||
variable: Variable,
|
||||
initial_value: &Value,
|
||||
next: &Global,
|
||||
linkage: Linkage,
|
||||
emitted: u1,
|
||||
}
|
||||
|
||||
Local = struct
|
||||
{
|
||||
variable: Variable,
|
||||
initial_value: &Value,
|
||||
next: &Local,
|
||||
}
|
||||
|
||||
Argument = struct
|
||||
{
|
||||
variable: Variable,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
MacroDeclaration = struct
|
||||
{
|
||||
foo: u32,
|
||||
}
|
||||
|
||||
MacroInstantiation = struct
|
||||
{
|
||||
foo: u32,
|
||||
}
|
||||
|
||||
LLVMContext = opaque;
|
||||
LLVMModule = opaque;
|
||||
LLVMBuilder = opaque;
|
||||
LLVMDIBuilder = opaque;
|
||||
LLVMValue = opaque;
|
||||
LLVMType = opaque;
|
||||
LLVMMetadata = opaque;
|
||||
LLVMBasicBlock = opaque;
|
||||
LLVMIntrinsicId = typealias u32;
|
||||
|
||||
LLVMIntrinsicIndex = enum
|
||||
{
|
||||
trap,
|
||||
va_start,
|
||||
va_end,
|
||||
va_copy,
|
||||
}
|
||||
|
||||
ModuleLLVM = struct
|
||||
{
|
||||
context: &LLVMContext,
|
||||
module: &LLVMModule,
|
||||
builder: &LLVMBuilder,
|
||||
di_builder: &LLVMDIBuilder,
|
||||
file: &LLVMMetadata,
|
||||
compile_unit: &LLVMMetadata,
|
||||
pointer_type: &LLVMType,
|
||||
void_type: &LLVMType,
|
||||
intrinsic_table: enum_array[LLVMIntrinsicIndex](LLVMIntrinsicId),
|
||||
memcmp: &LLVMValue,
|
||||
inlined_at: &LLVMMetadata,
|
||||
continue_block: &LLVMBasicBlock,
|
||||
exit_block: &LLVMBasicBlock,
|
||||
debug_tag: u32,
|
||||
}
|
||||
|
||||
Module = struct
|
||||
{
|
||||
arena: &Arena,
|
||||
base_type_allocation: &Type,
|
||||
void_value: &Value,
|
||||
// Parser data
|
||||
content: []u8,
|
||||
offset: u64,
|
||||
line_offset: u64,
|
||||
line_character_offset: u64,
|
||||
|
||||
first_pointer_type: &Type,
|
||||
first_slice_type: &Type,
|
||||
first_pair_struct_type: &Type,
|
||||
first_array_type: &Type,
|
||||
|
||||
first_type: &Type,
|
||||
last_type: &Type,
|
||||
va_list_type: &Type,
|
||||
|
||||
void_value: &Value,
|
||||
first_global: &Global,
|
||||
last_global: &Global,
|
||||
first_macro_declaration: &MacroDeclaration,
|
||||
last_macro_declaration: &MacroDeclaration,
|
||||
|
||||
current_function: &Global,
|
||||
current_macro_declaration: &MacroDeclaration,
|
||||
current_macro_instantiation: &MacroInstantiation,
|
||||
|
||||
llvm: ModuleLLVM,
|
||||
scope: Scope,
|
||||
|
||||
name: []u8,
|
||||
path: []u8,
|
||||
executable: []u8,
|
||||
objects: [][]u8,
|
||||
libraries: [][]u8,
|
||||
|
||||
target: Target,
|
||||
build_mode: BuildMode,
|
||||
has_debug_info: u1,
|
||||
silent: u1,
|
||||
}
|
||||
|
||||
module_integer_type = fn (module: &Module, integer: TypeInteger) &Type
|
||||
@ -642,12 +841,16 @@ module_integer_type = fn (module: &Module, integer: TypeInteger) &Type
|
||||
assert(integer.bit_count != 0);
|
||||
assert(integer.bit_count <= 64);
|
||||
>index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), #extend(integer.bit_count) - 1 + 64 * #extend(integer.signed));
|
||||
return module.base_type_allocation + index;
|
||||
>result = module.first_type + index;
|
||||
assert(result.id == .integer);
|
||||
assert(result.content.integer.bit_count == integer.bit_count);
|
||||
assert(result.content.integer.signed == integer.signed);
|
||||
return result;
|
||||
}
|
||||
|
||||
module_void_type = fn (module: &Module) &Type
|
||||
{
|
||||
return module.base_type_allocation + void_offset;
|
||||
return module.first_type + void_offset;
|
||||
}
|
||||
|
||||
module_noreturn_type = fn (module: &Module) &Type
|
||||
@ -697,7 +900,7 @@ is_hex = fn (ch: u8) u1
|
||||
|
||||
is_identifier_start = fn (ch: u8) u1
|
||||
{
|
||||
return (ch >= 'a' and ch >= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_';
|
||||
return (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_';
|
||||
}
|
||||
|
||||
is_identifier = fn (ch: u8) u1
|
||||
@ -724,6 +927,29 @@ get_column = fn (module: &Module) u32
|
||||
return #truncate(column);
|
||||
}
|
||||
|
||||
Checkpoint = struct
|
||||
{
|
||||
offset: u64,
|
||||
line_offset: u64,
|
||||
line_character_offset: u64,
|
||||
}
|
||||
|
||||
get_checkpoint = fn (module: &Module) Checkpoint
|
||||
{
|
||||
return {
|
||||
.offset = module.offset,
|
||||
.line_offset = module.line_offset,
|
||||
.line_character_offset = module.line_character_offset,
|
||||
};
|
||||
}
|
||||
|
||||
set_checkpoint = fn (module: &Module, checkpoint: Checkpoint) void
|
||||
{
|
||||
module.offset = checkpoint.offset;
|
||||
module.line_offset = checkpoint.line_offset;
|
||||
module.line_character_offset = checkpoint.line_character_offset;
|
||||
}
|
||||
|
||||
skip_space = fn (module: &Module) void
|
||||
{
|
||||
while (1)
|
||||
@ -817,8 +1043,512 @@ parse_identifier = fn (module: &Module) []u8
|
||||
return result;
|
||||
}
|
||||
|
||||
new_value = fn (module: &Module) &Value
|
||||
{
|
||||
>value = arena_allocate[Value](module.arena, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
new_type = fn (module: &Module, type: Type) &Type
|
||||
{
|
||||
>result = arena_allocate[Type](module.arena, 1);
|
||||
result.& = type;
|
||||
|
||||
if (module.last_type)
|
||||
{
|
||||
assert(module.first_type != zero);
|
||||
module.last_type.next = result;
|
||||
module.last_type = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(!module.first_type);
|
||||
module.first_type = result;
|
||||
module.last_type = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
GlobalAttributeKeyword = enum
|
||||
{
|
||||
export,
|
||||
extern,
|
||||
}
|
||||
|
||||
GlobalKeyword = enum
|
||||
{
|
||||
bits,
|
||||
enum,
|
||||
fn,
|
||||
macro,
|
||||
opaque,
|
||||
struct,
|
||||
typealias,
|
||||
union,
|
||||
}
|
||||
|
||||
FunctionTypeAttribute = enum
|
||||
{
|
||||
cc,
|
||||
}
|
||||
|
||||
left_bracket: u8 = '[';
|
||||
right_bracket: u8 = ']';
|
||||
left_parenthesis: u8 = '(';
|
||||
right_parenthesis: u8 = ')';
|
||||
left_brace: u8 = '{';
|
||||
right_brace: u8 = '}';
|
||||
|
||||
accumulate_decimal = fn(accumulator: u64, ch: u8) u64
|
||||
{
|
||||
assert(is_decimal(ch));
|
||||
return (accumulator * 10) + #extend(ch - '0');
|
||||
}
|
||||
|
||||
parse_integer_decimal_assume_valid = fn (string: []u8) u64
|
||||
{
|
||||
>value: u64 = 0;
|
||||
|
||||
for (ch: string)
|
||||
{
|
||||
value = accumulate_decimal(value, ch);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
TypeKeyword = enum
|
||||
{
|
||||
void,
|
||||
noreturn,
|
||||
enum_array,
|
||||
}
|
||||
|
||||
parse_type = fn (module: &Module, scope: &Scope) &Type
|
||||
{
|
||||
>start_character = module.content[module.offset];
|
||||
|
||||
if (is_identifier_start(start_character))
|
||||
{
|
||||
>identifier = parse_identifier(module);
|
||||
|
||||
>type_keyword_s2e = #string_to_enum(TypeKeyword, identifier);
|
||||
|
||||
if (type_keyword_s2e.is_valid)
|
||||
{
|
||||
>type_keyword = type_keyword_s2e.enum_value;
|
||||
|
||||
switch (type_keyword)
|
||||
{
|
||||
.void =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.noreturn =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.enum_array =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
>is_integer_type = identifier.length > 1 and (identifier[0] == 's' or identifier[0] == 'u');
|
||||
|
||||
if (is_integer_type)
|
||||
{
|
||||
for (ch: identifier[1..])
|
||||
{
|
||||
is_integer_type = is_integer_type and is_decimal(ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_integer_type)
|
||||
{
|
||||
>is_signed: u1 = undefined;
|
||||
switch (identifier[0])
|
||||
{
|
||||
's' =>
|
||||
{
|
||||
is_signed = 1;
|
||||
},
|
||||
'u' =>
|
||||
{
|
||||
is_signed = 0;
|
||||
},
|
||||
else =>
|
||||
{
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
|
||||
>bit_count = parse_integer_decimal_assume_valid(identifier[1..]);
|
||||
|
||||
if (bit_count == 0)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
if (bit_count > 64)
|
||||
{
|
||||
if (bit_count != 128)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
|
||||
>result = module_integer_type(module, { .bit_count = #truncate(bit_count), .signed = is_signed });
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
>type = module.first_type;
|
||||
|
||||
while (type)
|
||||
{
|
||||
if (string_equal(identifier, type.name))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
type = type.next;
|
||||
}
|
||||
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (start_character == '&')
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
else if (start_character == left_bracket)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
else if (start_character == '#')
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
|
||||
parse = fn (module: &Module) void
|
||||
{
|
||||
>scope = &module.scope;
|
||||
|
||||
while (1)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
if (module.offset == module.content.length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
>is_export: u1 = 0;
|
||||
>is_extern: u1 = 0;
|
||||
|
||||
>global_line = get_line(module);
|
||||
>global_column = get_line(module);
|
||||
|
||||
if (consume_character_if_match(module, left_bracket))
|
||||
{
|
||||
while (module.offset < module.content.length)
|
||||
{
|
||||
>global_attribute_keyword_string = parse_identifier(module);
|
||||
>global_attribute_keyword_s2e = #string_to_enum(GlobalAttributeKeyword, global_attribute_keyword_string);
|
||||
if (!global_attribute_keyword_s2e.is_valid)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
>global_attribute_keyword = global_attribute_keyword_s2e.enum_value;
|
||||
switch (global_attribute_keyword)
|
||||
{
|
||||
.export =>
|
||||
{
|
||||
is_export = 1;
|
||||
},
|
||||
.extern =>
|
||||
{
|
||||
is_extern = 1;
|
||||
},
|
||||
}
|
||||
|
||||
if (consume_character_if_match(module, right_bracket))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
}
|
||||
|
||||
>global_name = parse_identifier(module);
|
||||
|
||||
>last_global = module.first_global;
|
||||
|
||||
while (last_global)
|
||||
{
|
||||
if (string_equal(global_name, last_global.variable.name))
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
if (!last_global.next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
last_global = last_global.next;
|
||||
}
|
||||
|
||||
>type_it = module.first_type;
|
||||
>forward_declaration: &Type = zero;
|
||||
|
||||
while (type_it)
|
||||
{
|
||||
if (string_equal(global_name, type_it.name))
|
||||
{
|
||||
if (type_it.id == .forward_declaration)
|
||||
{
|
||||
forward_declaration = type_it;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
|
||||
if (!type_it.next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
type_it = type_it.next;
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
>global_type: &Type = zero;
|
||||
|
||||
if (consume_character_if_match(module, ':'))
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
global_type = parse_type(module, scope);
|
||||
|
||||
skip_space(module);
|
||||
}
|
||||
|
||||
expect_character(module, '=');
|
||||
|
||||
skip_space(module);
|
||||
|
||||
>is_global_keyword: u1 = 0;
|
||||
|
||||
if (is_identifier_start(module.content[module.offset]))
|
||||
{
|
||||
>checkpoint = get_checkpoint(module);
|
||||
>global_keyword_string = parse_identifier(module);
|
||||
skip_space(module);
|
||||
|
||||
>global_keyword_s2e = #string_to_enum(GlobalKeyword, global_keyword_string);
|
||||
|
||||
is_global_keyword = global_keyword_s2e.is_valid;
|
||||
|
||||
if (is_global_keyword)
|
||||
{
|
||||
>global_keyword = global_keyword_s2e.enum_value;
|
||||
|
||||
switch (global_keyword)
|
||||
{
|
||||
.bits =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.enum =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.fn =>
|
||||
{
|
||||
>calling_convention: CallingConvention = .c;
|
||||
>function_attributes: FunctionAttributes = zero;
|
||||
>is_variable_arguments: u1 = 0;
|
||||
|
||||
if (consume_character_if_match(module, left_bracket))
|
||||
{
|
||||
while (module.offset < module.content.length)
|
||||
{
|
||||
>function_identifier = parse_identifier(module);
|
||||
|
||||
>function_type_attribute_s2e = #string_to_enum(FunctionTypeAttribute, function_identifier);
|
||||
|
||||
if (!function_type_attribute_s2e.is_valid)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
>function_type_attribute = function_type_attribute_s2e.enum_value;
|
||||
|
||||
switch (function_type_attribute)
|
||||
{
|
||||
.cc =>
|
||||
{
|
||||
expect_character(module, left_parenthesis);
|
||||
skip_space(module);
|
||||
>calling_convention_string = parse_identifier(module);
|
||||
|
||||
>calling_convention_s2e = #string_to_enum(CallingConvention, calling_convention_string);
|
||||
|
||||
if (!calling_convention_s2e.is_valid)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
>candidate_calling_convention = calling_convention_s2e.enum_value;
|
||||
calling_convention = candidate_calling_convention;
|
||||
|
||||
skip_space(module);
|
||||
expect_character(module, right_parenthesis);
|
||||
},
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, right_bracket))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
expect_character(module, left_parenthesis);
|
||||
|
||||
>semantic_argument_type_buffer: [64]&Type = undefined;
|
||||
>semantic_argument_name_buffer: [64][]u8 = undefined;
|
||||
>argument_line_buffer: [64]u32 = undefined;
|
||||
>semantic_argument_count: u64 = 0;
|
||||
|
||||
while (module.offset < module.content.length)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
|
||||
if (consume_character_if_match(module, right_parenthesis))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
>line = get_line(module);
|
||||
argument_line_buffer[semantic_argument_count] = line;
|
||||
|
||||
>argument_name = parse_identifier(module);
|
||||
semantic_argument_name_buffer[semantic_argument_count] = argument_name;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
expect_character(module, ':');
|
||||
|
||||
skip_space(module);
|
||||
|
||||
>argument_type = parse_type(module, scope);
|
||||
semantic_argument_type_buffer[semantic_argument_count] = argument_type;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
consume_character_if_match(module, '.');
|
||||
|
||||
semantic_argument_count += 1;
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
>return_type = parse_type(module, scope);
|
||||
|
||||
skip_space(module);
|
||||
|
||||
>argument_types: []&Type = zero;
|
||||
|
||||
if (semantic_argument_count != 0)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
|
||||
>is_declaration = consume_character_if_match(module, ';');
|
||||
|
||||
>function_type = new_type(module, {
|
||||
.content = {
|
||||
.function = {
|
||||
.semantic_return_type = return_type,
|
||||
.semantic_argument_types = argument_types,
|
||||
.calling_convention = calling_convention,
|
||||
.is_variable_arguments = is_variable_arguments,
|
||||
},
|
||||
},
|
||||
.id = .function,
|
||||
.name = "",
|
||||
zero,
|
||||
});
|
||||
#trap();
|
||||
},
|
||||
.macro =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.opaque =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.struct =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.typealias =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.union =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
set_checkpoint(module, checkpoint);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_global_keyword)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit = fn (module: &Module) void
|
||||
@ -836,6 +1566,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void
|
||||
);
|
||||
|
||||
>type_it = base_type_allocation;
|
||||
>previous: &Type = zero;
|
||||
|
||||
for (sign: signs)
|
||||
{
|
||||
@ -858,7 +1589,16 @@ compile = fn (arena: &Arena, options: CompileOptions) void
|
||||
},
|
||||
.id = .integer,
|
||||
.name = name,
|
||||
zero,
|
||||
};
|
||||
|
||||
if (previous)
|
||||
{
|
||||
previous.next = type_it;
|
||||
}
|
||||
|
||||
previous = type_it;
|
||||
|
||||
type_it += 1;
|
||||
}
|
||||
}
|
||||
@ -875,7 +1615,12 @@ compile = fn (arena: &Arena, options: CompileOptions) void
|
||||
},
|
||||
.id = .integer,
|
||||
.name = name,
|
||||
zero,
|
||||
};
|
||||
|
||||
previous.next = type_it;
|
||||
previous = type_it;
|
||||
|
||||
type_it += 1;
|
||||
}
|
||||
|
||||
@ -884,30 +1629,44 @@ compile = fn (arena: &Arena, options: CompileOptions) void
|
||||
>noreturn_type = type_it;
|
||||
type_it += 1;
|
||||
|
||||
previous.next = void_type;
|
||||
|
||||
void_type.& = {
|
||||
.id = .void,
|
||||
.name = "void",
|
||||
.next = noreturn_type,
|
||||
zero,
|
||||
};
|
||||
|
||||
noreturn_type.& = {
|
||||
.id = .noreturn,
|
||||
.name = "noreturn",
|
||||
zero,
|
||||
};
|
||||
|
||||
>void_value = arena_allocate[Value](arena, 1);
|
||||
void_value.& = {
|
||||
.type = void_type,
|
||||
.id = .infer_or_ignore,
|
||||
.type = void_type,
|
||||
zero,
|
||||
};
|
||||
|
||||
>module: Module = {
|
||||
.arena = arena,
|
||||
.base_type_allocation = base_type_allocation,
|
||||
.void_value = void_value,
|
||||
.content = options.content,
|
||||
.offset = 0,
|
||||
.line_offset = 0,
|
||||
.line_character_offset = 0,
|
||||
.first_type = base_type_allocation,
|
||||
.last_type = noreturn_type,
|
||||
.void_value = void_value,
|
||||
.name = options.name,
|
||||
.path = options.path,
|
||||
.executable = options.executable,
|
||||
.objects = options.objects,
|
||||
.libraries = options.libraries,
|
||||
.target = options.target,
|
||||
.build_mode = options.build_mode,
|
||||
.has_debug_info = options.has_debug_info,
|
||||
.silent = options.silent,
|
||||
zero,
|
||||
};
|
||||
|
||||
parse(&module);
|
||||
@ -969,18 +1728,12 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile) void
|
||||
>c_abi_object_path = ""; // TODO
|
||||
|
||||
>objects: [][]u8 = undefined;
|
||||
if (string_equal(base_name, "c_abi"))
|
||||
{
|
||||
objects = [ output_object_path, c_abi_object_path ][..];
|
||||
}
|
||||
else
|
||||
{
|
||||
objects = [ output_object_path ][..];
|
||||
}
|
||||
objects = [ output_object_path ][..];
|
||||
|
||||
>options: CompileOptions = {
|
||||
.executable = output_executable_path,
|
||||
.objects = objects,
|
||||
.libraries = zero,
|
||||
.name = base_name,
|
||||
.build_mode = compile_options.build_mode,
|
||||
.content = file_content,
|
||||
|
@ -306,8 +306,11 @@ global_variable String names[] =
|
||||
string_literal("noreturn_macro"),
|
||||
string_literal("generic_pointer_array"),
|
||||
|
||||
string_literal("self_referential_struct"), // TODO
|
||||
// string_literal("forward_declared_type"),
|
||||
string_literal("self_referential_struct"),
|
||||
string_literal("forward_declared_type"),
|
||||
|
||||
string_literal("enum_array"),
|
||||
string_literal("opaque"),
|
||||
};
|
||||
|
||||
void entry_point(Slice<const char*> arguments, Slice<char* const> environment)
|
||||
@ -488,7 +491,7 @@ void entry_point(Slice<const char*> arguments, Slice<char* const> environment)
|
||||
auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0;
|
||||
if (!success)
|
||||
{
|
||||
print(string_literal("Standalone test failed: "));
|
||||
print(string_literal("Self-hosted test failed: "));
|
||||
print(name);
|
||||
print(string_literal("\n"));
|
||||
bb_fail();
|
||||
|
@ -314,6 +314,8 @@ enum class TypeId
|
||||
unresolved,
|
||||
vector,
|
||||
floating_point,
|
||||
enum_array,
|
||||
opaque,
|
||||
};
|
||||
|
||||
struct TypeInteger
|
||||
@ -448,6 +450,13 @@ struct LLVMType
|
||||
LLVMMetadataRef debug;
|
||||
};
|
||||
|
||||
struct TypeEnumArray
|
||||
{
|
||||
Type* enum_type;
|
||||
Type* element_type;
|
||||
Type* next;
|
||||
};
|
||||
|
||||
struct Type
|
||||
{
|
||||
union
|
||||
@ -461,6 +470,7 @@ struct Type
|
||||
TypeBits bits;
|
||||
TypeAlias alias;
|
||||
TypeUnion union_type;
|
||||
TypeEnumArray enum_array;
|
||||
};
|
||||
TypeId id;
|
||||
String name;
|
||||
@ -528,6 +538,17 @@ fn u64 get_byte_size(Type* type)
|
||||
auto result = type->union_type.byte_size;
|
||||
return result;
|
||||
} break;
|
||||
case TypeId::enum_array:
|
||||
{
|
||||
auto enum_type = type->enum_array.enum_type;
|
||||
assert(enum_type->id == TypeId::enumerator);
|
||||
auto element_count = enum_type->enumerator.fields.length;
|
||||
auto element_type = type->enum_array.element_type;
|
||||
auto element_size = get_byte_size(element_type);
|
||||
|
||||
auto result = element_size * element_count;
|
||||
return result;
|
||||
} break;
|
||||
default: trap();
|
||||
}
|
||||
}
|
||||
@ -575,6 +596,10 @@ fn u32 get_byte_alignment(Type* type)
|
||||
{
|
||||
return get_byte_alignment(type->alias.type);
|
||||
} break;
|
||||
case TypeId::enum_array:
|
||||
{
|
||||
return get_byte_alignment(type->enum_array.element_type);
|
||||
} break;
|
||||
default: trap();
|
||||
}
|
||||
}
|
||||
@ -987,6 +1012,7 @@ struct Value
|
||||
case ValueId::enum_literal:
|
||||
case ValueId::unary_type:
|
||||
case ValueId::string_literal:
|
||||
case ValueId::zero:
|
||||
return true;
|
||||
case ValueId::unary:
|
||||
case ValueId::binary:
|
||||
@ -1126,6 +1152,7 @@ struct Module
|
||||
Type* first_slice_type;
|
||||
Type* first_pair_struct_type;
|
||||
Type* first_array_type;
|
||||
Type* first_enum_array_type;
|
||||
|
||||
Type* first_type;
|
||||
Type* last_type;
|
||||
@ -1714,5 +1741,52 @@ fn Local* new_local(Module* module, Scope* scope)
|
||||
return result;
|
||||
}
|
||||
|
||||
fn Type* get_enum_array_type(Module* module, Type* enum_type, Type* element_type)
|
||||
{
|
||||
assert(enum_type);
|
||||
assert(element_type);
|
||||
|
||||
Type* last_enum_type = module->first_enum_array_type;
|
||||
|
||||
if (last_enum_type)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
assert(last_enum_type->id == TypeId::enum_array);
|
||||
|
||||
if (last_enum_type->enum_array.enum_type == enum_type && last_enum_type->enum_array.element_type == element_type)
|
||||
{
|
||||
return last_enum_type;
|
||||
}
|
||||
|
||||
if (!last_enum_type->enum_array.next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
last_enum_type = last_enum_type->enum_array.next;
|
||||
}
|
||||
}
|
||||
|
||||
String name_parts[] = {
|
||||
string_literal("enum_array["),
|
||||
enum_type->name,
|
||||
string_literal("]("),
|
||||
element_type->name,
|
||||
string_literal(")"),
|
||||
};
|
||||
|
||||
auto enum_array_type = type_allocate_init(module, {
|
||||
.enum_array = {
|
||||
.enum_type = enum_type,
|
||||
.element_type = element_type,
|
||||
},
|
||||
.id = TypeId::enum_array,
|
||||
.name = arena_join_string(module->arena, array_to_slice(name_parts)),
|
||||
});
|
||||
return enum_array_type;
|
||||
}
|
||||
|
||||
|
||||
void parse(Module* module);
|
||||
void emit(Module* module);
|
||||
|
837
src/emitter.cpp
837
src/emitter.cpp
File diff suppressed because it is too large
Load Diff
228
src/parser.cpp
228
src/parser.cpp
@ -450,7 +450,6 @@ fn u64 parse_integer_decimal_assume_valid(String string)
|
||||
|
||||
fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder);
|
||||
|
||||
|
||||
fn Type* parse_type(Module* module, Scope* scope)
|
||||
{
|
||||
auto start_character = module->content[module->offset];
|
||||
@ -465,6 +464,20 @@ fn Type* parse_type(Module* module, Scope* scope)
|
||||
{
|
||||
return noreturn_type(module);
|
||||
}
|
||||
else if (identifier.equal(string_literal("enum_array")))
|
||||
{
|
||||
skip_space(module);
|
||||
expect_character(module, left_bracket);
|
||||
auto enum_type = parse_type(module, scope);
|
||||
expect_character(module, right_bracket);
|
||||
|
||||
expect_character(module, left_parenthesis);
|
||||
auto element_type = parse_type(module, scope);
|
||||
expect_character(module, right_parenthesis);
|
||||
|
||||
auto enum_array_type = get_enum_array_type(module, enum_type, element_type);
|
||||
return enum_array_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto is_int_type = identifier.length > 1 && (identifier[0] == 's' || identifier[0] == 'u');
|
||||
@ -1191,6 +1204,83 @@ fn Token tokenize(Module* module)
|
||||
|
||||
fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder);
|
||||
|
||||
fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuilder builder, u8 end_ch)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
u64 field_count = 0;
|
||||
String name_buffer[64];
|
||||
Value* value_buffer[64];
|
||||
bool zero = false;
|
||||
|
||||
while (1)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, end_ch))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto field_index = field_count;
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
auto name = parse_identifier(module);
|
||||
name_buffer[field_index] = name;
|
||||
skip_space(module);
|
||||
expect_character(module, '=');
|
||||
skip_space(module);
|
||||
|
||||
auto value = parse_value(module, scope, {});
|
||||
value_buffer[field_index] = value;
|
||||
skip_space(module);
|
||||
consume_character_if_match(module, ',');
|
||||
}
|
||||
else
|
||||
{
|
||||
auto token = tokenize(module);
|
||||
zero = token.id == TokenId::value_keyword && token.value_keyword == ValueKeyword::zero;
|
||||
if (zero)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, ','))
|
||||
{
|
||||
skip_space(module);
|
||||
}
|
||||
|
||||
expect_character(module, right_brace);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
|
||||
field_count += 1;
|
||||
}
|
||||
|
||||
auto names = arena_allocate<String>(module->arena, field_count);
|
||||
memcpy(names.pointer, name_buffer, sizeof(String) * field_count);
|
||||
auto values = new_value_array(module, field_count);
|
||||
memcpy(values.pointer, value_buffer, sizeof(Value*) * field_count);
|
||||
|
||||
auto result = new_value(module);
|
||||
*result = {
|
||||
.aggregate_initialization = {
|
||||
.names = names,
|
||||
.values = values,
|
||||
.is_constant = false,
|
||||
.zero = zero,
|
||||
},
|
||||
.id = ValueId::aggregate_initialization,
|
||||
.kind = builder.kind,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn Value* parse_precedence(Module* module, Scope* scope, ValueBuilder builder);
|
||||
fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
{
|
||||
@ -1422,33 +1512,42 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
u64 element_count = 0;
|
||||
Value* value_buffer[64];
|
||||
|
||||
while (1)
|
||||
{
|
||||
skip_space(module);
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, right_bracket))
|
||||
if (module->content[module->offset] == '.')
|
||||
{
|
||||
result = parse_aggregate_initialization(module, scope, builder, right_bracket);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
break;
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, right_bracket))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto value = parse_value(module, scope, {});
|
||||
value_buffer[element_count] = value;
|
||||
element_count += 1;
|
||||
|
||||
consume_character_if_match(module, ',');
|
||||
}
|
||||
|
||||
auto value = parse_value(module, scope, {});
|
||||
value_buffer[element_count] = value;
|
||||
element_count += 1;
|
||||
auto values = new_value_array(module, element_count);
|
||||
memcpy(values.pointer, value_buffer, element_count * sizeof(Value*));
|
||||
|
||||
consume_character_if_match(module, ',');
|
||||
result = new_value(module);
|
||||
*result = {
|
||||
.array_initialization = {
|
||||
.values = values,
|
||||
.is_constant = false, // This is analyzed later
|
||||
},
|
||||
.id = ValueId::array_initialization,
|
||||
};
|
||||
}
|
||||
|
||||
auto values = new_value_array(module, element_count);
|
||||
memcpy(values.pointer, value_buffer, element_count * sizeof(Value*));
|
||||
|
||||
result = new_value(module);
|
||||
*result = {
|
||||
.array_initialization = {
|
||||
.values = values,
|
||||
.is_constant = false, // This is analyzed later
|
||||
},
|
||||
.id = ValueId::array_initialization,
|
||||
};
|
||||
} break;
|
||||
case TokenId::dot:
|
||||
{
|
||||
@ -1477,76 +1576,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
} break;
|
||||
case TokenId::left_brace:
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
u64 field_count = 0;
|
||||
String name_buffer[64];
|
||||
Value* value_buffer[64];
|
||||
bool zero = false;
|
||||
|
||||
while (1)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, right_brace))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto field_index = field_count;
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
auto name = parse_identifier(module);
|
||||
name_buffer[field_index] = name;
|
||||
skip_space(module);
|
||||
expect_character(module, '=');
|
||||
skip_space(module);
|
||||
|
||||
auto value = parse_value(module, scope, {});
|
||||
value_buffer[field_index] = value;
|
||||
skip_space(module);
|
||||
consume_character_if_match(module, ',');
|
||||
}
|
||||
else
|
||||
{
|
||||
auto token = tokenize(module);
|
||||
zero = token.id == TokenId::value_keyword && token.value_keyword == ValueKeyword::zero;
|
||||
if (zero)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, ','))
|
||||
{
|
||||
skip_space(module);
|
||||
}
|
||||
|
||||
expect_character(module, right_brace);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
|
||||
field_count += 1;
|
||||
}
|
||||
|
||||
auto names = arena_allocate<String>(module->arena, field_count);
|
||||
memcpy(names.pointer, name_buffer, sizeof(String) * field_count);
|
||||
auto values = new_value_array(module, field_count);
|
||||
memcpy(values.pointer, value_buffer, sizeof(Value*) * field_count);
|
||||
|
||||
result = new_value(module);
|
||||
*result = {
|
||||
.aggregate_initialization = {
|
||||
.names = names,
|
||||
.values = values,
|
||||
.is_constant = false,
|
||||
.zero = zero,
|
||||
},
|
||||
.id = ValueId::aggregate_initialization,
|
||||
};
|
||||
result = parse_aggregate_initialization(module, scope, builder, right_brace);
|
||||
} break;
|
||||
case TokenId::value_keyword:
|
||||
{
|
||||
@ -2779,6 +2809,7 @@ void parse(Module* module)
|
||||
enumerator,
|
||||
function,
|
||||
macro,
|
||||
opaque,
|
||||
structure,
|
||||
typealias,
|
||||
union_type,
|
||||
@ -2798,6 +2829,7 @@ void parse(Module* module)
|
||||
string_literal("enum"),
|
||||
string_literal("fn"),
|
||||
string_literal("macro"),
|
||||
string_literal("opaque"),
|
||||
string_literal("struct"),
|
||||
string_literal("typealias"),
|
||||
string_literal("union"),
|
||||
@ -3485,6 +3517,8 @@ void parse(Module* module)
|
||||
field_count += 1;
|
||||
}
|
||||
|
||||
byte_size = align_forward(byte_size, byte_alignment);
|
||||
|
||||
skip_space(module);
|
||||
consume_character_if_match(module, ';');
|
||||
|
||||
@ -3605,6 +3639,16 @@ void parse(Module* module)
|
||||
};
|
||||
union_type->id = TypeId::union_type;
|
||||
} break;
|
||||
case GlobalKeyword::opaque:
|
||||
{
|
||||
skip_space(module);
|
||||
expect_character(module, ';');
|
||||
auto opaque_type = type_allocate_init(module, {
|
||||
.id = TypeId::opaque,
|
||||
.name = global_name,
|
||||
});
|
||||
unused(opaque_type);
|
||||
} break;
|
||||
case GlobalKeyword::count:
|
||||
{
|
||||
set_checkpoint(module, checkpoint);
|
||||
|
22
tests/enum_array.bbb
Normal file
22
tests/enum_array.bbb
Normal file
@ -0,0 +1,22 @@
|
||||
require = fn (ok: u1) void
|
||||
{
|
||||
if (!ok) #trap();
|
||||
}
|
||||
|
||||
E = enum
|
||||
{
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
}
|
||||
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>some_enum_array: enum_array[E](u32) = [ .a = 4, .b = 3, .c = 2, .d = 1 ];
|
||||
require(some_enum_array[.a] == 4);
|
||||
require(some_enum_array[.b] == 3);
|
||||
require(some_enum_array[.c] == 2);
|
||||
require(some_enum_array[.d] == 1);
|
||||
return 0;
|
||||
}
|
16
tests/opaque.bbb
Normal file
16
tests/opaque.bbb
Normal file
@ -0,0 +1,16 @@
|
||||
OpaqueType = opaque;
|
||||
|
||||
[extern] memcpy = fn [cc(c)] (destination: &s32, source: &s32, size: u64) &OpaqueType;
|
||||
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>destination: s32 = 1;
|
||||
>source: s32 = 0;
|
||||
>opaque_pointer = memcpy(&destination, &source, #byte_size(s32));
|
||||
>pointer: &s32 = #pointer_cast(opaque_pointer);
|
||||
if (pointer != &destination)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
return destination;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user