Merge pull request #32 from birth-software/build-compile
implement basic builder
This commit is contained in:
commit
3d271de830
@ -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,109 +1245,132 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
|
||||
|
||||
assert(module.main_package.dependencies.size == 2);
|
||||
|
||||
_ = try module.importPackage(compilation.base_allocator, module.main_package.dependencies.get("std").?);
|
||||
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);
|
||||
}
|
||||
|
||||
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", .{
|
||||
.bit_count = 64,
|
||||
.signedness = .unsigned,
|
||||
}),
|
||||
.ssize => @unionInit(Type, "integer", .{
|
||||
.bit_count = 64,
|
||||
.signedness = .signed,
|
||||
}),
|
||||
else => @unionInit(Type, enum_field.name, {}),
|
||||
});
|
||||
}
|
||||
|
||||
inline for (@typeInfo(HardwareUnsignedIntegerType).Enum.fields) |enum_field| {
|
||||
_ = try module.types.array.append(compilation.base_allocator, .{
|
||||
.integer = .{
|
||||
.signedness = .unsigned,
|
||||
.bit_count = switch (@field(HardwareUnsignedIntegerType, enum_field.name)) {
|
||||
.u8 => 8,
|
||||
.u16 => 16,
|
||||
.u32 => 32,
|
||||
.u64 => 64,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
inline for (@typeInfo(HardwareSignedIntegerType).Enum.fields) |enum_field| {
|
||||
_ = try module.types.array.append(compilation.base_allocator, .{
|
||||
.integer = .{
|
||||
.signedness = .signed,
|
||||
.bit_count = switch (@field(HardwareSignedIntegerType, enum_field.name)) {
|
||||
.s8 => 8,
|
||||
.s16 => 16,
|
||||
.s32 => 32,
|
||||
.s64 => 64,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (extra_common_type_data) |type_data| {
|
||||
_ = try module.types.array.append(compilation.base_allocator, type_data);
|
||||
}
|
||||
semantic_analyzer.pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{
|
||||
.pointer = .{
|
||||
.element_type = Type.any,
|
||||
.many = false,
|
||||
.@"const" = true,
|
||||
.termination = .none,
|
||||
},
|
||||
});
|
||||
semantic_analyzer.optional_pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{
|
||||
.optional = .{
|
||||
.element_type = semantic_analyzer.pointer_to_any_type,
|
||||
},
|
||||
});
|
||||
semantic_analyzer.optional_any = try module.types.array.append(compilation.base_allocator, .{
|
||||
.optional = .{
|
||||
.element_type = Type.any,
|
||||
},
|
||||
});
|
||||
|
||||
semantic_analyzer.unreachable_index = try module.values.array.append(compilation.base_allocator, .@"unreachable");
|
||||
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);
|
||||
|
||||
const value_index = try module.values.array.append(compilation.base_allocator, .{
|
||||
.unresolved = .{
|
||||
.node_index = .{ .value = 0 },
|
||||
},
|
||||
});
|
||||
|
||||
try semantic_analyzer.initialize(compilation, module, packages[0], value_index);
|
||||
|
||||
if (descriptor.transpile_to_c) {
|
||||
try c_transpiler.initialize(compilation, module, descriptor);
|
||||
if (descriptor.is_build) {
|
||||
var process = std.ChildProcess.init(&.{descriptor.executable_path}, compilation.base_allocator);
|
||||
switch (try process.spawnAndWait()) {
|
||||
.Exited => |exit_code| {
|
||||
if (exit_code != 0) {
|
||||
@panic("Exited with errors");
|
||||
}
|
||||
},
|
||||
else => @panic("Unexpected program state"),
|
||||
}
|
||||
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", .{
|
||||
.bit_count = 64,
|
||||
.signedness = .unsigned,
|
||||
}),
|
||||
.ssize => @unionInit(Type, "integer", .{
|
||||
.bit_count = 64,
|
||||
.signedness = .signed,
|
||||
}),
|
||||
else => @unionInit(Type, enum_field.name, {}),
|
||||
});
|
||||
}
|
||||
|
||||
inline for (@typeInfo(HardwareUnsignedIntegerType).Enum.fields) |enum_field| {
|
||||
_ = try module.types.array.append(compilation.base_allocator, .{
|
||||
.integer = .{
|
||||
.signedness = .unsigned,
|
||||
.bit_count = switch (@field(HardwareUnsignedIntegerType, enum_field.name)) {
|
||||
.u8 => 8,
|
||||
.u16 => 16,
|
||||
.u32 => 32,
|
||||
.u64 => 64,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
inline for (@typeInfo(HardwareSignedIntegerType).Enum.fields) |enum_field| {
|
||||
_ = try module.types.array.append(compilation.base_allocator, .{
|
||||
.integer = .{
|
||||
.signedness = .signed,
|
||||
.bit_count = switch (@field(HardwareSignedIntegerType, enum_field.name)) {
|
||||
.s8 => 8,
|
||||
.s16 => 16,
|
||||
.s32 => 32,
|
||||
.s64 => 64,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (extra_common_type_data) |type_data| {
|
||||
_ = try module.types.array.append(compilation.base_allocator, type_data);
|
||||
}
|
||||
semantic_analyzer.pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{
|
||||
.pointer = .{
|
||||
.element_type = Type.any,
|
||||
.many = false,
|
||||
.@"const" = true,
|
||||
.termination = .none,
|
||||
},
|
||||
});
|
||||
semantic_analyzer.optional_pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{
|
||||
.optional = .{
|
||||
.element_type = semantic_analyzer.pointer_to_any_type,
|
||||
},
|
||||
});
|
||||
semantic_analyzer.optional_any = try module.types.array.append(compilation.base_allocator, .{
|
||||
.optional = .{
|
||||
.element_type = Type.any,
|
||||
},
|
||||
});
|
||||
|
||||
semantic_analyzer.unreachable_index = try module.values.array.append(compilation.base_allocator, .@"unreachable");
|
||||
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 = .{
|
||||
.node_index = .{ .value = 0 },
|
||||
},
|
||||
});
|
||||
|
||||
try semantic_analyzer.initialize(compilation, module, packages[0], value_index);
|
||||
|
||||
if (descriptor.transpile_to_c) {
|
||||
try c_transpiler.initialize(compilation, module, descriptor);
|
||||
if (descriptor.is_build) {
|
||||
const argv = [_][]const u8{ descriptor.executable_path, "--compiler", compilation.executable_absolute_path };
|
||||
const process_result = try std.ChildProcess.run(.{
|
||||
.allocator = compilation.base_allocator,
|
||||
.argv = &argv,
|
||||
});
|
||||
|
||||
switch (process_result.term) {
|
||||
.Exited => |exit_code| {
|
||||
if (exit_code != 0) {
|
||||
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"),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable;
|
||||
// const ir = try intermediate_representation.initialize(compilation, module);
|
||||
//
|
||||
// switch (descriptor.target.cpu.arch) {
|
||||
// inline else => |arch| try emit.get(arch).initialize(compilation.base_allocator, ir, descriptor),
|
||||
// }
|
||||
}
|
||||
} else {
|
||||
unreachable;
|
||||
// const ir = try intermediate_representation.initialize(compilation, module);
|
||||
//
|
||||
// switch (descriptor.target.cpu.arch) {
|
||||
// inline else => |arch| try emit.get(arch).initialize(compilation.base_allocator, ir, descriptor),
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
@ -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),
|
||||
|
@ -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
3
ci.sh
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
284
lib/std/os.nat
284
lib/std/os.nat
@ -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 {
|
||||
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;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
else => #error("OS not supported"),
|
||||
}
|
||||
const event_file_descriptor = fn(initial_value: u32, flags: u32) ?s32 {
|
||||
switch (current) {
|
||||
.linux => {
|
||||
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 dup2 = fn(old_file_descriptor: system.FileDescriptor, new_file_descriptor: system.FileDescriptor) bool {
|
||||
switch (current) {
|
||||
.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 get = fn(descriptor: StdFileDescriptor) FileDescriptor{
|
||||
switch (current) {
|
||||
.linux, .macos => {
|
||||
return FileDescriptor{
|
||||
.handle = #cast(descriptor),
|
||||
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),
|
||||
};
|
||||
},
|
||||
else => #error("OS not supported"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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");
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1,4 +1,4 @@
|
||||
const main = fn () s32 {
|
||||
const main = fn() s32 {
|
||||
var counter: s32 = 0;
|
||||
const loop = 10;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
const std = #import("std");
|
||||
|
||||
const main = fn() s32 {
|
||||
if (std.os.duplicate_process()) |pid| {
|
||||
if (pid == 0) {
|
||||
|
@ -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");
|
||||
|
@ -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
11
test/loop_break/main.nat
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -1,4 +1,4 @@
|
||||
const main = fn () s32 {
|
||||
const main = fn() s32 {
|
||||
var false_boolean: bool = false;
|
||||
if (false_boolean) {
|
||||
return 1;
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user