All namespaces live under meme.alpha to signal that the API is pre-1.0 and may change. When the API stabilizes, namespaces will move to meme.
The public API for reading and printing meme syntax, organized in three tracks:
text-to-form form-to-text
meme str ──→ meme->forms ──→ forms ──→ forms->meme ──→ meme str
clj str ──→ clj->forms ──→ forms ──→ forms->clj ──→ clj str
text-to-text (compositions)
meme str ──→ meme->clj ──→ clj str
clj str ──→ clj->meme ──→ meme str
(meme.alpha.core/meme->forms s)
(meme.alpha.core/meme->forms s opts)
Read a meme source string. Returns a vector of Clojure forms. All platforms.
Options:
:resolve-keyword — function that resolves auto-resolve keyword strings ("::foo") to keywords at read time. When absent on JVM/Babashka, :: keywords are deferred to eval time via (read-string "::foo"). Required on CLJS (errors without it, since cljs.reader cannot resolve :: in the correct namespace).(meme->forms "+(1 2 3)")
;=> [(+ 1 2 3)]
(meme->forms "def(x 42)\nprintln(x)")
;=> [(def x 42) (println x)]
(meme.alpha.core/forms->meme forms)
Print a sequence of Clojure forms as meme text. All platforms.
(forms->meme ['(+ 1 2 3)])
;=> "+(1 2 3)"
(meme.alpha.core/forms->clj forms)
Print Clojure forms as a Clojure source string. All platforms.
(forms->clj ['(def x 42) '(println x)])
;=> "(def x 42)\n\n(println x)"
(meme.alpha.core/clj->forms clj-src)
Read a Clojure source string, return a vector of forms. JVM/Babashka only.
(clj->forms "(defn f [x] (+ x 1))")
;=> [(defn f [x] (+ x 1))]
(meme.alpha.core/pprint-meme forms)
(meme.alpha.core/pprint-meme forms opts)
Pretty-print Clojure forms as multi-line, indented meme text. Uses indented parenthesized form for calls that exceed the line width. Preserves comments from :ws metadata (attached by the pipeline's scan stage). All platforms.
Options:
:width — target line width (default: 80)(pprint-meme ['(defn greet [name] (println (str "Hello " name)))])
;=> "defn(greet [name]\n println(str(\"Hello \" name)))"
(meme.alpha.core/meme->clj meme-src)
Convert meme source string to Clojure source string. All platforms. Equivalent to (forms->clj (meme->forms meme-src)).
(meme->clj "println(\"hello\")")
;=> "(println \"hello\")"
(meme.alpha.core/clj->meme clj-src)
Convert a Clojure source string to meme source string. JVM/Babashka only. Equivalent to (forms->meme (clj->forms clj-src)).
(clj->meme "(defn f [x] (+ x 1))")
;=> "defn(f [x] +(x 1))"
(meme.alpha.core/run-pipeline source)
(meme.alpha.core/run-pipeline source opts)
Run the full pipeline: source → scan → group → parse. Returns a context map with intermediate state. All platforms. Useful for tooling that needs access to raw tokens, tokens, or parsed forms.
(run-pipeline "foo(1 2)")
;=> {:source "foo(1 2)"
; :opts nil
; :raw-tokens [...tokenizer output with :ws...]
; :tokens [...same (grouper is pass-through)...]
; :forms [(foo 1 2)]}
Low-level reader API.
(meme.alpha.parse.reader/read-meme-string-from-tokens tokens)
(meme.alpha.parse.reader/read-meme-string-from-tokens tokens opts source)
Parse a token vector into Clojure forms. Used by meme.alpha.pipeline/parse. Most callers should use meme.alpha.core/meme->forms instead.
tokens — a token vector (output of meme.alpha.scan.grouper/group-tokens or directly from the tokenizer)opts — same options as meme->forms (e.g., :resolve-keyword)source — original source text for error context (optional)Low-level printer API.
(meme.alpha.emit.printer/print-form form)
Print a single Clojure form as meme text.
(print-form '(+ 1 2))
;=> "+(1 2)"
(print-form '(:balance account))
;=> ":balance(account)"
(print-form '(def x 42))
;=> "def(x 42)"
(meme.alpha.emit.printer/print-meme-string forms)
Print a sequence of Clojure forms as meme text, separated by blank lines.
Low-level pretty-printer API.
(meme.alpha.emit.pprint/pprint-form form)
(meme.alpha.emit.pprint/pprint-form form opts)
Pretty-print a single Clojure form as meme text. Width-aware — uses indented parenthesized form for calls that don't fit on one line. Preserves comments from :ws metadata.
Options:
:width — target line width (default: 80)(meme.alpha.emit.pprint/pprint-forms forms)
(meme.alpha.emit.pprint/pprint-forms forms opts)
Pretty-print a sequence of Clojure forms as meme text, separated by blank lines. Preserves comments from :ws metadata, including trailing comments after the last form.
(meme.alpha.runtime.repl/input-state s)
Returns the parse state of a meme input string: :complete (parsed successfully), :incomplete (unclosed delimiter — keep reading), or :invalid (malformed, non-recoverable error). Used internally by the REPL for multi-line input handling; also useful for editor integration.
(input-state "+(1 2)") ;=> :complete
(input-state "f(") ;=> :incomplete
(input-state "(bare parens)") ;=> :invalid
(meme.alpha.runtime.repl/start)
(meme.alpha.runtime.repl/start opts)
Start the meme REPL. Reads meme syntax, evaluates as Clojure, prints results.
Options:
:read-line — custom line reader function (default: read-line, required on ClojureScript):eval — custom eval function (default: eval, required on ClojureScript):resolve-keyword — function to resolve :: keywords at read time (default: clojure.core/read-string on JVM; required on CLJS for code that uses :: keywords)$ bb meme
meme REPL. Type meme expressions, balanced input to eval. Ctrl-D to exit.
user=> +(1 2)
3
user=> map(inc [1 2 3])
(2 3 4)
The prompt shows the current namespace (e.g., user=> on JVM/Babashka, meme=> on ClojureScript).
Run .meme files or meme source strings.
(meme.alpha.runtime.run/run-string s)
(meme.alpha.runtime.run/run-string s eval-fn)
(meme.alpha.runtime.run/run-string s opts)
Read meme source string, eval each form, return the last result. Strips leading #! shebang lines before parsing. The second argument can be an eval function (backward compatible) or an opts map.
Options (when passing a map):
:eval — eval function (default: eval; required on CLJS):resolve-keyword — function to resolve :: keywords at read time (default: none — :: keywords resolve at eval time in the file's declared namespace. Required on CLJS for code that uses :: keywords)(run-string "def(x 42)\n+(x 1)")
;=> 43
(meme.alpha.runtime.run/run-file path)
(meme.alpha.runtime.run/run-file path eval-fn)
(meme.alpha.runtime.run/run-file path opts)
Read and eval a .meme file. Returns the last result. Uses slurp internally (JVM/Babashka only). Second argument follows same convention as run-string.
(run-file "test/examples/tests/01_core_rules.meme")
Explicit pipeline composition. Each stage is a ctx → ctx function operating on a shared context map with keys :source, :opts, :raw-tokens, :tokens, :forms.
(meme.alpha.pipeline/scan ctx)
Tokenize source text into flat tokens with whitespace attachment. Reads :source from ctx, assocs :raw-tokens. Each token carries a :ws key with the leading whitespace and comments between it and the previous token. Trailing whitespace (after the last token) is stored as :trailing-ws metadata on the token vector. This is how the pretty-printer preserves comments.
(meme.alpha.pipeline/group ctx)
Pass-through stage — returns tokens unchanged. Retained for pipeline symmetry. Reads :raw-tokens and :source from ctx, assocs :tokens.
(meme.alpha.pipeline/parse ctx)
Parse grouped tokens into Clojure forms. Reads :tokens, :opts, :source from ctx, assocs :forms.
(meme.alpha.pipeline/run source)
(meme.alpha.pipeline/run source opts)
Run the full pipeline: scan → group → parse. Returns the complete context map.
(meme.alpha.pipeline/run "+(1 2)")
;=> {:source "+(1 2)", :opts nil,
; :raw-tokens [...], :tokens [...], :forms [(+ 1 2)]}
Pass-through stage — all forms are now parsed natively by the recursive-descent parser. Retained for pipeline symmetry.
(meme.alpha.scan.grouper/group-tokens tokens source)
Returns tokens unchanged. All forms (reader conditionals, namespaced maps, syntax-quote) are parsed natively by the reader. source is accepted for API compatibility but unused.
Source-position utilities shared across pipeline stages. Defines how (line, col) maps to character offsets — used by the tokenizer's attach-whitespace pass.
(meme.alpha.scan.source/line-col->offset source line col)
Convert 1-indexed line and column to a 0-indexed character offset in source. Used by attach-whitespace (tokenizer) to locate token positions in the original source string. Returns the source length if the position is past the end.
Unified CLI for meme. Implemented in meme syntax (cli.meme), loaded by a .clj shim. JVM/Babashka only.
| Command | Description |
|---|---|
meme run <file> | Run a .meme file |
meme repl | Start the meme REPL |
meme convert <file\|dir> | Convert between .meme and .clj (direction detected from extension) |
meme format <file\|dir> | Format .meme files via pprint (in-place by default, --stdout to print) |
meme version | Print version |
All file commands accept directories (processed recursively) and multiple paths. convert and format accept --stdout to print to stdout instead of writing files.
Entry point: -main dispatches via babashka.cli. For Clojure JVM, use -T:meme (e.g., clojure -T:meme run :file '"hello.meme"').
Value resolution. Converts raw token text to Clojure values. Centralizes all host reader delegation (read-string calls) with consistent error wrapping and location info.
All resolvers take the raw token text and a loc map ({:line N :col M}) for error reporting:
(meme.alpha.parse.resolve/resolve-number raw loc) ;; "42" → 42
(meme.alpha.parse.resolve/resolve-string raw loc) ;; "\"hi\"" → "hi"
(meme.alpha.parse.resolve/resolve-char raw loc) ;; "\\newline" → \newline
(meme.alpha.parse.resolve/resolve-regex raw loc) ;; "#\"\\d+\"" → #"\d+"
(meme.alpha.parse.resolve/resolve-auto-keyword raw loc resolve-fn)
Resolve an auto-resolve keyword (::foo). If resolve-fn is provided, resolves at read time. Otherwise, defers to eval time via (read-string "::foo").
(meme.alpha.parse.resolve/resolve-tagged-literal tag data loc)
Resolve a tagged literal. JVM: produces a TaggedLiteral object via clojure.core/tagged-literal. CLJS: throws an error.
The meme reader fails fast on invalid input. Parse errors are thrown as ex-info exceptions with :line and :col data when available.
Common errors:
| Error message | Cause |
|---|---|
Expected :close-paren but got EOF | Unclosed ( |
Unquote (~) outside syntax-quote | ~ only valid inside ` |
(try
(meme.alpha.core/meme->forms "foo(")
(catch Exception e
(ex-data e)))
;=> {:line 1, :col 4}
Error recovery is not supported — the reader stops at the first error. This is documented as future work in the PRD.
Error infrastructure used by the tokenizer and reader. Portable (.cljc).
(meme.alpha.errors/source-context source line)
Extract the source line at a 1-indexed line number from source (a string). Returns the line text, or nil if out of range.
(meme.alpha.errors/meme-error message opts)
Throw ex-info with a consistent error structure. opts is a map with:
:line, :col — source location (appended to message as (line N, col M)):cause — optional upstream exception:incomplete — when true, signals the REPL that more input may complete the formAll tokenizer and reader errors go through this function.
(meme.alpha.errors/format-error exception source)
Format an exception for display. Produces a multi-line string with:
"Error: ")^) or span underline (~~~) pointing at the column(s) — uses ^ for single-column errors, ~ for multi-column spans when :end-col is present in ex-data:secondary is present in ex-data — a sequence of {:line :col :label} maps):hint is present in ex-data)If source is nil/blank or the exception lacks :line/:col, only the prefixed message is returned.
Can you improve this documentation?Edit on GitHub
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 |