Merge pull request #32 from birth-software/build-compile

implement basic builder
This commit is contained in:
David 2023-12-17 12:11:00 +01:00 committed by GitHub
commit 3d271de830
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 maybe_main_package_path: ?[]const u8 = null;
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 i: usize = 0; var i: usize = 0;
while (i < arguments.len) : (i += 1) { while (i < arguments.len) : (i += 1) {
@ -128,6 +129,16 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor {
} else { } else {
reportUnterminatedArgumentError(current_argument); reportUnterminatedArgumentError(current_argument);
} }
} else if (equal(u8, current_argument, "-parse")) {
if (i + 1 != arguments.len) {
i += 1;
const arg = arguments[i];
maybe_main_package_path = arg;
maybe_only_parse = true;
} else {
reportUnterminatedArgumentError(current_argument);
}
} else { } else {
maybe_main_package_path = current_argument; 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 cross_target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = target_triplet });
const target = cross_target.toTarget(); const target = cross_target.toTarget();
const transpile_to_c = should_transpile_to_c orelse true; const transpile_to_c = should_transpile_to_c orelse true;
const only_parse = maybe_only_parse orelse false;
var is_build = false; var is_build = false;
const main_package_path = if (maybe_main_package_path) |path| path else blk: { 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, .target = target,
.transpile_to_c = transpile_to_c, .transpile_to_c = transpile_to_c,
.is_build = is_build, .is_build = is_build,
.only_parse = only_parse,
}; };
} }
@ -211,24 +224,6 @@ pub const ContainerInitialization = struct {
pub const Index = List.Index; 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) { pub const Type = union(enum) {
any, any,
void, void,
@ -277,6 +272,24 @@ pub const Type = union(enum) {
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; 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 { pub const Integer = struct {
bit_count: u16, bit_count: u16,
signedness: Signedness, 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 // 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 //@"asm", this is processed separately as it need special parsing
cast,
@"error", @"error",
import, import,
min,
size,
syscall, syscall,
cast,
}; };
pub const FixedTypeKeyword = enum { pub const FixedTypeKeyword = enum {
@ -460,14 +500,36 @@ pub const Declaration = struct {
init_value: Value.Index, init_value: Value.Index,
name: u32, name: u32,
argument_index: ?u32, argument_index: ?u32,
type: Type.Index, // A union is needed here because of global lazy declarations
type: Declaration.Type,
scope: Scope.Index, scope: Scope.Index,
pub const Reference = struct { pub const Reference = struct {
value: Declaration.Index, 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 List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
@ -478,7 +540,7 @@ pub const Function = struct {
prototype: Type.Index, prototype: Type.Index,
pub const Prototype = struct { pub const Prototype = struct {
arguments: ?[]const Declaration.Index, arguments: ArrayList(Declaration.Index),
return_type: Type.Index, return_type: Type.Index,
attributes: Attributes = .{}, attributes: Attributes = .{},
@ -610,6 +672,7 @@ pub const BinaryOperation = struct {
compare_greater_or_equal, compare_greater_or_equal,
compare_less_than, compare_less_than,
compare_less_or_equal, compare_less_or_equal,
compare_not_equal,
}; };
}; };
@ -644,6 +707,19 @@ pub const Branch = struct {
pub const Index = List.Index; 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 { pub const FieldAccess = struct {
declaration_reference: Value.Index, declaration_reference: Value.Index,
field: ContainerField.Index, field: ContainerField.Index,
@ -728,12 +804,14 @@ pub const Assembly = struct {
pub const Instruction = enum { pub const Instruction = enum {
@"and", @"and",
call, call,
mov,
xor, xor,
}; };
pub const Register = enum { pub const Register = enum {
ebp, ebp,
rsp, rsp,
rdi,
}; };
}; };
}; };
@ -745,12 +823,14 @@ pub const StringLiteral = struct {
pub const Value = union(enum) { pub const Value = union(enum) {
void, void,
bool: bool, @"break",
undefined, undefined,
@"unreachable", @"unreachable",
bool: bool,
pointer_null_literal, pointer_null_literal,
optional_null_literal, optional_null_literal,
unresolved: Unresolved, unresolved: Unresolved,
intrinsic: Intrinsic.Index,
declaration: Declaration.Index, declaration: Declaration.Index,
declaration_reference: Declaration.Reference, declaration_reference: Declaration.Reference,
loop: Loop.Index, loop: Loop.Index,
@ -760,20 +840,16 @@ pub const Value = union(enum) {
assign: Assignment.Index, assign: Assignment.Index,
type: Type.Index, type: Type.Index,
integer: Integer, integer: Integer,
syscall: Syscall.Index,
call: Call.Index, call: Call.Index,
argument_list: ArgumentList, argument_list: ArgumentList,
@"return": Return.Index, @"return": Return.Index,
argument: Declaration.Index, argument: Declaration.Index,
string_literal: StringLiteral, string_literal: StringLiteral,
enum_field: Enum.Field.Index, enum_field: Type.Enum.Field.Index,
extern_function: Function.Prototype.Index, extern_function: Function.Prototype.Index,
sign_extend: Cast.Index,
zero_extend: Cast.Index,
binary_operation: BinaryOperation.Index, binary_operation: BinaryOperation.Index,
unary_operation: UnaryOperation.Index, unary_operation: UnaryOperation.Index,
branch: Branch.Index, branch: Branch.Index,
cast: Cast.Index,
container_initialization: ContainerInitialization.Index, container_initialization: ContainerInitialization.Index,
array_initialization: ContainerInitialization.Index, array_initialization: ContainerInitialization.Index,
field_access: FieldAccess.Index, field_access: FieldAccess.Index,
@ -781,10 +857,11 @@ pub const Value = union(enum) {
indexed_access: IndexedAccess.Index, indexed_access: IndexedAccess.Index,
optional_check: OptionalCheck.Index, optional_check: OptionalCheck.Index,
optional_unwrap: OptionalUnwrap.Index, optional_unwrap: OptionalUnwrap.Index,
optional_cast: Cast.Index, // optional_cast: Cast.Index,
array_coerce_to_slice: Cast.Index, array_coerce_to_slice: Cast.Index,
slice: Slice.Index, slice: Slice.Index,
assembly_block: Assembly.Block.Index, assembly_block: Assembly.Block.Index,
switch_expression: Switch.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
@ -800,20 +877,26 @@ pub const Value = union(enum) {
}; };
pub fn isComptime(value: *Value, module: *Module) bool { pub fn isComptime(value: *Value, module: *Module) bool {
_ = module;
return switch (value.*) { return switch (value.*) {
.integer => |integer| integer.type.eq(Type.comptime_int), .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), .declaration_reference => false,
.bool, .void, .undefined, .function_definition, .type, .enum_field => true, .bool, .void, .function_definition, .type, .enum_field => true,
// TODO: // TODO:
.call, .call,
.syscall, // .syscall,
.binary_operation, .binary_operation,
.container_initialization, .container_initialization,
.cast, // .cast,
.optional_unwrap, .optional_unwrap,
.pointer_null_literal, .pointer_null_literal,
.indexed_access, .indexed_access,
.slice, .slice,
.array_initialization,
.undefined,
.intrinsic,
.field_access,
=> false, => false,
// TODO: // TODO:
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
@ -824,7 +907,7 @@ pub const Value = union(enum) {
const result = switch (value) { const result = switch (value) {
.call => |call_index| module.values.calls.get(call_index).type, .call => |call_index| module.values.calls.get(call_index).type,
.integer => |integer| integer.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, .string_literal => |string_literal| string_literal.type,
.type => Type.type, .type => Type.type,
.enum_field => |enum_field_index| module.types.enum_fields.get(enum_field_index).parent, .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, .function_declaration => |function_index| module.types.function_declarations.get(function_index).prototype,
.binary_operation => |binary_operation| module.values.binary_operations.get(binary_operation).type, .binary_operation => |binary_operation| module.values.binary_operations.get(binary_operation).type,
.bool => Type.boolean, .bool => Type.boolean,
.declaration => Type.void, // .declaration => Type.void,
.container_initialization, .container_initialization,
.array_initialization, .array_initialization,
=> |initialization| module.values.container_initializations.get(initialization).type, => |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, .unary_operation => |unary_operation_index| module.values.unary_operations.get(unary_operation_index).type,
.pointer_null_literal => semantic_analyzer.optional_pointer_to_any_type, .pointer_null_literal => semantic_analyzer.optional_pointer_to_any_type,
.optional_null_literal => semantic_analyzer.optional_any, .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, .field_access => |field_access_index| module.types.container_fields.get(module.values.field_accesses.get(field_access_index).field).type,
.cast, // .cast,
.optional_cast, // .optional_cast,
.array_coerce_to_slice, // .array_coerce_to_slice => |cast_index| module.values.casts.get(cast_index).type,
=> |cast_index| module.values.casts.get(cast_index).type,
.slice => |slice_index| module.values.slices.get(slice_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, .slice_access => |slice_access_index| module.values.slice_accesses.get(slice_access_index).type,
.optional_check => Type.boolean, .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); 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,
.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)), else => |t| @panic(@tagName(t)),
}; };
}, },
@ -880,12 +978,13 @@ pub const Module = struct {
blocks: BlockList(Block) = .{}, blocks: BlockList(Block) = .{},
loops: BlockList(Loop) = .{}, loops: BlockList(Loop) = .{},
assignments: BlockList(Assignment) = .{}, assignments: BlockList(Assignment) = .{},
syscalls: BlockList(Syscall) = .{}, intrinsics: BlockList(Intrinsic) = .{},
// syscalls: BlockList(Syscall) = .{},
calls: BlockList(Call) = .{}, calls: BlockList(Call) = .{},
argument_lists: BlockList(ArgumentList) = .{}, argument_lists: BlockList(ArgumentList) = .{},
returns: BlockList(Return) = .{}, returns: BlockList(Return) = .{},
container_initializations: BlockList(ContainerInitialization) = .{}, container_initializations: BlockList(ContainerInitialization) = .{},
casts: BlockList(Cast) = .{}, // casts: BlockList(Cast) = .{},
branches: BlockList(Branch) = .{}, branches: BlockList(Branch) = .{},
binary_operations: BlockList(BinaryOperation) = .{}, binary_operations: BlockList(BinaryOperation) = .{},
unary_operations: BlockList(UnaryOperation) = .{}, unary_operations: BlockList(UnaryOperation) = .{},
@ -896,13 +995,14 @@ pub const Module = struct {
optional_unwraps: BlockList(OptionalUnwrap) = .{}, optional_unwraps: BlockList(OptionalUnwrap) = .{},
assembly_blocks: BlockList(Assembly.Block) = .{}, assembly_blocks: BlockList(Assembly.Block) = .{},
assembly_instructions: BlockList(Assembly.Instruction) = .{}, assembly_instructions: BlockList(Assembly.Instruction) = .{},
switches: BlockList(Switch) = .{},
} = .{}, } = .{},
types: struct { types: struct {
array: BlockList(Type) = .{}, array: BlockList(Type) = .{},
enums: BlockList(Enum) = .{}, enums: BlockList(Type.Enum) = .{},
structs: BlockList(Struct) = .{}, structs: BlockList(Struct) = .{},
container_fields: BlockList(ContainerField) = .{}, container_fields: BlockList(ContainerField) = .{},
enum_fields: BlockList(Enum.Field) = .{}, enum_fields: BlockList(Type.Enum.Field) = .{},
function_definitions: BlockList(Function) = .{}, function_definitions: BlockList(Function) = .{},
function_declarations: BlockList(Function) = .{}, function_declarations: BlockList(Function) = .{},
function_prototypes: BlockList(Function.Prototype) = .{}, function_prototypes: BlockList(Function.Prototype) = .{},
@ -928,6 +1028,7 @@ pub const Module = struct {
target: std.Target, target: std.Target,
transpile_to_c: bool, transpile_to_c: bool,
is_build: bool, is_build: bool,
only_parse: bool,
}; };
const ImportFileResult = struct { const ImportFileResult = struct {
@ -1144,109 +1245,132 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
assert(module.main_package.dependencies.size == 2); 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| { for (module.map.imports.values()) |import| {
try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import); try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import);
} }
inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { if (!descriptor.only_parse) {
_ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) { inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| {
.usize => @unionInit(Type, "integer", .{ _ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) {
.bit_count = 64, .usize => @unionInit(Type, "integer", .{
.signedness = .unsigned, .bit_count = 64,
}), .signedness = .unsigned,
.ssize => @unionInit(Type, "integer", .{ }),
.bit_count = 64, .ssize => @unionInit(Type, "integer", .{
.signedness = .signed, .bit_count = 64,
}), .signedness = .signed,
else => @unionInit(Type, enum_field.name, {}), }),
}); else => @unionInit(Type, enum_field.name, {}),
} });
}
inline for (@typeInfo(HardwareUnsignedIntegerType).Enum.fields) |enum_field| {
_ = try module.types.array.append(compilation.base_allocator, .{ inline for (@typeInfo(HardwareUnsignedIntegerType).Enum.fields) |enum_field| {
.integer = .{ _ = try module.types.array.append(compilation.base_allocator, .{
.signedness = .unsigned, .integer = .{
.bit_count = switch (@field(HardwareUnsignedIntegerType, enum_field.name)) { .signedness = .unsigned,
.u8 => 8, .bit_count = switch (@field(HardwareUnsignedIntegerType, enum_field.name)) {
.u16 => 16, .u8 => 8,
.u32 => 32, .u16 => 16,
.u64 => 64, .u32 => 32,
}, .u64 => 64,
}, },
}); },
} });
}
inline for (@typeInfo(HardwareSignedIntegerType).Enum.fields) |enum_field| {
_ = try module.types.array.append(compilation.base_allocator, .{ inline for (@typeInfo(HardwareSignedIntegerType).Enum.fields) |enum_field| {
.integer = .{ _ = try module.types.array.append(compilation.base_allocator, .{
.signedness = .signed, .integer = .{
.bit_count = switch (@field(HardwareSignedIntegerType, enum_field.name)) { .signedness = .signed,
.s8 => 8, .bit_count = switch (@field(HardwareSignedIntegerType, enum_field.name)) {
.s16 => 16, .s8 => 8,
.s32 => 32, .s16 => 16,
.s64 => 64, .s32 => 32,
}, .s64 => 64,
}, },
}); },
} });
}
for (extra_common_type_data) |type_data| {
_ = try module.types.array.append(compilation.base_allocator, type_data); 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 = .{ semantic_analyzer.pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{
.element_type = Type.any, .pointer = .{
.many = false, .element_type = Type.any,
.@"const" = true, .many = false,
.termination = .none, .@"const" = true,
}, .termination = .none,
}); },
semantic_analyzer.optional_pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{ });
.optional = .{ semantic_analyzer.optional_pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{
.element_type = semantic_analyzer.pointer_to_any_type, .optional = .{
}, .element_type = semantic_analyzer.pointer_to_any_type,
}); },
semantic_analyzer.optional_any = try module.types.array.append(compilation.base_allocator, .{ });
.optional = .{ semantic_analyzer.optional_any = try module.types.array.append(compilation.base_allocator, .{
.element_type = Type.any, .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.unreachable_index = try module.values.array.append(compilation.base_allocator, .@"unreachable");
semantic_analyzer.optional_null_index = try module.values.array.append(compilation.base_allocator, .optional_null_literal); semantic_analyzer.pointer_null_index = try module.values.array.append(compilation.base_allocator, .pointer_null_literal);
semantic_analyzer.undefined_index = try module.values.array.append(compilation.base_allocator, .undefined); 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, .{ semantic_analyzer.boolean_false = try module.values.array.append(compilation.base_allocator, .{
.unresolved = .{ .bool = false,
.node_index = .{ .value = 0 }, });
}, semantic_analyzer.boolean_true = try module.values.array.append(compilation.base_allocator, .{
}); .bool = true,
});
try semantic_analyzer.initialize(compilation, module, packages[0], value_index);
const value_index = try module.values.array.append(compilation.base_allocator, .{
if (descriptor.transpile_to_c) { .unresolved = .{
try c_transpiler.initialize(compilation, module, descriptor); .node_index = .{ .value = 0 },
if (descriptor.is_build) { },
var process = std.ChildProcess.init(&.{descriptor.executable_path}, compilation.base_allocator); });
switch (try process.spawnAndWait()) {
.Exited => |exit_code| { try semantic_analyzer.initialize(compilation, module, packages[0], value_index);
if (exit_code != 0) {
@panic("Exited with errors"); if (descriptor.transpile_to_c) {
} try c_transpiler.initialize(compilation, module, descriptor);
}, if (descriptor.is_build) {
else => @panic("Unexpected program state"), 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 { fn parse(file: *File, allocator: Allocator, file_index: File.Index) !void {
assert(file.status == .lexed); assert(file.status == .lexed);
file.syntactic_analyzer_result = try syntactic_analyzer.analyze(allocator, file.lexical_analyzer_result.tokens.items, file.source_code, file_index); 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) { // if (!@import("builtin").is_test) {
// print("[SYNTACTIC ANALYSIS] {} ns\n", .{file.syntactic_analyzer_result.time}); // 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 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 { 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); 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 { return struct {
// TODO: make this not reallocate the whole block. Instead, use a pointer to the block as the ArrayList item // 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, len: usize = 0,
first_block: u32 = 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 max_allocation = list.blocks.items.len * item_count;
const result = switch (list.len < max_allocation) { const result = switch (list.len < max_allocation) {
true => blk: { true => blk: {
const block = &list.blocks.items[list.first_block]; const block = list.blocks.items[list.first_block];
if (block.allocateIndex()) |element_index| { if (block.allocateIndex()) |element_index| {
break :blk Index{ break :blk Index{
.element = element_index, .element = element_index,
@ -148,13 +148,11 @@ pub fn BlockList(comptime T: type) type {
}, },
false => blk: { false => blk: {
const block_index = list.blocks.items.len; const block_index = list.blocks.items.len;
const new_block = list.blocks.addOneAssumeCapacity(); const new_block = try allocator.create(Block);
new_block.* = .{}; new_block.* = .{};
list.blocks.appendAssumeCapacity(new_block);
const element_index = new_block.allocateIndex() catch unreachable; const element_index = new_block.allocateIndex() catch unreachable;
const ptr = &new_block.items[element_index];
_ = ptr;
list.first_block += @intFromBool(block_index != 0); list.first_block += @intFromBool(block_index != 0);
break :blk Index{ break :blk Index{
.element = element_index, .element = element_index,
.block = @intCast(block_index), .block = @intCast(block_index),

View File

@ -14,7 +14,8 @@ const File = Compilation.File;
const logln = Compilation.logln; const logln = Compilation.logln;
const fs = @import("../fs.zig"); 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, start: u32,
len: u24, len: u24,
id: Id, id: Id,
@ -85,6 +86,7 @@ pub const Token = packed struct(u64) {
fixed_keyword_cc = 0x98, fixed_keyword_cc = 0x98,
fixed_keyword_for = 0x99, fixed_keyword_for = 0x99,
fixed_keyword_undefined = 0x9a, fixed_keyword_undefined = 0x9a,
fixed_keyword_break = 0x9b,
}; };
pub const Index = u32; pub const Index = u32;
@ -119,6 +121,7 @@ pub const FixedKeyword = enum {
cc, cc,
@"for", @"for",
undefined, undefined,
@"break",
}; };
pub const Result = struct { 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 "\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 $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 "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 if [[ "$failed_compilation_count" != "0" ]]; then
printf $"\nFailed compilations:\n" printf $"\nFailed compilations:\n"
@ -74,6 +73,8 @@ if [[ "$failed_test_count" != "0" ]]; then
done done
fi fi
echo -e "\e[35m=========\e[0m"
if [[ "$failed_test_count" == "0" && "$failed_compilation_count" == "0" ]]; then if [[ "$failed_test_count" == "0" && "$failed_compilation_count" == "0" ]]; then
echo -e "\e[32mSUCCESS!\e[0m" echo -e "\e[32mSUCCESS!\e[0m"
true true

View File

@ -1,7 +1,47 @@
const std = #import("std"); const std = #import("std");
const assert = std.assert;
const Allocator = std.Allocator;
const Target = std.Target; const Target = std.Target;
const Executable = struct{ const Executable = struct{
target: Target, 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, .windows => windows,
}; };
const exit = fn(exit_code: s32) noreturn { const exit = fn(exit_code: s32) noreturn {
switch (current) { switch (current) {
.linux => _ = #syscall(231, exit_code), .linux => _ = #syscall(#cast(linux.Syscall.exit_group), exit_code),
.macos => macos.exit(exit_code), .macos => macos.exit(exit_code),
.windows => windows.ExitProcess(exit_code), .windows => windows.ExitProcess(exit_code),
} }
@ -18,7 +17,64 @@ const exit = fn(exit_code: s32) noreturn {
unreachable; 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){ const ProtectionFlags = struct(u32){
read: bool, read: bool,
@ -83,16 +139,11 @@ const max_path_byte_count = switch (current) {
else => #error("OS not supported"), else => #error("OS not supported"),
}; };
const current_executable_path = fn(allocator: &Allocator) ?[]u8 { const current_executable_path = fn(buffer: []u8) ?[]u8 {
switch (current) { switch (current) {
.linux => { .linux => {
var buffer: [max_path_byte_count]u8 = undefined; if (readlink(file_path = "/proc/self/exe", buffer)) |bytes| {
if (readlink(file_path = "/proc/self/exe", buffer = buffer.&)) |bytes| { return bytes;
if (allocator.duplicate_bytes(bytes)) |result| {
return result;
} else {
return null;
}
} else { } else {
return null; 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) { switch (current) {
.linux => { .linux => {
if (linux.unwrapSyscall(syscall_result = linux.fork())) |fork_result| { 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{ const event_file_descriptor = fn(initial_value: u32, flags: u32) ?s32 {
handle: system.FileDescriptor, switch (current) {
.linux => {
const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) ?usize { if (linux.unwrapSyscall(syscall_result = linux.event_file_descriptor(count = initial_value, flags))) |raw_result| {
switch (current) { const result: s32 = #cast(raw_result);
.linux => { return result;
const raw_result = #syscall(1, file_descriptor.handle, #cast(bytes.ptr), bytes.len); } else {
if (linux.unwrapSyscall(syscall_result = raw_result)) |byte_count| { return null;
return byte_count; }
} else { },
return null; else => #error("OS not supported"),
}
},
else => #error("OS not supported"),
}
} }
}; }
const StdFileDescriptor = enum { const dup2 = fn(old_file_descriptor: system.FileDescriptor, new_file_descriptor: system.FileDescriptor) bool {
stdin = 0, switch (current) {
stdout = 1, .linux => {
stderr = 2, 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{ const open = fn(path: [&:0]const u8, flags: u32, permissions: u32) ?FileDescriptor{
switch (current) { switch (current) {
.linux, .macos => { .linux => {
return FileDescriptor{ if (linux.unwrapSyscall(syscall_result = linux.open(path, flags, permissions))) |raw_result| {
.handle = #cast(descriptor), 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 linux = #import("os/linux.nat");
const macos = #import("os/macos.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; 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 unwrapSyscall = fn(syscall_result: usize) ?usize {
const signed_syscall_result: ssize = #cast(syscall_result); const signed_syscall_result: ssize = #cast(syscall_result);
if (signed_syscall_result >= 0) { if (signed_syscall_result >= 0) {
@ -443,3 +493,26 @@ const unwrapSyscall = fn(syscall_result: usize) ?usize {
return null; 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) { const _start = fn () noreturn export cc(.naked) {
#asm({ #asm({
xor ebp, ebp; xor ebp, ebp;
mov rdi, rsp;
and rsp, 0xfffffffffffffff0; and rsp, 0xfffffffffffffff0;
call {start}; 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(); const result = #import("main").main();
std.os.exit(exit_code = result); 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 dividend: s32 = 30;
const divisor: s32 = 6; const divisor: s32 = 6;
const div: s32 = dividend / divisor; const div: s32 = dividend / divisor;

View File

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

View File

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

View File

@ -1,11 +1,11 @@
const std = #import("std"); const std = #import("std");
const main = fn() s32{
const main = fn() s32 {
if (std.os.duplicate_process()) |pid| { if (std.os.duplicate_process()) |pid| {
if (pid == 0) { if (pid == 0) {
std.print(bytes = "Hello from child\n"); std.print(bytes = "Hello from child\n");
const argv = [_:null] ?[&:0]const u8{"/usr/bin/ls"}; 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 = std.start.environment_values);
std.os.execute(path = "/usr/bin/ls", argv = argv.&, env = env.&);
return 1; return 1;
} else { } else {
std.print(bytes = "Hello from parent\n"); 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 a: s32 = 5;
const b: s32 = 4; const b: s32 = 4;
return a * b - a * b; 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 std = #import("std");
const print = std.print; const print = std.print;
const main = fn () s32 { const main = fn() s32 {
if (std.os.current_executable_path(allocator = std.page_allocator.allocator.&)) |bytes| { var buffer: [std.os.max_path_byte_count + 1]u8 = undefined;
if (std.os.current_executable_path(buffer = buffer.&)) |bytes| {
print(bytes); print(bytes);
print(bytes = "\n"); print(bytes = "\n");
return 0; return 0;

View File

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

View File

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