quick sort
This commit is contained in:
@@ -45,6 +45,7 @@ pub const Node = struct {
|
||||
import_decl: ImportDecl,
|
||||
namespace_decl: NamespaceDecl,
|
||||
array_type_expr: ArrayTypeExpr,
|
||||
slice_type_expr: SliceTypeExpr,
|
||||
array_literal: ArrayLiteral,
|
||||
parameterized_type_expr: ParameterizedTypeExpr,
|
||||
index_expr: IndexExpr,
|
||||
@@ -279,6 +280,10 @@ pub const ArrayTypeExpr = struct {
|
||||
element_type: *Node, // type_expr for the element type
|
||||
};
|
||||
|
||||
pub const SliceTypeExpr = struct {
|
||||
element_type: *Node, // type_expr for the element type
|
||||
};
|
||||
|
||||
pub const ArrayLiteral = struct {
|
||||
elements: []const *Node,
|
||||
type_expr: ?*Node = null,
|
||||
|
||||
162
src/codegen.zig
162
src/codegen.zig
@@ -81,6 +81,8 @@ pub const CodeGen = struct {
|
||||
// Cache of auto-generated to_string functions for complex types
|
||||
// Variadic function info: maps function name to variadic metadata
|
||||
variadic_functions: std.StringHashMap(VariadicInfo),
|
||||
// Maps function name to resolved sx parameter types (for accurate type conversion at call sites)
|
||||
fn_param_types: std.StringHashMap([]const Type),
|
||||
// Enriched Any type entries: maps type name to tag + category + sx type
|
||||
any_type_entries: std.StringHashMap(AnyTypeEntry),
|
||||
// Current match arm type entries (set during category match arm body generation)
|
||||
@@ -173,6 +175,7 @@ pub const CodeGen = struct {
|
||||
.namespaces = std.StringHashMap(void).init(allocator),
|
||||
.builtin_functions = std.StringHashMap(void).init(allocator),
|
||||
.variadic_functions = std.StringHashMap(VariadicInfo).init(allocator),
|
||||
.fn_param_types = std.StringHashMap([]const Type).init(allocator),
|
||||
.any_type_id_map = std.StringHashMap(u64).init(allocator),
|
||||
.any_type_entries = std.StringHashMap(AnyTypeEntry).init(allocator),
|
||||
};
|
||||
@@ -689,6 +692,13 @@ pub const CodeGen = struct {
|
||||
const elem_name = elem_type.displayName(self.allocator) catch unreachable;
|
||||
return .{ .array_type = .{ .element_name = elem_name, .length = length } };
|
||||
}
|
||||
// Slice type: []T
|
||||
if (tn.data == .slice_type_expr) {
|
||||
const ste = tn.data.slice_type_expr;
|
||||
const elem_type = self.resolveType(ste.element_type);
|
||||
const elem_name = elem_type.displayName(self.allocator) catch unreachable;
|
||||
return .{ .slice_type = .{ .element_name = elem_name } };
|
||||
}
|
||||
// Parameterized type: Vector(N, T) or generic struct instantiation
|
||||
if (tn.data == .parameterized_type_expr) {
|
||||
const pte = tn.data.parameterized_type_expr;
|
||||
@@ -1088,6 +1098,18 @@ pub const CodeGen = struct {
|
||||
const fn_type = try self.buildFnType(fd.params, fd.return_type, fd.name);
|
||||
const name_z = try self.allocator.dupeZ(u8, llvm_name);
|
||||
_ = c.LLVMAddFunction(self.module, name_z.ptr, fn_type);
|
||||
// Track resolved parameter types for accurate call-site conversion
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fd.params) |param| {
|
||||
if (param.is_comptime) continue;
|
||||
if (param.is_variadic) {
|
||||
const elem_name = if (param.type_expr.data == .type_expr) param.type_expr.data.type_expr.name else "s32";
|
||||
try param_types.append(self.allocator, .{ .slice_type = .{ .element_name = elem_name } });
|
||||
} else {
|
||||
try param_types.append(self.allocator, self.resolveType(param.type_expr));
|
||||
}
|
||||
}
|
||||
try self.fn_param_types.put(llvm_name, try param_types.toOwnedSlice(self.allocator));
|
||||
// Track variadic function info for call site packing
|
||||
for (fd.params, 0..) |param, i| {
|
||||
if (param.is_variadic) {
|
||||
@@ -2039,7 +2061,28 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.emitError("index assignment requires a string or array target");
|
||||
if (obj_ty.isSlice()) {
|
||||
const slice_info = obj_ty.slice_type;
|
||||
const elem_ty = Type.fromName(slice_info.element_name) orelse return self.emitError("unknown slice element type");
|
||||
const elem_llvm_ty = self.typeToLLVM(elem_ty);
|
||||
// Load slice value to get ptr
|
||||
const slice_val = blk: {
|
||||
if (ie.object.data == .identifier) {
|
||||
if (self.named_values.get(ie.object.data.identifier.name)) |entry| {
|
||||
break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), entry.ptr, "slice_load");
|
||||
}
|
||||
}
|
||||
break :blk try self.genExpr(ie.object);
|
||||
};
|
||||
const ptr = c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_ptr");
|
||||
const idx = try self.genExpr(ie.index);
|
||||
const val = try self.genExpr(asgn.value);
|
||||
var gep_indices = [_]c.LLVMValueRef{idx};
|
||||
const gep_ptr = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr, &gep_indices, 1, "sliceidx");
|
||||
_ = c.LLVMBuildStore(self.builder, val, gep_ptr);
|
||||
return null;
|
||||
}
|
||||
return self.emitError("index assignment requires a string, array, or slice target");
|
||||
}
|
||||
|
||||
fn unescapeString(allocator: std.mem.Allocator, raw: []const u8) ![]u8 {
|
||||
@@ -2165,11 +2208,12 @@ pub const CodeGen = struct {
|
||||
return self.genUnionLiteral(ul, null);
|
||||
},
|
||||
.array_literal => |al| {
|
||||
// Typed array/vector literal: Type.[elems]
|
||||
// Typed array/vector/slice literal: Type.[elems]
|
||||
if (al.type_expr) |te| {
|
||||
const ty = self.resolveType(te);
|
||||
if (ty.isVector()) return self.genVectorLiteral(al, ty);
|
||||
if (ty.isArray()) return self.genArrayLiteral(al, ty);
|
||||
if (ty.isSlice()) return self.genSliceLiteral(al, ty);
|
||||
}
|
||||
// If current return type is vector, build as vector SSA value
|
||||
if (self.current_return_type.isVector()) {
|
||||
@@ -2527,6 +2571,40 @@ pub const CodeGen = struct {
|
||||
return alloca;
|
||||
}
|
||||
|
||||
fn genSliceLiteral(self: *CodeGen, al: ast.ArrayLiteral, slice_ty: Type) !c.LLVMValueRef {
|
||||
const elem_name = slice_ty.slice_type.element_name;
|
||||
const elem_sx_ty = Type.fromName(elem_name) orelse return self.emitErrorFmt("unknown slice element type '{s}'", .{elem_name});
|
||||
const n: u32 = @intCast(al.elements.len);
|
||||
|
||||
// Create backing array [N]elem on the stack
|
||||
const arr_ty: Type = .{ .array_type = .{ .element_name = elem_name, .length = n } };
|
||||
const llvm_arr_ty = self.typeToLLVM(arr_ty);
|
||||
const arr_alloca = c.LLVMBuildAlloca(self.builder, llvm_arr_ty, "slice_backing");
|
||||
|
||||
// Fill elements
|
||||
for (0..n) |i| {
|
||||
const val = try self.genExprAsType(al.elements[i], elem_sx_ty);
|
||||
var indices = [_]c.LLVMValueRef{
|
||||
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0),
|
||||
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0),
|
||||
};
|
||||
const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "slice_elem");
|
||||
_ = c.LLVMBuildStore(self.builder, val, gep);
|
||||
}
|
||||
|
||||
// Build slice {ptr, len}
|
||||
const i32_ty = c.LLVMInt32TypeInContext(self.context);
|
||||
const zero = c.LLVMConstInt(i32_ty, 0, 0);
|
||||
var gep_indices = [_]c.LLVMValueRef{ zero, zero };
|
||||
const elem_ptr = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &gep_indices, 2, "slice_data");
|
||||
const slice_llvm_ty = self.getStringStructType();
|
||||
var slice_val = c.LLVMGetUndef(slice_llvm_ty);
|
||||
slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, elem_ptr, 0, "slice_ptr");
|
||||
const len_val = c.LLVMConstInt(i32_ty, n, 0);
|
||||
slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, len_val, 1, "slice_len");
|
||||
return slice_val;
|
||||
}
|
||||
|
||||
fn genVectorLiteral(self: *CodeGen, al: ast.ArrayLiteral, vec_ty: Type) !c.LLVMValueRef {
|
||||
const vec_info = vec_ty.vector_type;
|
||||
const elem_sx_ty = Type.fromName(vec_info.element_name) orelse return self.emitErrorFmt("unknown vector element type '{s}'", .{vec_info.element_name});
|
||||
@@ -2646,6 +2724,41 @@ pub const CodeGen = struct {
|
||||
return self.genVectorLiteral(node.data.array_literal, target_ty);
|
||||
}
|
||||
|
||||
// Array literal with target slice type: build stack-backed slice
|
||||
if (node.data == .array_literal and target_ty.isSlice()) {
|
||||
return self.genSliceLiteral(node.data.array_literal, target_ty);
|
||||
}
|
||||
|
||||
// Array to slice coercion: [N]T → []T
|
||||
if (target_ty.isSlice()) {
|
||||
const src_ty = self.inferType(node);
|
||||
if (src_ty.isArray()) {
|
||||
const arr_info = src_ty.array_type;
|
||||
// Get the alloca pointer for the array (not the loaded value)
|
||||
const arr_alloca = blk: {
|
||||
if (node.data == .identifier) {
|
||||
if (self.named_values.get(node.data.identifier.name)) |entry| {
|
||||
break :blk entry.ptr;
|
||||
}
|
||||
}
|
||||
// Fallback: generate the expression and hope it returns a pointer
|
||||
break :blk try self.genExpr(node);
|
||||
};
|
||||
// GEP to get pointer to first element
|
||||
const i32_ty = c.LLVMInt32TypeInContext(self.context);
|
||||
const zero = c.LLVMConstInt(i32_ty, 0, 0);
|
||||
var indices = [_]c.LLVMValueRef{ zero, zero };
|
||||
const elem_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(src_ty), arr_alloca, &indices, 2, "arr_data");
|
||||
// Build slice struct {ptr, len}
|
||||
const slice_ty = self.getStringStructType();
|
||||
var slice_val = c.LLVMGetUndef(slice_ty);
|
||||
slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, elem_ptr, 0, "slice_ptr");
|
||||
const len_val = c.LLVMConstInt(i32_ty, arr_info.length, 0);
|
||||
slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, len_val, 1, "slice_len");
|
||||
return slice_val;
|
||||
}
|
||||
}
|
||||
|
||||
const val = try self.genExpr(node);
|
||||
const src_ty = self.inferType(node);
|
||||
|
||||
@@ -3672,10 +3785,20 @@ pub const CodeGen = struct {
|
||||
try arg_vals.append(self.allocator, slice_val);
|
||||
}
|
||||
} else {
|
||||
// Normal (non-variadic) call
|
||||
// Normal (non-variadic) call — use stored sx param types when available
|
||||
const stored_param_types = self.fn_param_types.get(callee_name) orelse blk: {
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name });
|
||||
break :blk self.fn_param_types.get(qualified);
|
||||
}
|
||||
break :blk null;
|
||||
};
|
||||
for (call_node.args, 0..) |arg, i| {
|
||||
if (i < num_params) {
|
||||
const param_ty = self.llvmTypeToSxType(param_llvm_types[i]);
|
||||
const param_ty = if (stored_param_types != null and i < stored_param_types.?.len)
|
||||
stored_param_types.?[i]
|
||||
else
|
||||
self.llvmTypeToSxType(param_llvm_types[i]);
|
||||
try arg_vals.append(self.allocator, try self.genExprAsType(arg, param_ty));
|
||||
} else {
|
||||
try arg_vals.append(self.allocator, try self.genExpr(arg));
|
||||
@@ -3754,6 +3877,7 @@ pub const CodeGen = struct {
|
||||
var bindings = std.StringHashMap(Type).init(self.allocator);
|
||||
for (fd.params, 0..) |param, i| {
|
||||
if (param.is_comptime) continue;
|
||||
// Direct type param: (a: $T) or (a: T)
|
||||
if (param.type_expr.data == .type_expr) {
|
||||
const type_name = param.type_expr.data.type_expr.name;
|
||||
// Check if this type name is a type parameter
|
||||
@@ -3772,6 +3896,32 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Slice type param: (items: []$T) — infer T from array or slice element type
|
||||
if (param.type_expr.data == .slice_type_expr) {
|
||||
const elem_node = param.type_expr.data.slice_type_expr.element_type;
|
||||
if (elem_node.data == .type_expr) {
|
||||
const type_name = elem_node.data.type_expr.name;
|
||||
for (fd.type_params) |tp| {
|
||||
if (std.mem.eql(u8, tp.name, type_name)) {
|
||||
if (i < call_node.args.len) {
|
||||
const arg_ty = self.inferType(call_node.args[i]);
|
||||
const elem_ty = if (arg_ty.isArray())
|
||||
Type.fromName(arg_ty.array_type.element_name) orelse arg_ty
|
||||
else if (arg_ty.isSlice())
|
||||
Type.fromName(arg_ty.slice_type.element_name) orelse arg_ty
|
||||
else
|
||||
arg_ty;
|
||||
if (bindings.get(type_name)) |existing| {
|
||||
try bindings.put(type_name, Type.widen(existing, elem_ty));
|
||||
} else {
|
||||
try bindings.put(type_name, elem_ty);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_comptime_values) {
|
||||
@@ -3802,13 +3952,15 @@ pub const CodeGen = struct {
|
||||
const args_slice = try arg_vals.toOwnedSlice(self.allocator);
|
||||
|
||||
const fn_type = c.LLVMGlobalGetValueType(callee_fn);
|
||||
const ret_ty = c.LLVMGetReturnType(fn_type);
|
||||
const call_name: [*c]const u8 = if (ret_ty == c.LLVMVoidTypeInContext(self.context)) "" else "calltmp";
|
||||
return c.LLVMBuildCall2(
|
||||
self.builder,
|
||||
fn_type,
|
||||
callee_fn,
|
||||
if (args_slice.len > 0) args_slice.ptr else null,
|
||||
@intCast(args_slice.len),
|
||||
"calltmp",
|
||||
call_name,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -206,9 +206,15 @@ pub const Parser = struct {
|
||||
fn parseTypeExpr(self: *Parser) anyerror!*Node {
|
||||
const start = self.current.loc.start;
|
||||
|
||||
// Array type: [N]T
|
||||
// Array type: [N]T or Slice type: []T
|
||||
if (self.current.tag == .l_bracket) {
|
||||
self.advance(); // skip '['
|
||||
if (self.current.tag == .r_bracket) {
|
||||
// Slice type: []T
|
||||
self.advance(); // skip ']'
|
||||
const elem_type = try self.parseTypeExpr();
|
||||
return try self.createNode(start, .{ .slice_type_expr = .{ .element_type = elem_type } });
|
||||
}
|
||||
const len_node = try self.parseExpr();
|
||||
try self.expect(.r_bracket);
|
||||
const elem_type = try self.parseTypeExpr();
|
||||
@@ -231,11 +237,20 @@ pub const Parser = struct {
|
||||
|
||||
// Qualified name: ns.Type or ns.Type(args)
|
||||
while (self.current.tag == .dot) {
|
||||
const dot_lexer = self.lexer;
|
||||
const dot_current = self.current;
|
||||
const dot_prev_end = self.prev_end;
|
||||
self.advance();
|
||||
if (self.current.tag == .identifier or self.current.tag.isTypeKeyword()) {
|
||||
name = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ name, self.tokenSlice(self.current) });
|
||||
self.advance();
|
||||
} else break;
|
||||
} else {
|
||||
// Not a qualified name continuation — restore the dot
|
||||
self.lexer = dot_lexer;
|
||||
self.current = dot_current;
|
||||
self.prev_end = dot_prev_end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Parameterized type: Vector(N, T) or later generic struct instantiation
|
||||
@@ -533,17 +548,27 @@ pub const Parser = struct {
|
||||
if (!found) {
|
||||
try type_params.append(self.allocator, .{ .name = param.name, .constraint = param.type_expr });
|
||||
}
|
||||
} else if (param.type_expr.data == .type_expr and param.type_expr.data.type_expr.is_generic) {
|
||||
var found = false;
|
||||
for (type_params.items) |existing| {
|
||||
if (std.mem.eql(u8, existing.name, param.type_expr.data.type_expr.name)) {
|
||||
found = true;
|
||||
break;
|
||||
} else {
|
||||
// Check for generic type param: direct $T or nested inside []$T
|
||||
const generic_type_expr: ?*Node = if (param.type_expr.data == .type_expr and param.type_expr.data.type_expr.is_generic)
|
||||
param.type_expr
|
||||
else if (param.type_expr.data == .slice_type_expr) blk: {
|
||||
const elem = param.type_expr.data.slice_type_expr.element_type;
|
||||
break :blk if (elem.data == .type_expr and elem.data.type_expr.is_generic) elem else null;
|
||||
} else null;
|
||||
|
||||
if (generic_type_expr) |gte| {
|
||||
var found = false;
|
||||
for (type_params.items) |existing| {
|
||||
if (std.mem.eql(u8, existing.name, gte.data.type_expr.name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
const type_constraint = try self.createNode(param.type_expr.span.start, .{ .type_expr = .{ .name = "Type" } });
|
||||
try type_params.append(self.allocator, .{ .name = gte.data.type_expr.name, .constraint = type_constraint });
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
const type_constraint = try self.createNode(param.type_expr.span.start, .{ .type_expr = .{ .name = "Type" } });
|
||||
try type_params.append(self.allocator, .{ .name = param.type_expr.data.type_expr.name, .constraint = type_constraint });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1034,6 +1059,10 @@ pub const Parser = struct {
|
||||
null;
|
||||
return try self.createNode(start, .{ .return_stmt = .{ .value = value } });
|
||||
},
|
||||
.l_bracket => {
|
||||
// Type expression in expression position: []T.[...] or [N]T.[...]
|
||||
return try self.parseTypeExpr();
|
||||
},
|
||||
.l_brace => {
|
||||
return self.parseBlock();
|
||||
},
|
||||
|
||||
11
src/sema.zig
11
src/sema.zig
@@ -224,6 +224,13 @@ pub const Analyzer = struct {
|
||||
const elem_name = elem_type.displayName(self.allocator) catch return .void_type;
|
||||
return .{ .array_type = .{ .element_name = elem_name, .length = length } };
|
||||
}
|
||||
// Slice type: []T
|
||||
if (tn.data == .slice_type_expr) {
|
||||
const ste = tn.data.slice_type_expr;
|
||||
const elem_type = self.resolveTypeNode(ste.element_type);
|
||||
const elem_name = elem_type.displayName(self.allocator) catch return .void_type;
|
||||
return .{ .slice_type = .{ .element_name = elem_name } };
|
||||
}
|
||||
// Parameterized type: Vector(N, T) or generic struct
|
||||
if (tn.data == .parameterized_type_expr) {
|
||||
// For now, skip generic instantiation — just return void_type
|
||||
@@ -406,7 +413,7 @@ pub const Analyzer = struct {
|
||||
try self.analyzeNode(val);
|
||||
}
|
||||
},
|
||||
.enum_decl, .struct_decl, .union_decl, .array_type_expr, .array_literal, .parameterized_type_expr, .index_expr, .insert_expr => {},
|
||||
.enum_decl, .struct_decl, .union_decl, .array_type_expr, .slice_type_expr, .array_literal, .parameterized_type_expr, .index_expr, .insert_expr => {},
|
||||
.namespace_decl => |ns| {
|
||||
try self.pushScope();
|
||||
for (ns.decls) |d| {
|
||||
@@ -630,6 +637,7 @@ pub const Analyzer = struct {
|
||||
.builtin_expr,
|
||||
.import_decl,
|
||||
.array_type_expr,
|
||||
.slice_type_expr,
|
||||
.array_literal,
|
||||
.parameterized_type_expr,
|
||||
.index_expr,
|
||||
@@ -876,6 +884,7 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
||||
.union_decl,
|
||||
.import_decl,
|
||||
.array_type_expr,
|
||||
.slice_type_expr,
|
||||
.array_literal,
|
||||
.parameterized_type_expr,
|
||||
.index_expr,
|
||||
|
||||
@@ -180,6 +180,10 @@ pub const Type = union(enum) {
|
||||
/// Everything else requires `xx`.
|
||||
pub fn isImplicitlyConvertibleTo(self: Type, target: Type) bool {
|
||||
if (std.meta.eql(self, target)) return true;
|
||||
// Slice types: compare element names by content (not pointer)
|
||||
if (self.isSlice() and target.isSlice()) {
|
||||
return std.mem.eql(u8, self.slice_type.element_name, target.slice_type.element_name);
|
||||
}
|
||||
|
||||
const src_float = self.isFloat();
|
||||
const dst_float = target.isFloat();
|
||||
|
||||
Reference in New Issue
Block a user