Write initial self-hosted code
This commit is contained in:
parent
5473d1c5a9
commit
203bee684c
@ -3791,6 +3791,7 @@ pub const Instruction = union(enum) {
|
||||
pointer_const_to_var,
|
||||
pointer_to_nullable,
|
||||
pointer_source_type_to_destination_type,
|
||||
pointer_to_not_nullable,
|
||||
slice_var_to_const,
|
||||
slice_to_nullable,
|
||||
slice_to_not_null,
|
||||
@ -6267,6 +6268,7 @@ pub const Builder = struct {
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
assert(expected_right_type != .null);
|
||||
const right = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = expected_right_type }, node.right, .right);
|
||||
const value_to_store = switch (node.id) {
|
||||
.assign => right,
|
||||
@ -8691,11 +8693,12 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
const cbb = unit.basic_blocks.get(builder.current_basic_block);
|
||||
const return_type = function_prototype.return_type;
|
||||
const return_type_index = function_prototype.return_type;
|
||||
const return_type = unit.types.get(return_type_index);
|
||||
|
||||
if (!cbb.terminated and (cbb.instructions.length > 0 or cbb.predecessors.length > 0)) {
|
||||
if (builder.return_block == .null) {
|
||||
switch (unit.types.get(return_type).*) {
|
||||
switch (return_type.*) {
|
||||
.void => try builder.buildRet(unit, context, .{
|
||||
.value = .{
|
||||
.@"comptime" = .void,
|
||||
@ -8713,7 +8716,7 @@ pub const Builder = struct {
|
||||
.value = .{
|
||||
.@"comptime" = .undefined,
|
||||
},
|
||||
.type = return_type,
|
||||
.type = return_type_index,
|
||||
};
|
||||
const insert = try unit.instructions.append(context.my_allocator, .{
|
||||
.insert_value = .{
|
||||
@ -8735,7 +8738,7 @@ pub const Builder = struct {
|
||||
.value = .{
|
||||
.runtime = insert,
|
||||
},
|
||||
.type = return_type,
|
||||
.type = return_type_index,
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
@ -8752,7 +8755,8 @@ pub const Builder = struct {
|
||||
|
||||
const phi = &unit.instructions.get(builder.return_phi).phi;
|
||||
|
||||
switch (unit.types.get(return_type).*) {
|
||||
|
||||
switch (return_type.*) {
|
||||
.void => unreachable,
|
||||
.noreturn => unreachable,
|
||||
.@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) {
|
||||
@ -8767,7 +8771,7 @@ pub const Builder = struct {
|
||||
.value = .{
|
||||
.@"comptime" = .undefined,
|
||||
},
|
||||
.type = return_type,
|
||||
.type = return_type_index,
|
||||
},
|
||||
.index = 1,
|
||||
.new_value = .{
|
||||
@ -8786,7 +8790,7 @@ pub const Builder = struct {
|
||||
.value = .{
|
||||
.runtime = return_value,
|
||||
},
|
||||
.type = return_type,
|
||||
.type = return_type_index,
|
||||
}, builder.current_basic_block);
|
||||
|
||||
try builder.jump(unit, context, builder.return_block);
|
||||
@ -9169,9 +9173,6 @@ pub const Builder = struct {
|
||||
.none => b: {
|
||||
const pointer_type = unit.types.get(pointer_value.type);
|
||||
const pointer_element_type = pointer_type.pointer.type;
|
||||
switch (unit.types.get(pointer_element_type).*) {
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
break :b pointer_element_type;
|
||||
},
|
||||
.type => |type_index| type_index,
|
||||
@ -9358,6 +9359,7 @@ pub const Builder = struct {
|
||||
.shift_left => .shift_left,
|
||||
.shift_right => .shift_right,
|
||||
.bool_and => .bit_and,
|
||||
.bool_or => .bit_or,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
@ -9536,6 +9538,7 @@ pub const Builder = struct {
|
||||
.bool => b: {
|
||||
const id: Instruction.IntegerBinaryOperation.Id = switch (binary_operation_id) {
|
||||
.bit_and => .bit_and,
|
||||
.bit_or => .bit_or,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
const i = try unit.instructions.append(context.my_allocator, .{
|
||||
@ -9607,17 +9610,18 @@ pub const Builder = struct {
|
||||
break :block result;
|
||||
},
|
||||
.block => block: {
|
||||
assert(type_expect == .none or type_expect.type == .void);
|
||||
const block = try builder.resolveBlock(unit, context, node_index);
|
||||
const block_i = try unit.instructions.append(context.my_allocator, .{
|
||||
.block = block,
|
||||
});
|
||||
|
||||
break :block .{
|
||||
.value = .{
|
||||
.runtime = block_i,
|
||||
},
|
||||
.type = .void,
|
||||
.type = if (type_expect == .none or type_expect.type == .void) .void else b: {
|
||||
assert(unit.basic_blocks.get( builder.current_basic_block).terminated);
|
||||
break :b .noreturn;
|
||||
},
|
||||
};
|
||||
},
|
||||
.container_literal => block: {
|
||||
@ -9688,8 +9692,23 @@ pub const Builder = struct {
|
||||
break :blk nullable_pointer;
|
||||
},
|
||||
},
|
||||
.@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) {
|
||||
else => |t| @panic(@tagName(t)),
|
||||
.slice => |slice| if (slice.nullable) b: {
|
||||
const constant_slice = try unit.constant_slices.append(context.my_allocator, .{
|
||||
.array = null,
|
||||
.start = 0,
|
||||
.end = 0,
|
||||
.type = type_index,
|
||||
});
|
||||
break :b V{
|
||||
.value = .{
|
||||
.@"comptime" = .{
|
||||
.constant_slice = constant_slice,
|
||||
},
|
||||
},
|
||||
.type = type_index,
|
||||
};
|
||||
} else {
|
||||
unreachable;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
@ -9698,7 +9717,8 @@ pub const Builder = struct {
|
||||
.slice => block: {
|
||||
const expression_to_slice = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.left, .left);
|
||||
|
||||
const range_node = unit.getNode(node.right);
|
||||
const slice_metadata_node = unit.getNode(node.right);
|
||||
const range_node = unit.getNode(slice_metadata_node.left);
|
||||
assert(range_node.id == .range);
|
||||
const range_start: V = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = Type.usize }, range_node.left, .right);
|
||||
const range_end: V = switch (range_node.right) {
|
||||
@ -11182,14 +11202,174 @@ pub const Builder = struct {
|
||||
else => unreachable,
|
||||
}, unreachable, &.{}),
|
||||
},
|
||||
.empty_container_literal_guess => {
|
||||
.empty_container_literal_guess => block: {
|
||||
assert(node.left != .null);
|
||||
assert(node.right != .null);
|
||||
const container_type = try builder.resolveType(unit, context, node.left, &.{});
|
||||
const node_list = unit.getNodeList(node.right);
|
||||
assert(node_list.len == 0);
|
||||
const result = try builder.resolveContainerLiteral(unit, context, node_list, container_type);
|
||||
return result;
|
||||
break :block result;
|
||||
},
|
||||
.if_else_payload => block: {
|
||||
assert(node.left != .null);
|
||||
assert(node.right != .null);
|
||||
|
||||
const if_else_node = unit.getNode(node.left);
|
||||
assert(if_else_node.id == .if_else);
|
||||
assert(if_else_node.left != .null);
|
||||
assert(if_else_node.right != .null);
|
||||
|
||||
const if_node = unit.getNode(if_else_node.left);
|
||||
assert(if_node.id == .@"if");
|
||||
assert(if_node.left != .null);
|
||||
assert(if_node.right != .null);
|
||||
|
||||
try builder.resolveBranchPayload(unit, context, .{
|
||||
.payload_node_index = node.right,
|
||||
.optional_node_index = if_node.left,
|
||||
.taken_expression_node_index = if_node.right,
|
||||
.not_taken_expression_node_index = if_else_node.right,
|
||||
});
|
||||
|
||||
break :block .{
|
||||
.value = .{
|
||||
.@"comptime" = .void,
|
||||
},
|
||||
.type = .void,
|
||||
};
|
||||
},
|
||||
.orelse_expression => block: {
|
||||
const v = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.left, .right);
|
||||
switch (unit.types.get(v.type).*) {
|
||||
.pointer => |pointer| if (pointer.nullable) {
|
||||
const type_to_expect = switch (type_expect) {
|
||||
.none => b: {
|
||||
var p = pointer;
|
||||
p.nullable = false;
|
||||
const non_null_pointer = try unit.getPointerType(context, p);
|
||||
break :b non_null_pointer;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
const new_type_expect = Type.Expect{ .type = type_to_expect };
|
||||
const null_pointer = V{
|
||||
.type = v.type,
|
||||
.value = .{
|
||||
.@"comptime" = .null_pointer,
|
||||
},
|
||||
};
|
||||
const cmp = try unit.instructions.append(context.my_allocator, .{
|
||||
.integer_compare = .{
|
||||
.left = v,
|
||||
.right = null_pointer,
|
||||
.type = v.type,
|
||||
.id = .equal,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, cmp);
|
||||
const is_null_block = try builder.newBasicBlock(unit, context);
|
||||
const is_not_null_block = try builder.newBasicBlock(unit, context);
|
||||
try builder.branch(unit, context, cmp, is_null_block, is_not_null_block);
|
||||
|
||||
builder.current_basic_block = is_null_block;
|
||||
|
||||
const else_expr = try builder.resolveRuntimeValue(unit, context, new_type_expect, node.right, .right);
|
||||
_ = else_expr; // autofix
|
||||
const is_block_terminated = unit.basic_blocks.get(builder.current_basic_block).terminated;
|
||||
if (!is_block_terminated) {
|
||||
unreachable;
|
||||
} else {
|
||||
builder.current_basic_block = is_not_null_block;
|
||||
const cast = try unit.instructions.append(context.my_allocator, .{
|
||||
.cast = .{
|
||||
.id = .pointer_to_not_nullable,
|
||||
.value = v,
|
||||
.type = type_to_expect,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, cast);
|
||||
|
||||
break :block .{
|
||||
.value = .{
|
||||
.runtime = cast,
|
||||
},
|
||||
.type = type_to_expect,
|
||||
};
|
||||
}
|
||||
} else unreachable,
|
||||
.slice => |slice| if (slice.nullable) {
|
||||
const type_to_expect = switch (type_expect) {
|
||||
.none => b: {
|
||||
var s = slice;
|
||||
s.nullable = false;
|
||||
const non_null_slice = try unit.getSliceType(context, s);
|
||||
break :b non_null_slice;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
const new_type_expect = Type.Expect{ .type = type_to_expect };
|
||||
const null_pointer = V{
|
||||
.type = slice.child_pointer_type,
|
||||
.value = .{
|
||||
.@"comptime" = .null_pointer,
|
||||
},
|
||||
};
|
||||
|
||||
const get_pointer = try unit.instructions.append(context.my_allocator, .{
|
||||
.extract_value = .{
|
||||
.expression = v,
|
||||
.index = 0,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, get_pointer);
|
||||
const cmp = try unit.instructions.append(context.my_allocator, .{
|
||||
.integer_compare = .{
|
||||
.left = .{
|
||||
.value = .{
|
||||
.runtime = get_pointer,
|
||||
},
|
||||
.type = slice.child_pointer_type,
|
||||
},
|
||||
.right = null_pointer,
|
||||
.type = v.type,
|
||||
.id = .equal,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, cmp);
|
||||
const is_null_block = try builder.newBasicBlock(unit, context);
|
||||
const is_not_null_block = try builder.newBasicBlock(unit, context);
|
||||
try builder.branch(unit, context, cmp, is_null_block, is_not_null_block);
|
||||
|
||||
builder.current_basic_block = is_null_block;
|
||||
|
||||
const else_expr = try builder.resolveRuntimeValue(unit, context, new_type_expect, node.right, .right);
|
||||
_ = else_expr; // autofix
|
||||
const is_block_terminated = unit.basic_blocks.get(builder.current_basic_block).terminated;
|
||||
if (!is_block_terminated) {
|
||||
unreachable;
|
||||
} else {
|
||||
builder.current_basic_block = is_not_null_block;
|
||||
const cast = try unit.instructions.append(context.my_allocator, .{
|
||||
.cast = .{
|
||||
.id = .slice_to_not_null,
|
||||
.value = v,
|
||||
.type = type_to_expect,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, cast);
|
||||
|
||||
break :block .{
|
||||
.value = .{
|
||||
.runtime = cast,
|
||||
},
|
||||
.type = type_to_expect,
|
||||
};
|
||||
}
|
||||
} else unreachable,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
@panic("todo");
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
@ -13239,6 +13419,7 @@ pub const Builder = struct {
|
||||
|
||||
const is_last_element_range = last_element_node.id == .range;
|
||||
const not_range_len = payloads.len - @intFromBool(is_last_element_range);
|
||||
|
||||
if (slices.length > 0) {
|
||||
const load_i = try unit.instructions.append(context.my_allocator, .{
|
||||
.load = .{
|
||||
@ -13316,9 +13497,16 @@ pub const Builder = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const body_node_index = statement_node.right;
|
||||
const for_expressions = unit.getNode(statement_node.right);
|
||||
assert(for_expressions.id == .for_expressions);
|
||||
const body_node_index = for_expressions.left;
|
||||
_ = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .void }, body_node_index, .right);
|
||||
|
||||
const else_node_index = for_expressions.right;
|
||||
if (else_node_index != .null) {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const load_iterator = try unit.instructions.append(context.my_allocator, .{
|
||||
.load = .{
|
||||
.value = .{
|
||||
@ -13506,7 +13694,9 @@ pub const Builder = struct {
|
||||
},
|
||||
else => node.right,
|
||||
};
|
||||
|
||||
const v = try builder.resolveRuntimeValue(unit, context, catch_type_expect, catch_expression_node_index, side);
|
||||
|
||||
switch (error_union.type) {
|
||||
.void, .noreturn => {
|
||||
assert(unit.basic_blocks.get(builder.current_basic_block).terminated);
|
||||
@ -13519,7 +13709,7 @@ pub const Builder = struct {
|
||||
phi: Instruction.Index,
|
||||
exit_block: BasicBlock.Index,
|
||||
};
|
||||
const catch_info: ?CatchInfo = if (!is_block_terminated) blk: {
|
||||
const maybe_catch_info: ?CatchInfo = if (!is_block_terminated) blk: {
|
||||
const expected_type = error_union.type;
|
||||
assert(v.type == expected_type);
|
||||
const phi_index = try unit.instructions.append(context.my_allocator, .{
|
||||
@ -13555,12 +13745,12 @@ pub const Builder = struct {
|
||||
},
|
||||
.type = error_union.type,
|
||||
};
|
||||
if (is_block_terminated) {
|
||||
return value;
|
||||
} else {
|
||||
const phi_index = catch_info.?.phi;
|
||||
|
||||
if (maybe_catch_info) |catch_info| {
|
||||
assert(!is_block_terminated);
|
||||
const phi_index = catch_info.phi;
|
||||
const phi = &unit.instructions.get(phi_index).phi;
|
||||
const exit_block = catch_info.?.exit_block;
|
||||
const exit_block = catch_info.exit_block;
|
||||
|
||||
try phi.addIncoming(context, value, builder.current_basic_block);
|
||||
|
||||
@ -13575,6 +13765,9 @@ pub const Builder = struct {
|
||||
},
|
||||
.type = error_union.type,
|
||||
};
|
||||
} else {
|
||||
assert(is_block_terminated);
|
||||
return value;
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -14755,15 +14948,43 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
fn emitReturn(builder: *Builder, unit: *Unit, context: *const Context, return_node_index: Node.Index) !void {
|
||||
const return_type_index = unit.getReturnType(builder.current_function);
|
||||
const return_node = unit.getNode(return_node_index);
|
||||
assert(return_node.id == .@"return");
|
||||
assert(return_node.left != .null);
|
||||
assert(return_node.right == .null);
|
||||
const return_value_node_index = return_node.left;
|
||||
const return_type = unit.getReturnType(builder.current_function);
|
||||
const return_value = try builder.resolveRuntimeValue(unit, context, Type.Expect{
|
||||
.type = return_type,
|
||||
}, return_value_node_index, .right);
|
||||
const return_value = if (return_node.left != .null) b: {
|
||||
const return_value_node_index = return_node.left;
|
||||
const return_value = try builder.resolveRuntimeValue(unit, context, Type.Expect{
|
||||
.type = return_type_index,
|
||||
}, return_value_node_index, .right);
|
||||
break :b return_value;
|
||||
} else switch (unit.types.get(return_type_index).*) {
|
||||
.@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) {
|
||||
.error_union => |error_union| if (error_union.type == .void) b: {
|
||||
const fields = &[_]V.Comptime{
|
||||
.undefined,
|
||||
.{
|
||||
.bool = false,
|
||||
},
|
||||
};
|
||||
const constant_struct = try unit.constant_structs.append(context.my_allocator, .{
|
||||
.fields = fields,
|
||||
.type = return_type_index,
|
||||
});
|
||||
|
||||
break :b V{
|
||||
.type = return_type_index,
|
||||
.value = .{
|
||||
.@"comptime" = .{
|
||||
.constant_struct = constant_struct,
|
||||
},
|
||||
},
|
||||
};
|
||||
} else unreachable,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
if (builder.return_block != .null) {
|
||||
if (builder.return_phi != .null) {
|
||||
@ -14777,7 +14998,7 @@ pub const Builder = struct {
|
||||
} else if (builder.exit_blocks.length > 0) {
|
||||
builder.return_phi = try unit.instructions.append(context.my_allocator, .{
|
||||
.phi = .{
|
||||
.type = return_type,
|
||||
.type = return_type_index,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -2051,7 +2051,7 @@ pub const LLVM = struct {
|
||||
|
||||
break :b slice_fields;
|
||||
} else b: {
|
||||
const ptr = llvm.pointer_type.?.toType().getPoison() orelse unreachable;
|
||||
const ptr = llvm.pointer_type.?.getNull();
|
||||
const len = llvm.context.getConstantInt(64, 0, false) orelse unreachable;
|
||||
break :b .{ ptr.toConstant(), len.toConstant() };
|
||||
};
|
||||
@ -2094,6 +2094,9 @@ pub const LLVM = struct {
|
||||
var field_values = try UnpinnedArray(*LLVM.Value.Constant).initialize_with_capacity(context.my_allocator, @intCast(constant_struct.fields.len));
|
||||
const sema_struct_index = unit.types.get(constant_struct.type).@"struct";
|
||||
const sema_struct = unit.structs.get(sema_struct_index);
|
||||
const llvm_type = try llvm.getType(unit, context, constant_struct.type);
|
||||
const struct_type = llvm_type.toStruct() orelse unreachable;
|
||||
|
||||
switch (sema_struct.kind) {
|
||||
.@"struct" => |*sema_struct_type| {
|
||||
for (constant_struct.fields, sema_struct_type.fields.slice()) |field_value, field_index| {
|
||||
@ -2101,14 +2104,28 @@ pub const LLVM = struct {
|
||||
const constant = try llvm.emitComptimeRightValue(unit, context, field_value, field.type);
|
||||
field_values.append_with_capacity(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.pointer, field_values.length) orelse unreachable;
|
||||
return const_struct;
|
||||
},
|
||||
.error_union => |error_union| {
|
||||
const abi_ty = unit.types.get(error_union.abi);
|
||||
switch (abi_ty.*) {
|
||||
.@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) {
|
||||
.raw_error_union => |err_union_base_type| {
|
||||
const field_types = [2]Compilation.Type.Index{err_union_base_type, .bool };
|
||||
for (field_types, constant_struct.fields) |field_type_index, field_value| {
|
||||
const constant = try llvm.emitComptimeRightValue(unit, context, field_value, field_type_index);
|
||||
field_values.append_with_capacity(constant);
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
|
||||
const const_struct = struct_type.getConstant(field_values.pointer, field_values.length) 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 {
|
||||
@ -2674,6 +2691,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
.slice_coerce_to_zero_termination,
|
||||
.slice_zero_to_no_termination,
|
||||
.pointer_to_nullable,
|
||||
.pointer_to_not_nullable,
|
||||
.pointer_const_to_var,
|
||||
.pointer_to_array_to_pointer_to_many,
|
||||
.pointer_source_type_to_destination_type,
|
||||
|
@ -199,6 +199,9 @@ pub const Node = struct {
|
||||
comptime_expression,
|
||||
self,
|
||||
any,
|
||||
for_expressions,
|
||||
slice_metadata,
|
||||
orelse_expression,
|
||||
};
|
||||
};
|
||||
|
||||
@ -552,6 +555,11 @@ const Analyzer = struct {
|
||||
|
||||
const while_block = try analyzer.block();
|
||||
|
||||
if (analyzer.peekToken() == .fixed_keyword_else) {
|
||||
analyzer.consumeToken();
|
||||
unreachable;
|
||||
}
|
||||
|
||||
return analyzer.addNode(.{
|
||||
.id = .@"while",
|
||||
.token = while_identifier_index,
|
||||
@ -782,7 +790,7 @@ const Analyzer = struct {
|
||||
.right = try analyzer.nodeList(payload_nodes),
|
||||
});
|
||||
|
||||
const for_content_node = switch (analyzer.peekToken()) {
|
||||
const true_expression = switch (analyzer.peekToken()) {
|
||||
.operator_left_brace => try analyzer.block(),
|
||||
else => blk: {
|
||||
const for_content_expression = try analyzer.expression();
|
||||
@ -791,11 +799,22 @@ const Analyzer = struct {
|
||||
},
|
||||
};
|
||||
|
||||
const else_expression: Node.Index = if (analyzer.peekToken() == .fixed_keyword_else) b: {
|
||||
analyzer.consumeToken();
|
||||
const else_expression = if (analyzer.peekToken() == .operator_left_brace) try analyzer.block() else try analyzer.expression();
|
||||
break :b else_expression;
|
||||
} else .null;
|
||||
|
||||
const for_node = try analyzer.addNode(.{
|
||||
.id = .for_loop,
|
||||
.token = token,
|
||||
.left = for_condition_node,
|
||||
.right = for_content_node,
|
||||
.right = try analyzer.addNode(.{
|
||||
.id = .for_expressions,
|
||||
.token = .null,
|
||||
.left = true_expression,
|
||||
.right = else_expression,
|
||||
})
|
||||
});
|
||||
|
||||
return for_node;
|
||||
@ -1016,6 +1035,7 @@ const Analyzer = struct {
|
||||
shift_left,
|
||||
shift_right,
|
||||
@"catch",
|
||||
@"orelse",
|
||||
};
|
||||
|
||||
const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{
|
||||
@ -1038,6 +1058,7 @@ const Analyzer = struct {
|
||||
.shift_left = 50,
|
||||
.shift_right = 50,
|
||||
.@"catch" = 40,
|
||||
.@"orelse" = 40,
|
||||
});
|
||||
|
||||
const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{
|
||||
@ -1060,6 +1081,7 @@ const Analyzer = struct {
|
||||
.shift_left = .left,
|
||||
.shift_right = .left,
|
||||
.@"catch" = .left,
|
||||
.@"orelse" = .left,
|
||||
});
|
||||
|
||||
const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{
|
||||
@ -1082,6 +1104,7 @@ const Analyzer = struct {
|
||||
.shift_left = .shift_left,
|
||||
.shift_right = .shift_right,
|
||||
.@"catch" = .catch_expression,
|
||||
.@"orelse" = .orelse_expression,
|
||||
});
|
||||
|
||||
fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index {
|
||||
@ -1139,6 +1162,7 @@ const Analyzer = struct {
|
||||
.operator_shift_left => .shift_left,
|
||||
.operator_shift_right => .shift_right,
|
||||
.fixed_keyword_catch => .@"catch",
|
||||
.fixed_keyword_orelse => .@"orelse",
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
@ -1251,7 +1275,13 @@ const Analyzer = struct {
|
||||
analyzer.consumeToken();
|
||||
break :blk token;
|
||||
},
|
||||
.left = try analyzer.expression(),
|
||||
.left = switch (analyzer.peekToken()) {
|
||||
.operator_comma,
|
||||
.operator_semicolon,
|
||||
.operator_compare_equal,
|
||||
=> Node.Index.null,
|
||||
else => try analyzer.expression(),
|
||||
},
|
||||
.right = Node.Index.null,
|
||||
}),
|
||||
.fixed_keyword_break => try analyzer.breakExpression(),
|
||||
@ -2102,19 +2132,34 @@ const Analyzer = struct {
|
||||
else => try analyzer.expression(),
|
||||
};
|
||||
|
||||
_ = try analyzer.expectToken(.operator_right_bracket);
|
||||
const slice_termination: Node.Index = if (analyzer.peekToken() == .operator_colon) b: {
|
||||
analyzer.consumeToken();
|
||||
const result = try analyzer.expression();
|
||||
break :b result;
|
||||
} else .null;
|
||||
|
||||
break :blk try analyzer.addNode(.{
|
||||
.id = .slice,
|
||||
const slice_metadata = try analyzer.addNode(.{
|
||||
.id = .slice_metadata,
|
||||
.token = token,
|
||||
.left = left,
|
||||
.right = try analyzer.addNode(.{
|
||||
.left = try analyzer.addNode(.{
|
||||
.id = .range,
|
||||
.token = token,
|
||||
.left = index_expression,
|
||||
.right = range_end_expression,
|
||||
}),
|
||||
.right = slice_termination,
|
||||
});
|
||||
|
||||
_ = try analyzer.expectToken(.operator_right_bracket);
|
||||
|
||||
const slice = try analyzer.addNode(.{
|
||||
.id = .slice,
|
||||
.token = token,
|
||||
.left = left,
|
||||
.right = slice_metadata,
|
||||
});
|
||||
|
||||
break :blk slice;
|
||||
} else {
|
||||
_ = try analyzer.expectToken(.operator_right_bracket);
|
||||
break :blk try analyzer.addNode(.{
|
||||
|
@ -397,11 +397,11 @@ pub fn build(b: *std.Build) !void {
|
||||
};
|
||||
|
||||
const llvm_include_dir = try std.mem.concat(b.allocator, u8, &.{ llvm_path, "/include" });
|
||||
compiler.addIncludePath(std.Build.LazyPath.relative(llvm_include_dir));
|
||||
compiler.addIncludePath(std.Build.path(b, llvm_include_dir));
|
||||
const llvm_lib_dir = try std.mem.concat(b.allocator, u8, &.{ llvm_path, "/lib" });
|
||||
|
||||
for (static_llvm_libraries) |llvm_library| {
|
||||
compiler.addObjectFile(std.Build.LazyPath.relative(try std.mem.concat(b.allocator, u8, &.{ llvm_lib_dir, "/", llvm_library })));
|
||||
compiler.addObjectFile(std.Build.path(b, try std.mem.concat(b.allocator, u8, &.{ llvm_lib_dir, "/", llvm_library })));
|
||||
}
|
||||
} else {
|
||||
compiler.linkSystemLibrary("LLVM-17");
|
||||
@ -463,7 +463,7 @@ pub fn build(b: *std.Build) !void {
|
||||
const install_exe = b.addInstallArtifact(compiler, .{});
|
||||
b.getInstallStep().dependOn(&install_exe.step);
|
||||
b.installDirectory(.{
|
||||
.source_dir = std.Build.LazyPath.relative("lib"),
|
||||
.source_dir = std.Build.path(b, "lib"),
|
||||
.install_dir = .bin,
|
||||
.install_subdir = "lib",
|
||||
});
|
||||
|
@ -24,17 +24,17 @@ const unwrap_syscall = fn(syscall_result: ssize) Error!usize {
|
||||
|
||||
const MapFlags = switch (os) {
|
||||
.macos => bitfield(u32){
|
||||
shared: bool,
|
||||
private: bool,
|
||||
shared: bool = false,
|
||||
private: bool = false,
|
||||
reserved: u2 = 0,
|
||||
fixed: bool,
|
||||
fixed: bool = false,
|
||||
reserved0: bool = 0,
|
||||
noreserve: bool,
|
||||
noreserve: bool = false,
|
||||
reserved1: u2 = 0,
|
||||
has_semaphore: bool,
|
||||
no_cache: bool,
|
||||
has_semaphore: bool = false,
|
||||
no_cache: bool = false,
|
||||
reserved2: u1 = 0,
|
||||
anonymous: bool,
|
||||
anonymous: bool = false,
|
||||
reserved3: u19 = 0,
|
||||
},
|
||||
.linux => linux.MapFlags,
|
||||
@ -79,5 +79,6 @@ const munmap :: extern = fn cc(.c) (address: [&]const u8, length: usize) s32;
|
||||
const execve :: extern = fn cc(.c) (path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) s32;
|
||||
const realpath :: extern = fn cc(.c) (path: [&:0]const u8, resolved_path: [&:0]u8) ?[&:0]u8;
|
||||
const waitpid :: extern = fn cc(.c) (pid: ProcessId, status: &s32, flags: s32) s32;
|
||||
const mprotect :: extern = fn cc(.c) (address: &any, size: usize, flags: ProtectionFlags) s32;
|
||||
|
||||
const _NSGetExecutablePath :: extern = fn cc(.c) (buffer: [&:0]u8, buffer_size: &u32) s32;
|
||||
|
@ -27,6 +27,21 @@ const assert = fn(ok: bool) void {
|
||||
}
|
||||
}
|
||||
|
||||
const byte_equal = fn (a: []const u8, b: []const u8) bool {
|
||||
var result = false;
|
||||
if (a.length == b.length) {
|
||||
result = true;
|
||||
for (a, b) |a_byte, b_byte| {
|
||||
if (a_byte != b_byte) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const print = fn(bytes: []const u8) void {
|
||||
const file_descriptor = os.StdFileDescriptor.get(descriptor = .stdout);
|
||||
const file_writer = FileWriter{
|
||||
@ -156,15 +171,20 @@ const copy_bytes = fn(destination: []u8, source: []const u8) void {
|
||||
}
|
||||
}
|
||||
|
||||
const c_len = fn (string: [&:0]const u8) usize {
|
||||
const c_len = fn (pointer_to_string: [&:0]const u8) usize {
|
||||
var i: usize = 0;
|
||||
while (string[i] != 0) {
|
||||
while (pointer_to_string[i] != 0) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
const c_slice = fn (pointer_to_string: [&:0]const u8) [:0]const u8 {
|
||||
const len = c_len(pointer_to_string);
|
||||
return pointer_to_string[0..len:0];
|
||||
}
|
||||
|
||||
const Target = struct {
|
||||
cpu: builtin.Cpu,
|
||||
os: builtin.Os,
|
||||
|
94
src/main.nat
94
src/main.nat
@ -1,17 +1,99 @@
|
||||
const std = #import("std");
|
||||
const Arena = std.Arena;
|
||||
const c_slice = std.c_slice;
|
||||
const byte_equal = std.byte_equal;
|
||||
const print = std.print;
|
||||
const exit = std.os.exit;
|
||||
|
||||
const ArgumentProcessingError = error{
|
||||
no_arguments,
|
||||
};
|
||||
|
||||
const lex = fn () void {
|
||||
|
||||
}
|
||||
|
||||
const get_argument = fn (real_argument: []const u8, wanted_argument: []const u8, command_arguments: []const [&:0]const u8, i_ptr: &usize) ?[]const u8 {
|
||||
const i = i_ptr.@;
|
||||
|
||||
const are_equal = byte_equal(real_argument, wanted_argument);
|
||||
if (are_equal) {
|
||||
if (i < command_arguments.length) {
|
||||
const new_i = i + 1;
|
||||
const argument = c_slice(command_arguments[new_i]);
|
||||
i_ptr.@ = new_i;
|
||||
return argument;
|
||||
} else {
|
||||
print("error: unterminated argument: '");
|
||||
print(real_argument);
|
||||
print("'\n");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const command_exe = fn (arena: &Arena, command_arguments: []const [&:0]const u8) void {
|
||||
var i: usize = 0;
|
||||
|
||||
var maybe_output_argument: ?[]const u8 = null;
|
||||
var maybe_main_source_file: ?[]const u8 = null;
|
||||
|
||||
while (i < command_arguments.length) {
|
||||
const command_argument = c_slice(command_arguments[i]);
|
||||
|
||||
if (get_argument(command_argument, "-o", command_arguments, i.&)) |out_arg| {
|
||||
maybe_output_argument = out_arg;
|
||||
} else if (get_argument(command_argument, "-main_source_file", command_arguments, i.&)) |src_arg| {
|
||||
maybe_main_source_file = src_arg;
|
||||
} else {
|
||||
print("error: unhandled argument: '");
|
||||
print(command_argument);
|
||||
print("'\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
const main_source_file = maybe_main_source_file orelse {
|
||||
print("error: no main source file specified\n");
|
||||
exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
const main = fn() *!void {
|
||||
const arena = try Arena.allocate(std.megabytes(64));
|
||||
const argument_count = std.start.argument_count;
|
||||
|
||||
if (argument_count <= 1) {
|
||||
return ArgumentProcessingError.no_arguments;
|
||||
}
|
||||
|
||||
const argument_values = std.start.argument_values;
|
||||
const arguments = argument_values[0..argument_count];
|
||||
|
||||
for (arguments) |c_argument| {
|
||||
const argument_len = std.c_len(c_argument);
|
||||
const argument = c_argument[0..argument_len];
|
||||
std.print("Argument: ");
|
||||
std.print(argument);
|
||||
std.print("\n");
|
||||
const command = c_slice(arguments[1]);
|
||||
const command_arguments = arguments[2..];
|
||||
|
||||
if (byte_equal(command, "build")) {
|
||||
print("TODO: build");
|
||||
} else if (byte_equal(command, "clang") or byte_equal(command, "-cc1") or byte_equal(command, "-cc1as")) {
|
||||
unreachable;
|
||||
} else if (byte_equal(command, "cc")) {
|
||||
unreachable;
|
||||
} else if (byte_equal(command, "c++")) {
|
||||
unreachable;
|
||||
} else if (byte_equal(command, "exe")) {
|
||||
command_exe(arena, command_arguments);
|
||||
} else if (byte_equal(command, "lib")) {
|
||||
unreachable;
|
||||
} else if (byte_equal(command, "obj")) {
|
||||
unreachable;
|
||||
} else if (byte_equal(command, "test")) {
|
||||
print("TODO: test");
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
26
test/standalone/byte_equal/main.nat
Normal file
26
test/standalone/byte_equal/main.nat
Normal file
@ -0,0 +1,26 @@
|
||||
const std = #import("std");
|
||||
const print = std.print;
|
||||
const exit = std.os.exit;
|
||||
const byte_equal = std.byte_equal;
|
||||
|
||||
const foo = fn (string: []const u8, wanted: []const u8) ?[]const u8 {
|
||||
if (byte_equal(string, wanted)) {
|
||||
return string;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const main = fn () *!void {
|
||||
const original: []const u8 = "fasdasD";
|
||||
var f = original;
|
||||
f = "foooo";
|
||||
|
||||
if (foo(f, original)) |string| {
|
||||
print("string: ");
|
||||
print(string);
|
||||
print("\n");
|
||||
exit(1);
|
||||
} else {
|
||||
}
|
||||
}
|
8
test/standalone/c_string/main.nat
Normal file
8
test/standalone/c_string/main.nat
Normal file
@ -0,0 +1,8 @@
|
||||
const std = #import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
const main = fn() *!void {
|
||||
const foo: [&:0]const u8 = "Foo";
|
||||
const foo_string = std.c_slice(foo);
|
||||
try expect(foo_string.length == 3);
|
||||
}
|
14
test/standalone/orelse/main.nat
Normal file
14
test/standalone/orelse/main.nat
Normal file
@ -0,0 +1,14 @@
|
||||
const std = #import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
const main = fn () *!void {
|
||||
var expected_a: s32 = 5;
|
||||
var a: ?&s32 = null;
|
||||
a = expected_a.&;
|
||||
try expect(a orelse unreachable == expected_a.&);
|
||||
|
||||
var not_expected_b: s32 = 6;
|
||||
var b: ?&s32 = not_expected_b.&;
|
||||
b = null;
|
||||
try expect(b orelse return == not_expected_b.&);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user