From a51fe26cbffe525ea9332feefdb01a66e0a1316d Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 27 May 2026 09:49:41 +0300 Subject: [PATCH] ffi M5.A.next.1b: parser accepts `..$args` as a variadic-pack param MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- examples/150-pack-parse.sx | 20 ++++++++++---------- src/parser.zig | 20 +++++++++++++++++--- tests/expected/150-pack-parse.exit | 2 +- tests/expected/150-pack-parse.txt | 2 +- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/examples/150-pack-parse.sx b/examples/150-pack-parse.sx index 23ef749..a3b5eaf 100644 --- a/examples/150-pack-parse.sx +++ b/examples/150-pack-parse.sx @@ -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; } diff --git a/src/parser.zig b/src/parser.zig index c75ab8e..7c42f0e 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -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) { diff --git a/tests/expected/150-pack-parse.exit b/tests/expected/150-pack-parse.exit index d00491f..573541a 100644 --- a/tests/expected/150-pack-parse.exit +++ b/tests/expected/150-pack-parse.exit @@ -1 +1 @@ -1 +0 diff --git a/tests/expected/150-pack-parse.txt b/tests/expected/150-pack-parse.txt index 58d8286..c58b4c9 100644 --- a/tests/expected/150-pack-parse.txt +++ b/tests/expected/150-pack-parse.txt @@ -1 +1 @@ -/Users/agra/projects/sx/examples/150-pack-parse.sx:15:9: error: expected parameter name +pack parse ok