This commit is contained in:
agra
2026-02-15 17:26:15 +02:00
parent 2727b5509e
commit a3be9cce7c
4 changed files with 356 additions and 260 deletions

View File

@@ -711,16 +711,11 @@ pub const Parser = struct {
/// Collect generic type params and comptime value params from parameter annotations.
fn collectTypeParams(self: *Parser, params: []const ast.Param) ![]const ast.StructTypeParam {
var type_params = std.ArrayList(ast.StructTypeParam).empty;
var seen = std.StringHashMap(void).init(self.allocator);
for (params) |param| {
if (param.is_comptime) {
var found = false;
for (type_params.items) |existing| {
if (std.mem.eql(u8, existing.name, param.name)) {
found = true;
break;
}
}
if (!found) {
if (!seen.contains(param.name)) {
try seen.put(param.name, {});
try type_params.append(self.allocator, .{ .name = param.name, .constraint = param.type_expr });
}
} else {
@@ -728,14 +723,8 @@ pub const Parser = struct {
var generic_names = std.ArrayList([]const u8).empty;
collectGenericNames(param.type_expr, &generic_names, self.allocator);
for (generic_names.items) |gen_name| {
var found = false;
for (type_params.items) |existing| {
if (std.mem.eql(u8, existing.name, gen_name)) {
found = true;
break;
}
}
if (!found) {
if (!seen.contains(gen_name)) {
try seen.put(gen_name, {});
const type_constraint = self.createNode(param.type_expr.span.start, .{ .type_expr = .{ .name = "Type" } }) catch continue;
type_params.append(self.allocator, .{ .name = gen_name, .constraint = type_constraint }) catch {};
}
@@ -1506,8 +1495,30 @@ pub const Parser = struct {
return try self.createNode(start_pos, .{ .match_expr = .{ .subject = subject, .arms = try arms.toOwnedSlice(self.allocator) } });
}
/// Save state, skip past matching parens, return the tag of the next token, then restore.
/// Returns null if no matching ')' found before EOF.
fn peekPastParens(self: *Parser) ?Tag {
const saved_lexer = self.lexer;
const saved_current = self.current;
const saved_prev_end = self.prev_end;
defer {
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 null;
self.advance(); // skip ')'
return self.current.tag;
}
fn isLambda(self: *Parser) bool {
// Peek ahead: save state, scan to matching ), check if => or -> ... => follows
const saved_lexer = self.lexer;
const saved_current = self.current;
const saved_prev_end = self.prev_end;
@@ -1517,32 +1528,23 @@ pub const Parser = struct {
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) {
self.advance(); // skip ')'
if (self.current.tag == .fat_arrow) return true;
// (params) -> ReturnType => expr
if (self.current.tag == .arrow) {
self.advance(); // skip '->'
// 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;
// Return type tokens: identifiers, dots, parens, type keywords, dollar, brackets
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.advance();
} else break;
}
// Use shared paren-scanning, then check for lambda patterns
const tag = self.peekPastParens() orelse return false;
if (tag == .fat_arrow) return true;
// (params) -> ReturnType => expr
if (tag == .arrow) {
self.advance(); // skip '->'
// 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 == .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.advance();
} else break;
}
}
return false;
@@ -1573,29 +1575,8 @@ pub const Parser = struct {
// ---- Helpers ----
fn isFunctionDef(self: *Parser) bool {
// Peek ahead: save state, scan to matching ), check what follows
const saved_lexer = self.lexer;
const saved_current = self.current;
const saved_prev_end = self.prev_end;
defer {
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) {
self.advance(); // skip ')'
// Function if followed by '{', '->', '#builtin', '#foreign', or '=>'
return self.current.tag == .l_brace or self.current.tag == .arrow or self.current.tag == .hash_builtin or self.current.tag == .hash_foreign or self.current.tag == .fat_arrow;
}
return false;
const tag = self.peekPastParens() orelse return false;
return tag == .l_brace or tag == .arrow or tag == .hash_builtin or tag == .hash_foreign or tag == .fat_arrow;
}
fn isAssignOp(self: *const Parser) bool {