commit
2848c8741b
@ -36,7 +36,7 @@ fn reportUnterminatedArgumentError(string: []const u8) noreturn {
|
||||
std.debug.panic("Unterminated argument: {s}", .{string});
|
||||
}
|
||||
|
||||
pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
|
||||
pub fn createContext(allocator: Allocator) !*const Context{
|
||||
const context: *Context = try allocator.create(Context);
|
||||
|
||||
const self_exe_path = try std.fs.selfExePathAlloc(allocator);
|
||||
@ -52,6 +52,42 @@ pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
|
||||
try context.build_directory.makePath(cache_dir_name);
|
||||
try context.build_directory.makePath(installation_dir_name);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
pub fn compileBuildExecutable(context: *const Context, arguments: [][:0]u8) !void {
|
||||
_ = arguments; // autofix
|
||||
const unit = try context.allocator.create(Unit);
|
||||
const target_query = try std.Target.Query.parse(.{});
|
||||
const target = try std.zig.system.resolveTargetQuery(target_query);
|
||||
unit.* = .{
|
||||
.descriptor = .{
|
||||
.main_package_path = "build.nat",
|
||||
.target = target,
|
||||
.only_parse = false,
|
||||
.executable_path = "nat/build",
|
||||
.link_libc = @import("builtin").os.tag == .macos,
|
||||
.generate_debug_information = true,
|
||||
.name = "build",
|
||||
},
|
||||
};
|
||||
|
||||
try unit.compile(context);
|
||||
const result = try std.ChildProcess.run(.{
|
||||
.allocator = context.allocator,
|
||||
.argv = &.{ "nat/build", "-compiler_path", context.executable_absolute_path },
|
||||
});
|
||||
switch (result.term) {
|
||||
.Exited => |exit_code| {
|
||||
if (exit_code != 0) @panic("Bad exit code");
|
||||
},
|
||||
.Signal => @panic("Signaled"),
|
||||
.Stopped => @panic("Stopped"),
|
||||
.Unknown => @panic("Unknown"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buildExecutable(context: *const Context, arguments: [][:0]u8) !void {
|
||||
var maybe_executable_path: ?[]const u8 = null;
|
||||
var maybe_main_package_path: ?[]const u8 = null;
|
||||
var target_triplet: []const u8 = switch (@import("builtin").os.tag) {
|
||||
@ -63,121 +99,112 @@ pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
|
||||
var link_libc = false;
|
||||
var maybe_executable_name: ?[]const u8 = null;
|
||||
const generate_debug_information = true;
|
||||
var is_build = false;
|
||||
|
||||
if (arguments.len == 0) {
|
||||
is_build = true;
|
||||
} else if (equal(u8, arguments[0], "init")) {
|
||||
if (arguments.len == 1) {
|
||||
unreachable;
|
||||
} else {
|
||||
@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;
|
||||
if (arguments.len == 0) return error.InvalidInput;
|
||||
|
||||
var log_argument_iterator = std.mem.splitScalar(u8, arguments[i], ',');
|
||||
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;
|
||||
|
||||
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;
|
||||
var log_argument_iterator = std.mem.splitScalar(u8, arguments[i], ',');
|
||||
|
||||
inline for (@typeInfo(LoggerScope).Enum.fields) |logger_scope_enum_field| {
|
||||
const log_scope = @field(LoggerScope, logger_scope_enum_field.name);
|
||||
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;
|
||||
|
||||
if (equal(u8, @tagName(log_scope), log_scope_candidate)) {
|
||||
const LogScope = getLoggerScopeType(log_scope);
|
||||
inline for (@typeInfo(LoggerScope).Enum.fields) |logger_scope_enum_field| {
|
||||
const log_scope = @field(LoggerScope, logger_scope_enum_field.name);
|
||||
|
||||
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, @tagName(log_scope), log_scope_candidate)) {
|
||||
const LogScope = getLoggerScopeType(log_scope);
|
||||
|
||||
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();
|
||||
}
|
||||
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);
|
||||
|
||||
logger_bitset.setPresent(log_scope, true);
|
||||
|
||||
recognized_scope = true;
|
||||
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();
|
||||
}
|
||||
} else if (!recognized_scope) std.debug.panic("Unrecognized log scope: {s}", .{log_scope_candidate});
|
||||
}
|
||||
} 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;
|
||||
logger_bitset.setPresent(log_scope, true);
|
||||
|
||||
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 if (equal(u8, current_argument, "-main_source_file")) {
|
||||
if (i + 1 != arguments.len) {
|
||||
i += 1;
|
||||
|
||||
const arg = arguments[i];
|
||||
maybe_main_package_path = arg;
|
||||
} else {
|
||||
reportUnterminatedArgumentError(current_argument);
|
||||
}
|
||||
} else if (equal(u8, current_argument, "-name")) {
|
||||
if (i + 1 != arguments.len) {
|
||||
i += 1;
|
||||
|
||||
const arg = arguments[i];
|
||||
maybe_executable_name = arg;
|
||||
} else {
|
||||
reportUnterminatedArgumentError(current_argument);
|
||||
recognized_scope = true;
|
||||
}
|
||||
} else if (!recognized_scope) std.debug.panic("Unrecognized log scope: {s}", .{log_scope_candidate});
|
||||
}
|
||||
} else {
|
||||
std.debug.panic("Unrecognized argument: {s}", .{current_argument});
|
||||
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 if (equal(u8, current_argument, "-main_source_file")) {
|
||||
if (i + 1 != arguments.len) {
|
||||
i += 1;
|
||||
|
||||
const arg = arguments[i];
|
||||
maybe_main_package_path = arg;
|
||||
} else {
|
||||
reportUnterminatedArgumentError(current_argument);
|
||||
}
|
||||
} else if (equal(u8, current_argument, "-name")) {
|
||||
if (i + 1 != arguments.len) {
|
||||
i += 1;
|
||||
|
||||
const arg = arguments[i];
|
||||
maybe_executable_name = arg;
|
||||
} else {
|
||||
reportUnterminatedArgumentError(current_argument);
|
||||
}
|
||||
} else {
|
||||
std.debug.panic("Unrecognized argument: {s}", .{current_argument});
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,25 +217,13 @@ pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
|
||||
file.close();
|
||||
|
||||
break :blk path;
|
||||
} else blk: {
|
||||
const build_file = "build.nat";
|
||||
const file = std.fs.cwd().openFile(build_file, .{}) catch return error.main_package_path_not_specified;
|
||||
file.close();
|
||||
is_build = true;
|
||||
} else unreachable;
|
||||
|
||||
break :blk build_file;
|
||||
};
|
||||
|
||||
const executable_name = if (is_build) b: {
|
||||
assert(maybe_executable_name == null);
|
||||
break :b "build";
|
||||
} else b: {
|
||||
break :b if (maybe_executable_name) |name| name else std.fs.path.basename(main_package_path[0 .. main_package_path.len - "/main.nat".len]);
|
||||
};
|
||||
const executable_name = if (maybe_executable_name) |name| name else std.fs.path.basename(main_package_path[0 .. main_package_path.len - "/main.nat".len]);
|
||||
|
||||
const executable_path = maybe_executable_path orelse blk: {
|
||||
assert(executable_name.len > 0);
|
||||
const result = try std.mem.concat(allocator, u8, &.{ "nat/", executable_name });
|
||||
const result = try std.mem.concat(context.allocator, u8, &.{ "nat/", executable_name });
|
||||
break :blk result;
|
||||
};
|
||||
|
||||
@ -218,7 +233,6 @@ pub fn buildExecutable(allocator: Allocator, arguments: [][:0]u8) !void {
|
||||
.main_package_path = main_package_path,
|
||||
.executable_path = executable_path,
|
||||
.target = target,
|
||||
.is_build = is_build,
|
||||
.only_parse = only_parse,
|
||||
.link_libc = switch (target.os.tag) {
|
||||
.linux => link_libc,
|
||||
@ -372,6 +386,12 @@ fn getTypeBitSize(ty: *Type, unit: *Unit) u32 {
|
||||
}
|
||||
},
|
||||
.pointer => 64,
|
||||
.@"enum" => |enum_index| b: {
|
||||
const enum_type = unit.enums.get(enum_index);
|
||||
const backing_type = unit.types.get(enum_type.backing_type);
|
||||
break :b getTypeBitSize(backing_type, unit);
|
||||
},
|
||||
.slice => 2 * @bitSizeOf(usize),
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
}
|
||||
@ -397,7 +417,7 @@ pub const Type = union(enum) {
|
||||
fn getByteSize(ty: *Type, unit: *Unit) u32 {
|
||||
_ = unit; // autofix
|
||||
return switch (ty.*) {
|
||||
.integer => |integer| integer.bit_count,
|
||||
.integer => |integer| @divExact(integer.bit_count, @bitSizeOf(u8)),
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
}
|
||||
@ -1312,6 +1332,18 @@ pub const Builder = struct {
|
||||
.pointer => |source_pointer| {
|
||||
if (destination_pointer.type == source_pointer.type) {
|
||||
if (destination_pointer.mutability == source_pointer.mutability) {
|
||||
if (destination_pointer.nullable != source_pointer.nullable) {
|
||||
std.debug.print("Dst: {} Src: {}\n",.{destination_pointer.nullable, source_pointer.nullable});
|
||||
if (destination_pointer.nullable) {
|
||||
assert(destination_pointer.termination != source_pointer.termination);
|
||||
unreachable;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
if (destination_pointer.termination != source_pointer.termination) {
|
||||
unreachable;
|
||||
}
|
||||
unreachable;
|
||||
} else {
|
||||
break :b .pointer_const_to_var;
|
||||
@ -1435,6 +1467,7 @@ pub const Builder = struct {
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
},
|
||||
.none => .usize,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
@ -2255,10 +2288,16 @@ pub const Builder = struct {
|
||||
|
||||
return .pointer_to_nullable;
|
||||
}
|
||||
} else {
|
||||
if (destination_pointer.termination != .none) {
|
||||
unreachable;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unreachable;
|
||||
std.debug.panic("Pointer unknown typecheck:\nDst: {}\n Src: {}", .{destination_pointer, source_pointer});
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
@ -2387,27 +2426,20 @@ pub const Builder = struct {
|
||||
}
|
||||
},
|
||||
.array => |destination_array| {
|
||||
switch (source.*) {
|
||||
.array => |source_array| {
|
||||
assert(destination_array.type == source_array.type);
|
||||
assert(destination_array.count == source_array.count);
|
||||
if (destination_array.termination != source_array.termination) {
|
||||
if (destination_array.termination == .none) {
|
||||
unreachable;
|
||||
} else {
|
||||
std.debug.panic("Expected {s} array termination, got {s}", .{@tagName(destination_array.termination), @tagName(source_array.termination)});
|
||||
}
|
||||
} else unreachable;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
// .array => |array| switch (unit.types.get(array_type).*) {
|
||||
// .array => |expected_array| {
|
||||
// },
|
||||
// else => |t| @panic(@tagName(t)),
|
||||
// },
|
||||
// else => |t| @panic(@tagName(t)),
|
||||
// }
|
||||
switch (source.*) {
|
||||
.array => |source_array| {
|
||||
assert(destination_array.type == source_array.type);
|
||||
assert(destination_array.count == source_array.count);
|
||||
if (destination_array.termination != source_array.termination) {
|
||||
if (destination_array.termination == .none) {
|
||||
unreachable;
|
||||
} else {
|
||||
std.debug.panic("Expected {s} array termination, got {s}", .{@tagName(destination_array.termination), @tagName(source_array.termination)});
|
||||
}
|
||||
} else unreachable;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
@ -3928,6 +3960,19 @@ pub const Builder = struct {
|
||||
const result = try builder.resolveContainerLiteral(unit, context, initialization_nodes, container_type_index);
|
||||
break :block result;
|
||||
},
|
||||
.anonymous_container_literal => block: {
|
||||
switch (type_expect) {
|
||||
.type => |type_index| {
|
||||
assert(node.left == .null);
|
||||
assert(node.right != .null);
|
||||
const initialization_nodes = unit.getNodeList(node.right);
|
||||
const result = try builder.resolveContainerLiteral(unit, context, initialization_nodes, type_index);
|
||||
break :block result;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
unreachable;
|
||||
},
|
||||
.enum_literal => block: {
|
||||
switch (type_expect) {
|
||||
.type => |type_index| {
|
||||
@ -5191,52 +5236,15 @@ pub const Builder = struct {
|
||||
.type = gep_type,
|
||||
};
|
||||
},
|
||||
.pointer => |child_pointer| switch (unit.types.get(child_pointer.type).*) {
|
||||
.array => |array| b: {
|
||||
.pointer => |child_pointer| switch (child_pointer.many) {
|
||||
true => b: {
|
||||
const load = try unit.instructions.append(context.allocator, .{
|
||||
.load = .{
|
||||
.value = array_like_expression,
|
||||
.type = pointer.type,
|
||||
},
|
||||
});
|
||||
});
|
||||
try builder.appendInstruction(unit, context, load);
|
||||
|
||||
const gep = try unit.instructions.append(context.allocator, .{
|
||||
.get_element_pointer = .{
|
||||
.pointer = load,
|
||||
.base_type = array.type,
|
||||
.is_struct = false,
|
||||
.index = index,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, gep);
|
||||
|
||||
const gep_type = try unit.getPointerType(context, .{
|
||||
.type = array.type,
|
||||
.termination = .none,
|
||||
.mutability = pointer.mutability,
|
||||
.many = false,
|
||||
.nullable = false,
|
||||
});
|
||||
|
||||
break :b .{
|
||||
.value = .{
|
||||
.runtime = gep,
|
||||
},
|
||||
.type = gep_type,
|
||||
};
|
||||
},
|
||||
.integer => b: {
|
||||
assert(child_pointer.many);
|
||||
|
||||
const load = try unit.instructions.append(context.allocator, .{
|
||||
.load = .{
|
||||
.value = array_like_expression,
|
||||
.type = pointer.type,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, load);
|
||||
|
||||
const gep = try unit.instructions.append(context.allocator, .{
|
||||
.get_element_pointer = .{
|
||||
.pointer = load,
|
||||
@ -5249,8 +5257,8 @@ pub const Builder = struct {
|
||||
|
||||
const gep_type = try unit.getPointerType(context, .{
|
||||
.type = child_pointer.type,
|
||||
.termination = .none,
|
||||
.mutability = pointer.mutability,
|
||||
.termination = child_pointer.termination,
|
||||
.mutability = child_pointer.mutability,
|
||||
.many = false,
|
||||
.nullable = false,
|
||||
});
|
||||
@ -5262,7 +5270,79 @@ pub const Builder = struct {
|
||||
.type = gep_type,
|
||||
};
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
false => switch (unit.types.get(child_pointer.type).*) {
|
||||
.array => |array| b: {
|
||||
const load = try unit.instructions.append(context.allocator, .{
|
||||
.load = .{
|
||||
.value = array_like_expression,
|
||||
.type = pointer.type,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, load);
|
||||
|
||||
const gep = try unit.instructions.append(context.allocator, .{
|
||||
.get_element_pointer = .{
|
||||
.pointer = load,
|
||||
.base_type = array.type,
|
||||
.is_struct = false,
|
||||
.index = index,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, gep);
|
||||
|
||||
const gep_type = try unit.getPointerType(context, .{
|
||||
.type = array.type,
|
||||
.termination = .none,
|
||||
.mutability = pointer.mutability,
|
||||
.many = false,
|
||||
.nullable = false,
|
||||
});
|
||||
|
||||
break :b .{
|
||||
.value = .{
|
||||
.runtime = gep,
|
||||
},
|
||||
.type = gep_type,
|
||||
};
|
||||
},
|
||||
.integer => b: {
|
||||
assert(child_pointer.many);
|
||||
|
||||
const load = try unit.instructions.append(context.allocator, .{
|
||||
.load = .{
|
||||
.value = array_like_expression,
|
||||
.type = pointer.type,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, load);
|
||||
|
||||
const gep = try unit.instructions.append(context.allocator, .{
|
||||
.get_element_pointer = .{
|
||||
.pointer = load,
|
||||
.base_type = child_pointer.type,
|
||||
.is_struct = false,
|
||||
.index = index,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, gep);
|
||||
|
||||
const gep_type = try unit.getPointerType(context, .{
|
||||
.type = child_pointer.type,
|
||||
.termination = .none,
|
||||
.mutability = pointer.mutability,
|
||||
.many = false,
|
||||
.nullable = false,
|
||||
});
|
||||
|
||||
break :b .{
|
||||
.value = .{
|
||||
.runtime = gep,
|
||||
},
|
||||
.type = gep_type,
|
||||
};
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
@ -5658,7 +5738,11 @@ pub const Builder = struct {
|
||||
if (array_type.count != expression_element_count) @panic("Array element count mismatch");
|
||||
|
||||
var is_comptime = true;
|
||||
var values = try ArrayList(V).initCapacity(context.allocator, nodes.len);
|
||||
const is_terminated = switch (array_type.termination) {
|
||||
.none => false,
|
||||
else => true,
|
||||
};
|
||||
var values = try ArrayList(V).initCapacity(context.allocator, nodes.len + @intFromBool(is_terminated));
|
||||
for (nodes) |node_index| {
|
||||
const value = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = array_type.type }, node_index, .right);
|
||||
// assert(value.value == .@"comptime");
|
||||
@ -5666,6 +5750,26 @@ pub const Builder = struct {
|
||||
values.appendAssumeCapacity(value);
|
||||
}
|
||||
|
||||
switch (array_type.termination) {
|
||||
.none => {},
|
||||
.zero => values.appendAssumeCapacity(.{
|
||||
.value = .{
|
||||
.@"comptime" = .{
|
||||
.constant_int = .{
|
||||
.value = 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
.type = array_type.type,
|
||||
}),
|
||||
.null => values.appendAssumeCapacity(.{
|
||||
.value = .{
|
||||
.@"comptime" = .null_pointer,
|
||||
},
|
||||
.type = array_type.type,
|
||||
}),
|
||||
}
|
||||
|
||||
if (is_comptime) {
|
||||
const constant_array = try unit.constant_arrays.append(context.allocator, .{
|
||||
.values = blk: {
|
||||
@ -7662,30 +7766,47 @@ pub const Builder = struct {
|
||||
.constant_int = .{
|
||||
.value = ct_int.value,
|
||||
},
|
||||
},
|
||||
},
|
||||
.type = ti,
|
||||
};
|
||||
},
|
||||
.signed => {
|
||||
if (destination_integer_type.signedness == .unsigned) {
|
||||
unreachable;
|
||||
} else {
|
||||
const value = -@as(i64, @intCast(ct_int.value));
|
||||
return .{
|
||||
.value = .{
|
||||
.@"comptime" = .{
|
||||
.constant_int = .{
|
||||
.value = @bitCast(value),
|
||||
},
|
||||
},
|
||||
},
|
||||
.type = ti,
|
||||
};
|
||||
},
|
||||
.signed => {
|
||||
if (destination_integer_type.signedness == .unsigned) {
|
||||
unreachable;
|
||||
} else {
|
||||
const value = -@as(i64, @intCast(ct_int.value));
|
||||
return .{
|
||||
.value = .{
|
||||
.@"comptime" = .{
|
||||
.constant_int = .{
|
||||
.value = @bitCast(value),
|
||||
},
|
||||
},
|
||||
},
|
||||
.type = ti,
|
||||
};
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
.pointer_to_nullable => {
|
||||
const cast = try unit.instructions.append(context.allocator, .{
|
||||
.cast = .{
|
||||
.id = .pointer_to_nullable,
|
||||
.value = result,
|
||||
.type = ti,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, cast);
|
||||
|
||||
return .{
|
||||
.value = .{
|
||||
.runtime = cast,
|
||||
},
|
||||
.type = ti,
|
||||
};
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
@ -8513,7 +8634,6 @@ pub const Descriptor = struct {
|
||||
main_package_path: []const u8,
|
||||
executable_path: []const u8,
|
||||
target: std.Target,
|
||||
is_build: bool,
|
||||
only_parse: bool,
|
||||
link_libc: bool,
|
||||
generate_debug_information: bool,
|
||||
|
@ -1255,7 +1255,11 @@ pub const LLVM = struct {
|
||||
},
|
||||
.array => |array| blk: {
|
||||
const element_type = try llvm.getType(unit, context, array.type);
|
||||
const array_type = LLVM.Type.Array.get(element_type, array.count) orelse return Type.Error.array;
|
||||
const extra_element = switch (array.termination) {
|
||||
.none => false,
|
||||
else => true,
|
||||
};
|
||||
const array_type = LLVM.Type.Array.get(element_type, array.count + @intFromBool(extra_element)) orelse return Type.Error.array;
|
||||
break :blk array_type.toType();
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
@ -2223,6 +2227,7 @@ pub const LLVM = struct {
|
||||
};
|
||||
list.appendAssumeCapacity(value);
|
||||
}
|
||||
|
||||
const result = array_type.getConstant(list.items.ptr, list.items.len) orelse unreachable;
|
||||
return result;
|
||||
}
|
||||
@ -3193,17 +3198,55 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
var arguments = ArrayList([*:0]const u8){};
|
||||
try arguments.append(context.allocator, driver_program);
|
||||
|
||||
try arguments.append(context.allocator, object_file_path.ptr);
|
||||
try arguments.append(context.allocator, "--error-limit=0");
|
||||
|
||||
try arguments.append(context.allocator, "-o");
|
||||
try arguments.append(context.allocator, destination_file_path.ptr);
|
||||
|
||||
if (format == .macho) {
|
||||
try arguments.append(context.allocator, "-dynamic");
|
||||
try arguments.appendSlice(context.allocator, &.{ "-platform_version", "macos", "13.4.1", "13.3" });
|
||||
try arguments.appendSlice(context.allocator, &.{ "-arch", "arm64" });
|
||||
try arguments.appendSlice(context.allocator, &.{ "-syslibroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" });
|
||||
try arguments.appendSlice(context.allocator, &.{ "-e", "_main" });
|
||||
try arguments.append(context.allocator, "-lSystem");
|
||||
try arguments.append(context.allocator, object_file_path.ptr);
|
||||
|
||||
switch (unit.descriptor.target.os.tag) {
|
||||
.macos => {
|
||||
try arguments.append(context.allocator, "-dynamic");
|
||||
try arguments.appendSlice(context.allocator, &.{ "-platform_version", "macos", "13.4.1", "13.3" });
|
||||
try arguments.append(context.allocator, "-arch");
|
||||
try arguments.append(context.allocator, switch (unit.descriptor.target.cpu.arch) {
|
||||
.aarch64 => "arm64",
|
||||
else => |t| @panic(@tagName(t)),
|
||||
});
|
||||
try arguments.appendSlice(context.allocator, &.{ "-syslibroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" });
|
||||
try arguments.appendSlice(context.allocator, &.{ "-e", "_main" });
|
||||
try arguments.append(context.allocator, "-lSystem");
|
||||
},
|
||||
.linux => {
|
||||
try arguments.appendSlice(context.allocator, &.{"--entry", "_start"});
|
||||
try arguments.append(context.allocator, "-m");
|
||||
try arguments.append(context.allocator, switch (unit.descriptor.target.cpu.arch) {
|
||||
.x86_64 => "elf_x86_64",
|
||||
else => |t| @panic(@tagName(t)),
|
||||
});
|
||||
|
||||
if (unit.descriptor.link_libc) {
|
||||
try arguments.append(context.allocator, "/usr/lib/crt1.o");
|
||||
try arguments.append(context.allocator, "/usr/lib/crti.o");
|
||||
try arguments.appendSlice(context.allocator, &.{"-L", "/usr/lib"});
|
||||
try arguments.appendSlice(context.allocator, &.{"-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"});
|
||||
try arguments.append(context.allocator, "--as-needed");
|
||||
try arguments.append(context.allocator, "-lm");
|
||||
try arguments.append(context.allocator, "-lpthread");
|
||||
try arguments.append(context.allocator, "-lc");
|
||||
try arguments.append(context.allocator, "-ldl");
|
||||
try arguments.append(context.allocator, "-lrt");
|
||||
try arguments.append(context.allocator, "-lutil");
|
||||
try arguments.append(context.allocator, "/usr/lib/crtn.o");
|
||||
}
|
||||
|
||||
// if (unit.descriptor.link_libc) {
|
||||
// try arguments.appendSlice(context.allocator, &.{ "-lc" });
|
||||
// }
|
||||
},
|
||||
.windows => {},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
|
||||
var stdout_ptr: [*]const u8 = undefined;
|
||||
|
@ -29,7 +29,8 @@ pub fn main() !void {
|
||||
const command_arguments = arguments[2..];
|
||||
|
||||
if (equal(u8, command, "build")) {
|
||||
todo();
|
||||
const context = try Compilation.createContext(allocator);
|
||||
try Compilation.compileBuildExecutable(context, command_arguments);
|
||||
} else if (equal(u8, command, "clang") or equal(u8, command, "-cc1") or equal(u8, command, "-cc1as")) {
|
||||
const exit_code = try clangMain(allocator, arguments);
|
||||
std.process.exit(exit_code);
|
||||
@ -40,7 +41,8 @@ pub fn main() !void {
|
||||
// TODO: transform our arguments to Clang and invoke it
|
||||
todo();
|
||||
} else if (equal(u8, command, "exe")) {
|
||||
try Compilation.buildExecutable(allocator, command_arguments);
|
||||
const context = try Compilation.createContext(allocator);
|
||||
try Compilation.buildExecutable(context, command_arguments);
|
||||
} else if (equal(u8, command, "lib")) {
|
||||
todo();
|
||||
} else if (equal(u8, command, "obj")) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const TestError = error{
|
||||
junk_in_test_directory,
|
||||
@ -9,36 +10,38 @@ const TestError = error{
|
||||
fail,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
std.debug.print("\n",.{});
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
const allocator = arena.allocator();
|
||||
const standalone_test_dir_path = "test/standalone";
|
||||
var standalone_test_dir = try std.fs.cwd().openDir(standalone_test_dir_path, .{
|
||||
fn collectDirectoryDirEntries(allocator: Allocator, path: []const u8) ![]const []const u8{
|
||||
var dir = try std.fs.cwd().openDir(path, .{
|
||||
.iterate = true,
|
||||
});
|
||||
var standalone_iterator = standalone_test_dir.iterate();
|
||||
var standalone_test_names = std.ArrayListUnmanaged([]const u8){};
|
||||
var dir_iterator = dir.iterate();
|
||||
var dir_entries = std.ArrayListUnmanaged([]const u8){};
|
||||
|
||||
while (try standalone_iterator.next()) |entry| {
|
||||
while (try dir_iterator.next()) |entry| {
|
||||
switch (entry.kind) {
|
||||
.directory => try standalone_test_names.append(allocator, try allocator.dupe(u8, entry.name)),
|
||||
.directory => try dir_entries.append(allocator, try allocator.dupe(u8, entry.name)),
|
||||
else => return error.junk_in_test_directory,
|
||||
}
|
||||
}
|
||||
|
||||
standalone_test_dir.close();
|
||||
dir.close();
|
||||
|
||||
const total_compilation_count = standalone_test_names.items.len;
|
||||
return dir_entries.items;
|
||||
}
|
||||
|
||||
fn runStandaloneTests(allocator: Allocator) !void {
|
||||
const standalone_test_dir_path = "test/standalone";
|
||||
const standalone_test_names = try collectDirectoryDirEntries(allocator, standalone_test_dir_path);
|
||||
|
||||
const total_compilation_count = standalone_test_names.len;
|
||||
var ran_compilation_count: usize = 0;
|
||||
var failed_compilation_count: usize = 0;
|
||||
|
||||
var ran_test_count: usize = 0;
|
||||
var failed_test_count: usize = 0;
|
||||
const total_test_count = standalone_test_names.items.len;
|
||||
const total_test_count = standalone_test_names.len;
|
||||
|
||||
for (standalone_test_names.items) |standalone_test_name| {
|
||||
std.debug.assert(!std.mem.eql(u8, standalone_test_name, "else_ifrld"));
|
||||
for (standalone_test_names) |standalone_test_name| {
|
||||
std.debug.print("{s}... ", .{standalone_test_name});
|
||||
const source_file_path = try std.mem.concat(allocator, u8, &.{standalone_test_dir_path, "/", standalone_test_name, "/main.nat"});
|
||||
const compile_run = try std.ChildProcess.run(.{
|
||||
@ -106,3 +109,100 @@ pub fn main() !void {
|
||||
return error.fail;
|
||||
}
|
||||
}
|
||||
|
||||
fn runBuildTests(allocator: Allocator) !void {
|
||||
std.debug.print("\n[BUILD TESTS]\n\n", .{});
|
||||
const test_dir_path = "test/build";
|
||||
const test_names = try collectDirectoryDirEntries(allocator, test_dir_path);
|
||||
const test_dir_realpath = try std.fs.cwd().realpathAlloc(allocator, test_dir_path);
|
||||
const compiler_realpath = try std.fs.cwd().realpathAlloc(allocator, "zig-out/bin/nat");
|
||||
try std.os.chdir(test_dir_realpath);
|
||||
|
||||
const total_compilation_count = test_names.len;
|
||||
var ran_compilation_count: usize = 0;
|
||||
var failed_compilation_count: usize = 0;
|
||||
|
||||
var ran_test_count: usize = 0;
|
||||
var failed_test_count: usize = 0;
|
||||
const total_test_count = test_names.len;
|
||||
|
||||
for (test_names) |test_name| {
|
||||
std.debug.print("{s}... ", .{test_name});
|
||||
try std.os.chdir(test_name);
|
||||
|
||||
const compile_run = try std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{compiler_realpath, "build"},
|
||||
});
|
||||
|
||||
ran_compilation_count += 1;
|
||||
|
||||
const compilation_result: TestError!bool = switch (compile_run.term) {
|
||||
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
|
||||
.Signal => error.signaled,
|
||||
.Stopped => error.stopped,
|
||||
.Unknown => error.unknown,
|
||||
};
|
||||
|
||||
const compilation_success = compilation_result catch b: {
|
||||
failed_compilation_count += 1;
|
||||
break :b false;
|
||||
};
|
||||
|
||||
std.debug.print("[COMPILATION {s}] ", .{if (compilation_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"});
|
||||
if (compile_run.stdout.len > 0) {
|
||||
std.debug.print("STDOUT:\n\n{s}\n\n", .{compile_run.stdout});
|
||||
}
|
||||
if (compile_run.stderr.len > 0) {
|
||||
std.debug.print("STDERR:\n\n{s}\n\n", .{compile_run.stderr});
|
||||
}
|
||||
|
||||
if (compilation_success) {
|
||||
const test_path = try std.mem.concat(allocator, u8, &.{"nat/", test_name});
|
||||
const test_run = try std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
// TODO: delete -main_source_file?
|
||||
.argv = &.{ test_path },
|
||||
});
|
||||
ran_test_count += 1;
|
||||
const test_result: TestError!bool = switch (test_run.term) {
|
||||
.Exited => |exit_code| if (exit_code == 0) true else error.abnormal_exit_code,
|
||||
.Signal => error.signaled,
|
||||
.Stopped => error.stopped,
|
||||
.Unknown => error.unknown,
|
||||
};
|
||||
|
||||
const test_success = test_result catch b: {
|
||||
failed_test_count += 1;
|
||||
break :b false;
|
||||
};
|
||||
std.debug.print("[TEST {s}]\n", .{if (test_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"});
|
||||
if (test_run.stdout.len > 0) {
|
||||
std.debug.print("STDOUT:\n\n{s}\n\n", .{test_run.stdout});
|
||||
}
|
||||
if (test_run.stderr.len > 0) {
|
||||
std.debug.print("STDERR:\n\n{s}\n\n", .{test_run.stderr});
|
||||
}
|
||||
} else {
|
||||
std.debug.print("\n", .{});
|
||||
}
|
||||
|
||||
try std.os.chdir(test_dir_realpath);
|
||||
}
|
||||
|
||||
std.debug.print("\nTOTAL COMPILATIONS: {}. FAILED: {}\n", .{total_compilation_count, failed_compilation_count});
|
||||
std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{total_test_count, ran_test_count, failed_test_count});
|
||||
|
||||
if (failed_compilation_count > 0 or failed_test_count > 0) {
|
||||
return error.fail;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
std.debug.print("\n",.{});
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
const allocator = arena.allocator();
|
||||
try runStandaloneTests(allocator);
|
||||
try runBuildTests(allocator);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ const Executable = struct{
|
||||
target: Target,
|
||||
main_source_path: [:0]const u8,
|
||||
link_libc: bool = false,
|
||||
name: []const u8,
|
||||
name: [:0]const u8,
|
||||
|
||||
const compile = fn(executable: Executable) bool {
|
||||
const argument_count = std.start.argument_count;
|
||||
@ -22,7 +22,13 @@ const Executable = struct{
|
||||
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, "-main_source_file", #cast(executable.main_source_path.ptr), "-link_libc", if (executable.link_libc) "true" else "false", "-name", #cast(executable.name.ptr) };
|
||||
var link_libc_arg: [&:0]const u8 = undefined;
|
||||
if (executable.link_libc) {
|
||||
link_libc_arg = "true";
|
||||
} else {
|
||||
link_libc_arg = "false";
|
||||
}
|
||||
const argv = [_:null] ?[&:0]const u8{ compiler_path, "exe", "-main_source_file", executable.main_source_path.ptr, "-link_libc", link_libc_arg, "-name", executable.name.ptr };
|
||||
std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values);
|
||||
return true;
|
||||
} else {
|
||||
|
@ -406,7 +406,7 @@ const waitpid = fn(pid: Process.Id, flags: u32) ?u32 {
|
||||
while (true) {
|
||||
const raw_syscall_result = linux.waitpid(pid, status = status.&, flags, resource_usage = 0);
|
||||
const signed_syscall_result: ssize = #cast(raw_syscall_result);
|
||||
if (raw_syscall_result != -4) {
|
||||
if (signed_syscall_result != -4) {
|
||||
if (linux.unwrap_syscall(syscall_result = raw_syscall_result)) |_| {
|
||||
return status;
|
||||
} else {
|
||||
|
@ -478,7 +478,8 @@ const pipe2 = fn (pipe_pointer: &[2]s32, flags: u32) usize {
|
||||
}
|
||||
|
||||
const waitpid = fn(pid: ProcessId, status: &u32, flags: u32, resource_usage: usize) usize {
|
||||
const result = #syscall(#cast(Syscall.wait4), pid, #cast(status), flags, resource_usage);
|
||||
const pid_unsigned: u32 = #cast(pid);
|
||||
const result = #syscall(#cast(Syscall.wait4), pid_unsigned, #cast(status), flags, resource_usage);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,10 @@ const start :: export = fn (argc_argv_address: usize) noreturn {
|
||||
argument_count = argument_count_ptr.@;
|
||||
argument_address_iterator += #size(usize);
|
||||
argument_values = #cast(argument_address_iterator);
|
||||
const argv = argument_values;
|
||||
argument_address_iterator += #size(usize) * (argument_count + 1);
|
||||
environment_values = #cast(argument_address_iterator);
|
||||
const env = environment_values;
|
||||
const result = #import("main").main();
|
||||
std.os.exit(exit_code = result);
|
||||
}
|
||||
|
7
test/standalone/null_terminated/main.nat
Normal file
7
test/standalone/null_terminated/main.nat
Normal file
@ -0,0 +1,7 @@
|
||||
const std = #import("std");
|
||||
const assert = std.assert;
|
||||
const main = fn() s32 {
|
||||
var foo = [2:null] ?[&:0]const u8 {"Hi", "Ho"};
|
||||
assert(foo[2] == null);
|
||||
return 0;
|
||||
}
|
6
test/standalone/size/main.nat
Normal file
6
test/standalone/size/main.nat
Normal file
@ -0,0 +1,6 @@
|
||||
const std = #import("std");
|
||||
const assert = std.assert;
|
||||
const main = fn () s32 {
|
||||
assert(#size(usize) == 8);
|
||||
return 0;
|
||||
}
|
7
test/standalone/zero_terminated/main.nat
Normal file
7
test/standalone/zero_terminated/main.nat
Normal file
@ -0,0 +1,7 @@
|
||||
const std = #import("std");
|
||||
const assert = std.assert;
|
||||
const main = fn () s32 {
|
||||
var a = [2:0]u8 {1, 2};
|
||||
assert(a[2] == 0);
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user