vision
Install

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.vis to 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 every test: 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.

compile-pipeline.viscompiler checked
to begin:
    say "ready: v3.0.2"
to begin:
    say "ready: v3.0.2"
A two-line program that prints one line. The captured stdout is exactly what the native binary prints.

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 to output.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 with this 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 no main(). to begin: is skipped. Pair with --header to give the host the prototypes it needs to call the verbs.
  • --double โ€” decimals lower to C double. This is the default; the flag is accepted as a no-op.
  • --float โ€” decimals lower to C float (the Ocean/DSP bit-exact mode). The compiler defaults to double; use --float when you need C float.
  • --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:

/Users/june/projects/vision/build/visionno-args (stderr)
usage: vision input.vis [-o output.c] [--double] [--library] [--header output.h]
`vision` with no arguments prints the one-line usage to stderr and exits 2. The compiler has no long `--help`; `--help` itself falls through to the same usage line and is not pinned separately here.

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.

/Users/june/projects/vision/build/vision why built-from-nothing-literalvision why <code> (known)

  `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 nothing
A real code from the compiler's own registry. The teaching paragraph is the calm explanation; the `for example:` block is a one-step reproducer.
/Users/june/projects/vision/build/vision why nonexistent-codevision why <code> (unknown)
vision: I don't have a note on "nonexistent-code" yet
An unknown code answers in the loving voice rather than failing loudly. The command exits 0 either way; the lookup is forgiving by design.

The 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.

/Users/june/projects/vision/build/vision explain zlibvision explain <library>
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 number
The header line includes the path of the header the compiler discovered. The function list that follows is the union of every exported symbol in that header.

The 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.

/Users/june/projects/vision/build/vision explain reference-values-shaped.visvision explain <program.vis>
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.
Every verb is world-free; the closing sentence names the proven property.

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.

/Users/june/projects/vision/build/vision explain memory reference-values-shaped.visvision explain memory <program.vis>
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.
Memory is the only verb that takes a sub-shape (`memory`) before the program path; the parser accepts `vision explain <program.vis>` and `vision explain memory <program.vis>` as two distinct verbs.

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.

/Users/june/projects/vision/build/vision audit reference-values-shaped.visvision audit <program.vis>
This program understands no C libraries.
A program that uses no C library prints the empty-surface line. A program that `this program understands zlib` and calls a zlib function prints the function name here.

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.

/Users/june/projects/vision/build/vision stats reference-values-shaped.visvision stats <program.vis>
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.
Counts of every top-level shape and the deepest block nesting.

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.

/Users/june/projects/vision/build/vision schema reference-values-shaped.visvision schema <program.vis>
{
  "$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"]
    }
  }
}
Two described things in the program yield two `$defs` entries. The schema's structure is deterministic in the field order.

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.

/Users/june/projects/vision/build/vision diagram reference-values-shaped.visvision diagram <program.vis>
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 terminal form. Verb-to-thing arrows are drawn as `โ”€โ”€โ†’`; thing-to-thing references are summarized in the closing count.

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.

/Users/june/projects/vision/build/vision fmt reference-values-shaped.visvision fmt <file.vis>
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 pinned output reproduces the canonical layout โ€” enums, kinds, unions, callables, verbs, then `to begin:`.

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.

/Users/june/projects/vision/build/vision --versionvision --version (stderr)
usage: vision input.vis [-o output.c] [--double] [--library] [--header output.h]
The current compiler has no `--version` flag; it falls through to the usage path and exits 2.

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 by vision test. Defaults to cc.
  • VISION_TEST_CFLAGS โ€” extra flags passed to the host C compiler by vision 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 --version flag. vision --version falls through to the usage path and exits 2. The CLI does not expose a compiler identity.
  • No long --help output. 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 running vision why or compiling with --explain. See the Diagnostics reference for the per-code coverage and the lookup mechanism.
  • The format and the AST are decoupled. vision fmt uses 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.