implement libc-linked executable

This commit is contained in:
David Gonzalez Martin 2023-12-17 23:39:31 +01:00
parent 5fff6c1870
commit 1ca6b09386
14 changed files with 336 additions and 205 deletions

View File

@ -47,7 +47,8 @@ fn reportUnterminatedArgumentError(string: []const u8) noreturn {
std.debug.panic("Unterminated argument: {s}", .{string}); 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..]; const arguments = (try std.process.argsAlloc(allocator))[1..];
var maybe_executable_path: ?[]const u8 = null; 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 target_triplet: []const u8 = "x86_64-linux-gnu";
var should_transpile_to_c: ?bool = null; var should_transpile_to_c: ?bool = null;
var maybe_only_parse: ?bool = null; var maybe_only_parse: ?bool = null;
var link_libc = false;
var i: usize = 0; if (arguments.len == 0) {
while (i < arguments.len) : (i += 1) { // foo
const current_argument = arguments[i]; } else if (equal(u8, arguments[0], "init")) {
if (equal(u8, current_argument, "-o")) { if (arguments.len == 1) {
if (i + 1 != arguments.len) { unreachable;
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 { } 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, .transpile_to_c = transpile_to_c,
.is_build = is_build, .is_build = is_build,
.only_parse = only_parse, .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(cache_dir_name);
try compilation.build_directory.makePath(installation_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); 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 = 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_index = indexed_expression.getType(module);
const indexed_expression_type = module.types.array.get(indexed_expression_type_index); const indexed_expression_type = module.types.array.get(indexed_expression_type_index);
break :blk switch (indexed_expression_type.*) { break :blk switch (indexed_expression_type.*) {
.slice => |slice| slice.element_type, .slice => |slice| slice.element_type,
.array => |array| array.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 optional_value = module.values.array.get(optional_unwrap.value);
const expected_optional_type_index = optional_value.getType(module); const expected_optional_type_index = optional_value.getType(module);
const expected_optional_type = module.types.array.get(expected_optional_type_index); const expected_optional_type = module.types.array.get(expected_optional_type_index);
break :blk switch (expected_optional_type.*) { break :blk switch (expected_optional_type.*) {
.optional => |optional| optional.element_type, .optional => |optional| optional.element_type,
else => |t| @panic(@tagName(t)), 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)), else => |t| @panic(@tagName(t)),
}; };
@ -1029,6 +1069,7 @@ pub const Module = struct {
transpile_to_c: bool, transpile_to_c: bool,
is_build: bool, is_build: bool,
only_parse: bool, only_parse: bool,
link_libc: bool,
}; };
const ImportFileResult = struct { 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 { 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); const module: *Module = try compilation.base_allocator.create(Module);
module.* = Module{ module.* = Module{
.main_package = blk: { .main_package = blk: {
@ -1207,6 +1231,24 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
.descriptor = 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 std_package_dir = "lib/std";
const package_descriptors = [2]struct { const package_descriptors = [2]struct {
@ -1245,7 +1287,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
assert(module.main_package.dependencies.size == 2); 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").?); _ = try module.importPackage(compilation.base_allocator, module.main_package.dependencies.get("std").?);
} else { } else {
_ = try module.importPackage(compilation.base_allocator, module.main_package); _ = 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); try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import);
} }
if (!descriptor.only_parse) { if (!module.descriptor.only_parse) {
inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| {
_ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) { _ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) {
.usize => @unionInit(Type, "integer", .{ .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); try semantic_analyzer.initialize(compilation, module, packages[0], value_index);
if (descriptor.transpile_to_c) { if (module.descriptor.transpile_to_c) {
try c_transpiler.initialize(compilation, module, descriptor); try c_transpiler.initialize(compilation, module);
if (descriptor.is_build) { if (module.descriptor.is_build) {
const argv = [_][]const u8{ descriptor.executable_path, "--compiler", compilation.executable_absolute_path }; const argv = [_][]const u8{ module.descriptor.executable_path, "--compiler", compilation.executable_absolute_path };
const process_result = try std.ChildProcess.run(.{ const process_result = try std.ChildProcess.run(.{
.allocator = compilation.base_allocator, .allocator = compilation.base_allocator,
.argv = &argv, .argv = &argv,

View File

@ -405,7 +405,7 @@ pub const TranslationUnit = struct {
const declaration = module.values.declarations.get(declaration_index); const declaration = module.values.declarations.get(declaration_index);
const base_declaration_name = blk: { const base_declaration_name = blk: {
const name = module.getName(declaration.name).?; 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"; break :blk "user_entry_point";
} else { } else {
break :blk name; 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 { 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); const is_main = equal(u8, "main", name);
switch (function_prototype.attributes.calling_convention) { if (is_main) {
.system_v => {}, try list.appendSlice(allocator, "int main(int argc, char** argv, char** envp)");
.naked => try list.appendSlice(allocator, "[[gnu::naked]] "), } else {
} const function_prototype = module.types.function_prototypes.get(function_prototype_index);
switch (function_prototype.attributes.calling_convention) {
try unit.writeType(module, list, allocator, function_prototype.return_type, ' '); .system_v => {},
.naked => try list.appendSlice(allocator, "[[gnu::naked]] "),
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, ", ");
} }
_ = 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 { 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 { 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); const branch = module.values.branches.get(branch_index);
try list.appendSlice(allocator, "if ("); const classic = switch (module.values.array.get(branch.taken_expression).*) {
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .block => true,
.value_index = branch.expression, else => false,
.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 (!branch.not_taken_expression.invalid) { if (classic) {
if (module.values.array.get(branch.taken_expression).* == .block) { try list.appendSlice(allocator, "if (");
_ = list.pop();
try list.appendSlice(allocator, " else ");
} else {
unreachable;
}
try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ 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, .type_index = function_return_type,
}); });
if (module.values.array.get(branch.not_taken_expression).* == .block) { if (!branch.not_taken_expression.invalid) {
return false; 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 { 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; const allocator = compilation.base_allocator;
var unit = try TranslationUnit.create(module, 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, .{}); const c_source_file = try std.fs.cwd().createFile(c_source_file_path, .{});
try unit.type_forward_declarations.append(allocator, '\n'); 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{ const c_flags = [_][]const u8{
"-std=c2x", "-std=c2x",
"-g", "-g",
"-funsigned-char",
}; };
var zig_command_line = ArrayList([]const u8){}; var zig_command_line = ArrayList([]const u8){};
try zig_command_line.append(allocator, "zig"); try zig_command_line.append(allocator, "zig");
try zig_command_line.append(allocator, "build-exe"); 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: { const local_cache_dir = std.fs.cwd().realpathAlloc(allocator, "zig-cache") catch b: {
std.fs.cwd().makeDir("nat/zig-cache") catch {}; std.fs.cwd().makeDir("nat/zig-cache") catch {};
break :b try std.fs.cwd().realpathAlloc(allocator, "nat/zig-cache"); 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, 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"); try zig_command_line.append(allocator, "-cflags");
for (c_flags) |c_flag| { for (c_flags) |c_flag| {

View File

@ -1177,6 +1177,10 @@ const Analyzer = struct {
.call, .call,
.slice_access, .slice_access,
=> null, => null,
.bool => |boolean_value| switch (boolean_value) {
true => return boolean_true,
false => return boolean_false,
},
.enum_field => value_index, .enum_field => value_index,
.indexed_access => |indexed_access_index| blk: { .indexed_access => |indexed_access_index| blk: {
const indexed_access = analyzer.module.values.indexed_accesses.get(indexed_access_index); 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).*) { 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, .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end,
.string_literal => true,
else => |t| @panic(@tagName(t)), 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).*) { 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, .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end,
.branch => |branch_index| analyzer.module.values.branches.get(branch_index).reaches_end, .branch => |branch_index| analyzer.module.values.branches.get(branch_index).reaches_end,
.string_literal => true,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
const reaches_end = if_result.reaches_end or false_reaches_end; 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_index = try analyzer.doIdentifier(struct_type.scope, ExpectType.none, node.right.value, scope_index);
const right_value = analyzer.module.values.array.get(right_index); const right_value = analyzer.module.values.array.get(right_index);
switch (right_value.*) { break :blk 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;
}, },
.@"enum" => |enum_index| { .@"enum" => |enum_index| {
const enum_type = analyzer.module.types.enums.get(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 { pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_value_index: Value.Index) !void {
_ = try analyzeExistingPackage(main_value_index, compilation, module, package); _ = 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 { pub fn analyzeExistingPackage(value_index: Value.Index, compilation: *Compilation, module: *Module, package: *Package) !Type.Index {

View File

@ -641,13 +641,13 @@ const Analyzer = struct {
break :blk payload_node; break :blk payload_node;
} else Node.Index.invalid; } 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(.{ const if_node = try analyzer.addNode(.{
.id = .@"if", .id = .@"if",
.token = if_token, .token = if_token,
.left = if_condition, .left = if_condition,
.right = if_block, .right = if_taken_expression,
}); });
const result = switch (analyzer.tokens[analyzer.token_i].id) { const result = switch (analyzer.tokens[analyzer.token_i].id) {
@ -1013,6 +1013,7 @@ const Analyzer = struct {
.identifier, .identifier,
.colon, .colon,
.fixed_keyword_if, .fixed_keyword_if,
.fixed_keyword_else,
.discard, .discard,
=> break, => break,
else => blk: { else => blk: {
@ -1542,6 +1543,7 @@ const Analyzer = struct {
.identifier, .identifier,
.number_literal, .number_literal,
.hash, .hash,
.fixed_keyword_if,
=> blk: { => blk: {
const field_expression_initializer = try analyzer.expression(); const field_expression_initializer = try analyzer.expression();
switch (analyzer.tokens[analyzer.token_i].id) { switch (analyzer.tokens[analyzer.token_i].id) {

1
lib/init/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
nat

21
lib/init/build.nat Normal file
View File

@ -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;
}
}

3
lib/init/src/main.nat Normal file
View File

@ -0,0 +1,3 @@
const main = fn () s32 {
return 0;
}

View File

@ -6,11 +6,22 @@ const Target = std.Target;
const Executable = struct{ const Executable = struct{
target: Target, target: Target,
main_source_path: [:0]const u8, 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 (std.os.duplicate_process()) |pid| {
if (pid == 0) { 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); std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values);
return true; return true;
} else { } else {

View File

@ -1,6 +1,11 @@
const std = #import("std"); const std = #import("std");
const builtin = #import("builtin");
comptime { comptime {
_ = _start; if (builtin.link_libc) {
_ = main;
} else {
_ = _start;
}
} }
const _start = fn () noreturn export cc(.naked) { 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(); const result = #import("main").main();
std.os.exit(exit_code = result); 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();
}

1
test/integral/first/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
nat

View File

@ -3,10 +3,6 @@ const assert = std.assert;
const Executable = std.build.Executable; const Executable = std.build.Executable;
const main = fn () s32 { 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{ const executable = Executable{
.target = .{ .target = .{
.cpu = .x86_64, .cpu = .x86_64,
@ -16,9 +12,7 @@ const main = fn () s32 {
.main_source_path = "src/main.nat", .main_source_path = "src/main.nat",
}; };
const compiler_path = argument_values[2]; if (executable.compile()) {
if (executable.compile(compiler_path)) {
return 0; return 0;
} else { } else {
std.print(bytes = "Executable failed to compile!\n"); std.print(bytes = "Executable failed to compile!\n");

1
test/integral/link_libc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
nat

View File

@ -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;
}
}

View File

@ -0,0 +1,3 @@
const main = fn () s32 {
return 0;
}