implement libc-linked executable
This commit is contained in:
parent
5fff6c1870
commit
1ca6b09386
@ -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,
|
||||||
|
@ -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| {
|
||||||
|
@ -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 {
|
||||||
|
@ -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
1
lib/init/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
nat
|
21
lib/init/build.nat
Normal file
21
lib/init/build.nat
Normal 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
3
lib/init/src/main.nat
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const main = fn () s32 {
|
||||||
|
return 0;
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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
1
test/integral/first/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
nat
|
@ -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
1
test/integral/link_libc/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
nat
|
22
test/integral/link_libc/build.nat
Normal file
22
test/integral/link_libc/build.nat
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
3
test/integral/link_libc/src/main.nat
Normal file
3
test/integral/link_libc/src/main.nat
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const main = fn () s32 {
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user