ffi M5.A.next.1b: parser accepts ..$args as a variadic-pack param

Extends parseParams in src/parser.zig:1558 to recognize a leading
`..` before the optional `$` sigil and the parameter name. The
old `args: ..T` form (variadic marker after the colon) still
works — both paths set the same `is_variadic` flag.

A pack declaration `..$args` parses as:
- `is_variadic = true` (from the leading `..`)
- `is_comptime = true` (from the `$` sigil)
- `type_expr = inferred_type` (no `:` annotation)

The no-colon branch now propagates `is_variadic` and `is_comptime`
onto the Param struct so later slices (type rep, impl matching,
monomorphisation) can read both flags from the parsed AST without
re-deriving from token sequence.

`examples/150-pack-parse.sx` flips from rejecting-with-error to
positive parse smoke. No semantic effect yet — `foo` is declared
but never instantiated.

191/191 example tests + `zig build test` green.
This commit is contained in:
agra
2026-05-27 09:49:41 +03:00
parent ad82847b76
commit a51fe26cbf
4 changed files with 29 additions and 15 deletions

View File

@@ -1,14 +1,14 @@
// Variadic heterogeneous type packs — `..$args` — parse lockin.
// Variadic heterogeneous type packs — `..$args` — parse smoke.
//
// First slice of the pack feature: the parser does not yet accept
// `..$args` (variadic-marker + comptime sigil + name, no type
// annotation). This test pins the current parse-rejection so the
// next commit's parser change is visible as a behavior shift.
// First positive slice of the pack feature: the parser accepts
// `..$args` (variadic marker + comptime sigil + name) as a
// parameter declaration. No semantic effect yet — the function
// is declared but never instantiated; main exists so the example
// has runnable output.
//
// Expected next commit: parser accepts `..$args` and this test
// either flips to a successful parse (compile error from later
// passes that haven't been wired up yet) or is replaced by a
// positive test.
// Next slices: type-system representation of the pack, impl
// matching for `Closure(..$args) -> $R`, runtime indexing
// (`args[$i]`), and the `#insert build_x($args, ...)` pattern.
#import "modules/std.sx";
@@ -17,6 +17,6 @@ foo :: (..$args) -> s64 {
}
main :: () -> s32 {
print("never reached\n");
print("pack parse ok\n");
return 0;
}

View File

@@ -1563,6 +1563,15 @@ pub const Parser = struct {
try self.expect(.comma);
if (self.current.tag == .r_paren) break;
}
// Leading `..` marks a variadic param at the binding site
// (e.g., `..$args` heterogeneous pack, or future homogeneous
// `..args: []$T`). The old `args: ..T` form keeps its marker
// after the colon (handled below).
var is_variadic = false;
if (self.current.tag == .dot_dot) {
is_variadic = true;
self.advance();
}
var is_ct_param = false;
if (self.current.tag == .dollar) {
is_ct_param = true;
@@ -1577,12 +1586,17 @@ pub const Parser = struct {
// Optional type annotation: if no ':', infer type from context
if (self.current.tag != .colon) {
const inferred_node = try self.createNode(param_name_span.start, .{ .inferred_type = {} });
try params.append(self.allocator, .{ .name = param_name, .name_span = param_name_span, .type_expr = inferred_node });
try params.append(self.allocator, .{ .name = param_name, .name_span = param_name_span, .type_expr = inferred_node, .is_variadic = is_variadic, .is_comptime = is_ct_param });
continue;
}
self.advance(); // consume ':'
const is_variadic = self.current.tag == .dot_dot;
if (is_variadic) self.advance();
// Old syntax keeps the variadic marker after the colon
// (`args: ..T`). Accept it even if a leading `..` already
// appeared — both forms set the same flag.
if (self.current.tag == .dot_dot) {
is_variadic = true;
self.advance();
}
const param_type = try self.parseTypeExpr();
var is_comptime_param = false;
if (is_ct_param and param_type.data == .type_expr) {

View File

@@ -1 +1 @@
1
0

View File

@@ -1 +1 @@
/Users/agra/projects/sx/examples/150-pack-parse.sx:15:9: error: expected parameter name
pack parse ok