closures
This commit is contained in:
@@ -68,6 +68,7 @@ pub const Node = struct {
|
||||
foreign_expr: ForeignExpr,
|
||||
library_decl: LibraryDecl,
|
||||
function_type_expr: FunctionTypeExpr,
|
||||
closure_type_expr: ClosureTypeExpr,
|
||||
tuple_type_expr: TupleTypeExpr,
|
||||
tuple_literal: TupleLiteral,
|
||||
ufcs_alias: UfcsAlias,
|
||||
@@ -427,6 +428,13 @@ pub const LibraryDecl = struct {
|
||||
|
||||
pub const FunctionTypeExpr = struct {
|
||||
param_types: []const *Node,
|
||||
param_names: ?[]const ?[]const u8 = null, // optional documentation names
|
||||
return_type: ?*Node, // null = void return
|
||||
};
|
||||
|
||||
pub const ClosureTypeExpr = struct {
|
||||
param_types: []const *Node,
|
||||
param_names: ?[]const ?[]const u8 = null, // optional documentation names
|
||||
return_type: ?*Node, // null = void return
|
||||
};
|
||||
|
||||
|
||||
882
src/codegen.zig
882
src/codegen.zig
File diff suppressed because it is too large
Load Diff
104
src/parser.zig
104
src/parser.zig
@@ -390,14 +390,28 @@ pub const Parser = struct {
|
||||
}
|
||||
// Function type: (ParamTypes) -> ReturnType
|
||||
// Tuple type: (T1, T2) or (T1) — no '->' after ')'
|
||||
// Named params (documentation only): (name: Type, ...) -> ReturnType
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
var param_types = std.ArrayList(*Node).empty;
|
||||
var param_names = std.ArrayList(?[]const u8).empty;
|
||||
var has_names = false;
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (param_types.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
if (self.current.tag == .r_paren) break; // trailing comma ok
|
||||
}
|
||||
// Check for optional param name: `name: Type`
|
||||
// An identifier followed by `:` (not `::` or `:=`) is a param name
|
||||
if (self.current.tag == .identifier and self.peekNext() == .colon) {
|
||||
const pname = self.tokenSlice(self.current);
|
||||
self.advance(); // skip name
|
||||
self.advance(); // skip ':'
|
||||
try param_names.append(self.allocator, pname);
|
||||
has_names = true;
|
||||
} else {
|
||||
try param_names.append(self.allocator, null);
|
||||
}
|
||||
try param_types.append(self.allocator, try self.parseTypeExpr());
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
@@ -407,6 +421,7 @@ pub const Parser = struct {
|
||||
const return_type = try self.parseTypeExpr();
|
||||
return try self.createNode(start, .{ .function_type_expr = .{
|
||||
.param_types = try param_types.toOwnedSlice(self.allocator),
|
||||
.param_names = if (has_names) try param_names.toOwnedSlice(self.allocator) else null,
|
||||
.return_type = return_type,
|
||||
} });
|
||||
}
|
||||
@@ -439,6 +454,42 @@ pub const Parser = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Closure type: Closure(params...) -> R
|
||||
if (std.mem.eql(u8, name, "Closure") and self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
var param_types = std.ArrayList(*Node).empty;
|
||||
var param_names = std.ArrayList(?[]const u8).empty;
|
||||
var has_names = false;
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (param_types.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
if (self.current.tag == .r_paren) break; // trailing comma ok
|
||||
}
|
||||
// Check for optional param name: `name: Type`
|
||||
if (self.current.tag == .identifier and self.peekNext() == .colon) {
|
||||
const pname = self.tokenSlice(self.current);
|
||||
self.advance(); // skip name
|
||||
self.advance(); // skip ':'
|
||||
try param_names.append(self.allocator, pname);
|
||||
has_names = true;
|
||||
} else {
|
||||
try param_names.append(self.allocator, null);
|
||||
}
|
||||
try param_types.append(self.allocator, try self.parseTypeExpr());
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
var return_type: ?*Node = null;
|
||||
if (self.current.tag == .arrow) {
|
||||
self.advance();
|
||||
return_type = try self.parseTypeExpr();
|
||||
}
|
||||
return try self.createNode(start, .{ .closure_type_expr = .{
|
||||
.param_types = try param_types.toOwnedSlice(self.allocator),
|
||||
.param_names = if (has_names) try param_names.toOwnedSlice(self.allocator) else null,
|
||||
.return_type = return_type,
|
||||
} });
|
||||
}
|
||||
|
||||
// Parameterized type: Vector(N, T) or later generic struct instantiation
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
@@ -1889,24 +1940,56 @@ pub const Parser = struct {
|
||||
self.prev_end = saved_prev_end;
|
||||
}
|
||||
|
||||
// Use shared paren-scanning, then check for lambda patterns
|
||||
const tag = self.peekPastParens() orelse return false;
|
||||
// Check upfront if parens look like function params (for block-body disambiguation)
|
||||
const has_param_parens = blk: {
|
||||
self.advance(); // skip '('
|
||||
if (self.current.tag == .r_paren) break :blk true; // empty parens
|
||||
if (self.current.tag != .identifier) break :blk false;
|
||||
self.advance();
|
||||
break :blk self.current.tag == .colon;
|
||||
};
|
||||
|
||||
// Restore to '(' and scan past parens inline (not via peekPastParens which restores state)
|
||||
self.lexer = saved_lexer;
|
||||
self.current = saved_current;
|
||||
self.prev_end = saved_prev_end;
|
||||
self.advance(); // skip '('
|
||||
var depth: u32 = 1;
|
||||
while (depth > 0 and self.current.tag != .eof) {
|
||||
if (self.current.tag == .l_paren) depth += 1;
|
||||
if (self.current.tag == .r_paren) depth -= 1;
|
||||
if (depth > 0) self.advance();
|
||||
}
|
||||
if (self.current.tag != .r_paren) return false;
|
||||
self.advance(); // skip ')' — now positioned on token after parens
|
||||
|
||||
const tag = self.current.tag;
|
||||
// (params) => expr
|
||||
if (tag == .fat_arrow) return true;
|
||||
// (params) -> ReturnType => expr
|
||||
// (params) -> ReturnType { stmts }
|
||||
if (tag == .arrow) {
|
||||
self.advance(); // skip '->'
|
||||
// Skip past the return type tokens until we see '=>' or something unexpected
|
||||
// Skip past the return type tokens until we see '=>', '{', or something unexpected
|
||||
while (self.current.tag != .eof) {
|
||||
if (self.current.tag == .fat_arrow) return true;
|
||||
if (self.current.tag == .l_brace) return true;
|
||||
if (self.current.tag == .identifier or self.current.tag.isTypeKeyword() or
|
||||
self.current.tag == .dot or self.current.tag == .dollar or
|
||||
self.current.tag == .l_bracket or self.current.tag == .r_bracket or
|
||||
self.current.tag == .l_paren or self.current.tag == .r_paren or
|
||||
self.current.tag == .comma or self.current.tag == .int_literal)
|
||||
self.current.tag == .comma or self.current.tag == .int_literal or
|
||||
self.current.tag == .star or self.current.tag == .question)
|
||||
{
|
||||
self.advance();
|
||||
} else break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// (params) { stmts } — block-body lambda
|
||||
// Only if contents look like function params (have `:` type annotations or is empty `()`)
|
||||
if (tag == .l_brace) {
|
||||
return has_param_parens;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1915,15 +1998,22 @@ pub const Parser = struct {
|
||||
const start = self.current.loc.start;
|
||||
const params = try self.parseParams();
|
||||
|
||||
// Optional return type: (params) -> Type => expr
|
||||
// Optional return type: (params) -> Type => expr OR (params) -> Type { stmts }
|
||||
var return_type: ?*Node = null;
|
||||
if (self.current.tag == .arrow) {
|
||||
self.advance();
|
||||
return_type = try self.parseTypeExpr();
|
||||
}
|
||||
|
||||
try self.expect(.fat_arrow);
|
||||
const body = try self.parseExpr();
|
||||
// Two body forms:
|
||||
// (params) => expr — expression lambda
|
||||
// (params) { stmts } — block-body lambda
|
||||
const body = if (self.current.tag == .l_brace)
|
||||
try self.parseBlock()
|
||||
else blk: {
|
||||
try self.expect(.fat_arrow);
|
||||
break :blk try self.parseExpr();
|
||||
};
|
||||
const type_params = try self.collectTypeParams(params);
|
||||
return try self.createNode(start, .{ .lambda = .{
|
||||
.params = params,
|
||||
|
||||
@@ -848,6 +848,7 @@ pub const Analyzer = struct {
|
||||
.foreign_expr,
|
||||
.library_decl,
|
||||
.function_type_expr,
|
||||
.closure_type_expr,
|
||||
.import_decl,
|
||||
.c_import_decl,
|
||||
.array_type_expr,
|
||||
@@ -1225,6 +1226,7 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
||||
.slice_expr,
|
||||
.tuple_type_expr,
|
||||
.ufcs_alias,
|
||||
.closure_type_expr,
|
||||
=> {},
|
||||
.tuple_literal => |tl| {
|
||||
for (tl.elements) |elem| {
|
||||
|
||||
@@ -22,6 +22,7 @@ pub const Type = union(enum) {
|
||||
many_pointer_type: ManyPointerTypeInfo,
|
||||
vector_type: VectorTypeInfo,
|
||||
function_type: FunctionTypeInfo,
|
||||
closure_type: ClosureTypeInfo,
|
||||
any_type,
|
||||
optional_type: OptionalTypeInfo,
|
||||
meta_type: MetaTypeInfo,
|
||||
@@ -44,6 +45,11 @@ pub const Type = union(enum) {
|
||||
return_type: *const Type,
|
||||
};
|
||||
|
||||
pub const ClosureTypeInfo = struct {
|
||||
param_types: []const Type,
|
||||
return_type: *const Type,
|
||||
};
|
||||
|
||||
pub const ArrayTypeInfo = struct {
|
||||
element_name: []const u8,
|
||||
length: u32,
|
||||
@@ -95,6 +101,14 @@ pub const Type = union(enum) {
|
||||
}
|
||||
return info.return_type.eql(o.return_type.*);
|
||||
},
|
||||
.closure_type => |info| {
|
||||
const o = other.closure_type;
|
||||
if (info.param_types.len != o.param_types.len) return false;
|
||||
for (info.param_types, o.param_types) |a, b| {
|
||||
if (!a.eql(b)) return false;
|
||||
}
|
||||
return info.return_type.eql(o.return_type.*);
|
||||
},
|
||||
.optional_type => |info| std.mem.eql(u8, info.child_name, other.optional_type.child_name),
|
||||
.meta_type => |info| std.mem.eql(u8, info.name, other.meta_type.name),
|
||||
.tuple_type => |info| {
|
||||
@@ -302,6 +316,21 @@ pub const Type = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isClosureType(self: Type) bool {
|
||||
return switch (self) {
|
||||
.closure_type => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true for both bare function pointers and closures
|
||||
pub fn isCallable(self: Type) bool {
|
||||
return switch (self) {
|
||||
.function_type, .closure_type => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isArray(self: Type) bool {
|
||||
return switch (self) {
|
||||
.array_type => true,
|
||||
@@ -356,6 +385,7 @@ pub const Type = union(enum) {
|
||||
.f64 => 64,
|
||||
.boolean => 1,
|
||||
.pointer_type, .many_pointer_type, .function_type => 64,
|
||||
.closure_type => 128, // { ptr, ptr } = 16 bytes
|
||||
else => 0,
|
||||
};
|
||||
}
|
||||
@@ -506,6 +536,20 @@ pub const Type = union(enum) {
|
||||
}
|
||||
return try buf.toOwnedSlice(allocator);
|
||||
},
|
||||
.closure_type => |info| {
|
||||
var buf = std.ArrayList(u8).empty;
|
||||
try buf.appendSlice(allocator, "Closure(");
|
||||
for (info.param_types, 0..) |pt, i| {
|
||||
if (i > 0) try buf.appendSlice(allocator, ", ");
|
||||
try buf.appendSlice(allocator, try pt.displayName(allocator));
|
||||
}
|
||||
try buf.append(allocator, ')');
|
||||
if (!std.meta.eql(info.return_type.*, Type.void_type)) {
|
||||
try buf.appendSlice(allocator, " -> ");
|
||||
try buf.appendSlice(allocator, try info.return_type.displayName(allocator));
|
||||
}
|
||||
return try buf.toOwnedSlice(allocator);
|
||||
},
|
||||
.optional_type => |info| return fmtAlloc(allocator, "?{s}", .{info.child_name}),
|
||||
.meta_type => |info| info.name,
|
||||
.tuple_type => |info| {
|
||||
|
||||
Reference in New Issue
Block a user