implement basic builder

This commit is contained in:
David Gonzalez Martin 2023-12-08 23:17:45 +01:00
parent 57325e4530
commit 8a042c58e5
20 changed files with 2852 additions and 1307 deletions

View File

@ -54,6 +54,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor {
var maybe_main_package_path: ?[]const u8 = null;
var target_triplet: []const u8 = "x86_64-linux-gnu";
var should_transpile_to_c: ?bool = null;
var maybe_only_parse: ?bool = null;
var i: usize = 0;
while (i < arguments.len) : (i += 1) {
@ -128,6 +129,16 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor {
} 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 {
maybe_main_package_path = current_argument;
}
@ -136,6 +147,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor {
const cross_target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = target_triplet });
const target = cross_target.toTarget();
const transpile_to_c = should_transpile_to_c orelse true;
const only_parse = maybe_only_parse orelse false;
var is_build = false;
const main_package_path = if (maybe_main_package_path) |path| path else blk: {
@ -160,6 +172,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor {
.target = target,
.transpile_to_c = transpile_to_c,
.is_build = is_build,
.only_parse = only_parse,
};
}
@ -211,24 +224,6 @@ pub const ContainerInitialization = struct {
pub const Index = List.Index;
};
pub const Enum = struct {
scope: Scope.Index,
fields: ArrayList(Enum.Field.Index) = .{},
backing_type: Type.Index,
pub const Field = struct {
name: u32,
value: Value.Index,
parent: Type.Index,
pub const List = BlockList(@This());
pub const Index = Enum.Field.List.Index;
};
pub const List = BlockList(@This());
pub const Index = List.Index;
};
pub const Type = union(enum) {
any,
void,
@ -277,6 +272,24 @@ pub const Type = union(enum) {
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Enum = struct {
scope: Scope.Index,
fields: ArrayList(Enum.Field.Index) = .{},
backing_type: Type.Index,
pub const Field = struct {
name: u32,
value: Value.Index,
parent: Type.Index,
pub const List = BlockList(@This());
pub const Index = Enum.Field.List.Index;
};
pub const List = BlockList(@This());
pub const Index = Enum.List.Index;
};
pub const Integer = struct {
bit_count: u16,
signedness: Signedness,
@ -365,13 +378,40 @@ pub const Type = union(enum) {
});
};
pub const Intrinsic = struct {
kind: Kind,
type: Type.Index,
pub const Kind = union(enum) {
cast: Value.Index,
array_coerce_to_slice: Value.Index,
min: Binary,
optional_wrap: Value.Index,
optional_unwrap: Value.Index,
optional_check: Value.Index,
sign_extend: Value.Index,
syscall: ArrayList(Value.Index),
zero_extend: Value.Index,
pub const Binary = struct {
left: Value.Index,
right: Value.Index,
};
};
pub const List = BlockList(@This());
pub const Index = @This().List.Index;
};
// Each time an enum is added here, a corresponding insertion in the initialization must be made
pub const Intrinsic = enum {
pub const ValidIntrinsic = enum {
//@"asm", this is processed separately as it need special parsing
cast,
@"error",
import,
min,
size,
syscall,
cast,
};
pub const FixedTypeKeyword = enum {
@ -460,14 +500,36 @@ pub const Declaration = struct {
init_value: Value.Index,
name: u32,
argument_index: ?u32,
type: Type.Index,
// A union is needed here because of global lazy declarations
type: Declaration.Type,
scope: Scope.Index,
pub const Reference = struct {
value: Declaration.Index,
type: Type.Index,
pub fn getType(reference: Reference, module: *Module) Compilation.Type.Index {
return module.values.declarations.get(reference.value).getType();
}
};
pub const Type = union(enum) {
resolved: Compilation.Type.Index,
inferred: Compilation.Type.Index,
unresolved: Node.Index,
};
pub fn getType(declaration: *Declaration) Compilation.Type.Index {
const type_index: Compilation.Type.Index = switch (declaration.type) {
.resolved => |resolved| resolved,
.inferred => |inferred| inferred,
.unresolved => unreachable,
};
assert(!type_index.invalid);
return type_index;
}
pub const List = BlockList(@This());
pub const Index = List.Index;
};
@ -478,7 +540,7 @@ pub const Function = struct {
prototype: Type.Index,
pub const Prototype = struct {
arguments: ?[]const Declaration.Index,
arguments: ArrayList(Declaration.Index),
return_type: Type.Index,
attributes: Attributes = .{},
@ -610,6 +672,7 @@ pub const BinaryOperation = struct {
compare_greater_or_equal,
compare_less_than,
compare_less_or_equal,
compare_not_equal,
};
};
@ -644,6 +707,19 @@ pub const Branch = struct {
pub const Index = List.Index;
};
pub const Switch = struct {
value: Value.Index,
groups: ArrayList(Group),
pub const Group = struct {
conditions: ArrayList(Value.Index),
expression: Value.Index,
};
pub const List = BlockList(@This());
pub const Index = List.Index;
};
pub const FieldAccess = struct {
declaration_reference: Value.Index,
field: ContainerField.Index,
@ -728,12 +804,14 @@ pub const Assembly = struct {
pub const Instruction = enum {
@"and",
call,
mov,
xor,
};
pub const Register = enum {
ebp,
rsp,
rdi,
};
};
};
@ -745,12 +823,14 @@ pub const StringLiteral = struct {
pub const Value = union(enum) {
void,
bool: bool,
@"break",
undefined,
@"unreachable",
bool: bool,
pointer_null_literal,
optional_null_literal,
unresolved: Unresolved,
intrinsic: Intrinsic.Index,
declaration: Declaration.Index,
declaration_reference: Declaration.Reference,
loop: Loop.Index,
@ -760,20 +840,16 @@ pub const Value = union(enum) {
assign: Assignment.Index,
type: Type.Index,
integer: Integer,
syscall: Syscall.Index,
call: Call.Index,
argument_list: ArgumentList,
@"return": Return.Index,
argument: Declaration.Index,
string_literal: StringLiteral,
enum_field: Enum.Field.Index,
enum_field: Type.Enum.Field.Index,
extern_function: Function.Prototype.Index,
sign_extend: Cast.Index,
zero_extend: Cast.Index,
binary_operation: BinaryOperation.Index,
unary_operation: UnaryOperation.Index,
branch: Branch.Index,
cast: Cast.Index,
container_initialization: ContainerInitialization.Index,
array_initialization: ContainerInitialization.Index,
field_access: FieldAccess.Index,
@ -781,10 +857,11 @@ pub const Value = union(enum) {
indexed_access: IndexedAccess.Index,
optional_check: OptionalCheck.Index,
optional_unwrap: OptionalUnwrap.Index,
optional_cast: Cast.Index,
// optional_cast: Cast.Index,
array_coerce_to_slice: Cast.Index,
slice: Slice.Index,
assembly_block: Assembly.Block.Index,
switch_expression: Switch.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
@ -800,20 +877,26 @@ pub const Value = union(enum) {
};
pub fn isComptime(value: *Value, module: *Module) bool {
_ = module;
return switch (value.*) {
.integer => |integer| integer.type.eq(Type.comptime_int),
.declaration_reference => |declaration_reference| module.values.declarations.get(declaration_reference.value).mutability == .@"const" and isComptime(module.values.array.get(module.values.declarations.get(declaration_reference.value).init_value), module),
.bool, .void, .undefined, .function_definition, .type, .enum_field => true,
.declaration_reference => false,
.bool, .void, .function_definition, .type, .enum_field => true,
// TODO:
.call,
.syscall,
// .syscall,
.binary_operation,
.container_initialization,
.cast,
// .cast,
.optional_unwrap,
.pointer_null_literal,
.indexed_access,
.slice,
.array_initialization,
.undefined,
.intrinsic,
.field_access,
=> false,
// TODO:
else => |t| @panic(@tagName(t)),
@ -824,7 +907,7 @@ pub const Value = union(enum) {
const result = switch (value) {
.call => |call_index| module.values.calls.get(call_index).type,
.integer => |integer| integer.type,
.declaration_reference => |declaration_reference| declaration_reference.type,
.declaration_reference => |declaration_reference| declaration_reference.getType(module),
.string_literal => |string_literal| string_literal.type,
.type => Type.type,
.enum_field => |enum_field_index| module.types.enum_fields.get(enum_field_index).parent,
@ -832,19 +915,18 @@ pub const Value = union(enum) {
.function_declaration => |function_index| module.types.function_declarations.get(function_index).prototype,
.binary_operation => |binary_operation| module.values.binary_operations.get(binary_operation).type,
.bool => Type.boolean,
.declaration => Type.void,
// .declaration => Type.void,
.container_initialization,
.array_initialization,
=> |initialization| module.values.container_initializations.get(initialization).type,
.syscall => Type.usize,
.intrinsic => |intrinsic_index| module.values.intrinsics.get(intrinsic_index).type,
.unary_operation => |unary_operation_index| module.values.unary_operations.get(unary_operation_index).type,
.pointer_null_literal => semantic_analyzer.optional_pointer_to_any_type,
.optional_null_literal => semantic_analyzer.optional_any,
.field_access => |field_access_index| module.types.container_fields.get(module.values.field_accesses.get(field_access_index).field).type,
.cast,
.optional_cast,
.array_coerce_to_slice,
=> |cast_index| module.values.casts.get(cast_index).type,
// .cast,
// .optional_cast,
// .array_coerce_to_slice => |cast_index| module.values.casts.get(cast_index).type,
.slice => |slice_index| module.values.slices.get(slice_index).type,
.slice_access => |slice_access_index| module.values.slice_accesses.get(slice_access_index).type,
.optional_check => Type.boolean,
@ -854,6 +936,22 @@ pub const Value = union(enum) {
const indexed_expression_type = module.types.array.get(indexed_expression_type_index);
break :blk switch (indexed_expression_type.*) {
.slice => |slice| slice.element_type,
.array => |array| array.element_type,
.pointer => |pointer| switch (pointer.many) {
true => pointer.element_type,
false => unreachable,
},
else => |t| @panic(@tagName(t)),
};
},
.undefined => Type.any,
.optional_unwrap => |optional_unwrap_index| blk: {
const optional_unwrap = module.values.optional_unwraps.get(optional_unwrap_index);
const optional_value = module.values.array.get(optional_unwrap.value);
const expected_optional_type_index = optional_value.getType(module);
const expected_optional_type = module.types.array.get(expected_optional_type_index);
break :blk switch (expected_optional_type.*) {
.optional => |optional| optional.element_type,
else => |t| @panic(@tagName(t)),
};
},
@ -880,12 +978,13 @@ pub const Module = struct {
blocks: BlockList(Block) = .{},
loops: BlockList(Loop) = .{},
assignments: BlockList(Assignment) = .{},
syscalls: BlockList(Syscall) = .{},
intrinsics: BlockList(Intrinsic) = .{},
// syscalls: BlockList(Syscall) = .{},
calls: BlockList(Call) = .{},
argument_lists: BlockList(ArgumentList) = .{},
returns: BlockList(Return) = .{},
container_initializations: BlockList(ContainerInitialization) = .{},
casts: BlockList(Cast) = .{},
// casts: BlockList(Cast) = .{},
branches: BlockList(Branch) = .{},
binary_operations: BlockList(BinaryOperation) = .{},
unary_operations: BlockList(UnaryOperation) = .{},
@ -896,13 +995,14 @@ pub const Module = struct {
optional_unwraps: BlockList(OptionalUnwrap) = .{},
assembly_blocks: BlockList(Assembly.Block) = .{},
assembly_instructions: BlockList(Assembly.Instruction) = .{},
switches: BlockList(Switch) = .{},
} = .{},
types: struct {
array: BlockList(Type) = .{},
enums: BlockList(Enum) = .{},
enums: BlockList(Type.Enum) = .{},
structs: BlockList(Struct) = .{},
container_fields: BlockList(ContainerField) = .{},
enum_fields: BlockList(Enum.Field) = .{},
enum_fields: BlockList(Type.Enum.Field) = .{},
function_definitions: BlockList(Function) = .{},
function_declarations: BlockList(Function) = .{},
function_prototypes: BlockList(Function.Prototype) = .{},
@ -928,6 +1028,7 @@ pub const Module = struct {
target: std.Target,
transpile_to_c: bool,
is_build: bool,
only_parse: bool,
};
const ImportFileResult = struct {
@ -1144,12 +1245,17 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
assert(module.main_package.dependencies.size == 2);
if (!descriptor.only_parse) {
_ = try module.importPackage(compilation.base_allocator, module.main_package.dependencies.get("std").?);
} else {
_ = try module.importPackage(compilation.base_allocator, module.main_package);
}
for (module.map.imports.values()) |import| {
try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import);
}
if (!descriptor.only_parse) {
inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| {
_ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) {
.usize => @unionInit(Type, "integer", .{
@ -1218,6 +1324,12 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
semantic_analyzer.pointer_null_index = try module.values.array.append(compilation.base_allocator, .pointer_null_literal);
semantic_analyzer.optional_null_index = try module.values.array.append(compilation.base_allocator, .optional_null_literal);
semantic_analyzer.undefined_index = try module.values.array.append(compilation.base_allocator, .undefined);
semantic_analyzer.boolean_false = try module.values.array.append(compilation.base_allocator, .{
.bool = false,
});
semantic_analyzer.boolean_true = try module.values.array.append(compilation.base_allocator, .{
.bool = true,
});
const value_index = try module.values.array.append(compilation.base_allocator, .{
.unresolved = .{
@ -1230,11 +1342,22 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
if (descriptor.transpile_to_c) {
try c_transpiler.initialize(compilation, module, descriptor);
if (descriptor.is_build) {
var process = std.ChildProcess.init(&.{descriptor.executable_path}, compilation.base_allocator);
switch (try process.spawnAndWait()) {
const argv = [_][]const u8{ descriptor.executable_path, "--compiler", compilation.executable_absolute_path };
const process_result = try std.ChildProcess.run(.{
.allocator = compilation.base_allocator,
.argv = &argv,
});
switch (process_result.term) {
.Exited => |exit_code| {
if (exit_code != 0) {
@panic("Exited with errors");
for (argv) |arg| {
std.debug.print("{s} ", .{arg});
}
std.debug.print("exited with failure: {}\n", .{exit_code});
std.debug.print("STDOUT:\n```\n{s}\n```\n", .{process_result.stdout});
std.debug.print("STDERR:\n```\n{s}\n```\n", .{process_result.stderr});
@panic("Internal error");
}
},
else => @panic("Unexpected program state"),
@ -1248,6 +1371,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
// inline else => |arch| try emit.get(arch).initialize(compilation.base_allocator, ir, descriptor),
// }
}
}
}
pub const ContainerType = enum {
@ -1317,6 +1441,10 @@ pub const File = struct {
fn parse(file: *File, allocator: Allocator, file_index: File.Index) !void {
assert(file.status == .lexed);
file.syntactic_analyzer_result = try syntactic_analyzer.analyze(allocator, file.lexical_analyzer_result.tokens.items, file.source_code, file_index);
// if (file_index.uniqueInteger() == 5) {
// assert(file.syntactic_analyzer_result.nodes.items[1356].id != .node_list);
// }
// if (!@import("builtin").is_test) {
// print("[SYNTACTIC ANALYSIS] {} ns\n", .{file.syntactic_analyzer_result.time});
// }
@ -1353,7 +1481,7 @@ fn getLoggerScopeType(comptime logger_scope: LoggerScope) type {
var logger_bitset = std.EnumSet(LoggerScope).initEmpty();
var writer = std.io.getStdErr().writer();
var writer = std.io.getStdOut().writer();
fn shouldLog(comptime logger_scope: LoggerScope, logger: getLoggerScopeType(logger_scope).Logger) bool {
return logger_bitset.contains(logger_scope) and getLoggerScopeType(logger_scope).Logger.bitset.contains(logger);

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,7 @@ pub fn BlockList(comptime T: type) type {
return struct {
// TODO: make this not reallocate the whole block. Instead, use a pointer to the block as the ArrayList item
blocks: ArrayList(Block) = .{},
blocks: ArrayList(*Block) = .{},
len: usize = 0,
first_block: u32 = 0,
@ -136,7 +136,7 @@ pub fn BlockList(comptime T: type) type {
const max_allocation = list.blocks.items.len * item_count;
const result = switch (list.len < max_allocation) {
true => blk: {
const block = &list.blocks.items[list.first_block];
const block = list.blocks.items[list.first_block];
if (block.allocateIndex()) |element_index| {
break :blk Index{
.element = element_index,
@ -148,13 +148,11 @@ pub fn BlockList(comptime T: type) type {
},
false => blk: {
const block_index = list.blocks.items.len;
const new_block = list.blocks.addOneAssumeCapacity();
const new_block = try allocator.create(Block);
new_block.* = .{};
list.blocks.appendAssumeCapacity(new_block);
const element_index = new_block.allocateIndex() catch unreachable;
const ptr = &new_block.items[element_index];
_ = ptr;
list.first_block += @intFromBool(block_index != 0);
break :blk Index{
.element = element_index,
.block = @intCast(block_index),

View File

@ -14,7 +14,8 @@ const File = Compilation.File;
const logln = Compilation.logln;
const fs = @import("../fs.zig");
pub const Token = packed struct(u64) {
// TODO: switch to packed struct when speed is important
pub const Token = struct {
start: u32,
len: u24,
id: Id,
@ -85,6 +86,7 @@ pub const Token = packed struct(u64) {
fixed_keyword_cc = 0x98,
fixed_keyword_for = 0x99,
fixed_keyword_undefined = 0x9a,
fixed_keyword_break = 0x9b,
};
pub const Index = u32;
@ -119,6 +121,7 @@ pub const FixedKeyword = enum {
cc,
@"for",
undefined,
@"break",
};
pub const Result = struct {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3
ci.sh
View File

@ -54,7 +54,6 @@ echo -e "\e[35m[SUMMARY]\e[0m"
echo -e "\e[35m=========\e[0m"
echo -e "Ran $total_test_count compilations (\e[32m$passed_compilation_count\e[0m succeeded, \e[31m$failed_compilation_count\e[0m failed)."
echo -e "Ran $ran_test_count tests (\e[32m $passed_test_count\e[0m passed, \e[31m$failed_test_count\e[0m failed)."
echo -e "\e[35m=========\e[0m"
if [[ "$failed_compilation_count" != "0" ]]; then
printf $"\nFailed compilations:\n"
@ -74,6 +73,8 @@ if [[ "$failed_test_count" != "0" ]]; then
done
fi
echo -e "\e[35m=========\e[0m"
if [[ "$failed_test_count" == "0" && "$failed_compilation_count" == "0" ]]; then
echo -e "\e[32mSUCCESS!\e[0m"
true

View File

@ -1,7 +1,47 @@
const std = #import("std");
const assert = std.assert;
const Allocator = std.Allocator;
const Target = std.Target;
const Executable = struct{
target: Target,
main_source_path: []const u8,
main_source_path: [:0]const u8,
const compile = fn(executable: Executable, compiler_path: [&:0]const u8) bool {
if (std.os.duplicate_process()) |pid| {
if (pid == 0) {
const argv = [_:null] ?[&:0]const u8{ compiler_path, #cast(executable.main_source_path.ptr), };
std.os.execute(path = compiler_path, argv = argv.&, env = std.start.environment_values);
return true;
} else {
if (std.os.waitpid(pid, flags = 0)) |raw_status| {
if (std.os.ifexited(status = raw_status)) {
const exit_status = std.os.exitstatus(status = raw_status);
if (exit_status == 0) {
return true;
} else {
std.print(bytes = "Bad exit code\n");
return false;
}
} else if (std.os.ifsignaled(status = raw_status)) {
std.print(bytes = "Signaled\n");
return false;
} else if (std.os.ifstopped(status = raw_status)) {
std.print(bytes = "Stopped\n");
return false;
} else {
std.print(bytes = "Unknown process termination\n");
return false;
}
} else {
std.print(bytes = "Wait failed\n");
return false;
}
}
} else {
std.print(bytes = "Unable to create child process\n");
return false;
}
}
};

View File

@ -7,10 +7,9 @@ const system = switch (current) {
.windows => windows,
};
const exit = fn(exit_code: s32) noreturn {
switch (current) {
.linux => _ = #syscall(231, exit_code),
.linux => _ = #syscall(#cast(linux.Syscall.exit_group), exit_code),
.macos => macos.exit(exit_code),
.windows => windows.ExitProcess(exit_code),
}
@ -18,7 +17,64 @@ const exit = fn(exit_code: s32) noreturn {
unreachable;
}
const ProcessId = system.ProcessId;
const max_file_operation_byte_count = switch (current) {
.linux => 0x7ffff000,
else => #error("OS not supported"),
};
const FileDescriptor = struct{
handle: system.FileDescriptor,
const read = fn(file_descriptor: FileDescriptor, bytes: []u8) ?usize {
if (bytes.len > 0) {
switch (current) {
.linux => {
const len: usize = #min(max_file_operation_byte_count, bytes.len);
if (linux.unwrapSyscall(syscall_result = #syscall(#cast(linux.Syscall.read), file_descriptor.handle, #cast(bytes.ptr), len))) |byte_count| {
return byte_count;
} else {
return null;
}
},
else => #error("OS not supported"),
}
} else {
return 0;
}
}
const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) ?usize {
switch (current) {
.linux => {
const len: usize = #min(max_file_operation_byte_count, bytes.len);
const raw_result = #syscall(#cast(linux.Syscall.write), file_descriptor.handle, #cast(bytes.ptr), len);
if (linux.unwrapSyscall(syscall_result = raw_result)) |byte_count| {
return byte_count;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
};
const StdFileDescriptor = enum {
stdin = 0,
stdout = 1,
stderr = 2,
const get = fn(descriptor: StdFileDescriptor) FileDescriptor{
switch (current) {
.linux, .macos => {
return FileDescriptor{
.handle = #cast(descriptor),
};
},
else => #error("OS not supported"),
}
}
};
const ProtectionFlags = struct(u32){
read: bool,
@ -83,16 +139,11 @@ const max_path_byte_count = switch (current) {
else => #error("OS not supported"),
};
const current_executable_path = fn(allocator: &Allocator) ?[]u8 {
const current_executable_path = fn(buffer: []u8) ?[]u8 {
switch (current) {
.linux => {
var buffer: [max_path_byte_count]u8 = undefined;
if (readlink(file_path = "/proc/self/exe", buffer = buffer.&)) |bytes| {
if (allocator.duplicate_bytes(bytes)) |result| {
return result;
} else {
return null;
}
if (readlink(file_path = "/proc/self/exe", buffer)) |bytes| {
return bytes;
} else {
return null;
}
@ -101,7 +152,11 @@ const current_executable_path = fn(allocator: &Allocator) ?[]u8 {
}
}
const duplicate_process = fn () ?ProcessId {
const Process = struct{
const Id = system.ProcessId;
};
const duplicate_process = fn () ?Process.Id {
switch (current) {
.linux => {
if (linux.unwrapSyscall(syscall_result = linux.fork())) |fork_result| {
@ -123,41 +178,186 @@ const execute = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env:
}
}
const FileDescriptor = struct{
handle: system.FileDescriptor,
const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) ?usize {
const event_file_descriptor = fn(initial_value: u32, flags: u32) ?s32 {
switch (current) {
.linux => {
const raw_result = #syscall(1, file_descriptor.handle, #cast(bytes.ptr), bytes.len);
if (linux.unwrapSyscall(syscall_result = raw_result)) |byte_count| {
return byte_count;
if (linux.unwrapSyscall(syscall_result = linux.event_file_descriptor(count = initial_value, flags))) |raw_result| {
const result: s32 = #cast(raw_result);
return result;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
};
}
const StdFileDescriptor = enum {
stdin = 0,
stdout = 1,
stderr = 2,
const get = fn(descriptor: StdFileDescriptor) FileDescriptor{
const dup2 = fn(old_file_descriptor: system.FileDescriptor, new_file_descriptor: system.FileDescriptor) bool {
switch (current) {
.linux, .macos => {
return FileDescriptor{
.handle = #cast(descriptor),
};
.linux => {
if (linux.unwrapSyscall(syscall_result = linux.dup2(old = old_file_descriptor, new = new_file_descriptor))) |_| {
return true;
} else {
return false;
}
},
else => #error("OS not supported"),
}
}
};
}
const open = fn(path: [&:0]const u8, flags: u32, permissions: u32) ?FileDescriptor{
switch (current) {
.linux => {
if (linux.unwrapSyscall(syscall_result = linux.open(path, flags, permissions))) |raw_result| {
const file_descriptor = FileDescriptor{
.handle = #cast(raw_result),
};
return file_descriptor;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const close = fn(file_descriptor: s32) bool {
switch (current) {
.linux => {
if (linux.unwrapSyscall(syscall_result = linux.close(file_descriptor))) |_| {
return true;
} else {
return false;
}
},
else => #error("OS not supported"),
}
}
const pipe2 = fn(flags: u32) ?[2]system.FileDescriptor{
switch (current) {
.linux => {
var pipe: [2]s32 = undefined;
if (linux.unwrapSyscall(syscall_result = linux.pipe2(pipe_pointer = pipe.&, flags))) |_| {
return pipe;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const set_up_child_process_io_posix = fn(io_channel_behavior: IoChannelBehavior, pipe_file_descriptor: s32, std_io_channel_descriptor: s32, dev_null_file_descriptor: s32) bool {
switch (io_channel_behavior) {
.pipe => return dup2(old_file_descriptor = pipe_file_descriptor, new_file_descriptor = std_io_channel_descriptor),
.close => {
if (!close(file_descriptor = std_io_channel_descriptor)) {
unreachable;
}
return true;
},
.inherit => return true,
.ignore => return dup2(old_file_descriptor = dev_null_file_descriptor, new_file_descriptor = std_io_channel_descriptor),
}
}
const PollFileDescriptor = system.PollFileDescriptor;
const poll = fn(file_descriptors: []PollFileDescriptor, timeout: s32) ?usize {
switch (current) {
.linux => {
if (linux.unwrapSyscall(syscall_result = linux.poll(file_descriptors = file_descriptors.ptr, file_descriptor_count = file_descriptors.len, timeout = timeout))) |result| {
return result;
} else {
return null;
}
},
else => #error("OS not supported"),
}
}
const write_u64_pipe = fn (file_handle: s32, value: u64) ?usize {
const file = FileDescriptor{
.handle = file_handle,
};
const value_ptr: [&]u8 = #cast(value.&);
const bytes = value_ptr[0..#size(u64)];
return file.write(bytes);
}
const read_u64_pipe = fn (file_handle: s32) ?u64{
const file = FileDescriptor{
.handle = file_handle,
};
var value: u64 = 0;
const value_ptr: [&]u8 = #cast(value.&);
const bytes = value_ptr[0..#size(u64)];
if (file.read(bytes)) |character_read_count| {
if (character_read_count == #size(u64)) {
return value;
} else {
return null;
}
} else {
return null;
}
}
const termsig = fn(status: u32) u32 {
return status & 0x7f;
}
const ifexited = fn(status: u32) bool {
return termsig(status) == 0;
}
const exitstatus = fn(status: u32) u8 {
const result: u8 = #cast((status & 0xff00) >> 8);
return result;
}
const stopsig = fn(status: u32) u32 {
return exitstatus(status);
}
const ifstopped = fn(status: u32) bool {
const result: u16 = #cast(((status & 0xffff) * 0x10001) >> 8);
return result > 0x7f00;
}
const ifsignaled = fn(status: u32) bool {
return (status & 0xffff) - 1 < 0xff;
}
const waitpid = fn(pid: Process.Id, flags: u32) ?u32 {
switch (current) {
.linux => {
var status: u32 = undefined;
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 (linux.unwrapSyscall(syscall_result = raw_syscall_result)) |_| {
return status;
} else {
return null;
}
}
}
},
else => #error("OS not supported"),
}
}
const IoChannelBehavior = enum{
pipe,
close,
inherit,
ignore,
};
const linux = #import("os/linux.nat");
const macos = #import("os/macos.nat");

View File

@ -435,6 +435,56 @@ const execve = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env:
return result;
}
const event_file_descriptor = fn(count: u32, flags: u32) usize {
const result = #syscall(#cast(Syscall.eventfd2), #cast(count), #cast(flags));
return result;
}
const dup2 = fn(old: s32, new: s32) usize {
const result = #syscall(#cast(Syscall.dup2), #cast(old), #cast(new));
return result;
}
const open = fn(path: [&:0]const u8, flags: u32, permissions: u32) usize {
const result = #syscall(#cast(Syscall.open), #cast(path), flags, permissions);
return result;
}
const openat = fn(directory_file_descriptor: s32, path: [&:0]const u8, flags: u32, permissions: u32) usize {
const result = #syscall(#cast(Syscall.openat), #cast(directory_file_descriptor), #cast(path), flags, permissions);
return result;
}
const read = fn(file_descriptor: s32, bytes_ptr: [&]u8, bytes_len: usize) usize {
const result = #syscall(#cast(Syscall.read), #cast(file_descriptor), #cast(bytes_ptr), bytes_len);
return result;
}
const write = fn(file_descriptor: s32, bytes_ptr: [&]const u8, bytes_len: usize) usize {
const result = #syscall(#cast(Syscall.write), #cast(file_descriptor), #cast(bytes_ptr), bytes_len);
return result;
}
const close = fn(file_descriptor: s32) usize {
const result = #syscall(#cast(Syscall.close), #cast(file_descriptor));
return result;
}
const pipe2 = fn (pipe_pointer: &[2]s32, flags: u32) usize {
const result = #syscall(#cast(Syscall.pipe2), #cast(pipe_pointer), flags);
return result;
}
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);
return result;
}
const poll = fn(file_descriptors: [&]PollFileDescriptor, file_descriptor_count: usize, timeout: s32) usize {
const result = #syscall(#cast(Syscall.poll), #cast(file_descriptors), file_descriptor_count, #cast(timeout));
return result;
}
const unwrapSyscall = fn(syscall_result: usize) ?usize {
const signed_syscall_result: ssize = #cast(syscall_result);
if (signed_syscall_result >= 0) {
@ -443,3 +493,26 @@ const unwrapSyscall = fn(syscall_result: usize) ?usize {
return null;
}
}
const EventFileDescriptorFlags = enum(u32) {
semaphore = 1,
cloexec = 0o2000000,
nonblock = 0o4000,
};
const PollFileDescriptor = struct{
file_descriptor: FileDescriptor,
events: Poll,
revents: Poll,
const Poll = struct(u16) {
in: bool = false,
pri: bool = false,
out: bool = false,
err: bool = false,
hup: bool = false,
nval: bool = false,
rdnorm: bool = false,
rdband: bool = false,
};
};

View File

@ -6,12 +6,24 @@ comptime {
const _start = fn () noreturn export cc(.naked) {
#asm({
xor ebp, ebp;
mov rdi, rsp;
and rsp, 0xfffffffffffffff0;
call {start};
});
}
const start = fn() noreturn export {
var argument_count: usize = 0;
var argument_values: [&]const [&:0]const u8 = undefined;
var environment_values: [&:null]const ?[&:null]const u8 = undefined;
const start = fn(argc_argv_address: usize) noreturn export {
var argument_address_iterator = argc_argv_address;
const argument_count_ptr: &usize = #cast(argument_address_iterator);
argument_count = argument_count_ptr.@;
argument_address_iterator += #size(usize);
argument_values = #cast(argument_address_iterator);
argument_address_iterator += #size(usize) * (argument_count + 1);
environment_values = #cast(argument_address_iterator);
const result = #import("main").main();
std.os.exit(exit_code = result);
}

View File

@ -1,4 +1,4 @@
const main = fn () s32 {
const main = fn() s32 {
const dividend: s32 = 30;
const divisor: s32 = 6;
const div: s32 = dividend / divisor;

View File

@ -1,4 +1,4 @@
const main = fn () s32 {
const main = fn() s32 {
var counter: s32 = 0;
const loop = 10;

View File

@ -1,4 +1,5 @@
const std = #import("std");
const main = fn() s32 {
if (std.os.duplicate_process()) |pid| {
if (pid == 0) {

View File

@ -1,11 +1,11 @@
const std = #import("std");
const main = fn() s32{
const main = fn() s32 {
if (std.os.duplicate_process()) |pid| {
if (pid == 0) {
std.print(bytes = "Hello from child\n");
const argv = [_:null] ?[&:0]const u8{"/usr/bin/ls"};
const env = [_:null] ?[&:null]const u8 {};
std.os.execute(path = "/usr/bin/ls", argv = argv.&, env = env.&);
std.os.execute(path = "/usr/bin/ls", argv = argv.&, env = std.start.environment_values);
return 1;
} else {
std.print(bytes = "Hello from parent\n");

View File

@ -1,4 +1,4 @@
const main = fn () s32 {
const main = fn() s32 {
const a: s32 = 5;
const b: s32 = 4;
return a * b - a * b;

11
test/loop_break/main.nat Normal file
View File

@ -0,0 +1,11 @@
const main = fn() s32 {
var i: s32 = 0;
while (i < 10) {
i += 1;
if (i == 5) {
break;
}
}
return i - 5;
}

View File

@ -1,8 +1,9 @@
const std = #import("std");
const print = std.print;
const main = fn () s32 {
if (std.os.current_executable_path(allocator = std.page_allocator.allocator.&)) |bytes| {
const main = fn() s32 {
var buffer: [std.os.max_path_byte_count + 1]u8 = undefined;
if (std.os.current_executable_path(buffer = buffer.&)) |bytes| {
print(bytes);
print(bytes = "\n");
return 0;

View File

@ -1,4 +1,4 @@
const main = fn () s32 {
const main = fn() s32 {
var false_boolean: bool = false;
if (false_boolean) {
return 1;

View File

@ -2,6 +2,7 @@ const std = #import("std");
const main = fn() s32 {
const size = 0x1000;
if (std.page_allocator.allocate(size, alignment = 12)) |result| {
result[0] = 0;
std.print(bytes = "Allocation succeeded. Freeing...\n");