#include fn void compile(Arena* arena, Options options) { auto base_allocation_type_count = i128_offset + // 64 * 2 for basic integer types 2 + // u128, s128 2; // void, noreturn auto base_type_allocation = arena_allocate(arena, base_allocation_type_count); auto* type_it = base_type_allocation.pointer; bool signs[] = {false, true}; Type* previous = 0; for (bool sign: signs) { for (u32 bit_index = 0; bit_index < 64; bit_index += 1) { auto bit_count = bit_index + 1; auto first_digit = (u8)(bit_count < 10 ? bit_count % 10 + '0' : bit_count / 10 + '0'); auto second_digit = (u8)(bit_count > 9 ? bit_count % 10 + '0' : 0); u8 name_buffer[] = { u8(sign ? 's' : 'u'), first_digit, second_digit }; u64 name_length = 2 + (bit_count > 9); auto name_stack = String{name_buffer, name_length}; auto name = arena_duplicate_string(arena, name_stack); *type_it = { .integer = { .bit_count = bit_count, .is_signed = sign, }, .id = TypeId::integer, .name = name, }; if (previous) previous->next = type_it; previous = type_it; type_it += 1; } } for (bool sign: signs) { auto name = sign ? str("s128") : str("u128"); *type_it = { .integer = { .bit_count = 128, .is_signed = sign, }, .id = TypeId::integer, .name = name, .next = previous, }; if (previous) previous->next = type_it; previous = type_it; type_it += 1; } auto void_type = type_it; type_it += 1; auto noreturn_type = type_it; type_it += 1; assert(type_it - base_type_allocation.pointer == base_allocation_type_count); previous->next = void_type; *void_type = { .id = TypeId::void_type, .name = str("void"), .next = noreturn_type, }; *noreturn_type = { .id = TypeId::noreturn, .name = str("noreturn"), }; auto module = Module{ .arena = arena, .content = options.content, .first_type = base_type_allocation.pointer, .last_type = noreturn_type, .scope = { .kind = ScopeKind::global, }, .name = options.name, .path = options.path, .executable = options.executable, .objects = options.objects, .target = options.target, .build_mode = options.build_mode, .has_debug_info = options.has_debug_info, .silent = options.silent, }; module.void_value = new_value(&module); *module.void_value = { .type = void_type, .id = ValueId::infer_or_ignore, }; parse(&module); emit(&module); } fn void compile_file(Arena* arena, Compile options) { auto relative_file_path = options.relative_file_path; if (relative_file_path.length < 5) { fail(); } auto extension_start = string_last_character(relative_file_path, '.'); if (extension_start == string_no_match) { fail(); } if (!relative_file_path(extension_start).equal(str(".bbb"))) { fail(); } auto separator_index = string_last_character(relative_file_path, '/'); separator_index = separator_index == string_no_match ? 0 : separator_index; auto base_start = separator_index + (separator_index != 0 || relative_file_path[separator_index] == '/'); auto base_name = relative_file_path(base_start, extension_start); auto is_compiler = relative_file_path.equal(str("src/compiler.bbb")); String output_path_dir_parts[] = { str(base_cache_dir), is_compiler ? str("/compiler") : str("/"), build_mode_to_string(options.build_mode), str("_"), options.has_debug_info ? str("di") : str("nodi"), }; auto output_path_dir = arena_join_string(arena, array_to_slice(output_path_dir_parts)); make_directory(base_cache_dir); if (is_compiler) { make_directory(base_cache_dir "/compiler"); } make_directory(cstr(output_path_dir)); String output_path_base_parts[] = { output_path_dir, str("/"), base_name, }; auto output_path_base = arena_join_string(arena, array_to_slice(output_path_base_parts)); String output_object_path_parts[] = { output_path_base, str(".o"), }; auto output_object_path = arena_join_string(arena, array_to_slice(output_object_path_parts)); auto output_executable_path = output_path_base; auto file_content = file_read(arena, relative_file_path); auto file_path = path_absolute(arena, relative_file_path); auto c_abi_object_path = str("build/c_abi.o"); String objects[] = { c_abi_object_path, output_object_path, }; Slice object_slice = array_to_slice(objects); object_slice = object_slice(!base_name.equal(str("c_abi"))); compile(arena, { .content = file_content, .path = file_path, .executable = output_executable_path, .name = base_name, .objects = object_slice, .target = { .cpu = CPUArchitecture::x86_64, .os = OperatingSystem::linux_, }, .build_mode = options.build_mode, .has_debug_info = options.has_debug_info, .silent = options.silent, }); } global_variable String names[] = { str("minimal"), str("comments"), str("constant_add"), str("constant_and"), str("constant_div"), str("constant_mul"), str("constant_rem"), str("constant_or"), str("constant_sub"), str("constant_xor"), str("constant_shift_left"), str("constant_shift_right"), str("minimal_stack"), str("minimal_stack_arithmetic"), str("minimal_stack_arithmetic2"), str("minimal_stack_arithmetic3"), str("stack_negation"), str("stack_add"), str("stack_sub"), str("extend"), str("integer_max"), str("integer_hex"), str("basic_pointer"), str("basic_call"), str("basic_branch"), str("basic_array"), str("basic_enum"), str("basic_slice"), str("basic_string"), str("basic_varargs"), str("basic_while"), str("pointer"), str("pointer_cast"), str("u1_return"), str("local_type_inference"), str("global"), str("function_pointer"), str("extern"), str("byte_size"), str("argv"), str("assignment_operators"), str("not_pointer"), str("bits"), str("bits_no_backing_type"), str("bits_return_u1"), str("bits_zero"), str("comparison"), str("global_struct"), str("if_no_else"), str("if_no_else_void"), str("indirect"), str("indirect_struct"), str("indirect_varargs"), str("ret_c_bool"), str("return_type_builtin"), str("return_u64_u64"), str("select"), str("slice"), str("small_struct_ints"), str("struct_assignment"), str("struct"), str("struct_u64_u64"), str("struct_varargs"), str("struct_zero"), str("unreachable"), str("varargs"), str("c_abi0"), str("c_abi1"), str("c_med_struct_ints"), str("c_ret_struct_array"), str("c_split_struct_ints"), str("c_string_to_slice"), str("c_struct_with_array"), str("c_function_pointer"), str("c_abi"), str("string_to_enum"), str("abi_enum_bool"), str("empty_if"), str("else_if"), str("else_if_complicated"), str("shortcircuiting_if"), str("field_access_left_assign"), str("for_each"), str("pointer_decay"), str("enum_name"), str("slice_of_slices"), str("type_alias"), str("integer_formats"), str("return_small_struct"), str("basic_macro"), str("generic_macro"), str("for_each_int"), str("bool_array"), str("basic_union"), str("constant_global_reference"), str("generic_pointer_macro"), str("break_continue"), str("noreturn_macro"), str("self_referential_struct"), str("forward_declared_type"), str("generic_pointer_array"), }; void entry_point(Slice arguments, Slice environment) { Arena* arena = arena_initialize_default(8 * mb); if (arguments.length < 2) { fail_with_message(str("error: Not enough arguments\n")); } String command_string = c_string_to_slice(arguments[1]); String command_strings[] = { str("compile"), str("test"), }; static_assert(array_length(command_strings) == (u64)Command::count); backing_type(Command) i; for (i = 0; i < (backing_type(Command))Command::count; i += 1) { String candidate = command_strings[i]; if (candidate.equal(command_string)) { break; } } auto command = (Command)i; switch (command) { case Command::compile: { if (arguments.length < 3) { fail_with_message(str("Not enough arguments for command 'compile'\n")); } auto build_mode = BuildMode::debug_none; auto has_debug_info = true; if (arguments.length >= 4) { auto build_mode_string = c_string_to_slice(arguments[3]); String build_mode_strings[] = { str("debug_none"), str("debug"), str("soft_optimize"), str("optimize_for_speed"), str("optimize_for_size"), str("aggressively_optimize_for_speed"), str("aggressively_optimize_for_size"), }; backing_type(BuildMode) i; for (i = 0; i < (backing_type(BuildMode))BuildMode::count; i += 1) { String candidate = build_mode_strings[i]; if (build_mode_string.equal(candidate)) { break; } } build_mode = (BuildMode)i; if (build_mode == BuildMode::count) { fail_with_message(str("Invalid build mode\n")); } } if (arguments.length >= 5) { auto has_debug_info_string = c_string_to_slice(arguments[3]); if (has_debug_info_string.equal(str("true"))) { has_debug_info = true; } else if (has_debug_info_string.equal(str("false"))) { has_debug_info = false; } else { fail_with_message(str("Wrong value for has_debug_info\n")); } } auto relative_file_path = c_string_to_slice(arguments[2]); compile_file(arena, { .relative_file_path = relative_file_path, .build_mode = build_mode, .has_debug_info = has_debug_info, .silent = false, }); } break; case Command::test: { // TODO: provide more arguments if (arguments.length != 2) { fail_with_message(str("error: 'test' command takes no arguments")); } // TODO: introduce build mode, debug info switch, etc for (auto name: names) { auto position = arena->position; String relative_file_path_parts[] = { str("tests/"), name, str(".bbb") }; auto relative_file_path = arena_join_string(arena, array_to_slice(relative_file_path_parts)); auto build_mode = BuildMode::debug_none; bool has_debug_info = true; compile_file(arena, { .relative_file_path = relative_file_path, .build_mode = build_mode, .has_debug_info = has_debug_info, .silent = false, }); // TODO: introduce test arena_restore(arena, position); } } break; case Command::count: { fail_with_message(str("error: Invalid command\n")); } break; } }