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