Feature reference¶
A flat list of everything Capy ships with. For deep dives follow the links; this page is a quick reference for "is X supported?".
Engine model¶
| Feature | Status | Where |
|---|---|---|
| Zero default grammar — every shape is library-defined | ✅ | language-reference |
| Single-binary Go engine, no runtime deps | ✅ | go install github.com/olivierdevelops/capy/cmd/capy@latest |
| Programmatic Go API | ✅ | capy.NewLibrary(src) → lib.Run(script) / lib.RunMulti(script). See embedding. |
| Library introspection (names, args, docstrings, optional/default flags) | ✅ | lib.Introspect() / lib.CommentMarkers() — see embedding |
| Browser playground — same engine compiled to WebAssembly | ✅ | playground |
| Caret-pointed error messages with line:col + did-you-mean | ✅ | domain.CapyError, errors-and-debugging |
Library schema¶
| Section | Required? | Purpose |
|---|---|---|
extension |
optional | Suggested output file extension (informational). |
output_file |
optional | If set, capy writes here instead of stdout. |
types |
optional | Library-defined argument types with base, pattern, options. |
context |
optional | Initial schema for the accumulated context (lists/maps/scalars). |
functions |
required | Every recognised source-language construct. |
file_template |
optional | Top-level assembler block. Defaults to emitting body verbatim. |
Function definitions¶
| Field | Purpose |
|---|---|
args |
Ordered list of {kind: literal, value} and {kind: capture, name, type} entries. |
template |
Go text/template rendered into the body per match. |
run |
Inner-DSL snippet that mutates context. Does NOT execute user code. |
block.closer |
Mode-A block opener (INDENT/DEDENT body + named closer function). |
block.open + block.close |
Mode-B block opener (explicit delimiter pair). |
block_close_seq |
Mode-E block opener — multi-token sequence closer (matched-pair HTML/XML). |
arg capture x FUNC / FUNC* / FUNC+ |
Function-as-type capture (named nonterminal), optionally repeated with sep. |
priority |
Higher wins on ambiguous matches; default 0. |
Auto-name-prepend rule¶
If args: contains zero kind: literal entries, the engine prepends a
literal of the function's key. Add any literal and the function name
disappears from the surface — that's how operator-style patterns
(<var> = <value>, <a> -> <b>) work. Use bare to opt out and keep
the function callable as plain prose.
Authoring ergonomics¶
| Feature | What it gives you |
|---|---|
template … end |
Block sugar for a multi-line backtick write literal — same ${…} interpolation, no backtick bookkeeping. |
Optional args with default |
A trailing capture can declare default "value", making it omittable. One button function serves button "Save" and button "Save" "danger" "submit". Optional args must be trailing (checked at load time). |
| Group types | type X { group_open "[" group_close "]" } lets a capture consume a delimited span, so link [text](url), bold **x**, ~~strike~~ map onto one source line. See types. |
| Multi-line backtick captures | A `…` string capture in a user script spans newlines — wrap a paragraph across several lines. |
| Escapable backticks | \` inside a backtick capture is a literal backtick, so a Markdown code span survives without closing the capture. |
| UTF-8 prose | Accented Latin, CJK and emoji tokenise as ordinary idents — bare prose needs no quoting. |
${line} / ${col} source mapping |
Stamp the current statement's source position onto output for editor scroll-sync / inline errors. |
These ship with focused, golden-tested examples — open the ✨ Features category in the playground, or browse the new-feature showcase.
Built-in capture types¶
| Type | What it captures |
|---|---|
any |
Any single value expression (numbers, strings, lists, objects, dotted paths, paren sub-calls, comparisons). |
ident |
A single identifier token; bound as a string. |
raw |
One identifier OR string token. |
word |
A shell-style bare word — adjacent tokens with no source whitespace joined into one value (--oneline, k8s/deploy.yaml, restart-api). |
dotted_ident |
A dotted path IDENT(.IDENT)* captured as one string (err.kind). |
string |
A quoted string literal — OR a bare identifier. |
int |
An integer literal — OR a bare identifier. |
float |
A float literal — OR a bare identifier. |
bool |
true/false — OR a bare identifier. |
tail |
Every remaining token on the line, joined as one source-text string (preserves source spacing). Great for free-form trailing values. |
<library-type> |
Any name defined under types:, including group types (group_open/group_close) for inline syntax like [text](url). |
Bare identifiers always pass primitive type checks because the target language could resolve them as variables of any type at its own runtime.
Library-defined types¶
Declared under types: with three optional fields applied in order:
| Field | What it does |
|---|---|
base |
Built-in kind check first (string/int/float/bool/any). |
pattern |
Regex applied to the value's string form. |
options |
Enum membership. |
Common patterns shipped with samples: Email, Slug, EnvName,
SemVer, Status, Identifier.
Lexical features¶
| Feature | Notes |
|---|---|
| Identifiers | [A-Za-z_][A-Za-z0-9_]* |
| Numbers | Integers and floats, signed. |
| Strings | "...", '...', and `...` (template), all with ${expr} interpolation |
| Multi-character punctuation | Greedy run: ==, !=, <=, >=, :=, ->, =>, |>, etc. each lex as one token. |
| Comments | # to end of line. |
| Indentation | 4 spaces or 1 tab per level. Mixed/odd indent is a parse error. |
| Multi-line literals | {...}, [...], (...) allow newlines inside; value parsers skip them. |
| Object literal keys | Quoted strings OR bare identifiers ({name: "x", "id": 1}). |
Inner DSL (run: field)¶
All operations available inside a run: snippet:
| Statement | Purpose |
|---|---|
set <path> <value> |
Bind a field at a dotted/indexed path on context. |
append <path> <value> |
Push to a list. Creates the list if it doesn't exist. |
prepend <path> <value> |
Prepend to a list. |
merge <path> <map> |
Shallow-merge into a map. |
delete <path> |
Remove a field/key. |
if <expr> ... end |
Library-side conditional update. |
loop <var> in <expr> ... end |
Library-side iteration (NOT user-script iteration). |
regex_match value pattern |
Boolean expression value. |
error <message> |
Abort transpilation with a clear message. |
Paths root at context (or at a loop local). Bracket indexing
supported: context.scripts[name] evaluates name at runtime.
Expressions inside the function body support comparisons (==, !=,
<, <=, >, >=), unary not, lists [...], objects {...},
dotted paths.
Interpolation helpers¶
Available inside any write \...`literal via${expr | helper}or${helper arg expr}, in both per-function bodies and the top-levelfile_template`.
| Helper | Effect |
|---|---|
indent N |
Indent every line of a string by N spaces. |
decoded |
Resolve escape sequences (\n \t \" \` \xNN \uNNNN) in a captured string — round-trips even when the text contains bare quotes. The inverse of source-text capture. |
escapeHtml |
Neutralise HTML-special chars (& < > " ') — the safe way to interpolate user prose into HTML. |
lower / upper |
Case conversion. |
pascalCase / camelCase / snakeCase |
Identifier case conversion. |
dasherize |
Replace underscores with hyphens (for CSS, etc.). |
join SEP <list> |
Join a list of strings. |
split SEP <string> |
Split a string into a list. |
unquote |
Strip one layer of surrounding "...", '...', or `...`. |
unescape |
Resolve standard Go escape sequences (legacy; prefer decoded). |
toQuoted |
Wrap a string in JSON-style double quotes. |
asString |
Normalise a capture to ONE valid JSON string, quoting iff not already a string — correct for both a bare ident and a quoted string. |
toPyLit |
Format a Go value as a Python literal (True/False/None/lists/dicts). |
toJSON / toJSONIndent |
Marshal any value to compact / pretty JSON. |
trimPrefix P / trimSuffix S |
Strip a leading / trailing substring. |
nonEmpty |
True when the string is non-empty (handy in if). |
add / sub / mul |
Integer arithmetic. |
percent N D |
Format N/D as a percentage. |
stars N |
Render N filled stars (★) — used by the reading-log demo. |
Control flow (if, for) lives in the function body itself —
not inside ${...} interpolations.
Render locals — available inside every body / template¶
| Local | What it is |
|---|---|
${body} |
Inner block's rendered output (block-opener functions). |
${line} / ${col} |
Source line / column of the current statement — stamp onto output for source↔output mapping (editor scroll-sync, inline errors). A capture of the same name wins. |
${depth} / ${top_level} |
Nesting depth, and whether the statement is at the top level. |
Block functions¶
Two modes; declare exactly one per opener:
Mode A — indent + named closer¶
function if
arg literal "if"
arg capture cond any
block_closer end
write `if ${cond}:
${indent 4 body}
`
end
Body delimited by INDENT/DEDENT. Closer is itself a library function (may have a template, or be silent).
Mode B — explicit delimiters¶
Body delimited by the named tokens. No closer function needed.
Mode C — verbatim (raw bytes)¶
The body is captured as raw source bytes — not re-parsed as Capy.
Blank lines and #-prefixed lines survive byte-for-byte. Ideal for
code blocks, inline SVG, or embedded HTML. Pair with ${escapeHtml body}
to emit it safely. See samples/verbatim-pre.
Mode D — dedent¶
Body runs until the indentation returns to the opener's level — no explicit closer keyword.
Mode E — multi-token sequence closer (matched-pair HTML/XML)¶
function element
arg literal "<"
arg capture name ident
arg capture attrs attribute*
arg literal ">"
block_close_seq "</" name ">"
write `<${name}${attrs}>${body}</${name}>`
end
The body closes on an exact run of tokens, not a single keyword.
Segments are quoted literals or bare capture-name refs bound by the
opener, so a capture-bound closer (</NAME>) lets ONE function parse any
well-formed <tag>…</tag> — HTML or XML. Each open tag demands its own
matching close tag, so mismatched nesting (<div><p></div>) is a hard
parse error. See samples/html-xml-parser and
block-functions.
Nesting works freely across all modes, including mixed (Mode-A inside Mode-B and vice versa).
Function-as-type captures (named nonterminals)¶
A capture's type may name another library function instead of a
built-in type. The capture then matches that function's shape and renders
its template. Add a quantifier to repeat it, plus two independent
separators: sep "X" (consumed between repetitions on INPUT while
parsing) and join "Y" (inserted between rendered sub-results on
OUTPUT):
| Form | Meaning |
|---|---|
arg capture x SomeFunc |
exactly one SomeFunc (mandatory) |
arg capture xs SomeFunc* |
zero or more |
arg capture xs SomeFunc+ |
one or more |
arg capture xs SomeFunc+ sep "," |
one or more, comma-separated input |
arg capture xs SomeFunc* sep "," join ", " |
comma in, ", " out |
A pure-capture nonterminal (no arg literal) must be declared bare
to opt out of the auto-prepended name keyword. See
block-functions.
CLI¶
| Subcommand | Effect |
|---|---|
capy run <lib.capy> <script.capy> |
Transpile a script. Output to stdout unless --out or library output_file. |
capy check <lib.capy> |
Validate a library; report functions and types. Exit 0 if valid. |
capy docs <lib.capy> |
Render a Markdown reference doc from the library's description annotations. |
capy build <lib.capy> [-o <out>] |
Compile a library into a standalone single-purpose CLI binary. See compiling-libraries. |
capy lib new <name> |
Scaffold a new library. |
capy lib list / which / path |
Manage installed libraries on the CAPY_LIBS search path. See library-commands. |
capy new <dir> --using <library> |
Scaffold a new project from a library. |
capy <library> <command> [args] |
Dispatch a library-defined command (command "run" … end). |
capy version |
Print build version. |
capy help [<command>] |
Inline help. |
Flags for run:
| Flag | Effect |
|---|---|
--out <path> |
Override output_file. |
--no-color |
Disable ANSI escape codes (reserved). |
--debug |
Verbose engine tracing (reserved). |
-lib <path> |
Legacy library path (use the positional form instead). |
Output assembly¶
Each library function's body writes text into a per-block body
string. Block functions reference ${body} (or ${indent N body})
to get the rendered output of their children. The top-level
program's body, plus the accumulated context, are passed to
file_template for the final output.
Inside any function body / write literal:
| Reference | What it is |
|---|---|
${<capture>} |
Source-text form of a capture (with quotes for string literals). |
${body} |
Inner block's rendered output (only inside block-opener functions). |
${context.X} |
Read-only snapshot of the accumulated context at this point. |
${func arg arg} |
Call a helper inline (indent, pascalCase, toQuoted, …). |
Inside file_template:
| Reference | What it is |
|---|---|
${body} / write body |
Concatenation of all top-level statements' rendered output. |
${context.X} |
Final accumulated context. |
Source vs evaluated captures¶
A capture has two faces:
- **Inside
write \...`interpolation** — captures resolve to **source text** (with quotes for string literals). Soif x > 0exposescondas the literal textx > 0and a Python emitter can writeif ${cond}:` unchanged. - In
set/append/ifexpressions — captures resolve to evaluated Go values. Strings become Go strings without quotes, numbers become int64/float64, lists become[]any, objects becomemap[string]any. Unresolved bare identifiers fall back to their literal name.
This dual model means one capture serves both render-by-text (templates) and structured accumulation (context) without any explicit conversion.
AI / agent ecosystem¶
| Asset | Purpose |
|---|---|
| Claude Code skill | Full skill with SKILL.md + instructions + 5 reference docs |
| Slash commands | /capy-new, /capy-add-function, /capy-add-type, /capy-explain, /capy-debug |
CAPY_FOR_LLMS.md |
Single-page brief paste-able into any model |
| Cursor rule | Drop-in .cursor/rules/capy.md |
| Continue config | Adds LLM brief to context |
| Aider read | aider --read docs/CAPY_FOR_LLMS.md |
| Agent system prompt | Drop-in for any tool |
See Capy for AI agents for token-savings math and sandboxing patterns.
Editor support¶
| Editor | Where |
|---|---|
| VS Code | editors/vscode/capy/ — syntax highlighting for .capy source and libraries |
Distribution¶
| Method | Command |
|---|---|
| Go | go install github.com/olivierdevelops/capy/cmd/capy@latest |
| Binary release | Releases page |
| Install script | curl -fsSL https://raw.githubusercontent.com/olivierdevelops/capy/main/scripts/install.sh \| sh |
| Docker | docker build -t capy . (Dockerfile in repo root) |
| Homebrew | Coming when the olivierdevelops/homebrew-tap repo exists |
Pre-1.0 status¶
The library schema may evolve between minor versions before 1.0. Each breaking change appears in CHANGELOG with a migration note. The engine itself is stable enough to use in production for code generation; just pin a specific version.
Planned features: see roadmap.