diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index a01f286..4d038be 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -47,7 +47,8 @@ fn reportUnterminatedArgumentError(string: []const u8) noreturn { std.debug.panic("Unterminated argument: {s}", .{string}); } -fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { +fn parseArguments(compilation: *const Compilation) !Compilation.Module.Descriptor { + const allocator = compilation.base_allocator; const arguments = (try std.process.argsAlloc(allocator))[1..]; var maybe_executable_path: ?[]const u8 = null; @@ -55,92 +56,118 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { var target_triplet: []const u8 = "x86_64-linux-gnu"; var should_transpile_to_c: ?bool = null; var maybe_only_parse: ?bool = null; + var link_libc = false; - var i: usize = 0; - while (i < arguments.len) : (i += 1) { - const current_argument = arguments[i]; - if (equal(u8, current_argument, "-o")) { - if (i + 1 != arguments.len) { - maybe_executable_path = arguments[i + 1]; - assert(maybe_executable_path.?.len != 0); - i += 1; - } else { - reportUnterminatedArgumentError(current_argument); - } - } else if (equal(u8, current_argument, "-target")) { - if (i + 1 != arguments.len) { - target_triplet = arguments[i + 1]; - i += 1; - } else { - reportUnterminatedArgumentError(current_argument); - } - } else if (equal(u8, current_argument, "-log")) { - if (i + 1 != arguments.len) { - i += 1; - - var log_argument_iterator = std.mem.splitScalar(u8, arguments[i], ','); - - while (log_argument_iterator.next()) |log_argument| { - var log_argument_splitter = std.mem.splitScalar(u8, log_argument, '.'); - const log_scope_candidate = log_argument_splitter.next() orelse unreachable; - var recognized_scope = false; - - inline for (@typeInfo(LoggerScope).Enum.fields) |logger_scope_enum_field| { - const log_scope = @field(LoggerScope, logger_scope_enum_field.name); - - if (equal(u8, @tagName(log_scope), log_scope_candidate)) { - const LogScope = getLoggerScopeType(log_scope); - - if (log_argument_splitter.next()) |particular_log_candidate| { - var recognized_particular = false; - inline for (@typeInfo(LogScope.Logger).Enum.fields) |particular_log_field| { - const particular_log = @field(LogScope.Logger, particular_log_field.name); - - if (equal(u8, particular_log_candidate, @tagName(particular_log))) { - LogScope.Logger.bitset.setPresent(particular_log, true); - recognized_particular = true; - } - } else if (!recognized_particular) std.debug.panic("Unrecognized particular log \"{s}\" in scope {s}", .{ particular_log_candidate, @tagName(log_scope) }); - } else { - // LogScope.Logger.bitset = @TypeOf(LogScope.Logger.bitset).initFull(); - } - - logger_bitset.setPresent(log_scope, true); - - recognized_scope = true; - } - } else if (!recognized_scope) std.debug.panic("Unrecognized log scope: {s}", .{log_scope_candidate}); - } - } else { - reportUnterminatedArgumentError(current_argument); - } - } else if (equal(u8, current_argument, "-transpile_to_c")) { - if (i + 1 != arguments.len) { - i += 1; - - const arg = arguments[i]; - if (std.mem.eql(u8, arg, "true")) { - should_transpile_to_c = true; - } else if (std.mem.eql(u8, arg, "false")) { - should_transpile_to_c = false; - } else { - unreachable; - } - } else { - reportUnterminatedArgumentError(current_argument); - } - } else if (equal(u8, current_argument, "-parse")) { - if (i + 1 != arguments.len) { - i += 1; - - const arg = arguments[i]; - maybe_main_package_path = arg; - maybe_only_parse = true; - } else { - reportUnterminatedArgumentError(current_argument); - } + if (arguments.len == 0) { + // foo + } else if (equal(u8, arguments[0], "init")) { + if (arguments.len == 1) { + unreachable; } else { - maybe_main_package_path = current_argument; + @panic("Init does not take arguments"); + } + } else { + var i: usize = 0; + while (i < arguments.len) : (i += 1) { + const current_argument = arguments[i]; + if (equal(u8, current_argument, "-o")) { + if (i + 1 != arguments.len) { + maybe_executable_path = arguments[i + 1]; + assert(maybe_executable_path.?.len != 0); + i += 1; + } else { + reportUnterminatedArgumentError(current_argument); + } + } else if (equal(u8, current_argument, "-target")) { + if (i + 1 != arguments.len) { + target_triplet = arguments[i + 1]; + i += 1; + } else { + reportUnterminatedArgumentError(current_argument); + } + } else if (equal(u8, current_argument, "-log")) { + if (i + 1 != arguments.len) { + i += 1; + + var log_argument_iterator = std.mem.splitScalar(u8, arguments[i], ','); + + while (log_argument_iterator.next()) |log_argument| { + var log_argument_splitter = std.mem.splitScalar(u8, log_argument, '.'); + const log_scope_candidate = log_argument_splitter.next() orelse unreachable; + var recognized_scope = false; + + inline for (@typeInfo(LoggerScope).Enum.fields) |logger_scope_enum_field| { + const log_scope = @field(LoggerScope, logger_scope_enum_field.name); + + if (equal(u8, @tagName(log_scope), log_scope_candidate)) { + const LogScope = getLoggerScopeType(log_scope); + + if (log_argument_splitter.next()) |particular_log_candidate| { + var recognized_particular = false; + inline for (@typeInfo(LogScope.Logger).Enum.fields) |particular_log_field| { + const particular_log = @field(LogScope.Logger, particular_log_field.name); + + if (equal(u8, particular_log_candidate, @tagName(particular_log))) { + LogScope.Logger.bitset.setPresent(particular_log, true); + recognized_particular = true; + } + } else if (!recognized_particular) std.debug.panic("Unrecognized particular log \"{s}\" in scope {s}", .{ particular_log_candidate, @tagName(log_scope) }); + } else { + // LogScope.Logger.bitset = @TypeOf(LogScope.Logger.bitset).initFull(); + } + + logger_bitset.setPresent(log_scope, true); + + recognized_scope = true; + } + } else if (!recognized_scope) std.debug.panic("Unrecognized log scope: {s}", .{log_scope_candidate}); + } + } else { + reportUnterminatedArgumentError(current_argument); + } + } else if (equal(u8, current_argument, "-transpile_to_c")) { + if (i + 1 != arguments.len) { + i += 1; + + const arg = arguments[i]; + if (std.mem.eql(u8, arg, "true")) { + should_transpile_to_c = true; + } else if (std.mem.eql(u8, arg, "false")) { + should_transpile_to_c = false; + } else { + unreachable; + } + } else { + reportUnterminatedArgumentError(current_argument); + } + } else if (equal(u8, current_argument, "-parse")) { + if (i + 1 != arguments.len) { + i += 1; + + const arg = arguments[i]; + maybe_main_package_path = arg; + maybe_only_parse = true; + } else { + reportUnterminatedArgumentError(current_argument); + } + } else if (equal(u8, current_argument, "-link_libc")) { + if (i + 1 != arguments.len) { + i += 1; + + const arg = arguments[i]; + if (std.mem.eql(u8, arg, "true")) { + link_libc = true; + } else if (std.mem.eql(u8, arg, "false")) { + link_libc = false; + } else { + unreachable; + } + } else { + reportUnterminatedArgumentError(current_argument); + } + } else { + maybe_main_package_path = current_argument; + } } } @@ -173,6 +200,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { .transpile_to_c = transpile_to_c, .is_build = is_build, .only_parse = only_parse, + .link_libc = link_libc, }; } @@ -192,7 +220,7 @@ pub fn init(allocator: Allocator) !void { try compilation.build_directory.makePath(cache_dir_name); try compilation.build_directory.makePath(installation_dir_name); - const compilation_descriptor = try parseArguments(allocator); + const compilation_descriptor = try parseArguments(compilation); try compilation.compileModule(compilation_descriptor); } @@ -934,6 +962,7 @@ pub const Value = union(enum) { const indexed_expression = module.values.array.get(module.values.indexed_accesses.get(indexed_access_index).indexed_expression); const indexed_expression_type_index = indexed_expression.getType(module); const indexed_expression_type = module.types.array.get(indexed_expression_type_index); + break :blk switch (indexed_expression_type.*) { .slice => |slice| slice.element_type, .array => |array| array.element_type, @@ -950,11 +979,22 @@ pub const Value = union(enum) { const optional_value = module.values.array.get(optional_unwrap.value); const expected_optional_type_index = optional_value.getType(module); const expected_optional_type = module.types.array.get(expected_optional_type_index); + break :blk switch (expected_optional_type.*) { .optional => |optional| optional.element_type, else => |t| @panic(@tagName(t)), }; }, + .branch => |branch_index| { + // TODO + const branch = module.values.branches.get(branch_index); + const taken = module.values.array.get(branch.taken_expression); + const not_taken = module.values.array.get(branch.not_taken_expression); + const taken_type = taken.getType(module); + const not_taken_type = not_taken.getType(module); + _ = not_taken_type; + return taken_type; + }, else => |t| @panic(@tagName(t)), }; @@ -1029,6 +1069,7 @@ pub const Module = struct { transpile_to_c: bool, is_build: bool, only_parse: bool, + link_libc: bool, }; const ImportFileResult = struct { @@ -1170,23 +1211,6 @@ fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]const u8 { } pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !void { - // TODO: generate an actual file - const builtin_file_name = "builtin.nat"; - var cache_dir = try compilation.build_directory.openDir("cache", .{}); - const builtin_file = try cache_dir.createFile(builtin_file_name, .{}); - try builtin_file.writer().print( - \\const builtin = #import("std").builtin; - \\const cpu = builtin.Cpu.{s}; - \\const os = builtin.Os.{s}; - \\const abi = builtin.Abi.{s}; - \\ - , .{ - @tagName(descriptor.target.cpu.arch), - @tagName(descriptor.target.os.tag), - @tagName(descriptor.target.abi), - }); - builtin_file.close(); - const module: *Module = try compilation.base_allocator.create(Module); module.* = Module{ .main_package = blk: { @@ -1207,6 +1231,24 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! .descriptor = descriptor, }; + const builtin_file_name = "builtin.nat"; + var cache_dir = try compilation.build_directory.openDir("cache", .{}); + const builtin_file = try cache_dir.createFile(builtin_file_name, .{}); + try builtin_file.writer().print( + \\const builtin = #import("std").builtin; + \\const cpu = builtin.Cpu.{s}; + \\const os = builtin.Os.{s}; + \\const abi = builtin.Abi.{s}; + \\const link_libc = {}; + \\ + , .{ + @tagName(module.descriptor.target.cpu.arch), + @tagName(module.descriptor.target.os.tag), + @tagName(module.descriptor.target.abi), + module.descriptor.link_libc, + }); + builtin_file.close(); + const std_package_dir = "lib/std"; const package_descriptors = [2]struct { @@ -1245,7 +1287,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! assert(module.main_package.dependencies.size == 2); - if (!descriptor.only_parse) { + if (!module.descriptor.only_parse) { _ = try module.importPackage(compilation.base_allocator, module.main_package.dependencies.get("std").?); } else { _ = try module.importPackage(compilation.base_allocator, module.main_package); @@ -1255,7 +1297,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import); } - if (!descriptor.only_parse) { + if (!module.descriptor.only_parse) { inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { _ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) { .usize => @unionInit(Type, "integer", .{ @@ -1339,10 +1381,10 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! try semantic_analyzer.initialize(compilation, module, packages[0], value_index); - if (descriptor.transpile_to_c) { - try c_transpiler.initialize(compilation, module, descriptor); - if (descriptor.is_build) { - const argv = [_][]const u8{ descriptor.executable_path, "--compiler", compilation.executable_absolute_path }; + if (module.descriptor.transpile_to_c) { + try c_transpiler.initialize(compilation, module); + if (module.descriptor.is_build) { + const argv = [_][]const u8{ module.descriptor.executable_path, "--compiler", compilation.executable_absolute_path }; const process_result = try std.ChildProcess.run(.{ .allocator = compilation.base_allocator, .argv = &argv, diff --git a/bootstrap/backend/c_transpiler.zig b/bootstrap/backend/c_transpiler.zig index 3763614..1474100 100644 --- a/bootstrap/backend/c_transpiler.zig +++ b/bootstrap/backend/c_transpiler.zig @@ -405,7 +405,7 @@ pub const TranslationUnit = struct { const declaration = module.values.declarations.get(declaration_index); const base_declaration_name = blk: { const name = module.getName(declaration.name).?; - if (declaration.scope_type == .global and equal(u8, name, "main")) { + if (mangle and declaration.scope_type == .global and equal(u8, name, "main")) { break :blk "user_entry_point"; } else { break :blk name; @@ -476,34 +476,45 @@ pub const TranslationUnit = struct { } fn writeFunctionPrototype(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function_prototype_index: Compilation.Function.Prototype.Index, name: []const u8) !void { - const function_prototype = module.types.function_prototypes.get(function_prototype_index); - switch (function_prototype.attributes.calling_convention) { - .system_v => {}, - .naked => try list.appendSlice(allocator, "[[gnu::naked]] "), - } - - try unit.writeType(module, list, allocator, function_prototype.return_type, ' '); - - try list.append(allocator, ' '); - - try list.appendSlice(allocator, name); - - try list.append(allocator, '('); - - if (function_prototype.arguments.items.len > 0) { - for (function_prototype.arguments.items) |argument_index| { - const arg_declaration = module.values.declarations.get(argument_index); - try unit.writeType(module, list, allocator, arg_declaration.getType(), ' '); - try list.append(allocator, ' '); - const arg_name = module.getName(arg_declaration.name).?; - try list.appendSlice(allocator, arg_name); - try list.appendSlice(allocator, ", "); + const is_main = equal(u8, "main", name); + if (is_main) { + try list.appendSlice(allocator, "int main(int argc, char** argv, char** envp)"); + } else { + const function_prototype = module.types.function_prototypes.get(function_prototype_index); + switch (function_prototype.attributes.calling_convention) { + .system_v => {}, + .naked => try list.appendSlice(allocator, "[[gnu::naked]] "), } - _ = list.pop(); - _ = list.pop(); - } - try list.append(allocator, ')'); + try unit.writeType(module, list, allocator, function_prototype.return_type, ' '); + + try list.append(allocator, ' '); + + try list.appendSlice(allocator, name); + + try list.append(allocator, '('); + + + if (function_prototype.arguments.items.len > 0) { + for (function_prototype.arguments.items, 0..) |argument_index, index| { + _ = index; + + const arg_declaration = module.values.declarations.get(argument_index); + if (is_main) { + } else { + try unit.writeType(module, list, allocator, arg_declaration.getType(), ' '); + } + try list.append(allocator, ' '); + const arg_name = module.getName(arg_declaration.name).?; + try list.appendSlice(allocator, arg_name); + try list.appendSlice(allocator, ", "); + } + _ = list.pop(); + _ = list.pop(); + } + + try list.append(allocator, ')'); + } } fn writeFunctionHeader(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function_index: Compilation.Function.Index) ![]const u8 { @@ -1311,35 +1322,63 @@ pub const TranslationUnit = struct { fn writeBranch(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, branch_index: Compilation.Branch.Index, function_return_type: Type.Index, indentation: usize) !bool { const branch = module.values.branches.get(branch_index); - try list.appendSlice(allocator, "if ("); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = branch.expression, - .type_index = Type.Index.invalid, - }); - try list.appendSlice(allocator, ") "); - try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = branch.taken_expression, - .type_index = function_return_type, - }); + const classic = switch (module.values.array.get(branch.taken_expression).*) { + .block => true, + else => false, + }; - if (!branch.not_taken_expression.invalid) { - if (module.values.array.get(branch.taken_expression).* == .block) { - _ = list.pop(); - try list.appendSlice(allocator, " else "); - } else { - unreachable; - } + if (classic) { + try list.appendSlice(allocator, "if ("); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ - .value_index = branch.not_taken_expression, + .value_index = branch.expression, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ") "); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = branch.taken_expression, .type_index = function_return_type, }); - if (module.values.array.get(branch.not_taken_expression).* == .block) { - return false; - } - } + if (!branch.not_taken_expression.invalid) { + if (module.values.array.get(branch.taken_expression).* == .block) { + _ = list.pop(); + try list.appendSlice(allocator, " else "); + } else { + unreachable; + } + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = branch.not_taken_expression, + .type_index = function_return_type, + }); - return true; + if (module.values.array.get(branch.not_taken_expression).* == .block) { + return false; + } + } + + return true; + } else { + assert(!branch.not_taken_expression.invalid); + + try list.appendSlice(allocator, "("); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = branch.expression, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ") ? ("); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = branch.taken_expression, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ") : ("); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = branch.not_taken_expression, + .type_index = Type.Index.invalid, + }); + try list.appendSlice(allocator, ")"); + + return true; + } } const ValueArguments = struct { @@ -1891,10 +1930,10 @@ pub const TranslationUnit = struct { } }; -pub fn initialize(compilation: *Compilation, module: *Module, descriptor: Compilation.Module.Descriptor) !void { +pub fn initialize(compilation: *Compilation, module: *Module) !void { const allocator = compilation.base_allocator; var unit = try TranslationUnit.create(module, allocator); - const c_source_file_path = try std.mem.concat(allocator, u8, &.{ descriptor.executable_path, ".c" }); + const c_source_file_path = try std.mem.concat(allocator, u8, &.{ module.descriptor.executable_path, ".c" }); const c_source_file = try std.fs.cwd().createFile(c_source_file_path, .{}); try unit.type_forward_declarations.append(allocator, '\n'); @@ -1911,12 +1950,17 @@ pub fn initialize(compilation: *Compilation, module: *Module, descriptor: Compil const c_flags = [_][]const u8{ "-std=c2x", "-g", + "-funsigned-char", }; var zig_command_line = ArrayList([]const u8){}; try zig_command_line.append(allocator, "zig"); try zig_command_line.append(allocator, "build-exe"); + if (module.descriptor.link_libc) { + try zig_command_line.append(allocator, "-lc"); + } + const local_cache_dir = std.fs.cwd().realpathAlloc(allocator, "zig-cache") catch b: { std.fs.cwd().makeDir("nat/zig-cache") catch {}; break :b try std.fs.cwd().realpathAlloc(allocator, "nat/zig-cache"); @@ -1928,7 +1972,8 @@ pub fn initialize(compilation: *Compilation, module: *Module, descriptor: Compil try zig_command_line.append(allocator, "--global-cache-dir"); try zig_command_line.append(allocator, global_cache_dir); - try zig_command_line.append(allocator, try std.mem.concat(allocator, u8, &.{ "-femit-bin=", descriptor.executable_path })); + + try zig_command_line.append(allocator, try std.mem.concat(allocator, u8, &.{ "-femit-bin=", module.descriptor.executable_path })); try zig_command_line.append(allocator, "-cflags"); for (c_flags) |c_flag| { diff --git a/bootstrap/frontend/semantic_analyzer.zig b/bootstrap/frontend/semantic_analyzer.zig index 9494114..764814c 100644 --- a/bootstrap/frontend/semantic_analyzer.zig +++ b/bootstrap/frontend/semantic_analyzer.zig @@ -1177,6 +1177,10 @@ const Analyzer = struct { .call, .slice_access, => null, + .bool => |boolean_value| switch (boolean_value) { + true => return boolean_true, + false => return boolean_false, + }, .enum_field => value_index, .indexed_access => |indexed_access_index| blk: { const indexed_access = analyzer.module.values.indexed_accesses.get(indexed_access_index); @@ -1314,6 +1318,7 @@ const Analyzer = struct { const true_reaches_end = switch (analyzer.module.values.array.get(taken_expression_index).*) { .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end, + .string_literal => true, else => |t| @panic(@tagName(t)), }; @@ -1348,6 +1353,7 @@ const Analyzer = struct { const false_reaches_end = switch (analyzer.module.values.array.get(not_taken_expression_index).*) { .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end, .branch => |branch_index| analyzer.module.values.branches.get(branch_index).reaches_end, + .string_literal => true, else => |t| @panic(@tagName(t)), }; const reaches_end = if_result.reaches_end or false_reaches_end; @@ -1983,21 +1989,7 @@ const Analyzer = struct { const right_index = try analyzer.doIdentifier(struct_type.scope, ExpectType.none, node.right.value, scope_index); const right_value = analyzer.module.values.array.get(right_index); - switch (right_value.*) { - .function_definition, - .type, - .enum_field, - .declaration_reference, - .integer, - => break :blk right_value.*, - else => |t| @panic(@tagName(t)), - } - // - - logln(.sema, .node, "Right: {}", .{right_value}); - // struct_scope.declarations.get(identifier); - - unreachable; + break :blk right_value.*; }, .@"enum" => |enum_index| { const enum_type = analyzer.module.types.enums.get(enum_index); @@ -4339,22 +4331,6 @@ const Analyzer = struct { pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_value_index: Value.Index) !void { _ = try analyzeExistingPackage(main_value_index, compilation, module, package); - - var decl_iterator = module.values.declarations.iterator(); - while (decl_iterator.nextPointer()) |decl| { - const declaration_name = module.getName(decl.name).?; - if (equal(u8, declaration_name, "_start")) { - const value = module.values.array.get(decl.init_value); - module.entry_point = switch (value.*) { - .function_definition => |function_index| function_index, - .unresolved => panic("Unresolved declaration: {s}", .{declaration_name}), - else => |t| @panic(@tagName(t)), - }; - break; - } - } else { - @panic("Entry point not found"); - } } pub fn analyzeExistingPackage(value_index: Value.Index, compilation: *Compilation, module: *Module, package: *Package) !Type.Index { diff --git a/bootstrap/frontend/syntactic_analyzer.zig b/bootstrap/frontend/syntactic_analyzer.zig index 1e74553..a5ae29c 100644 --- a/bootstrap/frontend/syntactic_analyzer.zig +++ b/bootstrap/frontend/syntactic_analyzer.zig @@ -641,13 +641,13 @@ const Analyzer = struct { break :blk payload_node; } else Node.Index.invalid; - const if_block = try analyzer.block(.{ .is_comptime = false }); + const if_taken_expression = try analyzer.expression(); const if_node = try analyzer.addNode(.{ .id = .@"if", .token = if_token, .left = if_condition, - .right = if_block, + .right = if_taken_expression, }); const result = switch (analyzer.tokens[analyzer.token_i].id) { @@ -1013,6 +1013,7 @@ const Analyzer = struct { .identifier, .colon, .fixed_keyword_if, + .fixed_keyword_else, .discard, => break, else => blk: { @@ -1542,6 +1543,7 @@ const Analyzer = struct { .identifier, .number_literal, .hash, + .fixed_keyword_if, => blk: { const field_expression_initializer = try analyzer.expression(); switch (analyzer.tokens[analyzer.token_i].id) { diff --git a/lib/init/.gitignore b/lib/init/.gitignore new file mode 100644 index 0000000..911b137 --- /dev/null +++ b/lib/init/.gitignore @@ -0,0 +1 @@ +nat diff --git a/lib/init/build.nat b/lib/init/build.nat new file mode 100644 index 0000000..5096f83 --- /dev/null +++ b/lib/init/build.nat @@ -0,0 +1,21 @@ +const std = #import("std"); +const assert = std.assert; +const Executable = std.build.Executable; + +const main = fn () s32 { + const executable = Executable{ + .target = .{ + .cpu = .x86_64, + .os = .linux, + .abi = .gnu, + }, + .main_source_path = "src/main.nat", + }; + + if (executable.compile()) { + return 0; + } else { + std.print(bytes = "Executable failed to compile!\n"); + return 1; + } +} diff --git a/lib/init/src/main.nat b/lib/init/src/main.nat new file mode 100644 index 0000000..48de37a --- /dev/null +++ b/lib/init/src/main.nat @@ -0,0 +1,3 @@ +const main = fn () s32 { + return 0; +} diff --git a/lib/std/build.nat b/lib/std/build.nat index 119c5f5..2c3eaff 100644 --- a/lib/std/build.nat +++ b/lib/std/build.nat @@ -6,11 +6,22 @@ const Target = std.Target; const Executable = struct{ target: Target, main_source_path: [:0]const u8, + link_libc: bool = false, - const compile = fn(executable: Executable, compiler_path: [&:0]const u8) bool { + const compile = fn(executable: Executable) bool { + const argument_count = std.start.argument_count; + const argument_values = std.start.argument_values; + assert(ok = argument_count == 3); + const compiler_path = argument_values[2]; + + const result = executable.compile_with_compiler_path(compiler_path); + return result; + } + + const compile_with_compiler_path = fn(executable: Executable, compiler_path: [&:0]const u8) bool { if (std.os.duplicate_process()) |pid| { if (pid == 0) { - const argv = [_:null] ?[&:0]const u8{ compiler_path, #cast(executable.main_source_path.ptr), }; + const argv = [_:null] ?[&:0]const u8{ compiler_path, #cast(executable.main_source_path.ptr), "-link_libc", if (executable.link_libc) "true" else "false"}; std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values); return true; } else { diff --git a/lib/std/start.nat b/lib/std/start.nat index 7e4f274..6021d2d 100644 --- a/lib/std/start.nat +++ b/lib/std/start.nat @@ -1,6 +1,11 @@ const std = #import("std"); +const builtin = #import("builtin"); comptime { - _ = _start; + if (builtin.link_libc) { + _ = main; + } else { + _ = _start; + } } const _start = fn () noreturn export cc(.naked) { @@ -27,3 +32,7 @@ const start = fn(argc_argv_address: usize) noreturn export { const result = #import("main").main(); std.os.exit(exit_code = result); } + +const main = fn(argc: s32, argv: [&:null]?[&:null]u8, env: [&:null]?[&:null]u8) s32 export { + return #import("main").main(); +} diff --git a/test/integral/first/.gitignore b/test/integral/first/.gitignore new file mode 100644 index 0000000..911b137 --- /dev/null +++ b/test/integral/first/.gitignore @@ -0,0 +1 @@ +nat diff --git a/test/integral/first/build.nat b/test/integral/first/build.nat index 6b66fba..5096f83 100644 --- a/test/integral/first/build.nat +++ b/test/integral/first/build.nat @@ -3,10 +3,6 @@ const assert = std.assert; const Executable = std.build.Executable; const main = fn () s32 { - const argument_count = std.start.argument_count; - const argument_values = std.start.argument_values; - assert(ok = argument_count == 3); - const executable = Executable{ .target = .{ .cpu = .x86_64, @@ -16,9 +12,7 @@ const main = fn () s32 { .main_source_path = "src/main.nat", }; - const compiler_path = argument_values[2]; - - if (executable.compile(compiler_path)) { + if (executable.compile()) { return 0; } else { std.print(bytes = "Executable failed to compile!\n"); diff --git a/test/integral/link_libc/.gitignore b/test/integral/link_libc/.gitignore new file mode 100644 index 0000000..911b137 --- /dev/null +++ b/test/integral/link_libc/.gitignore @@ -0,0 +1 @@ +nat diff --git a/test/integral/link_libc/build.nat b/test/integral/link_libc/build.nat new file mode 100644 index 0000000..cfb4328 --- /dev/null +++ b/test/integral/link_libc/build.nat @@ -0,0 +1,22 @@ +const std = #import("std"); +const assert = std.assert; +const Executable = std.build.Executable; + +const main = fn () s32 { + const executable = Executable{ + .target = .{ + .cpu = .x86_64, + .os = .linux, + .abi = .gnu, + }, + .main_source_path = "src/main.nat", + .link_libc = true, + }; + + if (executable.compile()) { + return 0; + } else { + std.print(bytes = "Executable failed to compile!\n"); + return 1; + } +} diff --git a/test/integral/link_libc/src/main.nat b/test/integral/link_libc/src/main.nat new file mode 100644 index 0000000..48de37a --- /dev/null +++ b/test/integral/link_libc/src/main.nat @@ -0,0 +1,3 @@ +const main = fn () s32 { + return 0; +}