Merge pull request #96 from birth-software/test-and-errors

Implement basic tests and error unions
This commit is contained in:
David 2024-02-27 11:00:11 -06:00 committed by GitHub
commit f857dead54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 1668 additions and 513 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1180,7 +1180,13 @@ pub const LLVM = struct {
for (sema_function_prototype.argument_types) |argument_type_index| {
switch (unit.types.get(argument_type_index).*) {
// TODO: ABI
.integer, .pointer, .@"enum", .@"struct", .slice, .bool, => try parameter_types.append(context.allocator, try llvm.getType(unit, context, argument_type_index)),
.integer,
.pointer,
.@"enum",
.@"struct",
.slice,
.bool,
=> try parameter_types.append(context.allocator, try llvm.getType(unit, context, argument_type_index)),
else => |t| @panic(@tagName(t)),
}
// arg_types.appendAssumeCapacity(llvm_argument_type);
@ -1262,6 +1268,39 @@ pub const LLVM = struct {
const array_type = LLVM.Type.Array.get(element_type, array.count + @intFromBool(extra_element)) orelse return Type.Error.array;
break :blk array_type.toType();
},
.error_union => |error_union| {
const error_type = try llvm.getType(unit, context, error_union.@"error");
const payload_type = try llvm.getType(unit, context, error_union.type);
const payload_type_size = unit.types.get(error_union.type).getBitSize(unit);
switch (payload_type_size) {
0 => {
const integer_type = llvm.context.getIntegerType(31) orelse unreachable;
const boolean_type = try llvm.getType(unit, context, .bool);
const types = [2]*LLVM.Type{ integer_type.toType(), boolean_type };
const is_packed = false;
const struct_type = llvm.context.getStructType(&types, types.len, is_packed) orelse return Type.Error.@"struct";
return struct_type.toType();
},
else => unreachable,
}
_ = error_type;
_ = payload_type;
unreachable;
},
.error_set => |error_set_index| b: {
const error_set = unit.error_sets.get(error_set_index);
if (error_set.values.items.len > 0) {
unreachable;
} else {
const integer_type = llvm.context.getIntegerType(32) orelse unreachable;
break :b integer_type.toType();
}
},
.@"error" => b: {
const integer_type = llvm.context.getIntegerType(31) orelse unreachable;
break :b integer_type.toType();
},
else => |t| @panic(@tagName(t)),
};
@ -1327,7 +1366,13 @@ pub const LLVM = struct {
break :b name.items;
} else {
unreachable;
if (unit.type_declarations.get(sema_type_index)) |type_declaration| {
_ = type_declaration; // autofix
unreachable;
} else {
// TODO: fix
break :b "anon_struct";
}
}
},
// TODO: termination
@ -2031,7 +2076,7 @@ pub const LLVM = struct {
}
}
fn emitComptimeRightValue(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, ct: Compilation.V.Comptime, type_index: Compilation.Type.Index) !*LLVM.Value.Constant {
fn emitComptimeRightValue(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, ct: Compilation.V.Comptime, type_index: Compilation.Type.Index) anyerror!*LLVM.Value.Constant {
switch (ct) {
.constant_int => |integer| {
const integer_type = unit.types.get(type_index);
@ -2083,21 +2128,7 @@ pub const LLVM = struct {
const constant_int = llvm.context.getConstantInt(backing_integer_type.bit_count, value, signed) orelse unreachable;
return constant_int.toConstant();
},
.constant_struct => |constant_struct_index| {
const constant_struct = unit.constant_structs.get(constant_struct_index);
var field_values = try ArrayList(*LLVM.Value.Constant).initCapacity(context.allocator, constant_struct.fields.len);
const sema_struct_type = unit.structs.get(unit.types.get(constant_struct.type).@"struct");
for (constant_struct.fields, sema_struct_type.fields.items) |field_value, field_index| {
const field = unit.struct_fields.get(field_index);
const constant = try llvm.emitComptimeRightValue(unit, context, field_value, field.type);
field_values.appendAssumeCapacity(constant);
}
const llvm_type = try llvm.getType(unit, context, constant_struct.type);
const struct_type = llvm_type.toStruct() orelse unreachable;
const const_struct = struct_type.getConstant(field_values.items.ptr, field_values.items.len) orelse unreachable;
return const_struct;
},
.constant_struct => |constant_struct_index| return try llvm.getConstantStruct(unit, context, constant_struct_index),
.undefined => {
const undefined_type = try llvm.getType(unit, context, type_index);
const poison = undefined_type.getPoison() orelse unreachable;
@ -2130,6 +2161,13 @@ pub const LLVM = struct {
const constant_null_pointer = pointer_type.getNull();
return constant_null_pointer.toConstant();
},
.error_value => |error_field_index| {
const error_field = unit.error_fields.get(error_field_index);
const signed = false;
const bit_count = 31;
const constant_int = llvm.context.getConstantInt(bit_count, error_field.value, signed) orelse unreachable;
return constant_int.toConstant();
},
else => |t| @panic(@tagName(t)),
}
}
@ -2192,9 +2230,10 @@ pub const LLVM = struct {
const const_slice = unit.constant_slices.get(constant_slice_index);
const const_slice_type = try llvm.getType(unit, context, const_slice.type);
const slice_struct_type = const_slice_type.toStruct() orelse unreachable;
const ptr = llvm.global_variable_map.get(const_slice.ptr).?;
const ptr = llvm.global_variable_map.get(const_slice.array).?;
const signed = false;
const len = llvm.context.getConstantInt(@bitSizeOf(usize), const_slice.len, signed) orelse unreachable;
assert(const_slice.start == 0);
const len = llvm.context.getConstantInt(@bitSizeOf(usize), const_slice.end, signed) orelse unreachable;
const slice_fields = [2]*LLVM.Value.Constant{
ptr.toConstant(),
len.toConstant(),
@ -2223,6 +2262,7 @@ pub const LLVM = struct {
break :b constant_int.toConstant();
},
.constant_slice => |constant_slice_index| try llvm.getConstantSlice(unit, context, constant_slice_index),
.constant_struct => |constant_struct_index| try llvm.getConstantStruct(unit, context, constant_struct_index),
else => |t| @panic(@tagName(t)),
};
list.appendAssumeCapacity(value);
@ -2232,6 +2272,22 @@ pub const LLVM = struct {
return result;
}
fn getConstantStruct(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, constant_struct_index: Compilation.V.Comptime.ConstantStruct.Index) !*LLVM.Value.Constant {
const constant_struct = unit.constant_structs.get(constant_struct_index);
var field_values = try ArrayList(*LLVM.Value.Constant).initCapacity(context.allocator, constant_struct.fields.len);
const sema_struct_type = unit.structs.get(unit.types.get(constant_struct.type).@"struct");
for (constant_struct.fields, sema_struct_type.fields.items) |field_value, field_index| {
const field = unit.struct_fields.get(field_index);
const constant = try llvm.emitComptimeRightValue(unit, context, field_value, field.type);
field_values.appendAssumeCapacity(constant);
}
const llvm_type = try llvm.getType(unit, context, constant_struct.type);
const struct_type = llvm_type.toStruct() orelse unreachable;
const const_struct = struct_type.getConstant(field_values.items.ptr, field_values.items.len) orelse unreachable;
return const_struct;
}
fn callIntrinsic(llvm: *LLVM, intrinsic_name: []const u8, intrinsic_parameter_types: []const *LLVM.Type, intrinsic_arguments: []const *LLVM.Value) !*LLVM.Value {
const intrinsic_id = LLVM.lookupIntrinsic(intrinsic_name.ptr, intrinsic_name.len);
assert(intrinsic_id != .none);
@ -2341,7 +2397,7 @@ pub const LLVM = struct {
const function_name = unit.getIdentifier(declaration.declaration.name);
const subprogram = llvm.debug_info_builder.createFunction(debug_file.toScope(), function_name.ptr, function_name.len, function_name.ptr, function_name.len, debug_file, declaration.declaration.line + 1, subroutine_type, scope_line, subroutine_type_flags, subprogram_flags, subprogram_declaration) orelse unreachable;
function.setSubprogram(subprogram);
switch (declaration.initial_value) {
.function_declaration => {},
.function_definition => |function_definition_index| {
@ -2354,7 +2410,6 @@ pub const LLVM = struct {
}
}
}
};
fn getCallingConvention(calling_convention: Compilation.Function.CallingConvention) LLVM.Value.Constant.Function.CallingConvention {
@ -2508,6 +2563,8 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
const entry_block_node = try llvm.createBasicBlock(context, function_definition.basic_blocks.items[0], "fn_entry");
block_command_list.append(entry_block_node);
var phis = AutoArrayHashMap(Compilation.Instruction.Index, *LLVM.Value.Instruction.PhiNode){};
while (block_command_list.len != 0) {
const block_node = block_command_list.first orelse unreachable;
const basic_block_index = block_node.data;
@ -2625,14 +2682,8 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
switch (unit.types.get(stack_slot.type).*) {
.void, .noreturn, .type => unreachable,
.comptime_int => unreachable,
.bool => {},
.@"struct" => {},
.@"enum" => {},
.function => unreachable,
.integer => {},
.pointer => {},
.slice => {},
.array => {},
else => {},
}
const declaration_type = try llvm.getType(unit, context, stack_slot.type);
@ -2769,7 +2820,6 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
else => |t| @panic(@tagName(t)),
};
for (call.arguments, arguments) |argument_value, *argument| {
argument.* = try llvm.emitRightValue(unit, context, argument_value);
}
@ -2883,15 +2933,8 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
switch (unit.types.get(argument_type_index).*) {
.void, .noreturn, .type => unreachable,
.comptime_int => unreachable,
.bool => {},
// .bool => unreachable,
.@"struct" => {},
.@"enum" => {},
.function => unreachable,
.integer => {},
.pointer => {},
.slice => {},
.array => {},
else => {},
}
const argument_type = argument.toValue().getType();
const alloca_array_size: ?*LLVM.Value = null;
@ -3047,6 +3090,8 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
const not_taken_node = try llvm.createBasicBlock(context, branch.not_taken, "not_taken_block");
block_command_list.insertAfter(block_node, taken_node);
block_command_list.insertAfter(taken_node, not_taken_node);
// block_command_list.append(taken_node);
// block_command_list.append(taken_node);
// TODO: make this fast
const taken_block = llvm.llvm_block_map.get(taken_node.data).?;
@ -3064,11 +3109,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
const phi_name = "phi";
const phi_node = llvm.builder.createPhi(phi_type, reserved_value_count, phi_name, phi_name.len) orelse unreachable;
for (phi.values.items, phi.basic_blocks.items) |sema_value, sema_block| {
const value = llvm.llvm_value_map.get(sema_value) orelse try llvm.emitRightValue(unit, context, sema_value);
const value_basic_block = llvm.llvm_block_map.get(sema_block).?;
phi_node.addIncoming(value, value_basic_block);
}
try phis.putNoClobber(context.allocator, instruction_index, phi_node);
try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, phi_node.toValue());
},
@ -3106,6 +3147,16 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
_ = block_command_list.popFirst();
}
for (phis.keys(), phis.values()) |instruction_index, phi| {
const instruction = unit.instructions.get(instruction_index);
const sema_phi = instruction.phi;
for (sema_phi.values.items, sema_phi.basic_blocks.items) |sema_value, sema_block| {
const value_basic_block = llvm.llvm_block_map.get(sema_block).?;
const value = llvm.llvm_value_map.get(sema_value) orelse try llvm.emitRightValue(unit, context, sema_value);
phi.addIncoming(value, value_basic_block);
}
}
if (!builder.isCurrentBlockTerminated()) {
var message_len: usize = 0;
const function_str = llvm.function.toString(&message_len);
@ -3228,7 +3279,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
try arguments.append(context.allocator, "-lSystem");
},
.linux => {
try arguments.appendSlice(context.allocator, &.{"--entry", "_start"});
try arguments.appendSlice(context.allocator, &.{ "--entry", "_start" });
try arguments.append(context.allocator, "-m");
try arguments.append(context.allocator, switch (unit.descriptor.target.cpu.arch) {
.x86_64 => "elf_x86_64",
@ -3238,8 +3289,8 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
if (unit.descriptor.link_libc) {
try arguments.append(context.allocator, "/usr/lib/crt1.o");
try arguments.append(context.allocator, "/usr/lib/crti.o");
try arguments.appendSlice(context.allocator, &.{"-L", "/usr/lib"});
try arguments.appendSlice(context.allocator, &.{"-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"});
try arguments.appendSlice(context.allocator, &.{ "-L", "/usr/lib" });
try arguments.appendSlice(context.allocator, &.{ "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2" });
try arguments.append(context.allocator, "--as-needed");
try arguments.append(context.allocator, "-lm");
try arguments.append(context.allocator, "-lpthread");

View File

@ -100,7 +100,7 @@ pub fn getIndexForType(comptime T: type, comptime E: type) type {
const EnumField = std.builtin.Type.EnumField;
comptime var fields: []const EnumField = &.{};
// comptime var enum_value: comptime_int = 0;
fields = fields ++ @typeInfo(E).Enum.fields;
fields = fields ++ @typeInfo(E).Enum.fields;
// for (names) |name| {
// fields = fields ++ [1]EnumField{.{
@ -137,32 +137,31 @@ pub fn getIndexForType(comptime T: type, comptime E: type) type {
return @enumFromInt(value);
}
pub fn addInt(this: Index, value: IndexType) Index{
pub fn addInt(this: Index, value: IndexType) Index {
const this_int = @intFromEnum(this);
return @enumFromInt(this_int + value);
}
pub fn subInt(this: Index, value: IndexType) IndexType{
pub fn subInt(this: Index, value: IndexType) IndexType {
const this_int = @intFromEnum(this);
return this_int - value;
}
pub fn add(a: Index, b: Index) Index{
pub fn add(a: Index, b: Index) Index {
return @enumFromInt(@intFromEnum(a) + @intFromEnum(b));
}
pub fn sub(a: Index, b: Index) IndexType{
pub fn sub(a: Index, b: Index) IndexType {
return @intFromEnum(a) - @intFromEnum(b);
}
};
}
pub const ListType = enum{
pub const ListType = enum {
index,
pointer,
};
pub fn enumFromString(comptime E: type, string: []const u8) ?E {
return inline for (@typeInfo(E).Enum.fields) |enum_field| {
if (std.mem.eql(u8, string, enum_field.name)) {

View File

@ -15,7 +15,6 @@ const logln = Compilation.logln;
const Token = Compilation.Token;
const fs = @import("../fs.zig");
// Needed information
// Token: u8
// line: u32
@ -76,7 +75,7 @@ pub fn analyze(allocator: Allocator, text: []const u8, token_buffer: *Token.Buff
try token_buffer.tokens.ensureUnusedCapacity(allocator, text.len / 4);
logln(.lexer, .end, "START LEXER - TOKEN OFFSET: {} - LINE OFFSET: {}", .{Token.unwrap(lexer.offset), lexer.line_offset});
logln(.lexer, .end, "START LEXER - TOKEN OFFSET: {} - LINE OFFSET: {}", .{ Token.unwrap(lexer.offset), lexer.line_offset });
while (index < len) {
const start_index = index;
@ -229,7 +228,7 @@ pub fn analyze(allocator: Allocator, text: []const u8, token_buffer: *Token.Buff
index += 1;
break :blk .operator_compare_less_equal;
},
else =>break :blk .operator_compare_less,
else => break :blk .operator_compare_less,
}
},
'>' => blk: {
@ -249,7 +248,7 @@ pub fn analyze(allocator: Allocator, text: []const u8, token_buffer: *Token.Buff
index += 1;
break :blk .operator_compare_greater_equal;
},
else =>break :blk .operator_compare_greater,
else => break :blk .operator_compare_greater,
}
},
';' => blk: {
@ -281,7 +280,6 @@ pub fn analyze(allocator: Allocator, text: []const u8, token_buffer: *Token.Buff
},
else => break :blk .operator_bang,
}
},
'=' => blk: {
index += 1;
@ -421,10 +419,10 @@ pub fn analyze(allocator: Allocator, text: []const u8, token_buffer: *Token.Buff
});
const line_offset = token_buffer.line_offsets.items[line_index];
const column = start_index - line_offset;
logln(.lexer, .new_token, "T at line {}, column {}, byte offset {}, with length {} -line offset: {}- ({s})", .{line_index, column, start_index, token_length, line_offset, @tagName(token_id)});
logln(.lexer, .new_token, "T at line {}, column {}, byte offset {}, with length {} -line offset: {}- ({s})", .{ line_index, column, start_index, token_length, line_offset, @tagName(token_id) });
}
logln(.lexer, .end, "END LEXER - TOKEN OFFSET: {} - LINE OFFSET: {}", .{Token.unwrap(lexer.offset), lexer.line_offset});
logln(.lexer, .end, "END LEXER - TOKEN OFFSET: {} - LINE OFFSET: {}", .{ Token.unwrap(lexer.offset), lexer.line_offset });
lexer.count = Token.sub(token_buffer.getOffset(), lexer.offset);
lexer.line_count = token_buffer.getLineOffset() - lexer.line_offset;

View File

@ -65,7 +65,7 @@ pub const Node = struct {
token: Token.Index,
id: Id,
pub const List = BlockList(@This(), enum{});
pub const List = BlockList(@This(), enum {});
pub usingnamespace List.Index;
pub const Range = struct {
@ -187,6 +187,9 @@ pub const Node = struct {
test_declaration,
all_errors,
error_union,
catch_expression,
try_expression,
error_type,
};
};
@ -305,9 +308,11 @@ const Analyzer = struct {
const attribute_node = inline for (@typeInfo(Compilation.Debug.Declaration.Global.Attribute).Enum.fields) |enum_field| {
if (equal(u8, identifier_name, enum_field.name)) {
const attribute = @field(Compilation.Debug.Declaration.Global.Attribute, enum_field.name);
const attribute = @field(Compilation.Debug.Declaration.Global.Attribute, enum_field.name);
const attribute_node = switch (attribute) {
.@"export", .@"extern", => try analyzer.addNode(.{
.@"export",
.@"extern",
=> try analyzer.addNode(.{
.id = @field(Node.Id, "symbol_attribute_" ++ @tagName(attribute)),
.token = identifier,
.left = .null,
@ -391,9 +396,10 @@ const Analyzer = struct {
const attribute_node = inline for (@typeInfo(Compilation.Function.Attribute).Enum.fields) |enum_field| {
if (equal(u8, identifier_name, enum_field.name)) {
const attribute = @field(Compilation.Function.Attribute, enum_field.name);
const attribute = @field(Compilation.Function.Attribute, enum_field.name);
const attribute_node = switch (attribute) {
.naked, => try analyzer.addNode(.{
.naked,
=> try analyzer.addNode(.{
.id = @field(Node.Id, "function_attribute_" ++ @tagName(attribute)),
.token = identifier,
.left = .null,
@ -415,12 +421,12 @@ const Analyzer = struct {
const arguments = try analyzer.argumentList(.operator_left_parenthesis, .operator_right_parenthesis);
const return_type = try analyzer.typeExpression();
try attribute_and_return_type_node_list.append(analyzer.allocator, return_type);
const function_prototype = try analyzer.addNode(.{
.id = .function_prototype,
.token = token,
.left = arguments,
.right = try analyzer.nodeList( attribute_and_return_type_node_list),
.right = try analyzer.nodeList(attribute_and_return_type_node_list),
});
return function_prototype;
@ -473,16 +479,17 @@ const Analyzer = struct {
const first_statement_token = analyzer.peekToken();
logln(.parser, .block, "First statement token: {s}", .{@tagName(first_statement_token)});
const statement_index = switch (first_statement_token) {
.identifier => switch (analyzer.peekTokenAhead(1)) {
.operator_colon => {
unreachable;
},
else => try analyzer.assignExpressionStatement(),
},
.fixed_keyword_unreachable,
.fixed_keyword_return,
.discard,
=> try analyzer.assignExpressionStatement(),
else => try analyzer.assignExpressionStatement(),
// .identifier => switch (analyzer.peekTokenAhead(1)) {
// .operator_colon => {
// unreachable;
// },
// else => try analyzer.assignExpressionStatement(),
// },
// .fixed_keyword_unreachable,
// .fixed_keyword_return,
// .discard,
// => try analyzer.assignExpressionStatement(),
.fixed_keyword_while => try analyzer.whileExpression(),
.fixed_keyword_switch => try analyzer.switchExpression(),
@ -491,17 +498,12 @@ const Analyzer = struct {
.fixed_keyword_const,
.fixed_keyword_var,
=> try analyzer.symbolDeclaration(),
.fixed_keyword_break => b: {
const node_index = try analyzer.breakExpression();
_ = try analyzer.expectToken(.operator_semicolon);
break :b node_index;
},
.intrinsic => blk: {
const intrinsic = try analyzer.compilerIntrinsic();
_ = try analyzer.expectToken(.operator_semicolon);
break :blk intrinsic;
},
else => |t| @panic(@tagName(t)),
// .intrinsic => blk: {
// const intrinsic = try analyzer.compilerIntrinsic();
// _ = try analyzer.expectToken(.operator_semicolon);
// break :blk intrinsic;
// },
// else => |t| @panic(@tagName(t)),
};
const node = analyzer.nodes.get(statement_index);
@ -783,7 +785,7 @@ const Analyzer = struct {
const left = try analyzer.expression();
const expression_token = analyzer.token_i;
const expression_id: Node.Id = switch (analyzer.peekToken()) {
.operator_semicolon, .operator_comma => return left,
.operator_semicolon, .operator_comma, .operator_right_brace, .identifier => return left,
.operator_assign => .assign,
.operator_add_assign => .add_assign,
.operator_sub_assign => .sub_assign,
@ -951,6 +953,7 @@ const Analyzer = struct {
bit_or,
shift_left,
shift_right,
@"catch",
};
const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{
@ -970,6 +973,7 @@ const Analyzer = struct {
.bit_or = 40,
.shift_left = 50,
.shift_right = 50,
.@"catch" = 40,
});
const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{
@ -989,6 +993,7 @@ const Analyzer = struct {
.mod = .left,
.shift_left = .left,
.shift_right = .left,
.@"catch" = .left,
});
const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{
@ -1008,6 +1013,7 @@ const Analyzer = struct {
.mod = .mod,
.shift_left = .shift_left,
.shift_right = .shift_right,
.@"catch" = .catch_expression,
});
fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index {
@ -1062,6 +1068,7 @@ const Analyzer = struct {
.operator_xor => .bit_xor,
.operator_shift_left => .shift_left,
.operator_shift_right => .shift_right,
.fixed_keyword_catch => .@"catch",
else => |t| @panic(@tagName(t)),
};
@ -1114,6 +1121,7 @@ const Analyzer = struct {
},
.operator_bang => .boolean_not,
.operator_minus => .negation,
.fixed_keyword_try => .try_expression,
// .tilde => |t| @panic(@tagName(t)),
};
@ -1155,6 +1163,9 @@ const Analyzer = struct {
.discard,
.fixed_keyword_undefined,
.operator_left_bracket,
.fixed_keyword_const,
.fixed_keyword_var,
.fixed_keyword_error,
=> try analyzer.curlySuffixExpression(),
.fixed_keyword_fn => try analyzer.function(),
.fixed_keyword_return => try analyzer.addNode(.{
@ -1167,6 +1178,7 @@ const Analyzer = struct {
.left = try analyzer.expression(),
.right = Node.Index.null,
}),
.fixed_keyword_break => try analyzer.breakExpression(),
// todo:?
.operator_left_brace => try analyzer.block(),
.fixed_keyword_if => try analyzer.ifExpression(),
@ -1384,34 +1396,40 @@ const Analyzer = struct {
}
fn errorUnionExpression(analyzer: *Analyzer) !Node.Index {
if (analyzer.peekToken() == .operator_bang) {
const error_union_token = try analyzer.expectToken(.operator_bang);
if (analyzer.peekToken() == .operator_asterisk) {
analyzer.consumeToken();
// All errors
const all_errors_node = try analyzer.addNode(.{
.id = .all_errors,
.token = error_union_token,
.left = .null,
.right = .null,
});
const type_node = try analyzer.suffixExpression();
const initial = analyzer.token_i;
if (analyzer.peekToken() == .operator_asterisk and analyzer.peekTokenAhead(1) == .operator_bang) {
const asterisk = try analyzer.expectToken(.operator_asterisk);
analyzer.consumeToken();
const type_node = try analyzer.suffixExpression();
const error_union = try analyzer.addNode(.{
.id = .error_union,
.token = error_union_token,
.left = all_errors_node,
.right = type_node,
});
return error_union;
} else {
unreachable;
}
const all_errors_node = try analyzer.addNode(.{
.id = .all_errors,
.token = asterisk,
.left = .null,
.right = .null,
});
const error_union = try analyzer.addNode(.{
.id = .error_union,
.token = asterisk,
// All errors
.left = all_errors_node,
.right = type_node,
});
return error_union;
} else {
const suffix_expression = try analyzer.suffixExpression();
return switch (analyzer.peekToken()) {
.operator_bang => unreachable,
.operator_bang => try analyzer.addNode(.{
.id = .error_union,
.token = blk: {
analyzer.consumeToken();
break :blk initial;
},
.left = suffix_expression,
.right = try analyzer.typeExpression(),
}),
else => suffix_expression,
};
}
@ -1598,8 +1616,9 @@ const Analyzer = struct {
});
}
fn processContainerType(analyzer: *Analyzer, maybe_token_id: ?Token.Id) !Node.Index{
fn processContainerType(analyzer: *Analyzer, maybe_token_id: ?Token.Id) !Node.Index {
const token_i = if (maybe_token_id) |tid| try analyzer.expectToken(tid) else analyzer.token_i;
assert(Token.unwrap(analyzer.token_i) < analyzer.token_buffer.tokens.len);
const token_id = maybe_token_id orelse .fixed_keyword_struct;
const container_type: Compilation.ContainerType = switch (token_id) {
.fixed_keyword_struct => .@"struct",
@ -1608,8 +1627,8 @@ const Analyzer = struct {
};
const node_id: Node.Id = switch (token_id) {
.fixed_keyword_struct => .@"struct_type",
.fixed_keyword_enum => .@"enum_type",
.fixed_keyword_struct => .struct_type,
.fixed_keyword_enum => .enum_type,
else => unreachable,
};
@ -1710,7 +1729,7 @@ const Analyzer = struct {
const member_node = analyzer.nodes.get(member_node_index);
if (member_node.id == .identifier) {
const token_offset = analyzer.getTokenOffset(member_node.token);
std.debug.print("Node index #{} (list index {}):\n```\n{s}\n```\n", .{Node.unwrap(member_node_index), index, analyzer.source_file[token_offset..]});
std.debug.print("Node index #{} (list index {}):\n```\n{s}\n```\n", .{ Node.unwrap(member_node_index), index, analyzer.source_file[token_offset..] });
// std.debug.print("ID: {s}\n", .{analyzer.bytes(member_node.token)});
unreachable;
}
@ -1726,7 +1745,16 @@ const Analyzer = struct {
fn testDeclaration(analyzer: *Analyzer) !Node.Index {
const test_token = try analyzer.expectToken(.fixed_keyword_test);
const name_node: Node.Index = if (analyzer.peekToken() == .string_literal) try analyzer.identifierNode() else .null;
const name_node: Node.Index = if (analyzer.peekToken() == .string_literal) try analyzer.addNode(.{
.id = .string_literal,
.token = b: {
const index = analyzer.token_i;
analyzer.consumeToken();
break :b index;
},
.left = .null,
.right = .null,
}) else .null;
const test_block = try analyzer.block();
return try analyzer.addNode(.{
.token = test_token,
@ -1861,6 +1889,33 @@ const Analyzer = struct {
_ = try analyzer.expectToken(.operator_right_parenthesis);
break :blk expr;
},
.fixed_keyword_error => blk: {
analyzer.consumeToken();
if (analyzer.peekToken() == .operator_left_brace) {
analyzer.consumeToken();
var list = ArrayList(Node.Index){};
while (analyzer.peekToken() != .operator_right_brace) {
const identifier = try analyzer.identifierNode();
try list.append(analyzer.allocator, identifier);
const comma = try analyzer.expectToken(.operator_comma);
_ = comma; // autofix
}
analyzer.consumeToken();
break :blk try analyzer.addNode(.{
.id = .error_type,
.token = token_i,
.left = try analyzer.nodeList(list),
.right = .null,
});
} else {
const t = analyzer.peekToken();
_ = t; // autofix
unreachable;
}
},
else => |t| switch (t) {
.identifier => std.debug.panic("{s}: {s}", .{ @tagName(t), analyzer.bytes(token_i) }),
else => @panic(@tagName(t)),
@ -2031,7 +2086,6 @@ const Analyzer = struct {
}
};
// Here it is assumed that left brace is consumed
pub fn analyze(allocator: Allocator, lexer_result: lexer.Result, source_file: []const u8, token_buffer: *Token.Buffer, node_list: *Node.List, node_lists: *ArrayList(ArrayList(Node.Index))) !Result {
const start = std.time.Instant.now() catch unreachable;
@ -2065,5 +2119,3 @@ const Associativity = enum {
none,
left,
};

View File

@ -30,5 +30,5 @@ const panic = fn (reason: PanicReason) noreturn{
const TestFunction = struct{
name: []const u8,
function: &const fn () !*void,
function: &const fn () *!void,
};

View File

@ -31,8 +31,8 @@ const start :: export = fn (argc_argv_address: usize) noreturn {
argument_address_iterator += #size(usize) * (argument_count + 1);
environment_values = #cast(argument_address_iterator);
const env = environment_values;
const result = #import("main").main();
std.os.exit(exit_code = result);
#import("root").main() catch std.os.exit(1);
std.os.exit(0);
}
const main :: export = fn (argc: s32, argv: [&]const [&:0]const u8, env: [&:null]const ?[&:null]const u8) s32 {
@ -40,5 +40,6 @@ const main :: export = fn (argc: s32, argv: [&]const [&:0]const u8, env: [&:null
argument_count = argc_u;
argument_values = argv;
environment_values = env;
return #import("main").main();
#import("root").main() catch return 1;
return 0;
}

6
lib/test_runner.nat Normal file
View File

@ -0,0 +1,6 @@
const builtin = #import("builtin");
const main = fn () *!void {
for (builtin.test_functions) |test_function| {
try test_function.function();
}
}

View File

@ -1,3 +0,0 @@
const main = fn () s32 {
return 0;
}

View File

@ -2,7 +2,11 @@ const std = #import("std");
const assert = std.assert;
const Executable = std.build.Executable;
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const executable = Executable{
.target = .{
.cpu = .x86_64,
@ -10,13 +14,12 @@ const main = fn () s32 {
.abi = .gnu,
},
.main_source_path = "src/main.nat",
.name = "first",
.name = "first_build",
};
if (executable.compile()) {
return 0;
} else {
std.print(bytes = "Executable failed to compile!\n");
return 1;
return Error.unexpected_result;
}
}

View File

@ -0,0 +1,2 @@
const main = fn () *!void {
}

View File

@ -2,7 +2,11 @@ const std = #import("std");
const assert = std.assert;
const Executable = std.build.Executable;
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const executable = Executable{
.target = .{
.cpu = .x86_64,
@ -15,9 +19,8 @@ const main = fn () s32 {
};
if (executable.compile()) {
return 0;
} else {
std.print(bytes = "Executable failed to compile!\n");
return 1;
return Error.unexpected_result;
}
}

View File

@ -1,3 +1,2 @@
const main = fn () s32 {
return 0;
const main = fn () *!void {
}

View File

@ -1,8 +1,14 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
const a: s32 = 1;
const b: s32 = 2;
const c: s32 = a + b;
const d: s32 = 3;
const e: s32 = d - c;
return e;
if (e != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,6 +1,11 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var a: s32 = 5;
var b: s32 = 4;
var result = a & b;
return result - b;
if (result - b != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,10 +1,15 @@
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const ch = 'a';
var buffer: [1]u8 = undefined;
var ptr: &[1]u8 = buffer.&;
var index: usize = 0;
ptr[index] = ch;
const sub: u8 = ptr[index] - ch;
const result: u32 = sub;
return #cast(result);
if (sub != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,4 +1,8 @@
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
var buffer: [2]u8 = undefined;
const expected = 'a';
const index: usize = 1;
@ -6,7 +10,9 @@ const main = fn () s32 {
const ch = buffer[index];
const sub = expected - ch;
const result: u32 = sub;
return #cast(result);
if (result != 0) {
return Error.unexpected_result;
}
}
const foo = fn (buffer: &[2]u8, index: usize, ch: u8) void {

View File

@ -1,9 +1,13 @@
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const ch = 'a';
var buffer: [1]u8 = undefined;
var index: usize = 0;
buffer[index] = ch;
const sub: u8 = buffer[index] - ch;
const result: u32 = sub;
return #cast(result);
if (buffer[index] - ch != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,10 +1,9 @@
const std = #import("std");
const assert = std.assert;
const main = fn() s32 {
const main = fn() *!void {
assert(true);
var a: s32 = 1;
const is_not_one = a != 1;
assert(!is_not_one);
return 0;
}

View File

@ -8,7 +8,7 @@ const BitStruct = struct(u8) {
d: u5,
};
const main = fn () s32 {
const main = fn () *!void {
var bs = BitStruct{
.a = false,
.b = true,
@ -26,5 +26,4 @@ const main = fn () s32 {
};
const bitcast_const_bs: u8 = #cast(const_bs);
assert(bitcast_const_bs == 5);
return 0;
}

View File

@ -17,7 +17,7 @@ const transform = fn (a: A) B {
};
}
const main = fn () s32 {
const main = fn () *!void {
var a = A{
.a = 3,
.b = 8,
@ -26,6 +26,4 @@ const main = fn () s32 {
const b = transform(a);
assert(a.a == b.a);
assert(a.b == b.b);
return 0;
}

View File

@ -1,4 +1,7 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var i: s32 = 0;
const j: s32 = 5;
for (0..10) |_| {
@ -8,5 +11,7 @@ const main = fn() s32 {
i += 1;
}
return i - j;
if (i - j != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,7 +1,10 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
if (true) {
return 0;
} else {
return 1;
return Error.unexpected_result;
}
}

View File

@ -1,8 +1,13 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
const dividend: s32 = 30;
const divisor: s32 = 6;
const div: s32 = dividend / divisor;
const n: s32 = 5;
return n - div;
if (n - div != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,13 +1,20 @@
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const a = foo(5);
if (a != 123) {
return 1;
return Error.unexpected_result;
}
const b = foo(5);
if (b != 123) {
return 1;
return Error.unexpected_result;
}
if (a - b != 0) {
return Error.unexpected_result;
}
return a - b;
}
const foo = fn (arg: s32) s32 {

View File

@ -1,3 +1 @@
const main = fn() s32 {
return 0;
}
const main = fn() *!void { }

View File

@ -1,4 +1,8 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var counter: s32 = 0;
const loop = 10;
@ -6,5 +10,7 @@ const main = fn() s32 {
counter += 1;
}
return loop - counter;
if (loop - counter != 0) {
return Error.unexpected_result;
}
}

View File

@ -20,12 +20,17 @@ const print_values = fn(slice: []const u8) void {
}
}
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const a = [_]u8{1, 1, 4, 5, 6};
const b = [_]u8{1, 4, 6};
const expected_result: usize = a.len + b.len;
const result = count_slice_byte_count(slices = .{a.&, b.&}.&);
print_values(slice = a.&);
const main_result: u32 = #cast(expected_result - result);
return #cast(main_result);
if (expected_result - result != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,16 +1,18 @@
const Error = error{
unexpected_result,
};
const std = #import("std");
const main = fn() s32 {
const main = fn() Error!void {
if (std.os.duplicate_process()) |pid| {
if (pid == 0) {
std.print(bytes = "Hello from child\n");
std.os.exit(exit_code = 0);
} else {
std.print(bytes = "Hello from parent\n");
return 0;
}
} else {
std.print(bytes = "Unable to create child process\n");
return 1;
return Error.unexpected_result;
}
}

View File

@ -1,18 +1,21 @@
const std = #import("std");
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
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"};
_ = std.os.execute(path = "/usr/bin/ls", argv = argv.&, env = std.start.environment_values);
return 1;
return Error.unexpected_result;
} else {
std.print(bytes = "Hello from parent\n");
return 0;
}
} else {
std.print(bytes = "Unable to create child process\n");
return 1;
return Error.unexpected_result;
}
}

View File

@ -4,8 +4,14 @@ const foo = fn () s32 {
return expected_number;
}
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
var function_pointer = foo.&;
const result = function_pointer();
return result - expected_number;
if (result - expected_number != 0) {
return Error.unexpected_result;
}
}

View File

@ -13,11 +13,17 @@ const Struct = struct{
}
};
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
var s = Struct{
.a = expected_number,
.handler = Struct.handler_function.&,
};
return s.handler(s.&) - expected_number;
if (s.handler(s.&) - expected_number != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,6 +1,5 @@
const std = #import("std");
const main = fn() s32 {
const main = fn() *!void {
std.print(bytes = "Hello world!\n");
return 0;
}

View File

@ -1,13 +1,18 @@
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const a = foo(5);
if (a != 6) {
return 1;
return Error.unexpected_result;
}
const b = foo(5);
if (b != 6) {
return 1;
return Error.unexpected_result;
}
if (a - b != 0) {
return Error.unexpected_result;
}
return a - b;
}
const foo = fn (arg: s32) s32 {

View File

@ -1,13 +1,20 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
const a = foo(5);
if (a != 12412) {
return 1;
return Error.unexpected_result;
}
const b = foo(5);
if (b != 12412) {
return 1;
return Error.unexpected_result;
}
if (a - b != 0) {
return Error.unexpected_result;
}
return a - b;
}
const foo = fn(arg: s32) s32 {

View File

@ -1,5 +1,10 @@
const main = fn() s32 {
const a: s32 = 5;
const b: s32 = 4;
return a * b - a * b;
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var a: s32 = 5;
var b: s32 = 4;
if (a * b - a * b != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,4 +1,8 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var i: s32 = 0;
while (i < 10) {
i += 1;
@ -7,5 +11,7 @@ const main = fn() s32 {
}
}
return i - 5;
if (i - 5 != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,13 +1,19 @@
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const a = foo(5142);
if (a != 2501) {
return 1;
return Error.unexpected_result;
}
const b = foo(5142);
if (b != 2501) {
return 1;
return Error.unexpected_result;
}
if (a - b != 0) {
return Error.unexpected_result;
}
return #cast(a - b);
}
const foo = fn (arg: u32) u32 {

View File

@ -1,7 +1,12 @@
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const a = foo(5142);
const b = foo(5142);
return #cast(a - b);
if (a - b != 0) {
return Error.unexpected_result;
}
}
const foo = fn (arg: u32) u32 {

View File

@ -1,7 +1,13 @@
const main = fn () s32 {
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const a = foo(5);
const b = foo(5);
return a - b;
if (a - b != 0) {
return Error.unexpected_result;
}
}
const foo = fn (arg: s32) s32 {

View File

@ -1,7 +1,6 @@
const std = #import("std");
const assert = std.assert;
const main = fn() s32 {
const main = fn() *!void {
var foo = [2:null] ?[&:0]const u8 {"Hi", "Ho"};
assert(foo[2] == null);
return 0;
}

View File

@ -1,8 +1,6 @@
const foo = fn(slice: []u8) ?[]u8 {
return slice[0..1];
}
const main = fn() s32 {
const main = fn() *!void {
_ = foo(slice = .{ 0, 1, 2, 3 }.&);
return 0;
}

View File

@ -1,7 +1,13 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
const a: u32 = 0xffff;
const b: u32 = 0xffff0000;
const c: u32 = 0xffffffff;
const result = c - (a | b);
return #cast(result);
if (result != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,14 +1,16 @@
const std = #import("std");
const print = std.print;
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var buffer: [std.os.max_path_byte_count:0]u8 = undefined;
if (std.os.current_executable_path(buffer = buffer.&)) |bytes| {
print(bytes);
print(bytes = "\n");
return 0;
} else {
print(bytes = "Failed\n");
return 1;
return Error.unexpected_result;
}
}

View File

@ -1,7 +1,13 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var x: u32 = 1;
x = x << 5;
x = x >> 5;
const b: u32 = 1;
return #cast(x - b);
if (x - b != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,8 +1,10 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var false_boolean: bool = false;
if (false_boolean) {
return 1;
} else {
return 0;
return Error.unexpected_result;
}
}

View File

@ -1,6 +1,9 @@
const std = #import("std");
const assert = std.assert;
const main = fn () s32 {
assert(#size(usize) == 8);
return 0;
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
if (#size(usize) != 8) {
return Error.unexpected_result;
}
}

View File

@ -1,4 +1,8 @@
const main = fn () s32{
const Error = error{
unexpected_result,
};
const main = fn () Error!void{
const a: [4]u8 = .{1, 2, 3, 4};
const b: []const []const u8 = .{a.&}.&;
@ -10,5 +14,7 @@ const main = fn () s32{
}
}
return #cast(sum - 10);
if (sum - 10 != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,7 +1,12 @@
const main = fn () s32{
const Error = error{
unexpected_result,
};
const main = fn () Error!void {
const a: [4]u8 = .{1, 2, 3, 4};
const b: []const []const u8 = .{a.&}.&;
const c: u8 = b[0][0] - 1;
const d: u32 = c;
return #cast(c);
if (c != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,6 +1,5 @@
const main = fn () s32 {
const main = fn () *!void {
_ = foo();
return 0;
}
const foo = fn () []u8 {

View File

@ -1,12 +1,18 @@
const std = #import("std");
const assert = std.assert;
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var buffer: [65]u8 = undefined;
const slice = foo(5, buffer.&);
assert(slice.len + 5 == buffer.len);
const result: u32 = #cast(slice.len + 5 - buffer.len);
return #cast(result);
if (result != 0) {
return Error.unexpected_result;
}
}
const foo = fn(n: usize, buffer: &[65]u8) []u8 {
return buffer[n..];

View File

@ -1,4 +1,9 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var a : s32 = 0;
return a;
if (a != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,6 +1,10 @@
const std = #import("std");
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
const size = 0x1000;
if (std.page_allocator.allocate(size, alignment = 12)) |result| {
@ -8,13 +12,12 @@ const main = fn() s32 {
std.print(bytes = "Allocation succeeded. Freeing...\n");
if (std.page_allocator.free(bytes_ptr = result.ptr, bytes_len = result.len)) {
std.print(bytes = "Memory freed successfully\n");
return 0;
} else {
std.print(bytes = "Memory freed with errors\n");
return 1;
return Error.unexpected_result;
}
} else {
std.print(bytes = "Allocation failed!\n");
return 1;
return Error.unexpected_result;
}
}

View File

@ -1,7 +1,13 @@
const main = fn() s32 {
const Error = error{
unexpected_result,
};
const main = fn() Error!void {
var a: s32 = 561;
var b: s32 = 124;
var c: s32 = a ^ b;
var d: s32 = a ^ b;
return c ^ d;
if (c ^ d != 0) {
return Error.unexpected_result;
}
}

View File

@ -1,7 +1,6 @@
const std = #import("std");
const assert = std.assert;
const main = fn () s32 {
const main = fn () *!void {
var a = [2:0]u8 {1, 2};
assert(a[2] == 0);
return 0;
}

View File

@ -0,0 +1,2 @@
test "First test" {
}

View File

@ -1,2 +0,0 @@
test {
}