Tooling reference
Tooling reference
The compiler is one binary with eleven distinct verbs. Every command is read-only against source except the default compile path, the formatter's `-w`, and the tester. This page is the ground-truth shape of the command-line surface, verified by running each verb against the compiler on disk.
Every command documented here was run against the compiler at /Users/june/projects/vision/build/vision. The pinned stdout/stderr is byte-for-byte the real output. The compile pipeline fixture uses the same scripts/check-examples.mjs gate as the rest of the site (Vision โ C โ native โ run), and the standalone tooling commands use a parallel scripts/check-tooling.mjs gate that runs each pinned invocation and compares its stdout or stderr against the captured file. vision why <code> and the --explain flag are also documented in the Diagnostics reference; the construction rules each command depends on are in the Values & described things reference and the surrounding language references.
The compiler's command surface
The compiler exposes eleven verbs. Each one is dispatched on the first argument to vision; the default (no verb) is the compile path.
- Default (no verb) โ compile
input.visto C. why <code>โ look up a diagnostic's teaching paragraph by code.explain <library>โ print a discovered C library's function list.explain <program.vis>โ narrate what a Vision program does in plain English.explain memory <program.vis>โ show how every value in a program is handled (automatic / explicit / by hand).audit <program.vis>โ list the C functions the program actually reaches into.stats <program.vis>โ a portrait of the program's shape: verbs, described things, enums, libraries, deepest nesting.schema <program.vis>โ emit a JSON Schema (draft 2020-12) for the program's described-things.diagram <program.vis> [--svg]โ a structural map of the program (default: terminal box-drawing;--svg: a dependency-free standalone SVG).fmt <file.vis> [-w]โ re-lay-out the source in canonical form (default: stdout;-w: rewrite in place).test <file.vis>โ compile and run everytest:block in the file.lspโ speak the Language Server Protocol over JSON-RPC on stdin/stdout.
The default vision input.vis form is the only one that writes to disk by default. fmt -w rewrites the source file (atomic rename) and test writes a private temp tree under /tmp before deleting it. Every other command is read-only against source and prints to stdout.
Compiling a program
The compiler's default mode reads one .vis file, resolves any links-included peer files into a flat program, runs lex โ parse โ check โ memory-analyze โ emit, and writes a single C translation unit. The emitted C uses GNU statement expressions and __attribute__((cleanup)), so the host C compiler must be GNU-C-compatible.
The shortest invocation is one file in, one file out, then the host compiler and one run. The pinned pipeline below exercises the full shape on the dev compiler.
to begin:
say "ready: v3.0.2"to begin:
say "ready: v3.0.2"The compiler accepts a small, stable flag set on the default path. Every flag below is parsed in src/main.c and reflected in the generated C or the generated header.
-o <output.c>โ write the generated C tooutput.c. Without this flag the compiler writes to stdout, which is rarely what you want for a full program.--header <output.h>โ write C prototypes for every verb declared withthis program exposes ... to the host. The header is generated in addition to the program C, not instead of it.--libraryโ emit a host-callable C unit with nomain().to begin:is skipped. Pair with--headerto give the host the prototypes it needs to call the verbs.--doubleโ decimals lower to Cdouble. This is the default; the flag is accepted as a no-op.--floatโ decimals lower to Cfloat(the Ocean/DSP bit-exact mode). The compiler defaults todouble; use--floatwhen you need Cfloat.--explainโ append the teaching note under each diagnostic in stderr. The default (--quiet) prints the bare headline only.--no-source-mapโ omit the default-on#line N "abs.vis"directives from the emitted C. The default keeps them so clang and ASan backtraces name the real Vision line, not a generated-C line number.
The usage string the compiler itself prints is a one-line synopsis and intentionally short:
usage: vision input.vis [-o output.c] [--double] [--library] [--header output.h]The website's content gates validate pinned outputs before the site builds. This is a website-only concern, not part of the compiler CLI surface.
Looking up a diagnostic: vision why
Every diagnostic in the compiler has a stable, beginner-readable code. vision why <code> prints the same teaching paragraph that --explain appends under each error, plus a small worked example. The lookup is pure read-only against src/diag.c's registry.
`built from (...)` matches already-bound locals to fields by name, so every item inside the parentheses must itself have a field name. A bare `nothing` has no name and would make field wiring positional. Omit an optional field when absence need not be written, or use block construction to name the field explicitly.
for example:
the card is a card:
subtitle is nothingvision: I don't have a note on "nonexistent-code" yetThe default stderr format vision: near line N โ <headline> does not include the code โ the code is recovered through vision why <code> or by compiling with --explain. The Diagnostics reference documents the codes themselves; this page documents the lookup.
Discovering a C library: vision explain <library>
The explain verb branches on whether the second argument is a .vis file (program narration) or a bare word (library surface). The library form reads the library's installed header through the system clang (src/cbind.c's cbind_discover) and prints every function the compiler actually knows how to bind. The list is never a hardcoded table โ whatever the library's header says, the compiler says.
Vision understands zlib โ 81 functions, discovered from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/zlib.h:
zlibVersion takes nothing, gives some text
deflate takes something and a whole number, gives a whole number
deflateEnd takes something, gives a whole number
inflate takes something and a whole number, gives a whole number
inflateEnd takes something, gives a whole number
deflateSetDictionary takes something, a Bytef handle, and something, gives a whole number
deflateGetDictionary takes something, a Bytef handle, and a uInt handle, gives a whole number
deflateCopy takes something and something, gives a whole number
deflateReset takes something, gives a whole number
deflateParams takes something, a whole number, and a whole number, gives a whole number
deflateTune takes something, a whole number, a whole number, a whole number, and a whole number, gives a whole number
deflateBound takes something and something, gives something
deflatePending takes something, some whole numbers, and some whole numbers, gives a whole number
deflatePrime takes something, a whole number, and a whole number, gives a whole number
deflateSetHeader takes something and something, gives a whole number
inflateSetDictionary takes something, a Bytef handle, and something, gives a whole number
inflateGetDictionary takes something, a Bytef handle, and a uInt handle, gives a whole number
inflateSync takes something, gives a whole number
inflateCopy takes something and something, gives a whole number
inflateReset takes something, gives a whole number
inflateReset2 takes something and a whole number, gives a whole number
inflatePrime takes something, a whole number, and a whole number, gives a whole number
inflateMark takes something, gives a whole number
inflateGetHeader takes something and something, gives a whole number
inflateBack takes something, something, raw memory, something, and raw memory, gives a whole number
inflateBackEnd takes something, gives a whole number
zlibCompileFlags takes nothing, gives something
compress takes a Bytef handle, a uLongf handle, a Bytef handle, and something, gives a whole number
compress2 takes a Bytef handle, a uLongf handle, a Bytef handle, something, and a whole number, gives a whole number
compressBound takes something, gives something
uncompress takes a Bytef handle, a uLongf handle, a Bytef handle, and something, gives a whole number
uncompress2 takes a Bytef handle, a uLongf handle, a Bytef handle, and a uLong handle, gives a whole number
gzdopen takes a whole number and some text, gives something
gzbuffer takes something and a whole number, gives a whole number
gzsetparams takes something, a whole number, and a whole number, gives a whole number
gzread takes something, something, and a whole number, gives a whole number
gzfread takes something, a whole number, a whole number, and something, gives a whole number
gzwrite takes something, something, and a whole number, gives a whole number
gzfwrite takes something, a whole number, a whole number, and something, gives a whole number
gzprintf takes something, some text, and something, gives a whole number
gzputs takes something and some text, gives a whole number
gzgets takes something, some text, and a whole number, gives some text
gzputc takes something and a whole number, gives a whole number
gzgetc takes something, gives a whole number
gzungetc takes a whole number and something, gives a whole number
gzflush takes something and a whole number, gives a whole number
gzrewind takes something, gives a whole number
gzeof takes something, gives a whole number
gzdirect takes something, gives a whole number
gzclose takes something, gives a whole number
gzclose_r takes something, gives a whole number
gzclose_w takes something, gives a whole number
gzerror takes something and some whole numbers, gives some text
gzclearerr takes something, gives nothing
adler32 takes something, a Bytef handle, and something, gives something
adler32_z takes something, a Bytef handle, and a whole number, gives something
crc32 takes something, a Bytef handle, and something, gives something
crc32_z takes something, a Bytef handle, and a whole number, gives something
crc32_combine_op takes something, something, and something, gives something
deflateInit_ takes something, a whole number, some text, and a whole number, gives a whole number
inflateInit_ takes something, some text, and a whole number, gives a whole number
deflateInit2_ takes something, a whole number, a whole number, a whole number, a whole number, a whole number, some text, and a whole number, gives a whole number
inflateInit2_ takes something, a whole number, some text, and a whole number, gives a whole number
inflateBackInit_ takes something, a whole number, some text, some text, and a whole number, gives a whole number
gzgetc_ takes something, gives a whole number
gzopen takes some text and some text, gives something
gzseek takes something, a whole number, and a whole number, gives a whole number
gztell takes something, gives a whole number
gzoffset takes something, gives a whole number
adler32_combine takes something, something, and a whole number, gives something
crc32_combine takes something, something, and a whole number, gives something
crc32_combine_gen takes a whole number, gives something
zError takes a whole number, gives some text
inflateSyncPoint takes something, gives a whole number
get_crc_table takes nothing, gives a z_crc_t handle
inflateUndermine takes something and a whole number, gives a whole number
inflateValidate takes something and a whole number, gives a whole number
inflateCodesUsed takes something, gives a whole number
inflateResetKeep takes something, gives a whole number
deflateResetKeep takes something, gives a whole number
gzvprintf takes something, some text, and something, gives a whole numberThe function list is the same surface a by hand: section or a this program understands <library> declaration can reach into. If the compiler cannot bind a function the program calls, no-library-for-c-function reports the missing name and tells the reader to run vision explain <library> to find the right spelling. The C-binding flow is documented in the Host & C interop reference.
Reading a Vision program: explain, audit, stats, schema, diagram
Five read-only commands derive a calm description of a parsed-and-checked program. Every one of them parses the file, runs the checker, and prints. None of them write to disk.
vision explain <program.vis>
Narrates what the program does in plain English โ one bullet per verb, noting whether each one stays inside the program or touches the world. Programs whose verbs are all world-free close with this program touches nothing outside itself โ it is provably world-free.
what this program does
to compact item โ stays inside the program โ it touches nothing outside
to show item render โ stays inside the program โ it touches nothing outside
to describe outcome โ stays inside the program โ it touches nothing outside
to begin โ stays inside the program โ it touches nothing outside
this program touches nothing outside itself โ it is provably world-free.vision explain memory <program.vis>
The memory audit (ยง1.2 in the spec) โ how every value is handled. A program whose values are all automatic closes with Every value is automatic and proven. No `memory:` block is needed โ nothing here to audit by hand. A program with at least onememory: block instead prints the per-value audit.
memory โ how every value is handled (ยง1.2)
to compact item โ every value automatic (stack/by-value, proven)
to show item render โ every value automatic (stack/by-value, proven)
to describe outcome โ every value automatic (stack/by-value, proven)
to begin โ every value automatic (stack/by-value, proven)
Every value is automatic and proven. No `memory:` block is needed โ
nothing here to audit by hand.vision audit <program.vis>
The realized foreign surface โ which C functions the program actually reaches into. The set is derived from the checked AST, so it cannot drift from the source.
This program understands no C libraries.vision stats <program.vis>
A calm portrait: how many verbs (and how many touch the world, fail, or ask a question), described things, enums, C libraries, lists, by-hand sections, servers, subprocess calls, and the deepest block the program nests to. The world-free closing sentence mirrors explain's.
stats โ a portrait of this program
verbs 4
world-touching 0
fallible 0
questions 0
described things 2 (4 fields)
enums 1 (2 members)
c libraries 0
lists 0
by hand sections 0
servers 0
subprocess calls 0
deepest nesting 2
every verb is provably world-free โ nothing here reaches outside the program.vision schema <program.vis>
Emits a JSON Schema (draft 2020-12) for the program's described-things, one $defs entry each. Field types map from the checker's resolved shape (text โ string, whole โ integer, enum โ enum). Output is valid JSON.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"release": {
"type": "object",
"properties": {
"title": { "type": "string" },
"phase": { "enum": ["draft", "ready"] }
},
"required": ["title", "phase"]
},
"coordinates": {
"type": "object",
"properties": {
"horizontal": { "type": "integer" },
"vertical": { "type": "integer" }
},
"required": ["horizontal", "vertical"]
}
}
}vision diagram <program.vis> [--svg]
A structural map: which verbs operate on which described things, and which described things reference one another. The default rendering is a calm box-drawing map to the terminal; --svg emits a dependency-free standalone SVG (no <script>, no external assets) suitable for inline embedding.
diagram โ the shape of this program
described things
โโโโโโโโโโโ
โ release โ
โโโโโโโโโโโ
โโโโโโโโโโโโโโโ
โ coordinates โ
โโโโโโโโโโโโโโโ
verbs
to compact item โโโ release
to show item render โโโ release
to describe outcome
to begin โโโ coordinates
2 described things, 4 verbs, 0 references between described things.The --svg form is byte-for-byte deterministic given the same source, so the diagram is suitable for embedding in docs. The SVG is dependency-free: no <script> tag, no external font, no remote stylesheet. The first emitted line is <svg xmlns="..."> with the size and viewBox derived from the diagram's content.
Formatting source: vision fmt
vision fmt <file.vis> re-lays the source in canonical form. The default writes to stdout; -w rewrites the file in place via a temp file and atomic rename. The compiler refuses to format a file it cannot parse, so -w never lands a mangled file.
a phase is one of draft, ready
kind release:
title: text
phase: phase
kind coordinates:
horizontal: whole
vertical: whole
a lookup is one of:
a found, with a detail
a missing
a presenter is a way to present, given a release called item, returns text
to compact(item: release), returns text:
answer "{item.phase}: {item.title}"
to show(item: release, render: presenter):
say render(item)
to describe(outcome: lookup), returns text:
when outcome is a found:
answer "found: {outcome.detail}"
else:
answer "missing"
to begin:
the item is a release:
title is "3.0.2"
phase is ready
show(item, compact)
the horizontal is 7
the vertical is 9
the point is a coordinates built from (vertical, horizontal)
say "point: {point.horizontal},{point.vertical}"
the match is a found
match.detail is "reference"
say describe(match)The formatter comes from src/fmt.c's trivia-retaining lex pass; it never walks the AST. Comments are preserved; quoted strings are passed through verbatim. Run vision fmt -w *.vis to rewrite every Vision file in a tree.
Running tests: vision test
vision test <file.vis> compiles every test "...": block in the file into one binary and runs it. Each test: block runs in its own fork, so a failed should be, a give up, or a hard crash in one block is reported without stopping the others. The binary prints pass/fail counts and exits non-zero if any block failed.
The test path uses the host C compiler (default cc; override with VISION_CC) and respects VISION_TEST_CFLAGS for extra flags (space-separated; a bounded set). The harness stores its generated C and binary in a private mkdtemp directory under /tmp and removes the tree on the way out, so a remove() of the directory never fails on leftover contents.
A file with no test: blocks prints no tests found and exits 0. to begin: is not run.
Language server: vision lsp
vision lsp speaks the Language Server Protocol over JSON-RPC on stdin/stdout. The editor becomes the source of truth for open buffers; diagnostics flow through the same DiagSink as the compiler's CLI sink. The LSP server is read-only against disk.
The LSP's position features use emit_occurrences, a tolerant lexical-mode scan of the in-memory editing buffer. References and rename skip // and em-dash comments and string-literal chunks; interpolation holes are scanned as code. This is not an AST-backed semantic-binding guarantee โ clients must not assume it resolves aliases, shadowing, or every binding identity, especially in a malformed mid-edit buffer.
Output and exit codes
Every compiler command writes its primary output to stdout (compiled C, teach paragraph, narration, schema, diagram SVG, formatted source). Diagnostics โ parse errors, check errors, memory errors, link directives that fail โ go to stderr. Successful invocations exit 0; any error during the lex/parse/check/memory/emit pipeline exits 1. Missing arguments or unknown flag values print the one-line usage and exit 2.
The default vision (no args) and vision --help both write the same one-line usage to stderr and exit 2. vision --version is the same code path โ see the limitations section for why it does not print a version.
usage: vision input.vis [-o output.c] [--double] [--library] [--header output.h]Environment variables
Two environment variables configure the compiler, both read by the test path. Neither changes the compiler's CLI surface.
VISION_CCโ the host C compiler used byvision test. Defaults tocc.VISION_TEST_CFLAGSโ extra flags passed to the host C compiler byvision test(space-separated). Used to inject sanitizers (-fsanitize=address,undefined,-g,-O0) so the fork/pipe isolation path can be proven memory-clean.
The website harness honors VISION_COMPILER when the compiler is not at build/vision. This is a website-only setting, not a compiler CLI variable.
Current limitations
The compiler's CLI surface is deliberately small but real. The following should be treated as gaps, not as supported behavior:
- No
--versionflag.vision --versionfalls through to the usage path and exits 2. The CLI does not expose a compiler identity. - No long
--helpoutput. The usage line is one line of stderr. The full flag set (--float,--explain,--quiet,--no-source-map) is parsed by the compiler but not listed in the usage string. The detailed list lives on this page. - The usage string is stale. The line the compiler prints (
usage: vision input.vis [-o output.c] [--double] [--library] [--header output.h]) mentions--doubleโ the new default โ and omits--float,--explain,--quiet, and--no-source-map. SPEC.md notes this gap; a future release would refresh the usage line to match the parser. - The verb surface is closed. The eleven verbs are the entire compiler surface; there is no plugin or extension mechanism. New tooling commands land as new verbs in
src/main.c. - Tooling output is environment-dependent for C libraries.
vision explain zlib's header line includes the absolute path of the header the compiler discovered, and the function list varies with the installed SDK. The pinned fixture on this page is valid only on the dev machine; the gate verifies byte-for-byte, so a different SDK path will fail the gate until the pin is updated. The list shape and entry format are stable. - The default sink does not print the diagnostic code. A reader who sees
vision: near line N โ <headline>on stderr cannot recover the code without runningvision whyor compiling with--explain. See the Diagnostics reference for the per-code coverage and the lookup mechanism. - The format and the AST are decoupled.
vision fmtuses a trivia-retaining lex pass; it does not walk the AST. The formatter cannot enforce semantic normalization (it never sees one), only lexical layout. A semantically-equivalent but lexically-different program formats identically.