1078 lines
23 KiB
Plaintext
1078 lines
23 KiB
Plaintext
mode_t = typealias u64;
|
|
int = typealias s32;
|
|
usize = typealias u64;
|
|
ssize = typealias s64;
|
|
File = typealias s32;
|
|
|
|
uid_t = typealias u32;
|
|
gid_t = typealias u32;
|
|
off_t = typealias s64;
|
|
ino_t = typealias u64;
|
|
dev_t = typealias u64;
|
|
|
|
timespec = struct
|
|
{
|
|
seconds: ssize,
|
|
nanoseconds: ssize,
|
|
};
|
|
|
|
Stat = struct
|
|
{
|
|
dev: dev_t,
|
|
ino: ino_t,
|
|
nlink: usize,
|
|
|
|
mode: u32,
|
|
uid: uid_t,
|
|
gid: gid_t,
|
|
_: u32,
|
|
rdev: dev_t,
|
|
size: off_t,
|
|
blksize: ssize,
|
|
blocks: s64,
|
|
|
|
atim: timespec,
|
|
mtime: timespec,
|
|
ctim: timespec,
|
|
_: [3]ssize,
|
|
}
|
|
|
|
OAccessMode = enum u2
|
|
{
|
|
read_only = 0,
|
|
write_only = 1,
|
|
read_write = 2,
|
|
}
|
|
|
|
O = bits u32
|
|
{
|
|
access_mode: OAccessMode,
|
|
_: u4,
|
|
creat: u1,
|
|
excl: u1,
|
|
noctty: u1,
|
|
trunc: u1,
|
|
append: u1,
|
|
nonblock: u1,
|
|
dsync: u1,
|
|
async: u1,
|
|
direct: u1,
|
|
_: u1,
|
|
directory: u1,
|
|
nofollow: u1,
|
|
noatime: u1,
|
|
cloexec: u1,
|
|
sync: u1,
|
|
path: u1,
|
|
tmpfile: u1,
|
|
_: u9,
|
|
}
|
|
|
|
[extern] memcmp = fn [cc(c)] (a: &u8, b: &u8, byte_count: usize) int;
|
|
[extern] memcpy = fn [cc(c)] (destination: &u8, source: &u8, byte_count: u64) &u8;
|
|
[extern] exit = fn [cc(c)] (exit_code: int) noreturn;
|
|
|
|
[extern] realpath = fn [cc(c)] (source_path: &u8, resolved_path: &u8) &u8;
|
|
[extern] mkdir = fn [cc(c)] (path: &u8, mode: mode_t) int;
|
|
|
|
[extern] open = fn [cc(c)] (path: &u8, o: O, ...) int;
|
|
[extern] close = fn [cc(c)] (fd: File) int;
|
|
[extern] fstat = fn [cc(c)] (fd: File, s: &Stat) int;
|
|
[extern] write = fn [cc(c)] (fd: File, pointer: &u8, byte_count: u64) ssize;
|
|
[extern] read = fn [cc(c)] (fd: File, pointer: &u8, byte_count: u64) ssize;
|
|
|
|
assert = macro (ok: u1) void
|
|
{
|
|
if (!ok)
|
|
{
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
align_forward = fn (value: u64, alignment: u64) u64
|
|
{
|
|
assert(alignment != 0);
|
|
>mask = alignment - 1;
|
|
>result = (value + mask) & ~mask;
|
|
return result;
|
|
}
|
|
|
|
string_no_match = #integer_max(u64);
|
|
|
|
c_string_length = fn (c_string: &u8) u64
|
|
{
|
|
>it = c_string;
|
|
|
|
while (it.&)
|
|
{
|
|
it = it + 1;
|
|
}
|
|
|
|
return #int_from_pointer(it) - #int_from_pointer(c_string);
|
|
}
|
|
|
|
c_string_to_slice = fn (c_string: &u8) []u8
|
|
{
|
|
>length = c_string_length(c_string);
|
|
return c_string[0..length];
|
|
}
|
|
|
|
string_equal = fn(a: []u8, b: []u8) u1
|
|
{
|
|
>result: #ReturnType = 0;
|
|
|
|
if (a.length == b.length)
|
|
{
|
|
result = memcmp(a.pointer, b.pointer, a.length) == 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
string_last_character = fn(string: []u8, character: u8) u64
|
|
{
|
|
>i = string.length;
|
|
|
|
while (i > 0)
|
|
{
|
|
i -= 1;
|
|
|
|
if (string[i] == character)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return string_no_match;
|
|
}
|
|
|
|
OS_Linux_PROT = bits u32
|
|
{
|
|
read: u1,
|
|
write: u1,
|
|
execute: u1,
|
|
sem: u1,
|
|
_: u28,
|
|
}
|
|
|
|
OS_Linux_MAP_Type = enum u4
|
|
{
|
|
shared = 0x1,
|
|
private = 0x2,
|
|
shared_validate = 0x3,
|
|
}
|
|
|
|
OS_Linux_MAP = bits u32
|
|
{
|
|
type: OS_Linux_MAP_Type,
|
|
fixed: u1,
|
|
anonymous: u1,
|
|
bit_32: u1,
|
|
_: u1,
|
|
grows_down: u1,
|
|
_: u2,
|
|
deny_write: u1,
|
|
executable: u1,
|
|
locked: u1,
|
|
no_reserve: u1,
|
|
populate: u1,
|
|
non_block: u1,
|
|
stack: u1,
|
|
huge_tlb: u1,
|
|
sync: u1,
|
|
fixed_noreplace: u1,
|
|
_: u5,
|
|
uninitialized: u1,
|
|
_: u5,
|
|
}
|
|
|
|
[extern] mmap = fn [cc(c)] (address: u64, size: u64, protection: OS_Linux_PROT, map: OS_Linux_MAP, file_descriptor: s32, offset: s64) &u8;
|
|
[extern] mprotect = fn [cc(c)] (address: u64, size: u64, protection: OS_Linux_PROT) s32;
|
|
|
|
OS_ProtectionFlags = bits
|
|
{
|
|
read: u1,
|
|
write: u1,
|
|
execute: u1,
|
|
}
|
|
|
|
OS_MapFlags = bits
|
|
{
|
|
private: u1,
|
|
anonymous: u1,
|
|
no_reserve: u1,
|
|
populate: u1,
|
|
}
|
|
|
|
os_linux_protection_flags = fn(map_flags: OS_ProtectionFlags) OS_Linux_PROT
|
|
{
|
|
return {
|
|
.read = map_flags.read,
|
|
.write = map_flags.write,
|
|
.execute = map_flags.execute,
|
|
zero,
|
|
};
|
|
}
|
|
|
|
os_linux_map_flags = fn(map_flags: OS_MapFlags) OS_Linux_MAP
|
|
{
|
|
return {
|
|
.type = #select(map_flags.private, .private, .shared),
|
|
.anonymous = map_flags.anonymous,
|
|
.no_reserve = map_flags.no_reserve,
|
|
.populate = map_flags.populate,
|
|
zero,
|
|
};
|
|
}
|
|
|
|
os_reserve = fn (base: u64, size: u64, protection: OS_ProtectionFlags, map: OS_MapFlags) &u8
|
|
{
|
|
>protection_flags = os_linux_protection_flags(protection);
|
|
>map_flags = os_linux_map_flags(map);
|
|
>address = mmap(base, size, protection_flags, map_flags, -1, 0);
|
|
if (#int_from_pointer(address) == #integer_max(u64))
|
|
{
|
|
unreachable;
|
|
}
|
|
|
|
return address;
|
|
}
|
|
|
|
os_commit = fn (address: u64, size: u64, protection: OS_ProtectionFlags) void
|
|
{
|
|
>protection_flags = os_linux_protection_flags(protection);
|
|
>result = mprotect(address, size, protection_flags);
|
|
if (result != 0)
|
|
{
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
os_make_directory = fn (path: &u8) void
|
|
{
|
|
>result = mkdir(path, 0o755);
|
|
}
|
|
|
|
OpenFlags = bits
|
|
{
|
|
truncate: u1,
|
|
execute: u1,
|
|
write: u1,
|
|
read: u1,
|
|
create: u1,
|
|
directory: u1,
|
|
}
|
|
|
|
OpenPermissions = bits
|
|
{
|
|
read: u1,
|
|
write: u1,
|
|
execute: u1,
|
|
}
|
|
|
|
os_file_open = fn (path: &u8, flags: OpenFlags, permissions: OpenPermissions) File
|
|
{
|
|
>access_mode: OAccessMode = undefined;
|
|
if (flags.read and flags.write)
|
|
{
|
|
access_mode = .read_write;
|
|
}
|
|
else if (flags.read)
|
|
{
|
|
access_mode = .read_only;
|
|
}
|
|
else if (flags.write)
|
|
{
|
|
access_mode = .write_only;
|
|
}
|
|
else
|
|
{
|
|
unreachable;
|
|
}
|
|
|
|
>o: O = {
|
|
.access_mode = access_mode,
|
|
.trunc = flags.truncate,
|
|
.creat = flags.create,
|
|
.directory = flags.directory,
|
|
};
|
|
|
|
>mode: mode_t = #select(permissions.execute, 0o755, 0o644);
|
|
>fd = open(path, o, mode);
|
|
return fd;
|
|
}
|
|
|
|
os_file_close = fn (fd: File) void
|
|
{
|
|
>result = close(fd);
|
|
assert(result == 0);
|
|
}
|
|
|
|
os_file_is_valid = fn (fd: File) u1
|
|
{
|
|
return fd >= 0;
|
|
}
|
|
|
|
os_file_get_size = fn (fd: File) u64
|
|
{
|
|
>stat: Stat = undefined;
|
|
>result = fstat(fd, &stat);
|
|
assert(result == 0);
|
|
return #extend(stat.size);
|
|
}
|
|
|
|
os_file_read_partially = fn (fd: File, pointer: &u8, length: u64) u64
|
|
{
|
|
>result = read(fd, pointer, length);
|
|
assert(result > 0);
|
|
return #extend(result);
|
|
}
|
|
|
|
os_file_read = fn (fd: File, buffer: []u8, byte_count: u64) void
|
|
{
|
|
assert(byte_count <= buffer.length);
|
|
|
|
>total_read_byte_count: u64 = 0;
|
|
|
|
while (total_read_byte_count < byte_count)
|
|
{
|
|
>read_byte_count = os_file_read_partially(fd, buffer.pointer + total_read_byte_count, byte_count - total_read_byte_count);
|
|
total_read_byte_count += read_byte_count;
|
|
}
|
|
}
|
|
|
|
os_path_absolute_stack = fn (buffer: []u8, relative_file_path: &u8) []u8
|
|
{
|
|
>syscall_result = realpath(relative_file_path, buffer.pointer);
|
|
|
|
>result: []u8 = zero;
|
|
|
|
if (syscall_result)
|
|
{
|
|
result = c_string_to_slice(syscall_result);
|
|
assert(result.length < buffer.length);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Arena = struct
|
|
{
|
|
reserved_size: u64,
|
|
position: u64,
|
|
os_position: u64,
|
|
granularity: u64,
|
|
reserved: [32]u8,
|
|
}
|
|
|
|
minimum_position: u64 = #byte_size(Arena);
|
|
|
|
ArenaInitialization = struct
|
|
{
|
|
reserved_size: u64,
|
|
granularity: u64,
|
|
initial_size: u64,
|
|
}
|
|
|
|
arena_initialize = fn (initialization: ArenaInitialization) &Arena
|
|
{
|
|
>protection_flags: OS_ProtectionFlags = {
|
|
.read = 1,
|
|
.write = 1,
|
|
zero,
|
|
};
|
|
|
|
>map_flags: OS_MapFlags = {
|
|
.private = 1,
|
|
.anonymous = 1,
|
|
.no_reserve = 1,
|
|
.populate = 0,
|
|
};
|
|
|
|
>arena: &Arena = #pointer_cast(os_reserve(0, initialization.reserved_size, protection_flags, map_flags));
|
|
os_commit(#int_from_pointer(arena), initialization.initial_size, {
|
|
.read = 1,
|
|
.write = 1,
|
|
zero,
|
|
});
|
|
|
|
arena.& = {
|
|
.reserved_size = initialization.reserved_size,
|
|
.position = minimum_position,
|
|
.os_position = initialization.initial_size,
|
|
.granularity = initialization.granularity,
|
|
zero,
|
|
};
|
|
|
|
return arena;
|
|
}
|
|
|
|
arena_initialize_default = fn (initial_size: u64) &Arena
|
|
{
|
|
return arena_initialize({
|
|
.reserved_size = 4 * 1024 * 1024 * 1024,
|
|
.granularity = 4 * 1024,
|
|
.initial_size = initial_size,
|
|
});
|
|
}
|
|
|
|
arena_allocate_bytes = fn (arena: &Arena, size: u64, alignment: u64) &u8
|
|
{
|
|
>aligned_offset = align_forward(arena.position, alignment);
|
|
>aligned_size_after = aligned_offset + size;
|
|
|
|
>arena_byte_pointer: &u8 = #pointer_cast(arena);
|
|
|
|
if (aligned_size_after > arena.os_position)
|
|
{
|
|
>target_committed_size = align_forward(aligned_size_after, arena.granularity);
|
|
>size_to_commit = target_committed_size - arena.os_position;
|
|
>commit_pointer = arena_byte_pointer + arena.os_position;
|
|
os_commit(#int_from_pointer(commit_pointer), size_to_commit, {
|
|
.read = 1,
|
|
.write = 1,
|
|
zero,
|
|
});
|
|
arena.os_position = target_committed_size;
|
|
}
|
|
|
|
>result = arena_byte_pointer + aligned_offset;
|
|
arena.position = aligned_size_after;
|
|
assert(arena.position <= arena.os_position);
|
|
|
|
return result;
|
|
}
|
|
|
|
arena_allocate = macro [T] (arena: &Arena, count: u64) &T
|
|
{
|
|
return #pointer_cast(arena_allocate_bytes(arena, #byte_size(T) * count, #alignof(T)));
|
|
}
|
|
|
|
arena_duplicate_string = fn (arena: &Arena, string: []u8) []u8
|
|
{
|
|
>result = arena_allocate_bytes(arena, string.length + 1, 1);
|
|
memcpy(result, string.pointer, string.length);
|
|
return result[..string.length];
|
|
}
|
|
|
|
arena_join_string = fn (arena: &Arena, pieces: [][]u8) []u8
|
|
{
|
|
>size: u64 = 0;
|
|
|
|
for (p: pieces)
|
|
{
|
|
size += p.length;
|
|
}
|
|
|
|
>pointer = arena_allocate_bytes(arena, size + 1, 1);
|
|
>i: u64 = 0;
|
|
|
|
for (p: pieces)
|
|
{
|
|
memcpy(pointer + i, p.pointer, p.length);
|
|
i += p.length;
|
|
}
|
|
|
|
assert(i == size);
|
|
pointer[i] = 0;
|
|
|
|
return pointer[..size];
|
|
}
|
|
|
|
file_read = fn (arena: &Arena, path: []u8) []u8
|
|
{
|
|
>fd = os_file_open(path.pointer, { .read = 1 }, { .read = 1 });
|
|
|
|
>result: []u8 = zero;
|
|
|
|
if (os_file_is_valid(fd))
|
|
{
|
|
>file_size = os_file_get_size(fd);
|
|
>file_buffer = arena_allocate_bytes(arena, file_size, 1);
|
|
result = file_buffer[..file_size];
|
|
os_file_read(fd, result, file_size);
|
|
os_file_close(fd);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
path_absolute = fn (arena: &Arena, relative_file_path: &u8) []u8
|
|
{
|
|
>buffer: [4096]u8 = undefined;
|
|
>stack_slice = os_path_absolute_stack(buffer[..], relative_file_path);
|
|
>result = arena_duplicate_string(arena, stack_slice);
|
|
return result;
|
|
}
|
|
|
|
GlobalState = struct
|
|
{
|
|
arena: &Arena,
|
|
}
|
|
|
|
global_state: GlobalState = undefined;
|
|
|
|
global_state_initialize = fn () void
|
|
{
|
|
global_state = {
|
|
.arena = arena_initialize_default(2 * 1024 * 1024),
|
|
};
|
|
}
|
|
|
|
fail = fn () noreturn
|
|
{
|
|
exit(1);
|
|
}
|
|
|
|
CompilerCommand = enum
|
|
{
|
|
compile,
|
|
test,
|
|
}
|
|
|
|
BuildMode = enum
|
|
{
|
|
debug_none,
|
|
debug_fast,
|
|
debug_size,
|
|
soft_optimize,
|
|
optimize_for_speed,
|
|
optimize_for_size,
|
|
aggressively_optimize_for_speed,
|
|
aggressively_optimize_for_size,
|
|
}
|
|
|
|
CompileFile = struct
|
|
{
|
|
relative_file_path: []u8,
|
|
build_mode: BuildMode,
|
|
has_debug_info: u1,
|
|
silent: u1,
|
|
}
|
|
|
|
base_cache_dir = "bb-cache";
|
|
|
|
CPUArchitecture = enum
|
|
{
|
|
x86_64,
|
|
}
|
|
|
|
OperatingSystem = enum
|
|
{
|
|
linux,
|
|
}
|
|
|
|
Target = struct
|
|
{
|
|
cpu: CPUArchitecture,
|
|
os: OperatingSystem,
|
|
}
|
|
|
|
target_get_native = fn () Target
|
|
{
|
|
return { .cpu = .x86_64, .os = .linux };
|
|
}
|
|
|
|
CompileOptions = struct
|
|
{
|
|
content: []u8,
|
|
path: []u8,
|
|
executable: []u8,
|
|
name: []u8,
|
|
objects: [][]u8,
|
|
target: Target,
|
|
build_mode: BuildMode,
|
|
has_debug_info: u1,
|
|
silent: u1,
|
|
}
|
|
|
|
TypeId = enum
|
|
{
|
|
void,
|
|
noreturn,
|
|
integer,
|
|
}
|
|
|
|
TypeInteger = struct
|
|
{
|
|
bit_count: u32,
|
|
signed: u1,
|
|
}
|
|
|
|
TypeContent = union
|
|
{
|
|
integer: TypeInteger,
|
|
}
|
|
|
|
Type = struct
|
|
{
|
|
content: TypeContent,
|
|
id: TypeId,
|
|
name: []u8,
|
|
}
|
|
|
|
ValueId = enum
|
|
{
|
|
infer_or_ignore,
|
|
}
|
|
|
|
Value = struct
|
|
{
|
|
type: &Type,
|
|
id: ValueId,
|
|
}
|
|
|
|
i128_offset: u64 = 64 * 2;
|
|
void_offset: u64 = i128_offset + 2;
|
|
|
|
Module = struct
|
|
{
|
|
arena: &Arena,
|
|
base_type_allocation: &Type,
|
|
void_value: &Value,
|
|
// Parser data
|
|
content: []u8,
|
|
offset: u64,
|
|
line_offset: u64,
|
|
line_character_offset: u64,
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
module_void_type = fn (module: &Module) &Type
|
|
{
|
|
return module.base_type_allocation + void_offset;
|
|
}
|
|
|
|
module_noreturn_type = fn (module: &Module) &Type
|
|
{
|
|
return module_void_type(module) + 1;
|
|
}
|
|
|
|
is_space = fn (ch: u8) u1
|
|
{
|
|
return ch == ' ' or ch == '\n' or ch == '\t' or ch == '\r';
|
|
}
|
|
|
|
is_decimal = fn (ch: u8) u1
|
|
{
|
|
return ch >= '0' and ch <= '9';
|
|
}
|
|
|
|
is_octal = fn (ch: u8) u1
|
|
{
|
|
return ch >= '0' and ch <= '7';
|
|
}
|
|
|
|
is_binary = fn (ch: u8) u1
|
|
{
|
|
return ch == '0' or ch == '1';
|
|
}
|
|
|
|
is_hex_alpha_lower = fn (ch: u8) u1
|
|
{
|
|
return ch >= 'a' and ch <= 'f';
|
|
}
|
|
|
|
is_hex_alpha_upper = fn (ch: u8) u1
|
|
{
|
|
return ch >= 'A' and ch <= 'F';
|
|
}
|
|
|
|
is_hex_alpha = fn (ch: u8) u1
|
|
{
|
|
return is_hex_alpha_lower(ch) or is_hex_alpha_upper(ch);
|
|
}
|
|
|
|
is_hex = fn (ch: u8) u1
|
|
{
|
|
return is_decimal(ch) or is_hex_alpha(ch);
|
|
}
|
|
|
|
is_identifier_start = fn (ch: u8) u1
|
|
{
|
|
return (ch >= 'a' and ch >= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_';
|
|
}
|
|
|
|
is_identifier = fn (ch: u8) u1
|
|
{
|
|
return is_identifier_start(ch) or is_decimal(ch);
|
|
}
|
|
|
|
report_error = fn () noreturn
|
|
{
|
|
#trap();
|
|
}
|
|
|
|
get_line = fn (module: &Module) u32
|
|
{
|
|
>line = module.line_offset + 1;
|
|
assert(line <= #integer_max(u32));
|
|
return #truncate(line);
|
|
}
|
|
|
|
get_column = fn (module: &Module) u32
|
|
{
|
|
>column = module.offset - module.line_character_offset + 1;
|
|
assert(column <= #integer_max(u32));
|
|
return #truncate(column);
|
|
}
|
|
|
|
skip_space = fn (module: &Module) void
|
|
{
|
|
while (1)
|
|
{
|
|
>iteration_offset = module.offset;
|
|
|
|
while (module.offset < module.content.length and? is_space(module.content[module.offset]))
|
|
{
|
|
module.line_offset += #extend(module.content[module.offset] == '\n');
|
|
module.line_character_offset = #select(module.content[module.offset] == '\n', module.offset, module.line_character_offset);
|
|
module.offset += 1;
|
|
}
|
|
|
|
if (module.offset + 1 < module.content.length)
|
|
{
|
|
>i = module.offset;
|
|
>is_comment = module.content[i] == '/' and module.content[i + 1] == '/';
|
|
|
|
if (is_comment)
|
|
{
|
|
while (module.offset < module.content.length and? module.content[module.offset] != '\n')
|
|
{
|
|
module.offset += 1;
|
|
}
|
|
|
|
if (module.offset < module.content.length)
|
|
{
|
|
module.line_offset += 1;
|
|
module.line_character_offset = module.offset;
|
|
module.offset += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (module.offset - iteration_offset == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
consume_character_if_match = fn (module: &Module, expected_character: u8) u1
|
|
{
|
|
>is_ch = 0;
|
|
|
|
if (module.offset < module.content.length)
|
|
{
|
|
>ch = module.content[module.offset];
|
|
is_ch = expected_character == ch;
|
|
module.offset += is_ch;
|
|
}
|
|
|
|
return is_ch;
|
|
}
|
|
|
|
expect_character = fn (module: &Module, expected_character: u8) void
|
|
{
|
|
if (!consume_character_if_match(module, expected_character))
|
|
{
|
|
report_error();
|
|
}
|
|
}
|
|
|
|
parse_identifier = fn (module: &Module) []u8
|
|
{
|
|
>start = module.offset;
|
|
|
|
if (is_identifier_start(module.content[start]))
|
|
{
|
|
module.offset += 1;
|
|
|
|
while (module.offset < module.content.length)
|
|
{
|
|
if (is_identifier(module.content[module.offset]))
|
|
{
|
|
module.offset += 1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (module.offset - start == 0)
|
|
{
|
|
report_error();
|
|
}
|
|
|
|
>result = module.content[start..module.offset];
|
|
return result;
|
|
}
|
|
|
|
parse = fn (module: &Module) void
|
|
{
|
|
}
|
|
|
|
emit = fn (module: &Module) void
|
|
{
|
|
}
|
|
|
|
compile = fn (arena: &Arena, options: CompileOptions) void
|
|
{
|
|
>signs: [2]u1 = [0, 1];
|
|
|
|
>base_type_allocation = arena_allocate[Type](arena,
|
|
i128_offset // Basic integer types
|
|
+ 2 // u128, s128
|
|
+ 2 // void, noreturn
|
|
);
|
|
|
|
>type_it = base_type_allocation;
|
|
|
|
for (sign: signs)
|
|
{
|
|
for (b: 0..64)
|
|
{
|
|
>bit_count = b + 1;
|
|
>first_digit: u8 = #truncate(#select(bit_count < 10, bit_count % 10 + '0', bit_count / 10 + '0'));
|
|
>second_digit: u8 = #truncate(#select(bit_count > 9, bit_count % 10 + '0', 0));
|
|
>name_buffer: [3]u8 = [ #select(sign, 's', 'u'), first_digit, second_digit ];
|
|
>name_length: u64 = 2 + #extend(bit_count > 9);
|
|
|
|
>name = arena_duplicate_string(arena, name_buffer[..name_length]);
|
|
|
|
type_it.& = {
|
|
.content = {
|
|
.integer = {
|
|
.bit_count = #truncate(bit_count),
|
|
.signed = sign,
|
|
},
|
|
},
|
|
.id = .integer,
|
|
.name = name,
|
|
};
|
|
type_it += 1;
|
|
}
|
|
}
|
|
|
|
for (sign: signs)
|
|
{
|
|
>name = #select(sign, "s128", "u128");
|
|
type_it.& = {
|
|
.content = {
|
|
.integer = {
|
|
.bit_count = 128,
|
|
.signed = sign,
|
|
},
|
|
},
|
|
.id = .integer,
|
|
.name = name,
|
|
};
|
|
type_it += 1;
|
|
}
|
|
|
|
>void_type = type_it;
|
|
type_it += 1;
|
|
>noreturn_type = type_it;
|
|
type_it += 1;
|
|
|
|
void_type.& = {
|
|
.id = .void,
|
|
.name = "void",
|
|
};
|
|
|
|
noreturn_type.& = {
|
|
.id = .noreturn,
|
|
.name = "noreturn",
|
|
};
|
|
|
|
>void_value = arena_allocate[Value](arena, 1);
|
|
void_value.& = {
|
|
.type = void_type,
|
|
.id = .infer_or_ignore,
|
|
};
|
|
|
|
>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,
|
|
};
|
|
|
|
parse(&module);
|
|
emit(&module);
|
|
}
|
|
|
|
compile_file = fn (arena: &Arena, compile_options: CompileFile) void
|
|
{
|
|
>relative_file_path = compile_options.relative_file_path;
|
|
if (relative_file_path.length < 5)
|
|
{
|
|
fail();
|
|
}
|
|
|
|
>extension_start = string_last_character(relative_file_path, '.');
|
|
if (extension_start == string_no_match)
|
|
{
|
|
fail();
|
|
}
|
|
|
|
if (!string_equal(relative_file_path[extension_start..], ".bbb"))
|
|
{
|
|
fail();
|
|
}
|
|
|
|
>separator_index = string_last_character(relative_file_path, '/');
|
|
if (separator_index == string_no_match)
|
|
{
|
|
separator_index = 0;
|
|
}
|
|
|
|
>base_start = separator_index + #extend(separator_index != 0 or relative_file_path[separator_index] == '/');
|
|
>base_name = relative_file_path[base_start..extension_start];
|
|
|
|
>is_compiler = string_equal(relative_file_path, "src/compiler.bbb");
|
|
>outputh_path_dir = arena_join_string(arena, [
|
|
base_cache_dir,
|
|
#select(is_compiler, "/compiler/", "/"),
|
|
#enum_name(compile_options.build_mode),
|
|
"_",
|
|
#select(compile_options.has_debug_info, "di", "nodi"),
|
|
][..]);
|
|
|
|
os_make_directory(base_cache_dir.pointer);
|
|
if (is_compiler)
|
|
{
|
|
>compiler_dir = arena_join_string(arena, [ base_cache_dir, "/compiler" ][..]);
|
|
os_make_directory(compiler_dir.pointer);
|
|
}
|
|
os_make_directory(outputh_path_dir.pointer);
|
|
|
|
>outputh_path_base = arena_join_string(arena, [ outputh_path_dir, "/", base_name ][..]);
|
|
|
|
>output_object_path = arena_join_string(arena, [ outputh_path_base, ".o" ][..]);
|
|
>output_executable_path = outputh_path_base;
|
|
|
|
>file_content = file_read(arena, relative_file_path);
|
|
>file_path = path_absolute(arena, relative_file_path.pointer);
|
|
>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 ][..];
|
|
}
|
|
|
|
>options: CompileOptions = {
|
|
.executable = output_executable_path,
|
|
.objects = objects,
|
|
.name = base_name,
|
|
.build_mode = compile_options.build_mode,
|
|
.content = file_content,
|
|
.path = file_path,
|
|
.has_debug_info = compile_options.has_debug_info,
|
|
.target = target_get_native(),
|
|
.silent = compile_options.silent,
|
|
};
|
|
|
|
compile(arena, options);
|
|
}
|
|
|
|
[export] main = fn [cc(c)] (argument_count: u32, argv: &&u8) s32
|
|
{
|
|
global_state_initialize();
|
|
>arena = global_state.arena;
|
|
|
|
if (argument_count < 2)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
>command_string = c_string_to_slice(argv[1]);
|
|
|
|
>command_string_to_enum = #string_to_enum(CompilerCommand, command_string);
|
|
if (!command_string_to_enum.is_valid)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
>command = command_string_to_enum.enum_value;
|
|
switch (command)
|
|
{
|
|
.compile =>
|
|
{
|
|
if (argument_count < 3)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
>build_mode: BuildMode = .debug_none;
|
|
>has_debug_info: u1 = 1;
|
|
|
|
if (argument_count >= 4)
|
|
{
|
|
>build_mode_string_to_enum = #string_to_enum(BuildMode, c_string_to_slice(argv[3]));
|
|
if (!build_mode_string_to_enum.is_valid)
|
|
{
|
|
return 1;
|
|
}
|
|
build_mode = build_mode_string_to_enum.enum_value;
|
|
}
|
|
|
|
if (argument_count >= 5)
|
|
{
|
|
>has_debug_info_string = c_string_to_slice(argv[4]);
|
|
if (string_equal(has_debug_info_string, "true"))
|
|
{
|
|
has_debug_info = 1;
|
|
}
|
|
else if (string_equal(has_debug_info_string, "false"))
|
|
{
|
|
has_debug_info = 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
>relative_file_path_pointer = argv[2];
|
|
if (!relative_file_path_pointer)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
>relative_file_path = c_string_to_slice(relative_file_path_pointer);
|
|
|
|
compile_file(arena, {
|
|
.relative_file_path = relative_file_path,
|
|
.build_mode = build_mode,
|
|
.has_debug_info = has_debug_info,
|
|
.silent = 0,
|
|
});
|
|
},
|
|
.test =>
|
|
{
|
|
// TODO
|
|
#trap();
|
|
},
|
|
}
|
|
|
|
return 0;
|
|
}
|