Shift+Enter runs current input and creates a new cell. Type \ plus a name (e.g. \iota) or click a glyph below to enter APL symbols; hover a glyph in the editor for its help, and 🔗 on a cell copies a link to its code.
An APL to JavaScript transpiler.
Shift+Enter runs current input and creates a new cell. Type \ plus a name (e.g. \iota) or click a glyph below to enter APL symbols; hover a glyph in the editor for its help, and 🔗 on a cell copies a link to its code.
Type backslash plus name inside the editor to emit an APL symbol automatically.
Monadic use has one right argument (⍵); dyadic use has a left (⍺) and right argument.
Syntax notes: ⎕IO is always 0. Negative numbers use high minus and scientific notation is accepted (¯2.5E¯3); a quote inside a string is doubled ('it''s'); names may contain ∆/⍙; a string argument is a character vector (⍴'abc' → 3). Errors are reported with their APL class: SYNTAX, LENGTH, DOMAIN, INDEX or RANK ERROR.
| Glyph | Name | Monadic | Dyadic |
|---|
There's no separate shape-primitive layer - SVG is built by driving d3 directly through ⍠/⍔ chains, and any real DOM element a cell evaluates to (an <svg> from d3, or a chart from Plot/Observable Plot) is displayed as-is.
A command-list right argument to ⍠ chains calls, each one calling the previous step's result - so d3⍠(#create #svg)(#attr #width 120) is d3.create('svg').attr('width', 120). End with ⍠(#node ⎕null) to pull out the plain DOM element to display.
Worked examples of the FFI trio: ⍠ builds/reads plain objects - reading a function back binds it to its receiver, so it stays correctly callable even if stored away first; ⍔ relabels a value as an applicable function, e.g. (obj⍠'method')⍔⊢args to call a JS method; ⍞ is its inverse, turning an APL function into a plain value. #word is shorthand for 'word'.
APL.js is a small APL-to-JavaScript transpiler with a graphics-capable browser REPL, built as an educational project exploring how APL's array notation compiles to plain JS.
It's made of three pieces: the transpiler core (tokenizer, parser, primitive library), this multi-cell REPL with SVG/d3/Observable Plot output and a JS/JS-object FFI, and a simpler translator-only demo page.
Version 0.3 · github.com/vinicius-mello/apljs
Tokens are consumed right-to-left onto a single stack of {category, node} pairs (categories: value, function, monadic/dyadic operator, boundary markers...). After every shift, reduceStack pattern-matches a small window - the top 2 to 4 stack slots - against a fixed list of grammar rules (parentheses, strand formation, monadic/dyadic application, trains, operator binding...), collapsing a match into one node, and keeps retrying until nothing matches before the next token shifts on.
There's no bracket-indexing production - A[I] never enters the grammar. Indexing is just an ordinary function application of ⌷ (squad) like any other primitive, so the parser stays small and uniform: everything is function application/composition, nothing gets a special-cased sub-grammar.
There's no dedicated array class - an APL array is a plain (possibly nested) JS array, and a scalar is a bare JS number/string. Shape is normally inferred structurally: shapeRec walks Array.isArray/.length recursively rather than reading it off stored metadata.
That inference has two blind spots a handful of primitives patch by stamping an explicit .shape field on their result, which shapeRec checks before falling back to structural inference. Monadic enclose (⊂) tags its rank-0 result .shape = [], since a plain length-1 array is otherwise indistinguishable from a real 1-vector; pervasive functions (arithmetic, relational, catenate) know to see through that tag and rebuild it on their own result so the enclosure survives the operation. Reshape (⍴), take/drop and catenation tag their result whenever its shape has a zero dimension, since a zero collapses everything nested inside it to a bare [] with nothing left to structurally reveal the dimensions that followed it; outer product tags its result too, so cells that happen to be uniform arrays aren't misread as extra axes.
Text stays a plain JS string, so it reaches d3/Plot untouched, and is read two ways: a string argument is a character vector (a 1-character string a character scalar), while a string sitting as an item of an array is opaque, like an enclosed vector - 'ab' 'cd' is a 2-vector of strings, never a 2×2 character matrix. Structural primitives split a string argument into characters and join an all-character vector result back into a string.
⍠ (buildObject) does the actual work: monadically it turns a command list into a plain JS object; dyadically, a chained command list threads each step's result into the next call on a receiver, and reading a method back out (a bare key, no call) rebinds it to its receiver so it stays correctly callable even after being stored or passed around - binding at the call site alone wouldn't survive that.
⍔ (asFunction) is a genuine monadic operator: it relabels a value from V to F so it can sit in function position and be applied. ⍞ is its inverse, but it's not a runtime function at all - it's a parse-time-only marker that relabels the F/M/D token to its right as a plain V, so it compiles to a bare reference to the underlying JS function instead of applying it.