This commit is contained in:
agra
2026-02-23 13:45:44 +02:00
parent 1cc67f9b5a
commit 0cc7b69441
11 changed files with 1472 additions and 31 deletions

View File

@@ -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,