Meme lang composition: lossless pipeline with Pratt parser.
Pipeline: scanner → trivia-attacher → pratt-parser → cst-reader The Pratt parser produces a lossless CST; the CST reader lowers it to Clojure forms.
Meme lang composition: lossless pipeline with Pratt parser. Pipeline: scanner → trivia-attacher → pratt-parser → cst-reader The Pratt parser produces a lossless CST; the CST reader lowers it to Clojure forms.
CST reader: walks CST nodes from the Pratt parser and produces Clojure forms.
This is the lowering step: CST → Clojure forms. It mirrors the classic parser's output (same forms, same metadata, same AST node types) but reads from a lossless tree instead of a token stream.
Pipeline: scanner → trivia-attacher → pratt-parser → cst-reader
CST reader: walks CST nodes from the Pratt parser and produces Clojure forms. This is the lowering step: CST → Clojure forms. It mirrors the classic parser's output (same forms, same metadata, same AST node types) but reads from a lossless tree instead of a token stream. Pipeline: scanner → trivia-attacher → pratt-parser → **cst-reader**
Consistent error infrastructure for the meme reader/tokenizer.
All error throw sites should use meme-error to ensure uniform
location tracking and message formatting.
Consistent error infrastructure for the meme reader/tokenizer. All error throw sites should use `meme-error` to ensure uniform location tracking and message formatting.
Syntax-quote expansion: MemeSyntaxQuote AST nodes → plain Clojure forms. Called by runtime paths (run, repl) before eval. Not needed for tooling (tooling works with AST nodes directly).
Syntax-quote expansion: MemeSyntaxQuote AST nodes → plain Clojure forms. Called by runtime paths (run, repl) before eval. Not needed for tooling (tooling works with AST nodes directly).
Form-shape decomposition: semantic structure of Clojure special forms.
Decomposes a call's args into a vector of [slot-name value] pairs that formatters can apply layout opinions to. A decomposer returns nil when the form has no special structure — the printer falls back to plain-call rendering.
Slot vocabulary (the contract between form-shape and printer/style):
:name identifier being defined (defn, def, deftest, catch-binding) :doc docstring :params parameter vector :dispatch-val multimethod dispatch value; catch-exception class :dispatch-fn multimethod dispatch fn; condp pred :test if/when condition :expr case/condp/threading target :bindings let-style pair binding vector (rendered with columnar layout) :as-name as-> binding name :clause test/value pair (value is a [test value] 2-tuple) :arity complete single-arity form ([params] body+) :default case/condp default expression :body ordinary body expression
Slots are emitted in source order; style maps opine on slot names to choose head-line vs body placement and open-paren spacing.
Each decompose-* helper below takes the call's args and returns a
vector [[slot-name value] ...] in the shape described in its
docstring. Returning nil from a decomposer signals the args do not
match — decompose then falls through to structural inference (if
enabled) or to plain-call rendering.
Form-shape decomposition: semantic structure of Clojure special forms. Decomposes a call's args into a vector of [slot-name value] pairs that formatters can apply layout opinions to. A decomposer returns nil when the form has no special structure — the printer falls back to plain-call rendering. Slot vocabulary (the contract between form-shape and printer/style): :name identifier being defined (defn, def, deftest, catch-binding) :doc docstring :params parameter vector :dispatch-val multimethod dispatch value; catch-exception class :dispatch-fn multimethod dispatch fn; condp pred :test if/when condition :expr case/condp/threading target :bindings let-style pair binding vector (rendered with columnar layout) :as-name as-> binding name :clause test/value pair (value is a [test value] 2-tuple) :arity complete single-arity form ([params] body+) :default case/condp default expression :body ordinary body expression Slots are emitted in source order; style maps opine on slot names to choose head-line vs body placement and open-paren spacing. Each `decompose-*` helper below takes the call's args and returns a vector `[[slot-name value] ...]` in the shape described in its docstring. Returning nil from a decomposer signals the args do not match — `decompose` then falls through to structural inference (if enabled) or to plain-call rendering.
Canonical formatter: width-aware meme output.
Composes printer (form → Doc) with render (layout @ target width).
Used by meme format CLI command.
Owns the canonical style — layout policy for how calls are structured.
Uses meme-lang.form-shape/registry as the default form-shape vocabulary;
callers can override via the :form-shape opts key.
Canonical formatter: width-aware meme output. Composes printer (form → Doc) with render (layout @ target width). Used by `meme format` CLI command. Owns the canonical style — layout policy for how calls are structured. Uses `meme-lang.form-shape/registry` as the default form-shape vocabulary; callers can override via the `:form-shape` opts key.
Flat formatter: single-line meme output — true pass-through. Composes printer (form → Doc) with render (layout @ infinite width). No style opinions: no definition-form spacing, no head-line splitting.
Form-shape decomposition still runs so semantic slots like :bindings and :clause render correctly (binding vectors, case/cond pairs); flat layout at infinite width produces the same result regardless.
Flat formatter: single-line meme output — true pass-through. Composes printer (form → Doc) with render (layout @ infinite width). No style opinions: no definition-form spacing, no head-line splitting. Form-shape decomposition still runs so semantic slots like :bindings and :clause render correctly (binding vectors, case/cond pairs); flat layout at infinite width produces the same result regardless.
Shared form-level predicates and constructors. Cross-stage contracts that both the parser and printer depend on.
Shared form-level predicates and constructors. Cross-stage contracts that both the parser and printer depend on.
Meme language grammar spec.
Maps characters to scanlets — the complete syntactic specification of M-expression syntax as data. Lexlets provide the scanning layer, parselets provide the compound constructs, and the parser engine provides generic factories.
Meme language grammar spec. Maps characters to scanlets — the complete syntactic specification of M-expression syntax as data. Lexlets provide the scanning layer, parselets provide the compound constructs, and the parser engine provides generic factories.
Meme lexical scanlets: character predicates, consume helpers, and trivia consumers.
This file provides the lexical layer for the meme language. The grammar spec in meme-grammar references these functions by name. Generic scanlet builders (atom-scanlet, single-char-scanlet, delimited-scanlet) live in meme.tools.lexer.
Meme lexical scanlets: character predicates, consume helpers, and trivia consumers. This file provides the lexical layer for the meme language. The grammar spec in meme-grammar references these functions by name. Generic scanlet builders (atom-scanlet, single-char-scanlet, delimited-scanlet) live in meme.tools.lexer.
Meme-specific parselets for the Pratt parser.
Contains the compound parselets that handle meme's unique constructs: call adjacency detection, dispatch (#) sub-routing, tilde (~/@), and the M-expression call rule.
Meme-specific parselets for the Pratt parser. Contains the compound parselets that handle meme's unique constructs: call adjacency detection, dispatch (#) sub-routing, tilde (~/@), and the M-expression call rule.
Meme printer: Clojure forms → Doc trees. Builds Wadler-Lindig Doc trees from Clojure forms, handling meme syntax (call notation, sugar, metadata, comments) and Clojure output mode. Delegates to render for Doc algebra and layout.
The printer is parameterized by a style map that controls layout policy
(head-line-args, definition-form spacing, pair grouping, binding layout).
Formatters own style: canon passes a full style, flat passes nil for
true pass-through. See to-doc for the public entry point.
Meme printer: Clojure forms → Doc trees. Builds Wadler-Lindig Doc trees from Clojure forms, handling meme syntax (call notation, sugar, metadata, comments) and Clojure output mode. Delegates to render for Doc algebra and layout. The printer is parameterized by a *style map* that controls layout policy (head-line-args, definition-form spacing, pair grouping, binding layout). Formatters own style: canon passes a full style, flat passes nil for true pass-through. See `to-doc` for the public entry point.
Meme-specific REPL. Wires meme stages, error formatting, keyword resolution, and syntax-quote resolution into the generic REPL infrastructure.
Installs meme.loader by default so that require and load-file of
.meme namespaces work from the REPL. Pass :install-loader? false to
skip (for hosts that own their own load interception).
JVM/Babashka only.
Meme-specific REPL. Wires meme stages, error formatting, keyword resolution, and syntax-quote resolution into the generic REPL infrastructure. Installs `meme.loader` by default so that `require` and `load-file` of `.meme` namespaces work from the REPL. Pass `:install-loader? false` to skip (for hosts that own their own load interception). JVM/Babashka only.
Value resolution: converts raw token text to Clojure values. All resolution is native — no delegation to read-string.
Value resolution: converts raw token text to Clojure values. All resolution is native — no delegation to read-string.
Meme-specific eval pipeline. Wires meme stages, syntax-quote resolution, and BOM stripping into the generic run infrastructure.
Installs meme.loader by default so that require and load-file of
.meme namespaces work from within the evaluated code. Callers embedding
meme in a host that owns its own clojure.core/load interception can
pass :install-loader? false to skip.
Extension-based lang dispatch (running .calc/.prefix files as their
own langs) is opt-in via :resolve-lang-for-path — programmatic callers
that pass a real .meme path don't need it; the CLI wires it.
JVM/Babashka only.
Meme-specific eval pipeline. Wires meme stages, syntax-quote resolution, and BOM stripping into the generic run infrastructure. Installs `meme.loader` by default so that `require` and `load-file` of `.meme` namespaces work from within the evaluated code. Callers embedding meme in a host that owns its own `clojure.core/load` interception can pass `:install-loader? false` to skip. Extension-based lang dispatch (running `.calc`/`.prefix` files as their own langs) is opt-in via `:resolve-lang-for-path` — programmatic callers that pass a real `.meme` path don't need it; the CLI wires it. JVM/Babashka only.
Composable pipeline stages for the lossless reader.
Pipeline: step-parse → step-read → (optionally) step-expand-syntax-quotes
Each stage is a ctx → ctx function operating on a shared context map:
| Key | Type | Written by | Read by |
|---|---|---|---|
| :source | String | caller | parse |
| :opts | Map or nil | caller | parse, read |
| :cst | Vector | parse | read, (tooling) |
| :forms | Vector | read | caller, expand |
The table above is mirrored as machine-readable data in stage-contracts.
Each stage calls check-contract! at entry, so miscomposed pipelines
(e.g. calling step-read before step-parse) throw a clear pipeline-error
with the missing key(s) and the actual ctx-keys present, instead of
surfacing deep-inside NPEs.
Stages are independent. Compose in any order respecting dependencies. Skip step-read for tooling that works with CST directly.
Composable pipeline stages for the lossless reader. Pipeline: step-parse → step-read → (optionally) step-expand-syntax-quotes Each stage is a ctx → ctx function operating on a shared context map: | Key | Type | Written by | Read by | |--------------|----------------|-------------|------------------| | :source | String | caller | parse | | :opts | Map or nil | caller | parse, read | | :cst | Vector | parse | read, (tooling) | | :forms | Vector | read | caller, expand | The table above is mirrored as machine-readable data in `stage-contracts`. Each stage calls `check-contract!` at entry, so miscomposed pipelines (e.g. calling step-read before step-parse) throw a clear pipeline-error with the missing key(s) and the actual ctx-keys present, instead of surfacing deep-inside NPEs. Stages are independent. Compose in any order respecting dependencies. Skip step-read for tooling that works with CST directly.
Shared value → string serialization for the printer and rewrite emitter. Handles atomic Clojure values (strings, numbers, chars, regex, etc.) that both emit paths must render identically.
Shared value → string serialization for the printer and rewrite emitter. Handles atomic Clojure values (strings, numbers, chars, regex, etc.) that both emit paths must render identically.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |