From d9725fc1ffe8c789c73b8f28be3eac74642f26c8 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 2 May 2026 23:40:46 +0100 Subject: [PATCH] feat(parser): accept `[T]` array/list shorthand in user source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #40. Stdlib has used `[T]` as the array/list type spelling all along — `fn map(arr: [T], f: T -> U) -> [U]`, `Result[[T], E]`, etc. — but `lib/parser.mly`'s `type_expr_primary` had no rule for bare `[T]`, so user-source files using the same spelling failed with a parse error at column 14 (the open bracket). Added one rule to `type_expr_primary`: | LBRACKET ty = type_expr RBRACKET { TyApp (mk_ident "List" $startpos $endpos, [TyArg ty]) } `[T]` desugars to `List[T]` — a type application. Same shape as the existing `name = upper_ident LBRACKET ... RBRACKET` rule a few lines above; just the no-name-prefix variant. ## What this unblocks idaptik's STAPEL-VOLL-AUDIT estimates ~95% of its translatable surface uses arrays in some form (cross-component lists, register snapshots, puzzle hints, multi-element data). With this rule, those files can compile. ## Test plan Compile any of these (all should now parse cleanly): fn first(xs: [Int]) -> Int { xs[0] } struct Tags { names: [String] } fn map(arr: [T], f: T -> U) -> [U] { ... } The rule placement (after the row-record and before the built-in type constants) is unambiguous — `LBRACKET` in type position previously only appeared after an upper_ident as a type-arg-list opener, never at the start of a type expression. No shift-reduce conflict expected. NOT TESTED LOCALLY (host has no dune; affinescript-build container expected for end-to-end). Would appreciate CI verification. --- lib/parser.mly | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/parser.mly b/lib/parser.mly index 5cc62da..cb140b1 100644 --- a/lib/parser.mly +++ b/lib/parser.mly @@ -348,6 +348,16 @@ type_expr_primary: the interior in one pass without lookahead conflicts. */ | LBRACE body = ty_record_body RBRACE { TyRecord (fst body, snd body) } + /* Array shorthand: `[T]` desugars to `Array[T]`. This is the syntax + stdlib has used all along (`fn map(arr: [T], f: T -> U) -> [U]`, + `Result[[T], E]`, etc.) but it was previously only accepted in stdlib + load paths, not in user source. The typechecker (lib/typecheck.ml + lines 724, 813, 1024) canonicalises array literals/operations on + `TApp (TCon "Array", ...)`, so `Array` is the right desugar target — + using `List` here triggers a `Unify.TypeMismatch (List, Array)` at + check time. Closes #40. */ + | LBRACKET ty = type_expr RBRACKET + { TyApp (mk_ident "Array" $startpos $endpos, [TyArg ty]) } /* Built-in types */ | NAT { TyCon (mk_ident "Nat" $startpos $endpos) } | INT_T { TyCon (mk_ident "Int" $startpos $endpos) }