Liking cljdoc? Tell your friends :D

meme-lang.api

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.
raw docstring

meme-lang.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

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**
raw docstring

meme-lang.errors

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.
raw docstring

meme-lang.expander

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).
raw docstring

meme-lang.form-shape

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.
raw docstring

meme-lang.formatter.canon

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.
raw docstring

meme-lang.formatter.flat

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.
raw docstring

meme-lang.grammar

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.
raw docstring

meme-lang.lexlets

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.
raw docstring

meme-lang.parselets

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.
raw docstring

meme-lang.printer

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.
raw docstring

meme-lang.repl

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.
raw docstring

meme-lang.resolve

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.
raw docstring

meme-lang.run

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.
raw docstring

meme-lang.stages

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:

KeyTypeWritten byRead by
:sourceStringcallerparse
:optsMap or nilcallerparse, read
:cstVectorparseread, (tooling)
:formsVectorreadcaller, 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.
raw docstring

meme-lang.values

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.
raw docstring

meme.cli

Unified CLI. Commands dispatch through lang maps.

This namespace is the app — it explicitly requires each built-in lang, which triggers its self-registration in meme.registry. The registry itself imports no langs; adding a new built-in means a one-line require here plus the lang's own register-builtin! call.

Unified CLI. Commands dispatch through lang maps.

This namespace is the app — it explicitly requires each built-in lang,
which triggers its self-registration in meme.registry.  The registry
itself imports no langs; adding a new built-in means a one-line
require here plus the lang's own register-builtin! call.
raw docstring

meme.config

Project-local formatter configuration.

Reads .meme-format.edn from the project root (walking up from a starting directory), validates it, and translates it into opts suitable for meme-lang.formatter.canon/format-form.

Config schema:

{:width positive integer (default 80) :structural-fallback? boolean — infer defn-/let-like shapes for unregistered heads :form-shape map of symbol → symbol. Each entry aliases a user macro to an existing registry entry, e.g. {my-defn defn, my-let let}. :style partial override of the canonical style. Merged on top of canon/style; supports :head-line-slots and :force-open-space-for. :slot-renderers is not supported from EDN (renderers are fns — not EDN-representable).}

Unknown keys are ignored with a warning so that newer configs stay forward-compatible with older tooling.

Project-local formatter configuration.

Reads `.meme-format.edn` from the project root (walking up from a
starting directory), validates it, and translates it into opts
suitable for `meme-lang.formatter.canon/format-form`.

Config schema:

  {:width                 positive integer (default 80)
   :structural-fallback?  boolean — infer defn-/let-like shapes
                          for unregistered heads
   :form-shape            map of symbol → symbol.  Each entry aliases
                          a user macro to an existing registry entry,
                          e.g. {my-defn defn, my-let let}.
   :style                 partial override of the canonical style.
                          Merged on top of canon/style; supports
                          :head-line-slots and :force-open-space-for.
                          :slot-renderers is not supported from EDN
                          (renderers are fns — not EDN-representable).}

Unknown keys are ignored with a warning so that newer configs stay
forward-compatible with older tooling.
raw docstring

meme.loader

Namespace loader for registered languages. Intercepts clojure.core/load to search for source files with registered extensions on the classpath. When found, the source is loaded through the lang's :run function.

Any lang registered via meme.registry with an :extension and :run function gets require support automatically.

Installed implicitly by run-file and the REPL — no manual setup.

Security: a denylist prevents interception of core infrastructure namespaces (clojure., java., etc.). Only user/library namespaces are eligible for lang-based loading.

Namespace loader for registered languages.
Intercepts clojure.core/load to search for source files with
registered extensions on the classpath. When found, the source
is loaded through the lang's :run function.

Any lang registered via meme.registry with an :extension and :run
function gets require support automatically.

Installed implicitly by run-file and the REPL — no manual setup.

Security: a denylist prevents interception of core infrastructure
namespaces (clojure.*, java.*, etc.). Only user/library namespaces
are eligible for lang-based loading.
raw docstring

meme.registry

Lang registry: registration, resolution, and EDN loading. JVM/Babashka only.

A lang is a map of command functions: :run (fn [source opts] → result) — run a file :repl (fn [opts] → nil) — interactive loop :format (fn [source opts] → text) — format a file :to-clj (fn [source] → clj-text) — convert meme→clj :to-meme (fn [source] → meme-text) — convert clj→meme

Plus optional metadata: :extension ".ext" — file extension (string or vector) :extensions [".ext" ".e"] — file extensions (string or vector) :form-shape registry — lang-owned decomposer map consumed by the printer/formatter (see meme-lang.form-shape) Both extension forms are accepted and normalized to :extensions [...].

Every key is optional. A lang supports exactly the commands it has keys for.

Built-in langs are self-describing: each defines a lang-map in its own namespace. User langs can be registered via register! with EDN-style config maps.

String values in a lang-map (e.g. :run "prelude.meme" in EDN) are resolved through handlers installed via register-string-handler!. This keeps the registry lang-agnostic — langs install their own conventions rather than the registry hardcoding meme's.

Lang registry: registration, resolution, and EDN loading.
JVM/Babashka only.

A lang is a map of command functions:
  :run      (fn [source opts] → result)  — run a file
  :repl     (fn [opts] → nil)            — interactive loop
  :format   (fn [source opts] → text)    — format a file
  :to-clj   (fn [source] → clj-text)     — convert meme→clj
  :to-meme  (fn [source] → meme-text)    — convert clj→meme

Plus optional metadata:
  :extension   ".ext"           — file extension (string or vector)
  :extensions  [".ext" ".e"]  — file extensions (string or vector)
  :form-shape  registry           — lang-owned decomposer map consumed by
                                    the printer/formatter (see
                                    `meme-lang.form-shape`)
Both extension forms are accepted and normalized to :extensions [...].

Every key is optional. A lang supports exactly the commands it has keys for.

Built-in langs are self-describing: each defines a lang-map in its own namespace.
User langs can be registered via register! with EDN-style config maps.

String values in a lang-map (e.g. `:run "prelude.meme"` in EDN) are
resolved through handlers installed via `register-string-handler!`. This
keeps the registry lang-agnostic — langs install their own conventions
rather than the registry hardcoding meme's.
raw docstring

meme.tools.lexer

Generic scanlet builders for the Pratt parser engine.

Scanlet builders wrap language-specific consume functions into parselets that the grammar spec can reference. They bridge the gap between 'consume characters from source' and 'produce a CST node'.

Any language can use these builders with its own consume functions.

Generic scanlet builders for the Pratt parser engine.

Scanlet builders wrap language-specific consume functions into parselets
that the grammar spec can reference. They bridge the gap between
'consume characters from source' and 'produce a CST node'.

Any language can use these builders with its own consume functions.
raw docstring

meme.tools.parser

Unified scanlet-parselet Pratt parser.

The engine reads directly from a source string. The grammar spec defines everything language-specific: character dispatch (scanlets), trivia classification, prefix parselets (nud), and postfix rules (led).

A scanlet is (fn [engine] → CST node). It consumes characters from the source string using engine primitives and produces a CST node.

Grammar spec shape: {:nud {char → scanlet-fn} :nud-pred [[pred scanlet-fn] ...] :trivia {char → trivia-consumer-fn} :trivia-pred [[pred trivia-consumer-fn] ...] :led [{:char c :bp n :when pred :fn scanlet-fn} ...]}

Parselet factories (nud-atom, nud-prefix, nud-delimited, etc.) generate common patterns. Custom parselets are plain functions.

Unified scanlet-parselet Pratt parser.

The engine reads directly from a source string. The grammar spec defines
everything language-specific: character dispatch (scanlets), trivia
classification, prefix parselets (nud), and postfix rules (led).

A scanlet is (fn [engine] → CST node). It consumes characters from the
source string using engine primitives and produces a CST node.

Grammar spec shape:
  {:nud        {char → scanlet-fn}
   :nud-pred   [[pred scanlet-fn] ...]
   :trivia     {char → trivia-consumer-fn}
   :trivia-pred [[pred trivia-consumer-fn] ...]
   :led        [{:char c :bp n :when pred :fn scanlet-fn} ...]}

Parselet factories (nud-atom, nud-prefix, nud-delimited, etc.) generate
common patterns. Custom parselets are plain functions.
raw docstring

meme.tools.render

Wadler-Lindig document algebra and layout engine. Generic — no meme-specific knowledge. Reusable for any pretty-printing task.

Doc types form a small algebra: DocText — literal string DocLine — newline+indent (or flat-alt when flat) DocCat — concatenation DocNest — increase indent DocGroup — try flat, break if too wide DocIfBreak — conditional on flat/break mode

layout renders a Doc tree to a string at a given page width. Use ##Inf for single-line (flat) rendering.

Wadler-Lindig document algebra and layout engine.
Generic — no meme-specific knowledge. Reusable for any pretty-printing task.

Doc types form a small algebra:
  DocText     — literal string
  DocLine     — newline+indent (or flat-alt when flat)
  DocCat      — concatenation
  DocNest     — increase indent
  DocGroup    — try flat, break if too wide
  DocIfBreak  — conditional on flat/break mode

layout renders a Doc tree to a string at a given page width.
Use ##Inf for single-line (flat) rendering.
raw docstring

meme.tools.repl

Generic interactive eval loop. Language-agnostic. All language-specific behavior is injected via opts: :run-fn — (fn [source opts] → ctx-with-:forms) :expand-forms — (fn [forms opts] → expanded-forms) :format-error — (fn [exception input] → error-string) :eval — eval function (default: clojure.core/eval) :read-line — line reader fn (default: read-line) :banner — REPL banner string :reader-opts — opts map passed to run-fn :prelude — vector of forms to eval before REPL loop JVM/Babashka only.

Generic interactive eval loop. Language-agnostic.
All language-specific behavior is injected via opts:
  :run-fn        — (fn [source opts] → ctx-with-:forms)
  :expand-forms  — (fn [forms opts] → expanded-forms)
  :format-error  — (fn [exception input] → error-string)
  :eval          — eval function (default: clojure.core/eval)
  :read-line     — line reader fn (default: read-line)
  :banner        — REPL banner string
  :reader-opts   — opts map passed to run-fn
  :prelude       — vector of forms to eval before REPL loop
JVM/Babashka only.
raw docstring

meme.tools.run

Generic eval pipeline: source → parse → expand → eval. Language-agnostic. All language-specific behavior is injected via opts: :run-fn — (fn [source opts] → ctx-with-:forms) :expand-forms — (fn [forms opts] → expanded-forms) :eval — eval function (default: clojure.core/eval) :prelude — vector of forms to eval before user code JVM/Babashka only.

Generic eval pipeline: source → parse → expand → eval.
Language-agnostic. All language-specific behavior is injected via opts:
  :run-fn       — (fn [source opts] → ctx-with-:forms)
  :expand-forms — (fn [forms opts] → expanded-forms)
  :eval         — eval function (default: clojure.core/eval)
  :prelude      — vector of forms to eval before user code
JVM/Babashka only.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close