Forward declared type
This commit is contained in:
parent
5de4ba76f5
commit
f90b527286
629
src/compiler.bbb
629
src/compiler.bbb
@ -533,8 +533,7 @@ CompilerCommand = enum
|
|||||||
BuildMode = enum
|
BuildMode = enum
|
||||||
{
|
{
|
||||||
debug_none,
|
debug_none,
|
||||||
debug_fast,
|
debug,
|
||||||
debug_size,
|
|
||||||
soft_optimize,
|
soft_optimize,
|
||||||
optimize_for_speed,
|
optimize_for_speed,
|
||||||
optimize_for_size,
|
optimize_for_size,
|
||||||
@ -590,7 +589,21 @@ TypeId = enum
|
|||||||
{
|
{
|
||||||
void,
|
void,
|
||||||
noreturn,
|
noreturn,
|
||||||
|
forward_declaration,
|
||||||
integer,
|
integer,
|
||||||
|
function,
|
||||||
|
pointer,
|
||||||
|
array,
|
||||||
|
enum,
|
||||||
|
struct,
|
||||||
|
bits,
|
||||||
|
alias,
|
||||||
|
union,
|
||||||
|
unresolved,
|
||||||
|
vector,
|
||||||
|
floating_point,
|
||||||
|
enum_array,
|
||||||
|
opaque,
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeInteger = struct
|
TypeInteger = struct
|
||||||
@ -609,6 +622,7 @@ Type = struct
|
|||||||
content: TypeContent,
|
content: TypeContent,
|
||||||
id: TypeId,
|
id: TypeId,
|
||||||
name: []u8,
|
name: []u8,
|
||||||
|
next: &Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueId = enum
|
ValueId = enum
|
||||||
@ -625,16 +639,148 @@ Value = struct
|
|||||||
i128_offset: u64 = 64 * 2;
|
i128_offset: u64 = 64 * 2;
|
||||||
void_offset: u64 = i128_offset + 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
|
Module = struct
|
||||||
{
|
{
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
base_type_allocation: &Type,
|
|
||||||
void_value: &Value,
|
|
||||||
// Parser data
|
|
||||||
content: []u8,
|
content: []u8,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
line_offset: u64,
|
line_offset: u64,
|
||||||
line_character_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
|
module_integer_type = fn (module: &Module, integer: TypeInteger) &Type
|
||||||
@ -642,12 +788,12 @@ module_integer_type = fn (module: &Module, integer: TypeInteger) &Type
|
|||||||
assert(integer.bit_count != 0);
|
assert(integer.bit_count != 0);
|
||||||
assert(integer.bit_count <= 64);
|
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));
|
>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;
|
return module.first_type + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
module_void_type = fn (module: &Module) &Type
|
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
|
module_noreturn_type = fn (module: &Module) &Type
|
||||||
@ -697,7 +843,7 @@ is_hex = fn (ch: u8) u1
|
|||||||
|
|
||||||
is_identifier_start = 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
|
is_identifier = fn (ch: u8) u1
|
||||||
@ -724,6 +870,29 @@ get_column = fn (module: &Module) u32
|
|||||||
return #truncate(column);
|
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
|
skip_space = fn (module: &Module) void
|
||||||
{
|
{
|
||||||
while (1)
|
while (1)
|
||||||
@ -817,8 +986,447 @@ parse_identifier = fn (module: &Module) []u8
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CallingConvention = enum
|
||||||
|
{
|
||||||
|
c,
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineBehavior = enum
|
||||||
|
{
|
||||||
|
default = 0,
|
||||||
|
always_inline = 1,
|
||||||
|
no_inline = 2,
|
||||||
|
inline_hint = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionAttributes = struct
|
||||||
|
{
|
||||||
|
inline_behavior: InlineBehavior,
|
||||||
|
naked: u1,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
assert(is_decimal(ch));
|
||||||
|
return (accumulator * 10) + (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_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;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#trap();
|
||||||
|
}
|
||||||
|
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
|
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);
|
||||||
|
|
||||||
|
#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
|
emit = fn (module: &Module) void
|
||||||
@ -902,12 +1510,15 @@ compile = fn (arena: &Arena, options: CompileOptions) void
|
|||||||
|
|
||||||
>module: Module = {
|
>module: Module = {
|
||||||
.arena = arena,
|
.arena = arena,
|
||||||
.base_type_allocation = base_type_allocation,
|
.first_type = base_type_allocation,
|
||||||
.void_value = void_value,
|
.void_value = void_value,
|
||||||
.content = options.content,
|
.content = options.content,
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.line_offset = 0,
|
.line_offset = 0,
|
||||||
.line_character_offset = 0,
|
.line_character_offset = 0,
|
||||||
|
.scope = {
|
||||||
|
zero,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
parse(&module);
|
parse(&module);
|
||||||
|
@ -306,8 +306,11 @@ global_variable String names[] =
|
|||||||
string_literal("noreturn_macro"),
|
string_literal("noreturn_macro"),
|
||||||
string_literal("generic_pointer_array"),
|
string_literal("generic_pointer_array"),
|
||||||
|
|
||||||
string_literal("self_referential_struct"), // TODO
|
string_literal("self_referential_struct"),
|
||||||
// string_literal("forward_declared_type"),
|
string_literal("forward_declared_type"),
|
||||||
|
|
||||||
|
string_literal("enum_array"),
|
||||||
|
string_literal("opaque"),
|
||||||
};
|
};
|
||||||
|
|
||||||
void entry_point(Slice<const char*> arguments, Slice<char* const> environment)
|
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;
|
auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0;
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
print(string_literal("Standalone test failed: "));
|
print(string_literal("Self-hosted test failed: "));
|
||||||
print(name);
|
print(name);
|
||||||
print(string_literal("\n"));
|
print(string_literal("\n"));
|
||||||
bb_fail();
|
bb_fail();
|
||||||
|
@ -314,6 +314,8 @@ enum class TypeId
|
|||||||
unresolved,
|
unresolved,
|
||||||
vector,
|
vector,
|
||||||
floating_point,
|
floating_point,
|
||||||
|
enum_array,
|
||||||
|
opaque,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeInteger
|
struct TypeInteger
|
||||||
@ -448,6 +450,13 @@ struct LLVMType
|
|||||||
LLVMMetadataRef debug;
|
LLVMMetadataRef debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TypeEnumArray
|
||||||
|
{
|
||||||
|
Type* enum_type;
|
||||||
|
Type* element_type;
|
||||||
|
Type* next;
|
||||||
|
};
|
||||||
|
|
||||||
struct Type
|
struct Type
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
@ -461,6 +470,7 @@ struct Type
|
|||||||
TypeBits bits;
|
TypeBits bits;
|
||||||
TypeAlias alias;
|
TypeAlias alias;
|
||||||
TypeUnion union_type;
|
TypeUnion union_type;
|
||||||
|
TypeEnumArray enum_array;
|
||||||
};
|
};
|
||||||
TypeId id;
|
TypeId id;
|
||||||
String name;
|
String name;
|
||||||
@ -528,6 +538,17 @@ fn u64 get_byte_size(Type* type)
|
|||||||
auto result = type->union_type.byte_size;
|
auto result = type->union_type.byte_size;
|
||||||
return result;
|
return result;
|
||||||
} break;
|
} 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();
|
default: trap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,6 +596,10 @@ fn u32 get_byte_alignment(Type* type)
|
|||||||
{
|
{
|
||||||
return get_byte_alignment(type->alias.type);
|
return get_byte_alignment(type->alias.type);
|
||||||
} break;
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
return get_byte_alignment(type->enum_array.element_type);
|
||||||
|
} break;
|
||||||
default: trap();
|
default: trap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1126,6 +1151,7 @@ struct Module
|
|||||||
Type* first_slice_type;
|
Type* first_slice_type;
|
||||||
Type* first_pair_struct_type;
|
Type* first_pair_struct_type;
|
||||||
Type* first_array_type;
|
Type* first_array_type;
|
||||||
|
Type* first_enum_array_type;
|
||||||
|
|
||||||
Type* first_type;
|
Type* first_type;
|
||||||
Type* last_type;
|
Type* last_type;
|
||||||
@ -1714,5 +1740,52 @@ fn Local* new_local(Module* module, Scope* scope)
|
|||||||
return result;
|
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 parse(Module* module);
|
||||||
void emit(Module* module);
|
void emit(Module* module);
|
||||||
|
339
src/emitter.cpp
339
src/emitter.cpp
@ -17,6 +17,8 @@ fn void analyze_block(Module* module, Block* block);
|
|||||||
fn void emit_local_storage(Module* module, Variable* variable);
|
fn void emit_local_storage(Module* module, Variable* variable);
|
||||||
fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right);
|
fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right);
|
||||||
fn void emit_macro_instantiation(Module* module, Value* value);
|
fn void emit_macro_instantiation(Module* module, Value* value);
|
||||||
|
fn void emit_value(Module* module, Value* value, TypeKind type_kind);
|
||||||
|
fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind);
|
||||||
|
|
||||||
fn void emit_block(Module* module, LLVMBasicBlockRef basic_block)
|
fn void emit_block(Module* module, LLVMBasicBlockRef basic_block)
|
||||||
{
|
{
|
||||||
@ -42,6 +44,24 @@ fn void emit_block(Module* module, LLVMBasicBlockRef basic_block)
|
|||||||
LLVMPositionBuilderAtEnd(module->llvm.builder, basic_block);
|
LLVMPositionBuilderAtEnd(module->llvm.builder, basic_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn LLVMValueRef emit_condition(Module* module, Value* condition_value)
|
||||||
|
{
|
||||||
|
auto condition_llvm_value = condition_value->llvm;
|
||||||
|
auto condition_type = condition_value->type;
|
||||||
|
assert(condition_type);
|
||||||
|
assert(condition_llvm_value);
|
||||||
|
|
||||||
|
assert(condition_type->id == TypeId::integer || condition_type->id == TypeId::pointer);
|
||||||
|
if (!(condition_type->id == TypeId::integer && condition_type->integer.bit_count == 1))
|
||||||
|
{
|
||||||
|
condition_llvm_value = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, condition_llvm_value, LLVMConstNull(condition_type->llvm.abi), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(condition_llvm_value);
|
||||||
|
|
||||||
|
return condition_llvm_value;
|
||||||
|
}
|
||||||
|
|
||||||
fn LLVMValueRef emit_intrinsic_call(Module* module, IntrinsicIndex index, Slice<LLVMTypeRef> argument_types, Slice<LLVMValueRef> argument_values)
|
fn LLVMValueRef emit_intrinsic_call(Module* module, IntrinsicIndex index, Slice<LLVMTypeRef> argument_types, Slice<LLVMValueRef> argument_values)
|
||||||
{
|
{
|
||||||
auto intrinsic_id = module->llvm.intrinsic_table[(backing_type(IntrinsicIndex))index];
|
auto intrinsic_id = module->llvm.intrinsic_table[(backing_type(IntrinsicIndex))index];
|
||||||
@ -70,6 +90,7 @@ fn EvaluationKind get_evaluation_kind(Type* type)
|
|||||||
case TypeId::array:
|
case TypeId::array:
|
||||||
case TypeId::structure:
|
case TypeId::structure:
|
||||||
case TypeId::union_type:
|
case TypeId::union_type:
|
||||||
|
case TypeId::enum_array:
|
||||||
return EvaluationKind::aggregate;
|
return EvaluationKind::aggregate;
|
||||||
default:
|
default:
|
||||||
unreachable();
|
unreachable();
|
||||||
@ -216,8 +237,6 @@ fn void dump_module(Module* module)
|
|||||||
print(llvm_module_to_string(module->llvm.module));
|
print(llvm_module_to_string(module->llvm.module));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void emit_value(Module* module, Value* value, TypeKind type_kind);
|
|
||||||
|
|
||||||
fn LLVMCallConv llvm_calling_convention(CallingConvention calling_convention)
|
fn LLVMCallConv llvm_calling_convention(CallingConvention calling_convention)
|
||||||
{
|
{
|
||||||
LLVMCallConv cc;
|
LLVMCallConv cc;
|
||||||
@ -235,20 +254,6 @@ fn Type* resolve_alias(Module* module, Type* type)
|
|||||||
Type* result_type = 0;
|
Type* result_type = 0;
|
||||||
switch (type->id)
|
switch (type->id)
|
||||||
{
|
{
|
||||||
case TypeId::pointer:
|
|
||||||
{
|
|
||||||
auto* element_type = type->pointer.element_type;
|
|
||||||
auto* resolved_element_type = resolve_alias(module, element_type);
|
|
||||||
result_type = get_pointer_type(module, resolved_element_type);
|
|
||||||
} break;
|
|
||||||
case TypeId::array:
|
|
||||||
{
|
|
||||||
auto* element_type = type->array.element_type;
|
|
||||||
auto element_count = type->array.element_count;
|
|
||||||
assert(element_count);
|
|
||||||
auto* resolved_element_type = resolve_alias(module, element_type);
|
|
||||||
result_type = get_array_type(module, resolved_element_type, element_count);
|
|
||||||
} break;
|
|
||||||
case TypeId::void_type:
|
case TypeId::void_type:
|
||||||
case TypeId::noreturn:
|
case TypeId::noreturn:
|
||||||
case TypeId::integer:
|
case TypeId::integer:
|
||||||
@ -256,16 +261,53 @@ fn Type* resolve_alias(Module* module, Type* type)
|
|||||||
case TypeId::function:
|
case TypeId::function:
|
||||||
case TypeId::bits:
|
case TypeId::bits:
|
||||||
case TypeId::union_type:
|
case TypeId::union_type:
|
||||||
|
case TypeId::opaque:
|
||||||
{
|
{
|
||||||
result_type = type;
|
result_type = type;
|
||||||
} break;
|
} break;
|
||||||
|
case TypeId::pointer:
|
||||||
|
{
|
||||||
|
auto* element_type = type->pointer.element_type;
|
||||||
|
auto* resolved_element_type = resolve_alias(module, element_type);
|
||||||
|
if (element_type == resolved_element_type)
|
||||||
|
{
|
||||||
|
result_type = type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result_type = get_pointer_type(module, resolved_element_type);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case TypeId::array:
|
||||||
|
{
|
||||||
|
auto* element_type = type->array.element_type;
|
||||||
|
auto element_count = type->array.element_count;
|
||||||
|
assert(element_count);
|
||||||
|
auto* resolved_element_type = resolve_alias(module, element_type);
|
||||||
|
if (element_type == resolved_element_type)
|
||||||
|
{
|
||||||
|
result_type = type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result_type = get_array_type(module, resolved_element_type, element_count);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
case TypeId::structure:
|
case TypeId::structure:
|
||||||
{
|
{
|
||||||
if (type->structure.is_slice)
|
if (type->structure.is_slice)
|
||||||
{
|
{
|
||||||
auto element_type = resolve_alias(module, type->structure.fields[0].type->pointer.element_type);
|
auto old_element_type = type->structure.fields[0].type->pointer.element_type;
|
||||||
|
auto element_type = resolve_alias(module, old_element_type);
|
||||||
|
if (old_element_type == element_type)
|
||||||
|
{
|
||||||
|
result_type = type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
result_type = get_slice_type(module, element_type);
|
result_type = get_slice_type(module, element_type);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result_type = type;
|
result_type = type;
|
||||||
@ -275,6 +317,22 @@ fn Type* resolve_alias(Module* module, Type* type)
|
|||||||
{
|
{
|
||||||
result_type = resolve_alias(module, type->alias.type);
|
result_type = resolve_alias(module, type->alias.type);
|
||||||
} break;
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
auto old_enum_type = type->enum_array.enum_type;
|
||||||
|
auto old_element_type = type->enum_array.element_type;
|
||||||
|
auto enum_type = resolve_alias(module, old_enum_type);
|
||||||
|
auto element_type = resolve_alias(module, old_element_type);
|
||||||
|
|
||||||
|
if (old_enum_type == enum_type && old_element_type == element_type)
|
||||||
|
{
|
||||||
|
result_type = type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result_type = get_enum_array_type(module, enum_type, element_type);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
default: unreachable();
|
default: unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,9 +518,31 @@ fn bool contains_no_user_data(Type* type, u64 start, u64 end)
|
|||||||
return true;
|
return true;
|
||||||
} break;
|
} break;
|
||||||
case TypeId::array:
|
case TypeId::array:
|
||||||
|
case TypeId::enum_array:
|
||||||
{
|
{
|
||||||
auto element_type = type->array.element_type;
|
Type* element_type = 0;
|
||||||
auto element_count = type->array.element_count;
|
u64 element_count = 0;
|
||||||
|
|
||||||
|
switch (type->id)
|
||||||
|
{
|
||||||
|
case TypeId::array:
|
||||||
|
{
|
||||||
|
element_type = type->array.element_type;
|
||||||
|
element_count = type->array.element_count;
|
||||||
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
auto enum_type = type->enum_array.enum_type;
|
||||||
|
assert(enum_type->id == TypeId::enumerator);
|
||||||
|
element_count = enum_type->enumerator.fields.length;
|
||||||
|
element_type = type->enum_array.element_type;
|
||||||
|
} break;
|
||||||
|
default: unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(element_type);
|
||||||
|
assert(element_count);
|
||||||
|
|
||||||
auto element_size = get_byte_size(element_type);
|
auto element_size = get_byte_size(element_type);
|
||||||
|
|
||||||
for (u64 i = 0; i < element_count; i += 1)
|
for (u64 i = 0; i < element_count; i += 1)
|
||||||
@ -479,6 +559,7 @@ fn bool contains_no_user_data(Type* type, u64 start, u64 end)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trap();
|
trap();
|
||||||
} break;
|
} break;
|
||||||
default: return false;
|
default: return false;
|
||||||
@ -826,6 +907,17 @@ fn void resolve_type_in_place_abi(Module* module, Type* type)
|
|||||||
resolve_type_in_place_abi(module, aliased);
|
resolve_type_in_place_abi(module, aliased);
|
||||||
result = aliased->llvm.abi;
|
result = aliased->llvm.abi;
|
||||||
} break;
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
auto enum_type = type->enum_array.enum_type;
|
||||||
|
assert(enum_type->id == TypeId::enumerator);
|
||||||
|
auto element_type = type->enum_array.element_type;
|
||||||
|
resolve_type_in_place_memory(module, element_type);
|
||||||
|
auto element_count = enum_type->enumerator.fields.length;
|
||||||
|
assert(element_count);
|
||||||
|
auto array_type = LLVMArrayType2(element_type->llvm.memory, element_count);
|
||||||
|
result = array_type;
|
||||||
|
} break;
|
||||||
default: unreachable();
|
default: unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -849,6 +941,7 @@ fn void resolve_type_in_place_memory(Module* module, Type* type)
|
|||||||
case TypeId::pointer:
|
case TypeId::pointer:
|
||||||
case TypeId::array:
|
case TypeId::array:
|
||||||
case TypeId::structure:
|
case TypeId::structure:
|
||||||
|
case TypeId::enum_array:
|
||||||
result = type->llvm.abi;
|
result = type->llvm.abi;
|
||||||
break;
|
break;
|
||||||
case TypeId::integer:
|
case TypeId::integer:
|
||||||
@ -918,11 +1011,8 @@ fn void resolve_type_in_place_debug(Module* module, Type* type)
|
|||||||
case TypeId::pointer:
|
case TypeId::pointer:
|
||||||
{
|
{
|
||||||
resolve_type_in_place_debug(module, type->pointer.element_type);
|
resolve_type_in_place_debug(module, type->pointer.element_type);
|
||||||
if (type->llvm.debug)
|
result = type->llvm.debug;
|
||||||
{
|
if (!result)
|
||||||
trap();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, 64, 64, 0, (char*)type->name.pointer, type->name.length);
|
result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, 64, 64, 0, (char*)type->name.pointer, type->name.length);
|
||||||
}
|
}
|
||||||
@ -1026,6 +1116,23 @@ fn void resolve_type_in_place_debug(Module* module, Type* type)
|
|||||||
auto alignment = get_byte_alignment(aliased);
|
auto alignment = get_byte_alignment(aliased);
|
||||||
result = LLVMDIBuilderCreateTypedef(module->llvm.di_builder, aliased->llvm.debug, (char*) type->name.pointer, type->name.length, module->llvm.file, type->alias.line, type->alias.scope->llvm, alignment * 8);
|
result = LLVMDIBuilderCreateTypedef(module->llvm.di_builder, aliased->llvm.debug, (char*) type->name.pointer, type->name.length, module->llvm.file, type->alias.line, type->alias.scope->llvm, alignment * 8);
|
||||||
} break;
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
auto enum_type = type->enum_array.enum_type;
|
||||||
|
assert(enum_type->id == TypeId::enumerator);
|
||||||
|
auto element_type = type->enum_array.element_type;
|
||||||
|
auto element_count = enum_type->enumerator.fields.length;
|
||||||
|
resolve_type_in_place_debug(module, element_type);
|
||||||
|
assert(element_count);
|
||||||
|
auto bit_alignment = get_byte_alignment(type) * 8;
|
||||||
|
auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, element_count, bit_alignment, element_type->llvm.debug, 0, 0);
|
||||||
|
result = array_type;
|
||||||
|
} break;
|
||||||
|
case TypeId::opaque:
|
||||||
|
{
|
||||||
|
// TODO: ?
|
||||||
|
return;
|
||||||
|
} break;
|
||||||
default: unreachable();
|
default: unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3014,7 +3121,6 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type)
|
|||||||
} break;
|
} break;
|
||||||
case ValueId::array_expression:
|
case ValueId::array_expression:
|
||||||
{
|
{
|
||||||
analyze_type(module, value->array_expression.index, uint64(module));
|
|
||||||
auto array_like = value->array_expression.array_like;
|
auto array_like = value->array_expression.array_like;
|
||||||
array_like->kind = ValueKind::left;
|
array_like->kind = ValueKind::left;
|
||||||
analyze_type(module, array_like, 0);
|
analyze_type(module, array_like, 0);
|
||||||
@ -3026,6 +3132,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type)
|
|||||||
}
|
}
|
||||||
auto pointer_element_type = array_like_type->pointer.element_type;
|
auto pointer_element_type = array_like_type->pointer.element_type;
|
||||||
|
|
||||||
|
analyze_type(module, value->array_expression.index, pointer_element_type->id == TypeId::enum_array ? pointer_element_type->enum_array.enum_type : uint64(module));
|
||||||
|
|
||||||
Type* element_type = 0;
|
Type* element_type = 0;
|
||||||
switch (pointer_element_type->id)
|
switch (pointer_element_type->id)
|
||||||
{
|
{
|
||||||
@ -3048,6 +3156,10 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type)
|
|||||||
{
|
{
|
||||||
element_type = pointer_element_type->pointer.element_type;
|
element_type = pointer_element_type->pointer.element_type;
|
||||||
} break;
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
element_type = pointer_element_type->enum_array.element_type;
|
||||||
|
} break;
|
||||||
default: report_error();
|
default: report_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3118,6 +3230,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type)
|
|||||||
|
|
||||||
if (!result_field)
|
if (!result_field)
|
||||||
{
|
{
|
||||||
|
// Field not found
|
||||||
report_error();
|
report_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3349,7 +3462,6 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type)
|
|||||||
{
|
{
|
||||||
auto fields = resolved_type->bits.fields;
|
auto fields = resolved_type->bits.fields;
|
||||||
|
|
||||||
|
|
||||||
assert(values.length == names.length);
|
assert(values.length == names.length);
|
||||||
|
|
||||||
for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1)
|
for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1)
|
||||||
@ -3410,8 +3522,43 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type)
|
|||||||
|
|
||||||
auto field = &fields[i];
|
auto field = &fields[i];
|
||||||
analyze_type(module, initialization_value, field->type);
|
analyze_type(module, initialization_value, field->type);
|
||||||
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
bool is_ordered = true;
|
||||||
|
auto enum_type = resolved_type->enum_array.enum_type;
|
||||||
|
auto element_type = resolved_type->enum_array.element_type;
|
||||||
|
assert(enum_type->id == TypeId::enumerator);
|
||||||
|
auto fields = enum_type->enumerator.fields;
|
||||||
|
|
||||||
value_type = expected_type;
|
for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1)
|
||||||
|
{
|
||||||
|
auto value = values[initialization_index];
|
||||||
|
auto name = names[initialization_index];
|
||||||
|
|
||||||
|
u32 declaration_index;
|
||||||
|
for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1)
|
||||||
|
{
|
||||||
|
auto& field = fields[declaration_index];
|
||||||
|
|
||||||
|
if (name.equal(field.name))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declaration_index == fields.length)
|
||||||
|
{
|
||||||
|
report_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
is_ordered = is_ordered && declaration_index == initialization_index;
|
||||||
|
|
||||||
|
analyze_type(module, value, element_type);
|
||||||
|
is_constant = is_constant && value->is_constant();
|
||||||
|
}
|
||||||
|
|
||||||
|
value->aggregate_initialization.is_constant = is_constant && is_ordered;
|
||||||
} break;
|
} break;
|
||||||
default: report_error();
|
default: report_error();
|
||||||
}
|
}
|
||||||
@ -6072,6 +6219,23 @@ fn void analyze_block(Module* module, Block* block)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn LLVMValueRef emit_constant_array(Module* module, Slice<Value*> values, Type* element_type)
|
||||||
|
{
|
||||||
|
LLVMValueRef value_buffer[64];
|
||||||
|
|
||||||
|
resolve_type_in_place(module, element_type);
|
||||||
|
|
||||||
|
for (u64 i = 0; i < values.length; i += 1)
|
||||||
|
{
|
||||||
|
auto* v = values[i];
|
||||||
|
emit_value(module, v, TypeKind::memory);
|
||||||
|
value_buffer[i] = v->llvm;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto constant_array = LLVMConstArray2(element_type->llvm.memory, value_buffer, values.length);
|
||||||
|
return constant_array;
|
||||||
|
}
|
||||||
|
|
||||||
fn void emit_value(Module* module, Value* value, TypeKind type_kind)
|
fn void emit_value(Module* module, Value* value, TypeKind type_kind)
|
||||||
{
|
{
|
||||||
assert(value->type);
|
assert(value->type);
|
||||||
@ -6286,41 +6450,10 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto* left = value->binary.left;
|
auto* left = value->binary.left;
|
||||||
if (left->llvm)
|
|
||||||
{
|
|
||||||
assert(false); // TODO: check if this if is really necessary
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
emit_value(module, left, TypeKind::abi);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto left_llvm = left->llvm;
|
|
||||||
|
|
||||||
LLVMValueRef left_condition = 0;
|
|
||||||
|
|
||||||
switch (left->type->id)
|
|
||||||
{
|
|
||||||
case TypeId::integer:
|
|
||||||
{
|
|
||||||
switch (left->type->integer.bit_count)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
left_condition = left_llvm;
|
|
||||||
break;
|
|
||||||
default: trap();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
default: trap();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(left_condition);
|
|
||||||
|
|
||||||
auto llvm_function = module->current_function->variable.storage->llvm;
|
auto llvm_function = module->current_function->variable.storage->llvm;
|
||||||
assert(llvm_function);
|
assert(llvm_function);
|
||||||
|
|
||||||
auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder);
|
|
||||||
|
|
||||||
auto* right_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.right"), llvm_function);
|
auto* right_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.right"), llvm_function);
|
||||||
auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.end"), llvm_function);
|
auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.end"), llvm_function);
|
||||||
|
|
||||||
@ -6339,7 +6472,11 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLVMBuildCondBr(module->llvm.builder, left_condition, true_block, false_block);
|
emit_value(module, left, TypeKind::abi);
|
||||||
|
auto llvm_condition = emit_condition(module, left);
|
||||||
|
auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder);
|
||||||
|
|
||||||
|
LLVMBuildCondBr(module->llvm.builder, llvm_condition, true_block, false_block);
|
||||||
|
|
||||||
LLVMPositionBuilderAtEnd(module->llvm.builder, right_block);
|
LLVMPositionBuilderAtEnd(module->llvm.builder, right_block);
|
||||||
|
|
||||||
@ -6509,25 +6646,12 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind)
|
|||||||
case ValueId::array_initialization:
|
case ValueId::array_initialization:
|
||||||
{
|
{
|
||||||
auto values = value->array_initialization.values;
|
auto values = value->array_initialization.values;
|
||||||
auto element_count = values.length;
|
|
||||||
|
|
||||||
if (value->array_initialization.is_constant)
|
if (value->array_initialization.is_constant)
|
||||||
{
|
{
|
||||||
assert(value->kind == ValueKind::right);
|
assert(value->kind == ValueKind::right);
|
||||||
auto element_type = resolved_value_type->array.element_type;
|
auto element_type = resolved_value_type->array.element_type;
|
||||||
LLVMValueRef value_buffer[64];
|
llvm_value = emit_constant_array(module, values, element_type);
|
||||||
|
|
||||||
resolve_type_in_place(module, element_type);
|
|
||||||
|
|
||||||
for (u64 i = 0; i < element_count; i += 1)
|
|
||||||
{
|
|
||||||
auto* v = values[i];
|
|
||||||
emit_value(module, v, TypeKind::memory);
|
|
||||||
value_buffer[i] = v->llvm;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto constant_array = LLVMConstArray2(element_type->llvm.memory, value_buffer, element_count);
|
|
||||||
llvm_value = constant_array;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -6592,20 +6716,48 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind)
|
|||||||
|
|
||||||
switch (pointer_element_type->id)
|
switch (pointer_element_type->id)
|
||||||
{
|
{
|
||||||
|
case TypeId::enum_array:
|
||||||
case TypeId::array:
|
case TypeId::array:
|
||||||
{
|
{
|
||||||
auto array_type = pointer_element_type;
|
auto array_type = pointer_element_type;
|
||||||
|
|
||||||
auto uint64_type = uint64(module);
|
auto uint64_type = uint64(module);
|
||||||
resolve_type_in_place(module, uint64_type);
|
resolve_type_in_place(module, uint64_type);
|
||||||
auto zero_index = LLVMConstNull(uint64_type->llvm.abi);
|
auto u64_llvm = uint64_type->llvm.abi;
|
||||||
|
auto zero_index = LLVMConstNull(u64_llvm);
|
||||||
|
|
||||||
|
Type* element_type = 0;
|
||||||
|
LLVMValueRef llvm_index = index->llvm;
|
||||||
|
|
||||||
|
switch (pointer_element_type->id)
|
||||||
|
{
|
||||||
|
case TypeId::array:
|
||||||
|
{
|
||||||
|
element_type = array_type->array.element_type;
|
||||||
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
auto enum_type = array_type->enum_array.enum_type;
|
||||||
|
assert(enum_type->id == TypeId::enumerator);
|
||||||
|
auto enumerator_size = get_bit_size(enum_type->enumerator.backing_type);
|
||||||
|
if (enumerator_size != 64)
|
||||||
|
{
|
||||||
|
llvm_index = LLVMBuildIntCast2(module->llvm.builder, llvm_index, u64_llvm, false, "");
|
||||||
|
}
|
||||||
|
element_type = array_type->enum_array.element_type;
|
||||||
|
} break;
|
||||||
|
default: unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(element_type);
|
||||||
|
assert(llvm_index);
|
||||||
|
|
||||||
LLVMValueRef indices[] = { zero_index, index->llvm };
|
LLVMValueRef indices[] = { zero_index, index->llvm };
|
||||||
auto gep = create_gep(module, {
|
auto gep = create_gep(module, {
|
||||||
.type = array_type->llvm.memory,
|
.type = array_type->llvm.memory,
|
||||||
.pointer = array_like->llvm,
|
.pointer = array_like->llvm,
|
||||||
.indices = array_to_slice(indices),
|
.indices = array_to_slice(indices),
|
||||||
});
|
});
|
||||||
auto element_type = array_type->array.element_type;
|
|
||||||
|
|
||||||
switch (value->kind)
|
switch (value->kind)
|
||||||
{
|
{
|
||||||
@ -6877,6 +7029,12 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
case TypeId::enum_array:
|
||||||
|
{
|
||||||
|
assert(is_constant);
|
||||||
|
auto element_type = resolved_value_type->enum_array.element_type;
|
||||||
|
llvm_value = emit_constant_array(module, values, element_type);
|
||||||
|
} break;
|
||||||
default: unreachable();
|
default: unreachable();
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -7104,19 +7262,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3
|
|||||||
|
|
||||||
auto condition = statement->if_st.condition;
|
auto condition = statement->if_st.condition;
|
||||||
analyze_value(module, condition, 0, TypeKind::abi);
|
analyze_value(module, condition, 0, TypeKind::abi);
|
||||||
auto condition_type = condition->type;
|
auto llvm_condition = emit_condition(module, statement->if_st.condition);
|
||||||
|
|
||||||
LLVMValueRef llvm_condition = 0;
|
|
||||||
assert(condition_type->id == TypeId::integer || condition_type->id == TypeId::pointer);
|
|
||||||
|
|
||||||
llvm_condition = condition->llvm;
|
|
||||||
|
|
||||||
if (!(condition_type->id == TypeId::integer && condition_type->integer.bit_count == 1))
|
|
||||||
{
|
|
||||||
llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(llvm_condition);
|
|
||||||
|
|
||||||
LLVMBuildCondBr(module->llvm.builder, llvm_condition, taken_block, not_taken_block);
|
LLVMBuildCondBr(module->llvm.builder, llvm_condition, taken_block, not_taken_block);
|
||||||
LLVMPositionBuilderAtEnd(module->llvm.builder, taken_block);
|
LLVMPositionBuilderAtEnd(module->llvm.builder, taken_block);
|
||||||
@ -7187,22 +7333,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
analyze_value(module, condition, 0, TypeKind::abi);
|
analyze_value(module, condition, 0, TypeKind::abi);
|
||||||
|
auto llvm_condition = emit_condition(module, condition);
|
||||||
auto boolean = uint1(module);
|
|
||||||
|
|
||||||
LLVMValueRef llvm_condition = condition->llvm;
|
|
||||||
auto condition_type = condition->type;
|
|
||||||
if (condition_type != boolean)
|
|
||||||
{
|
|
||||||
switch (condition_type->id)
|
|
||||||
{
|
|
||||||
case TypeId::integer:
|
|
||||||
{
|
|
||||||
llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), "");
|
|
||||||
} break;
|
|
||||||
default: unreachable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LLVMBuildCondBr(module->llvm.builder, llvm_condition, body_block, exit_block);
|
LLVMBuildCondBr(module->llvm.builder, llvm_condition, body_block, exit_block);
|
||||||
}
|
}
|
||||||
|
184
src/parser.cpp
184
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 Value* parse_value(Module* module, Scope* scope, ValueBuilder builder);
|
||||||
|
|
||||||
|
|
||||||
fn Type* parse_type(Module* module, Scope* scope)
|
fn Type* parse_type(Module* module, Scope* scope)
|
||||||
{
|
{
|
||||||
auto start_character = module->content[module->offset];
|
auto start_character = module->content[module->offset];
|
||||||
@ -465,6 +464,20 @@ fn Type* parse_type(Module* module, Scope* scope)
|
|||||||
{
|
{
|
||||||
return noreturn_type(module);
|
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
|
else
|
||||||
{
|
{
|
||||||
auto is_int_type = identifier.length > 1 && (identifier[0] == 's' || identifier[0] == 'u');
|
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_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_precedence(Module* module, Scope* scope, ValueBuilder builder);
|
||||||
fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||||
{
|
{
|
||||||
@ -1422,6 +1512,14 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
|||||||
u64 element_count = 0;
|
u64 element_count = 0;
|
||||||
Value* value_buffer[64];
|
Value* value_buffer[64];
|
||||||
|
|
||||||
|
skip_space(module);
|
||||||
|
|
||||||
|
if (module->content[module->offset] == '.')
|
||||||
|
{
|
||||||
|
result = parse_aggregate_initialization(module, scope, builder, right_bracket);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
skip_space(module);
|
skip_space(module);
|
||||||
@ -1449,6 +1547,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
|||||||
},
|
},
|
||||||
.id = ValueId::array_initialization,
|
.id = ValueId::array_initialization,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case TokenId::dot:
|
case TokenId::dot:
|
||||||
{
|
{
|
||||||
@ -1477,76 +1576,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
|||||||
} break;
|
} break;
|
||||||
case TokenId::left_brace:
|
case TokenId::left_brace:
|
||||||
{
|
{
|
||||||
skip_space(module);
|
result = parse_aggregate_initialization(module, scope, builder, right_brace);
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
} break;
|
} break;
|
||||||
case TokenId::value_keyword:
|
case TokenId::value_keyword:
|
||||||
{
|
{
|
||||||
@ -2779,6 +2809,7 @@ void parse(Module* module)
|
|||||||
enumerator,
|
enumerator,
|
||||||
function,
|
function,
|
||||||
macro,
|
macro,
|
||||||
|
opaque,
|
||||||
structure,
|
structure,
|
||||||
typealias,
|
typealias,
|
||||||
union_type,
|
union_type,
|
||||||
@ -2798,6 +2829,7 @@ void parse(Module* module)
|
|||||||
string_literal("enum"),
|
string_literal("enum"),
|
||||||
string_literal("fn"),
|
string_literal("fn"),
|
||||||
string_literal("macro"),
|
string_literal("macro"),
|
||||||
|
string_literal("opaque"),
|
||||||
string_literal("struct"),
|
string_literal("struct"),
|
||||||
string_literal("typealias"),
|
string_literal("typealias"),
|
||||||
string_literal("union"),
|
string_literal("union"),
|
||||||
@ -3605,6 +3637,16 @@ void parse(Module* module)
|
|||||||
};
|
};
|
||||||
union_type->id = TypeId::union_type;
|
union_type->id = TypeId::union_type;
|
||||||
} break;
|
} 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:
|
case GlobalKeyword::count:
|
||||||
{
|
{
|
||||||
set_checkpoint(module, checkpoint);
|
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