This commit is contained in:
agra
2026-02-19 01:26:04 +02:00
parent fbf8a62362
commit e0e655cd36
11 changed files with 938 additions and 25 deletions

View File

@@ -165,6 +165,18 @@ pub const Parser = struct {
return self.parseUnionDecl(name, start_pos);
}
// UFCS alias: name :: ufcs target;
if (self.current.tag == .kw_ufcs) {
self.advance();
if (self.current.tag != .identifier) {
return self.fail("expected function name after 'ufcs'");
}
const target = self.tokenSlice(self.current);
self.advance();
try self.expect(.semicolon);
return try self.createNode(start_pos, .{ .ufcs_alias = .{ .name = name, .target = target } });
}
// Function declaration: (params) -> type { body } or () { body }
if (self.current.tag == .l_paren) {
// Look ahead: is this a function or an expression starting with `(`?
@@ -307,25 +319,32 @@ pub const Parser = struct {
self.advance();
return try self.createNode(start, .{ .type_expr = .{ .name = name, .is_generic = true } });
}
// Function pointer type: (ParamTypes) -> ReturnType
// Function type: (ParamTypes) -> ReturnType
// Tuple type: (T1, T2) or (T1) — no '->' after ')'
if (self.current.tag == .l_paren) {
self.advance(); // skip '('
var param_types = std.ArrayList(*Node).empty;
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
}
try param_types.append(self.allocator, try self.parseTypeExpr());
}
try self.expect(.r_paren);
var return_type: ?*Node = null;
if (self.current.tag == .arrow) {
// '->' present: function type
self.advance(); // skip '->'
return_type = try self.parseTypeExpr();
const return_type = try self.parseTypeExpr();
return try self.createNode(start, .{ .function_type_expr = .{
.param_types = try param_types.toOwnedSlice(self.allocator),
.return_type = return_type,
} });
}
return try self.createNode(start, .{ .function_type_expr = .{
.param_types = try param_types.toOwnedSlice(self.allocator),
.return_type = return_type,
// No '->': tuple type (even for single element)
return try self.createNode(start, .{ .tuple_type_expr = .{
.field_types = try param_types.toOwnedSlice(self.allocator),
.field_names = null,
} });
}
@@ -1162,14 +1181,18 @@ pub const Parser = struct {
// Dereference: expr.*
self.advance();
expr = try self.createNode(expr.span.start, .{ .deref_expr = .{ .operand = expr } });
} else {
// Field access
if (self.current.tag != .identifier) {
return self.fail("expected field name after '.'");
}
} else if (self.current.tag == .identifier) {
// Named field access: expr.field
const field = self.tokenSlice(self.current);
self.advance();
expr = try self.createNode(expr.span.start, .{ .field_access = .{ .object = expr, .field = field } });
} else if (self.current.tag == .int_literal) {
// Numeric field access: tuple.0, tuple.1
const field = self.tokenSlice(self.current);
self.advance();
expr = try self.createNode(expr.span.start, .{ .field_access = .{ .object = expr, .field = field } });
} else {
return self.fail("expected field name or index after '.'");
}
} else if (self.current.tag == .l_bracket) {
// Index or slice access: expr[expr] or expr[start..end]
@@ -1314,11 +1337,30 @@ pub const Parser = struct {
if (self.isLambda()) {
return self.parseLambda();
}
// Grouped expression
self.advance();
const expr = try self.parseExpr();
self.advance(); // skip '('
// Check for named tuple: (name: expr, ...)
if (self.current.tag == .identifier and self.peekNext() == .colon) {
return self.parseTupleLiteralNamed(start);
}
// Empty parens or first expression
if (self.current.tag == .r_paren) {
self.advance();
// () — empty tuple
return try self.createNode(start, .{ .tuple_literal = .{ .elements = &.{} } });
}
const first = try self.parseExpr();
// Check for comma → tuple
if (self.current.tag == .comma) {
return self.finishTupleAfterFirst(start, first);
}
// No comma → grouping
try self.expect(.r_paren);
return expr;
return first;
},
.kw_f32, .kw_f64, .kw_Type => {
// Type keyword used as expression (for type aliases: SOME_TYPE :: f64;)
@@ -1607,6 +1649,42 @@ pub const Parser = struct {
return try self.createNode(start_pos, .{ .match_expr = .{ .subject = subject, .arms = try arms.toOwnedSlice(self.allocator) } });
}
/// Parse a named tuple literal: (name: expr, name: expr, ...)
/// Called after '(' has been consumed and we've verified identifier + colon pattern.
fn parseTupleLiteralNamed(self: *Parser, start: u32) anyerror!*Node {
var elements = std.ArrayList(ast.TupleElement).empty;
while (self.current.tag != .r_paren and self.current.tag != .eof) {
if (self.current.tag != .identifier) {
return self.fail("expected field name in named tuple");
}
const name = self.tokenSlice(self.current);
self.advance();
try self.expect(.colon);
const value = try self.parseExpr();
try elements.append(self.allocator, .{ .name = name, .value = value });
if (self.current.tag == .comma) {
self.advance();
} else break;
}
try self.expect(.r_paren);
return try self.createNode(start, .{ .tuple_literal = .{ .elements = try elements.toOwnedSlice(self.allocator) } });
}
/// Finish parsing a tuple after the first positional element and a comma.
/// Called with first element already parsed and current token is ','.
fn finishTupleAfterFirst(self: *Parser, start: u32, first: *Node) anyerror!*Node {
var elements = std.ArrayList(ast.TupleElement).empty;
try elements.append(self.allocator, .{ .name = null, .value = first });
while (self.current.tag == .comma) {
self.advance(); // skip ','
if (self.current.tag == .r_paren) break; // trailing comma: (42,)
const value = try self.parseExpr();
try elements.append(self.allocator, .{ .name = null, .value = value });
}
try self.expect(.r_paren);
return try self.createNode(start, .{ .tuple_literal = .{ .elements = try elements.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 {
@@ -1755,7 +1833,7 @@ pub const Parser = struct {
.kw_and => 2,
.pipe => 3,
.ampersand => 3,
.equal_equal, .bang_equal, .less, .less_equal, .greater, .greater_equal => 4,
.equal_equal, .bang_equal, .less, .less_equal, .greater, .greater_equal, .kw_in => 4,
.plus, .minus => 5,
.star, .slash, .percent => 6,
else => 0,
@@ -1779,6 +1857,7 @@ pub const Parser = struct {
.less_equal => .lte,
.greater => .gt,
.greater_equal => .gte,
.kw_in => .in_op,
else => null,
};
}
@@ -1797,6 +1876,14 @@ pub const Parser = struct {
};
}
/// Peek at the next token's tag without consuming.
fn peekNext(self: *Parser) Tag {
const saved_lexer = self.lexer;
const tok = self.lexer.next();
self.lexer = saved_lexer;
return tok.tag;
}
fn advance(self: *Parser) void {
self.prev_end = self.current.loc.end;
self.current = self.lexer.next();