Write initial self-hosted code

This commit is contained in:
David Gonzalez Martin 2024-04-11 08:38:00 -06:00
parent 5473d1c5a9
commit 203bee684c
10 changed files with 499 additions and 64 deletions

View File

@ -3791,6 +3791,7 @@ pub const Instruction = union(enum) {
pointer_const_to_var, pointer_const_to_var,
pointer_to_nullable, pointer_to_nullable,
pointer_source_type_to_destination_type, pointer_source_type_to_destination_type,
pointer_to_not_nullable,
slice_var_to_const, slice_var_to_const,
slice_to_nullable, slice_to_nullable,
slice_to_not_null, slice_to_not_null,
@ -6267,6 +6268,7 @@ pub const Builder = struct {
}, },
else => |t| @panic(@tagName(t)), 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 right = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = expected_right_type }, node.right, .right);
const value_to_store = switch (node.id) { const value_to_store = switch (node.id) {
.assign => right, .assign => right,
@ -8691,11 +8693,12 @@ pub const Builder = struct {
} }
const cbb = unit.basic_blocks.get(builder.current_basic_block); 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 (!cbb.terminated and (cbb.instructions.length > 0 or cbb.predecessors.length > 0)) {
if (builder.return_block == .null) { if (builder.return_block == .null) {
switch (unit.types.get(return_type).*) { switch (return_type.*) {
.void => try builder.buildRet(unit, context, .{ .void => try builder.buildRet(unit, context, .{
.value = .{ .value = .{
.@"comptime" = .void, .@"comptime" = .void,
@ -8713,7 +8716,7 @@ pub const Builder = struct {
.value = .{ .value = .{
.@"comptime" = .undefined, .@"comptime" = .undefined,
}, },
.type = return_type, .type = return_type_index,
}; };
const insert = try unit.instructions.append(context.my_allocator, .{ const insert = try unit.instructions.append(context.my_allocator, .{
.insert_value = .{ .insert_value = .{
@ -8735,7 +8738,7 @@ pub const Builder = struct {
.value = .{ .value = .{
.runtime = insert, .runtime = insert,
}, },
.type = return_type, .type = return_type_index,
}); });
}, },
else => unreachable, else => unreachable,
@ -8752,7 +8755,8 @@ pub const Builder = struct {
const phi = &unit.instructions.get(builder.return_phi).phi; const phi = &unit.instructions.get(builder.return_phi).phi;
switch (unit.types.get(return_type).*) {
switch (return_type.*) {
.void => unreachable, .void => unreachable,
.noreturn => unreachable, .noreturn => unreachable,
.@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) { .@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) {
@ -8767,7 +8771,7 @@ pub const Builder = struct {
.value = .{ .value = .{
.@"comptime" = .undefined, .@"comptime" = .undefined,
}, },
.type = return_type, .type = return_type_index,
}, },
.index = 1, .index = 1,
.new_value = .{ .new_value = .{
@ -8786,7 +8790,7 @@ pub const Builder = struct {
.value = .{ .value = .{
.runtime = return_value, .runtime = return_value,
}, },
.type = return_type, .type = return_type_index,
}, builder.current_basic_block); }, builder.current_basic_block);
try builder.jump(unit, context, builder.return_block); try builder.jump(unit, context, builder.return_block);
@ -9169,9 +9173,6 @@ pub const Builder = struct {
.none => b: { .none => b: {
const pointer_type = unit.types.get(pointer_value.type); const pointer_type = unit.types.get(pointer_value.type);
const pointer_element_type = pointer_type.pointer.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; break :b pointer_element_type;
}, },
.type => |type_index| type_index, .type => |type_index| type_index,
@ -9358,6 +9359,7 @@ pub const Builder = struct {
.shift_left => .shift_left, .shift_left => .shift_left,
.shift_right => .shift_right, .shift_right => .shift_right,
.bool_and => .bit_and, .bool_and => .bit_and,
.bool_or => .bit_or,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
@ -9536,6 +9538,7 @@ pub const Builder = struct {
.bool => b: { .bool => b: {
const id: Instruction.IntegerBinaryOperation.Id = switch (binary_operation_id) { const id: Instruction.IntegerBinaryOperation.Id = switch (binary_operation_id) {
.bit_and => .bit_and, .bit_and => .bit_and,
.bit_or => .bit_or,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
const i = try unit.instructions.append(context.my_allocator, .{ const i = try unit.instructions.append(context.my_allocator, .{
@ -9607,17 +9610,18 @@ pub const Builder = struct {
break :block result; break :block result;
}, },
.block => block: { .block => block: {
assert(type_expect == .none or type_expect.type == .void);
const block = try builder.resolveBlock(unit, context, node_index); const block = try builder.resolveBlock(unit, context, node_index);
const block_i = try unit.instructions.append(context.my_allocator, .{ const block_i = try unit.instructions.append(context.my_allocator, .{
.block = block, .block = block,
}); });
break :block .{ break :block .{
.value = .{ .value = .{
.runtime = block_i, .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: { .container_literal => block: {
@ -9688,8 +9692,23 @@ pub const Builder = struct {
break :blk nullable_pointer; break :blk nullable_pointer;
}, },
}, },
.@"struct" => |struct_index| switch (unit.structs.get(struct_index).kind) { .slice => |slice| if (slice.nullable) b: {
else => |t| @panic(@tagName(t)), 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)), else => |t| @panic(@tagName(t)),
}, },
@ -9698,7 +9717,8 @@ pub const Builder = struct {
.slice => block: { .slice => block: {
const expression_to_slice = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.left, .left); 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); 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_start: V = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = Type.usize }, range_node.left, .right);
const range_end: V = switch (range_node.right) { const range_end: V = switch (range_node.right) {
@ -11182,14 +11202,174 @@ pub const Builder = struct {
else => unreachable, else => unreachable,
}, unreachable, &.{}), }, unreachable, &.{}),
}, },
.empty_container_literal_guess => { .empty_container_literal_guess => block: {
assert(node.left != .null); assert(node.left != .null);
assert(node.right != .null); assert(node.right != .null);
const container_type = try builder.resolveType(unit, context, node.left, &.{}); const container_type = try builder.resolveType(unit, context, node.left, &.{});
const node_list = unit.getNodeList(node.right); const node_list = unit.getNodeList(node.right);
assert(node_list.len == 0); assert(node_list.len == 0);
const result = try builder.resolveContainerLiteral(unit, context, node_list, container_type); 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)), else => |t| @panic(@tagName(t)),
}; };
@ -13239,6 +13419,7 @@ pub const Builder = struct {
const is_last_element_range = last_element_node.id == .range; const is_last_element_range = last_element_node.id == .range;
const not_range_len = payloads.len - @intFromBool(is_last_element_range); const not_range_len = payloads.len - @intFromBool(is_last_element_range);
if (slices.length > 0) { if (slices.length > 0) {
const load_i = try unit.instructions.append(context.my_allocator, .{ const load_i = try unit.instructions.append(context.my_allocator, .{
.load = .{ .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); _ = 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, .{ const load_iterator = try unit.instructions.append(context.my_allocator, .{
.load = .{ .load = .{
.value = .{ .value = .{
@ -13506,7 +13694,9 @@ pub const Builder = struct {
}, },
else => node.right, else => node.right,
}; };
const v = try builder.resolveRuntimeValue(unit, context, catch_type_expect, catch_expression_node_index, side); const v = try builder.resolveRuntimeValue(unit, context, catch_type_expect, catch_expression_node_index, side);
switch (error_union.type) { switch (error_union.type) {
.void, .noreturn => { .void, .noreturn => {
assert(unit.basic_blocks.get(builder.current_basic_block).terminated); assert(unit.basic_blocks.get(builder.current_basic_block).terminated);
@ -13519,7 +13709,7 @@ pub const Builder = struct {
phi: Instruction.Index, phi: Instruction.Index,
exit_block: BasicBlock.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; const expected_type = error_union.type;
assert(v.type == expected_type); assert(v.type == expected_type);
const phi_index = try unit.instructions.append(context.my_allocator, .{ const phi_index = try unit.instructions.append(context.my_allocator, .{
@ -13555,12 +13745,12 @@ pub const Builder = struct {
}, },
.type = error_union.type, .type = error_union.type,
}; };
if (is_block_terminated) {
return value; if (maybe_catch_info) |catch_info| {
} else { assert(!is_block_terminated);
const phi_index = catch_info.?.phi; const phi_index = catch_info.phi;
const phi = &unit.instructions.get(phi_index).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); try phi.addIncoming(context, value, builder.current_basic_block);
@ -13575,6 +13765,9 @@ pub const Builder = struct {
}, },
.type = error_union.type, .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 { 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); const return_node = unit.getNode(return_node_index);
assert(return_node.id == .@"return"); assert(return_node.id == .@"return");
assert(return_node.left != .null);
assert(return_node.right == .null); assert(return_node.right == .null);
const return_value = if (return_node.left != .null) b: {
const return_value_node_index = return_node.left; 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{ const return_value = try builder.resolveRuntimeValue(unit, context, Type.Expect{
.type = return_type, .type = return_type_index,
}, return_value_node_index, .right); }, 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_block != .null) {
if (builder.return_phi != .null) { if (builder.return_phi != .null) {
@ -14777,7 +14998,7 @@ pub const Builder = struct {
} else if (builder.exit_blocks.length > 0) { } else if (builder.exit_blocks.length > 0) {
builder.return_phi = try unit.instructions.append(context.my_allocator, .{ builder.return_phi = try unit.instructions.append(context.my_allocator, .{
.phi = .{ .phi = .{
.type = return_type, .type = return_type_index,
}, },
}); });

View File

@ -2051,7 +2051,7 @@ pub const LLVM = struct {
break :b slice_fields; break :b slice_fields;
} else b: { } 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; const len = llvm.context.getConstantInt(64, 0, false) orelse unreachable;
break :b .{ ptr.toConstant(), len.toConstant() }; 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)); 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_index = unit.types.get(constant_struct.type).@"struct";
const sema_struct = unit.structs.get(sema_struct_index); 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) { switch (sema_struct.kind) {
.@"struct" => |*sema_struct_type| { .@"struct" => |*sema_struct_type| {
for (constant_struct.fields, sema_struct_type.fields.slice()) |field_value, field_index| { 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); const constant = try llvm.emitComptimeRightValue(unit, context, field_value, field.type);
field_values.append_with_capacity(constant); field_values.append_with_capacity(constant);
} }
},
const llvm_type = try llvm.getType(unit, context, constant_struct.type); .error_union => |error_union| {
const struct_type = llvm_type.toStruct() orelse unreachable; const abi_ty = unit.types.get(error_union.abi);
const const_struct = struct_type.getConstant(field_values.pointer, field_values.length) orelse unreachable; switch (abi_ty.*) {
return const_struct; .@"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)),
} }
},
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 { 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_coerce_to_zero_termination,
.slice_zero_to_no_termination, .slice_zero_to_no_termination,
.pointer_to_nullable, .pointer_to_nullable,
.pointer_to_not_nullable,
.pointer_const_to_var, .pointer_const_to_var,
.pointer_to_array_to_pointer_to_many, .pointer_to_array_to_pointer_to_many,
.pointer_source_type_to_destination_type, .pointer_source_type_to_destination_type,

View File

@ -199,6 +199,9 @@ pub const Node = struct {
comptime_expression, comptime_expression,
self, self,
any, any,
for_expressions,
slice_metadata,
orelse_expression,
}; };
}; };
@ -552,6 +555,11 @@ const Analyzer = struct {
const while_block = try analyzer.block(); const while_block = try analyzer.block();
if (analyzer.peekToken() == .fixed_keyword_else) {
analyzer.consumeToken();
unreachable;
}
return analyzer.addNode(.{ return analyzer.addNode(.{
.id = .@"while", .id = .@"while",
.token = while_identifier_index, .token = while_identifier_index,
@ -782,7 +790,7 @@ const Analyzer = struct {
.right = try analyzer.nodeList(payload_nodes), .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(), .operator_left_brace => try analyzer.block(),
else => blk: { else => blk: {
const for_content_expression = try analyzer.expression(); 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(.{ const for_node = try analyzer.addNode(.{
.id = .for_loop, .id = .for_loop,
.token = token, .token = token,
.left = for_condition_node, .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; return for_node;
@ -1016,6 +1035,7 @@ const Analyzer = struct {
shift_left, shift_left,
shift_right, shift_right,
@"catch", @"catch",
@"orelse",
}; };
const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{ const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{
@ -1038,6 +1058,7 @@ const Analyzer = struct {
.shift_left = 50, .shift_left = 50,
.shift_right = 50, .shift_right = 50,
.@"catch" = 40, .@"catch" = 40,
.@"orelse" = 40,
}); });
const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{ const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{
@ -1060,6 +1081,7 @@ const Analyzer = struct {
.shift_left = .left, .shift_left = .left,
.shift_right = .left, .shift_right = .left,
.@"catch" = .left, .@"catch" = .left,
.@"orelse" = .left,
}); });
const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{ const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{
@ -1082,6 +1104,7 @@ const Analyzer = struct {
.shift_left = .shift_left, .shift_left = .shift_left,
.shift_right = .shift_right, .shift_right = .shift_right,
.@"catch" = .catch_expression, .@"catch" = .catch_expression,
.@"orelse" = .orelse_expression,
}); });
fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index { fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index {
@ -1139,6 +1162,7 @@ const Analyzer = struct {
.operator_shift_left => .shift_left, .operator_shift_left => .shift_left,
.operator_shift_right => .shift_right, .operator_shift_right => .shift_right,
.fixed_keyword_catch => .@"catch", .fixed_keyword_catch => .@"catch",
.fixed_keyword_orelse => .@"orelse",
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
@ -1251,7 +1275,13 @@ const Analyzer = struct {
analyzer.consumeToken(); analyzer.consumeToken();
break :blk token; 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, .right = Node.Index.null,
}), }),
.fixed_keyword_break => try analyzer.breakExpression(), .fixed_keyword_break => try analyzer.breakExpression(),
@ -2102,19 +2132,34 @@ const Analyzer = struct {
else => try analyzer.expression(), 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(.{ const slice_metadata = try analyzer.addNode(.{
.id = .slice, .id = .slice_metadata,
.token = token, .token = token,
.left = left, .left = try analyzer.addNode(.{
.right = try analyzer.addNode(.{
.id = .range, .id = .range,
.token = token, .token = token,
.left = index_expression, .left = index_expression,
.right = range_end_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 { } else {
_ = try analyzer.expectToken(.operator_right_bracket); _ = try analyzer.expectToken(.operator_right_bracket);
break :blk try analyzer.addNode(.{ break :blk try analyzer.addNode(.{

View File

@ -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" }); 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" }); const llvm_lib_dir = try std.mem.concat(b.allocator, u8, &.{ llvm_path, "/lib" });
for (static_llvm_libraries) |llvm_library| { 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 { } else {
compiler.linkSystemLibrary("LLVM-17"); compiler.linkSystemLibrary("LLVM-17");
@ -463,7 +463,7 @@ pub fn build(b: *std.Build) !void {
const install_exe = b.addInstallArtifact(compiler, .{}); const install_exe = b.addInstallArtifact(compiler, .{});
b.getInstallStep().dependOn(&install_exe.step); b.getInstallStep().dependOn(&install_exe.step);
b.installDirectory(.{ b.installDirectory(.{
.source_dir = std.Build.LazyPath.relative("lib"), .source_dir = std.Build.path(b, "lib"),
.install_dir = .bin, .install_dir = .bin,
.install_subdir = "lib", .install_subdir = "lib",
}); });

View File

@ -24,17 +24,17 @@ const unwrap_syscall = fn(syscall_result: ssize) Error!usize {
const MapFlags = switch (os) { const MapFlags = switch (os) {
.macos => bitfield(u32){ .macos => bitfield(u32){
shared: bool, shared: bool = false,
private: bool, private: bool = false,
reserved: u2 = 0, reserved: u2 = 0,
fixed: bool, fixed: bool = false,
reserved0: bool = 0, reserved0: bool = 0,
noreserve: bool, noreserve: bool = false,
reserved1: u2 = 0, reserved1: u2 = 0,
has_semaphore: bool, has_semaphore: bool = false,
no_cache: bool, no_cache: bool = false,
reserved2: u1 = 0, reserved2: u1 = 0,
anonymous: bool, anonymous: bool = false,
reserved3: u19 = 0, reserved3: u19 = 0,
}, },
.linux => linux.MapFlags, .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 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 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 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; const _NSGetExecutablePath :: extern = fn cc(.c) (buffer: [&:0]u8, buffer_size: &u32) s32;

View File

@ -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 print = fn(bytes: []const u8) void {
const file_descriptor = os.StdFileDescriptor.get(descriptor = .stdout); const file_descriptor = os.StdFileDescriptor.get(descriptor = .stdout);
const file_writer = FileWriter{ 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; var i: usize = 0;
while (string[i] != 0) { while (pointer_to_string[i] != 0) {
i += 1; i += 1;
} }
return i; 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 { const Target = struct {
cpu: builtin.Cpu, cpu: builtin.Cpu,
os: builtin.Os, os: builtin.Os,

View File

@ -1,17 +1,99 @@
const std = #import("std"); const std = #import("std");
const Arena = std.Arena; 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 main = fn() *!void {
const arena = try Arena.allocate(std.megabytes(64)); const arena = try Arena.allocate(std.megabytes(64));
const argument_count = std.start.argument_count; const argument_count = std.start.argument_count;
if (argument_count <= 1) {
return ArgumentProcessingError.no_arguments;
}
const argument_values = std.start.argument_values; const argument_values = std.start.argument_values;
const arguments = argument_values[0..argument_count]; const arguments = argument_values[0..argument_count];
for (arguments) |c_argument| { const command = c_slice(arguments[1]);
const argument_len = std.c_len(c_argument); const command_arguments = arguments[2..];
const argument = c_argument[0..argument_len];
std.print("Argument: "); if (byte_equal(command, "build")) {
std.print(argument); print("TODO: build");
std.print("\n"); } 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;
} }
} }

View 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 {
}
}

View 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);
}

View 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.&);
}