,LISCT — the Lisp dialect inside this app

A Lisp where , is the only escape

Everything is data by default. Prefix a name with , to evaluate it — that's the whole rule.

Read, learn, and run the dialect right here. The interpreter ships with the app.

The core idea, in four diagrams

LISCT is a tiny dialect designed to embed into the set-based data model of this app. The whole language fits in your head — these four visuals are the load-bearing concepts.

Permanent quasiquote

Imagine the entire program is wrapped in an invisible Scheme `(…). Bare lists are data. They self-evaluate.

The only thing that "happens" is when you puncture the data layer with ,name — that's Scheme's unquote, hoisted to the language baseline. Quote ' is mostly noise here; data is already the default.

eval modedata (everything is data by default)(a b c)→ no escape(,+ 1 2)3'(,+ 1 2)quote wall→ stays data
sourcetokensASTvalue(,+ 1 2)(,+12)tree ↓3ConsRef(+)ConsLit(1)ConsLit(2)nullConsRefLit

Source → Tokens → AST → Value

Three AST shapes do everything: Lit (literal), Ref (a ,name lookup), and Cons (the pair).

Evaluation is one function that walks the cons cells. If the head of a list evaluates to a procedure, call it; otherwise the list passes through as data unchanged.

Stack-safe tail calls

,if, ,cond, and lambda bodies return a Tail thunk instead of recursing.

The eval loop trampolines: it unwinds the thunk and continues in place. Canonical tail recursion runs in constant JS stack — try a 100 000-step loop in the playground below.

eval_(exp, env)while (true) { ... }Tail thunk?overwrite exp/envcontinue{__tail__: true, exp, env}valuereturnif / cond / lambda body → Tail (constant stack)everything else → plain value (consumes JS stack)
root+mapuserx ← 1outer lambday ← 8inner lambdaz ← 15,x1) inner: miss2) outer: miss3) root: hit→ 1lookup walks up via parent pointers; lambda captures thescope at its definition site → closures for free

Lexical scope chain

Each lambda call frame is a fresh Map whose parent points at the lexical environment of the function's definition site — closures fall out for free.

A reference walks up the chain until it finds a binding; if it falls off the root, you get undefined reference.

Try it now

The interpreter runs in your browser. Pick an example or write your own — bindings persist across runs in this session, so a ,define from one run is visible in the next.

Editor

⌘/Ctrl+Enter to run · 19 chars

Output
— output will appear here —

ready

Beta