Files
rose-ash/plans/apl-on-sx.md
giles fb72c4ab9c sx-loops: add common-lisp, apl, ruby, tcl (12 slots)
Plans + briefings for four new language loops, each with a delcc/JIT
showcase that the runtime already supports natively:

- common-lisp — conditions + restarts on delimited continuations
- apl — rank-polymorphic primitives + 6 operators on the JIT
- ruby — fibers as delcc, blocks/yield as escape continuations
- tcl — uplevel/upvar via first-class env chain, the Dodekalogue

Launcher scripts now spawn 12 windows (was 8).
2026-04-25 09:25:30 +00:00

7.1 KiB
Raw Blame History

APL-on-SX: rank-polymorphic primitives + glyph parser

The headline showcase is rank polymorphism — a single primitive (+, , , ) works uniformly on scalars, vectors, matrices, and higher-rank arrays. ~80 glyph primitives + 6 operators bind together with right-to-left evaluation; the entire language is a high-density combinator algebra. The JIT compiler + primitive table pay off massively here because almost every program is array → array pure pipelines.

End-state goal: Dyalog-flavoured APL subset, dfns + tradfns, classic programs (game-of-life, mandelbrot, prime-sieve, n-queens, conway), 100+ green tests.

Scope decisions (defaults — override by editing before we spawn)

  • Syntax: Dyalog APL surface, Unicode glyphs. -quad system functions for I/O. tradfn header.
  • Conformance: "Reads like APL, runs like APL." Not byte-compat with Dyalog; we care about right-to-left semantics and rank polymorphism.
  • Test corpus: custom — APL idioms (Roger Hui style), classic programs, plus ~50 pattern tests for primitives.
  • Out of scope: ⎕-namespaces beyond a handful, complex numbers, full TAO ordering, ⎕FX runtime function definition (use static only), nested-array-of-functions higher orders, the editor.
  • Glyphs: input via plain Unicode in .apl source files. Backtick-prefix shortcuts handled by the user's editor — we don't ship one.

Ground rules

  • Scope: only touch lib/apl/** and plans/apl-on-sx.md. Don't edit spec/, hosts/, shared/, or any other lib/<lang>/**. APL primitives go in lib/apl/runtime.sx.
  • SX files: use sx-tree MCP tools only.
  • Commits: one feature per commit. Keep ## Progress log updated and tick roadmap boxes.

Architecture sketch

APL source (Unicode glyphs)
    │
    ▼
lib/apl/tokenizer.sx   — glyphs, identifiers, numbers (¯ for negative), strings, strands
    │
    ▼
lib/apl/parser.sx      — right-to-left with valence resolution (mon vs dyadic by position)
    │
    ▼
lib/apl/transpile.sx   — AST → SX AST (entry: apl-eval-ast)
    │
    ▼
lib/apl/runtime.sx     — array model, ~80 primitives, 6 operators, dfns/tradfns

Core mapping:

  • Array = SX dict {:shape (d1 d2 …) :ravel #(v1 v2 …)}. Scalar is rank-0 (empty shape), vector is rank-1, matrix rank-2, etc. Type uniformity not required (heterogeneous nested arrays via "boxed" elements ⊂x).
  • Rank polymorphism — every scalar primitive is broadcast: 1 2 3 + 4 5 65 7 9; (2 36) + 1 ↦ broadcast scalar to matrix.
  • Conformability = matching shapes, or one-side scalar, or rank-1 cycling (deferred — keep strict in v1).
  • Valence = each glyph has a monadic and a dyadic meaning; resolution is purely positional (left-arg present → dyadic).
  • Operator = takes one or two function operands, returns a derived function ( = each f, f/ = reduce f, f∘g = compose, f⍨ = commute).
  • Tradfn ∇R←L F R; locals = named function with explicit header.
  • Dfn {+⍵} = anonymous, = left arg, = right arg, = recurse.

Roadmap

Phase 1 — tokenizer + parser

  • Tokenizer: Unicode glyphs (the full APL set: + - × ÷ * ⍟ ⌈ ⌊ | ! ? ○ ~ < ≤ = ≥ > ≠ ∊ ∧ ⍱ ⍲ , ⍪ ⌽ ⊖ ⍉ ↑ ↓ ⊂ ⊃ ⊆ ⍸ ⌷ ⍋ ⍒ ⊥ ⊣ ⊢ ⍎ ⍕ ⍝), operators (/ \ ¨ ⍨ ∘ . ⍣ ⍤ ⍥ @), numbers (¯ for negative, 1E2, 1J2 complex deferred), characters ('a', '' escape), strands (juxtaposition of literals: 1 2 3), names, comments ⍝ …
  • Parser: right-to-left; classify each token as function, operator, value, or name; resolve valence positionally; dfn {…} body, tradfn header, guards :, control words :If :While :For … (Dyalog-style)
  • Unit tests in lib/apl/tests/parse.sx

Phase 2 — array model + scalar primitives

  • Array constructor: make-array shape ravel, scalar v, vector v…, enclose/disclose
  • Shape arithmetic: (shape), , (ravel), (tally / first-axis-length), (depth)
  • Scalar arithmetic primitives broadcast: + - × ÷ ⌈ ⌊ * ⍟ | ! ○
  • Scalar comparison primitives: < ≤ = ≥ > ≠
  • Scalar logical: ~ ∧ ⍱ ⍲
  • Index generator: n (vector 1..n or 0..n-1 depending on ⎕IO)
  • ⎕IO = 1 default (Dyalog convention)
  • 40+ tests in lib/apl/tests/scalar.sx

Phase 3 — structural primitives + indexing

  • Reshape , ravel ,, transpose (full + dyadic axis spec)
  • Take , drop , rotate (last axis), (first axis)
  • Catenate , (last axis) and (first axis)
  • Index (squad), bracket-indexing A[I] (sugar for )
  • Grade-up , grade-down
  • Enclose , disclose , partition (subset deferred)
  • Membership , find (dyadic), without ~ (dyadic), unique (deferred to phase 6)
  • 40+ tests in lib/apl/tests/structural.sx

Phase 4 — operators (THE SHOWCASE)

  • Reduce f/ (last axis), f⌿ (first axis) — including ∧/, /, +/, ×/, ⌈/, ⌊/
  • Scan f\, f⍀
  • Each — applies f to each scalar/element
  • Outer product ∘.f1 2 3 ∘.× 1 2 3 ↦ multiplication table
  • Inner product f.g+.× is matrix multiply
  • Commute f⍨f⍨ xx f x, x f⍨ yy f x
  • Compose f∘g — applies g first then f
  • Power f⍣n — apply f n times; f⍣≡ until fixed point
  • Rank f⍤k — apply f at sub-rank k
  • At @ — selective replace
  • 40+ tests in lib/apl/tests/operators.sx

Phase 5 — dfns + tradfns + control flow

  • Dfn {…} with (left arg, may be absent → niladic/monadic), (right arg), (recurse), guards cond:expr, default left arg ←default
  • Local assignment via (lexical inside dfn)
  • Tradfn header: R←L F R;l1;l2, statement-by-statement, branch via →linenum
  • Dyalog control words: :If/:Else/:EndIf, :While/:EndWhile, :For X :In V :EndFor, :Select/:Case/:EndSelect, :Trap/:EndTrap
  • Niladic / monadic / dyadic dispatch (function valence at definition time)
  • lib/apl/conformance.sh + runner, scoreboard.json + scoreboard.md

Phase 6 — classic programs + drive corpus

  • Classic programs in lib/apl/tests/programs/:
    • life.apl — Conway's Game of Life as a one-liner using +/
    • mandelbrot.apl — complex iteration with rank-polymorphic + × (or real-axis subset)
    • primes.apl(2=+⌿0=A∘.|A)/A←N sieve
    • n-queens.apl — backtracking via reduce
    • quicksort.apl — the classic Roger Hui one-liner
  • System functions: ⎕FMT, ⎕FR (float repr), ⎕TS (timestamp), ⎕IO, ⎕ML (migration level — fixed at 1), ⎕← (print)
  • Drive corpus to 100+ green
  • Idiom corpus — lib/apl/tests/idioms.sx covering classic Roger Hui / Phil Last idioms

Progress log

Newest first.

  • (none yet)

Blockers

  • (none yet)