Guides · 17

Tooling

Vyi ships as one compiler binary (vyi) plus a language server that powers the VS Code extension and the web playground. This guide covers what you type at the shell, how to pick a target, what diagnostics look like, and what the editor integration gives you.

Install

The fastest path on linux (amd64 or arm64) is the install script — it fetches prebuilt vyi, vyi-language-server, and the standard library:

sh
curl -fsSL https://forgejo.roberthurst.ca/robert/Vyi/raw/branch/master/scripts/install.sh | bash
export PATH="$HOME/.local/bin:$PATH"
vyi help

To build from a source checkout instead:

sh
( cd bootstrap-compiler && go build -o vyi ./cmd/vyi )
export VYI_STDLIB_PATH="$PWD/packages"   # or --packages packages on each run
./bootstrap-compiler/vyi run path/to/main.vyi

Full detail — options, artifact URLs, language server, verification gate — is in reference/toolchain/install.

The vyi CLI

sh
vyi build [bin]  # compile to out/<target>/<bin> (all bins if omitted)
vyi run   [bin]  # compile and execute (all bins if omitted)
vyi check [bin]  # typecheck only; report diagnostics
vyi test  [dir]            # run every *.tests.vyi under dir (default: .)
vyi ast   [bin]  # print the AST as JSON (post-transform; see --pre-elaborate)
vyi help                   # summary of all of the above

One rule to remember: flags go between the subcommand and the entry pathvyi run --target x86-64-linux main.vyi. Anything after the entry is not parsed as a flag; for run it is forwarded to your program as its arguments (a leading -- separator is conventional):

sh
vyi run main.vyi -- --verbose input.txt   # your program's args, not vyi's

vyi build

Compiles the entry to an artifact — a .wasm module by default, or a native executable with --target x86-64-linux (native builds link with your system C compiler: cc, gcc, or clang).

sh
vyi build main.vyi                        # writes out/wasm/main
vyi build greeter                         # bin from package.vyipac
vyi build                                 # every entry in the package
vyi build -o app.wasm greeter             # choose the output path (single bin)
vyi build --target x86-64-linux greeter      # out/x86-64-linux/greeter

--emit picks the output kind:

sh
vyi build --emit=wat main.vyi             # WebAssembly text to stdout
vyi build --emit=asm --target x86-64-linux main.vyi   # native assembly text

(--emit=wasm is the default; wat and asm write to stdout unless -o names a file.)

In a project with a package.vyipac, the positional argument can also be a bin name from the manifest's entries block instead of a file path: vyi build mytool. (Guide 14 covers manifests.)

vyi run

Builds in memory and executes immediately. On the default wasm target the module runs under an embedded runtime with WASI: your stdin/stdout/stderr pipe straight through, the current directory is mounted so the program can open files relative to it, and arguments after the entry become the program's argv. With --target x86-64-linux it compiles, links, and executes a native binary directly. Either way the process exit code is your program's exit code — an fn main() !u8 returning Ok(3) exits with 3; an unhandled error prints error: … and exits 1; an unhandled panic prints the panic and a stack trace.

--timings prints a machine-readable compile/execute timing line to stderr after the run — useful for benchmarking your build loop.

vyi check

Runs the full front end and reports diagnostics without producing an artifact — the fast loop for editors and CI. Exit code 0 when clean, 1 when anything was reported. Add --json for tooling (see Diagnostics below).

vyi test

Discovers every *.tests.vyi file under the directory (recursively), compiles and runs each, and renders a pass/fail tree with a summary. Its exit code is the number of test files that failed, so it slots directly into CI. Test files are ordinary Vyi programs using the @testing package — guide 16 — Testing covers writing them. Accepted targets are wasm (default), x86-64-linux, and arm64-linux.

sh
vyi test                          # wasm (default)
vyi test --target x86-64-linux proj  # same tests as native ELFs

vyi ast

Prints an abstract syntax tree as JSON. Default is the post-transform tree the compiler checked; --pre-elaborate dumps the parse tree (FnDecl, OptionalType, …) before elaborate rewrites sugar. The playground AST tab uses --pre-elaborate. --spans includes source positions.

Common flags and the package root

Every subcommand accepts --target (see below), --os, and --packages <dir> — the root where @-packages like @io live. Resolution order: --packages, then $VYI_STDLIB_PATH ($VYI_PACKAGES is a legacy alias), then the install and development fallbacks documented in the CLI reference. You only reach for --packages or VYI_STDLIB_PATH when running a compiler you built yourself from an unusual location.

Targets

Vyi compiles to three targets:

  • wasm (the default) — a WebAssembly module. vyi run executes it immediately under the embedded runtime; the same .wasm file runs under any WASI-capable host.
  • x86-64-linux — a native linux/x86-64 ELF executable (System V ABI). Needs a C compiler on the machine to link.
  • arm64-linux — a native linux/AArch64 ELF executable. Same link requirements as x86-64-linux.
sh
vyi run main.vyi                       # wasm (default)
vyi run --target x86-64-linux main.vyi    # native x86-64
vyi run --target arm64-linux main.vyi  # native arm64

Programs behave the same on every target — same values, same output, same exit codes. The compiler's own test suite runs fixtures across targets and compares; you can switch targets late in a project without re-validating your logic.

Diagnostics

The compiler reports everything it can find in one pass — it does not stop at the first error, and it keeps analyzing even when the file has parse errors, so one vyi check shows you the whole repair list. Diagnostics come out as a single stream, sorted by position and deduplicated, each with a source excerpt and caret:

text
two.vyi:2:18: error: cannot assign ArrayPointer(u8) to i32
        con a: i32 = "hi"
                     ^^^^
two.vyi:3:19: error: cannot assign i32 to bool
        con b: bool = 3
                      ^
2 errors

vyi check --json emits the same diagnostics as a JSON array on stdout, each entry attributed to its file with a 1-based range — the format the playground and other tools consume:

json
[
  {
    "path": "err.vyi",
    "op": "undefinedName",
    "severity": "error",
    "message": "undefined name \"tru\"",
    "range": {
      "start": { "line": 2, "col": 18 },
      "end": { "line": 2, "col": 21 }
    }
  }
]

op is a stable machine-readable code for the diagnostic kind; message is the human text.

The VS Code extension

The vyi extension gives full language support for .vyi files. It runs one language-server instance per Vyi project (a folder containing package.vyipac) found anywhere under your workspace, plus one instance for stray .vyi files that belong to no project — so a repository holding several Vyi packages just works, and every feature sees the whole project, not just the open file.

What you get:

  • Diagnostics as you type, across all files of the project — edit one file and errors it causes in another surface immediately.
  • Hover with the declaration's signature, its resolved type, and its doc comment.
  • Completion that understands position: struct-literal positions offer the type's remaining fields, member access offers fields/variants/methods, and when the expected type at the cursor is an enum its variants are offered directly (matching-type candidates rank first).
  • Go to definition — including into the standard library and your dependencies; find references; rename across the project.
  • Quick fixes: fill a struct literal's missing fields, add a switch's missing arms, "did you mean …" for misspelled names, and add a missing import when another file exports the name.
  • Refactors: extract variable, extract function, inline variable (each extract drops you straight into a rename).
  • Signature help while typing a call, inlay hints showing inferred types, semantic highlighting, document and workspace symbols, and folding.

Doc comments are lines starting with /// immediately above a declaration; hover and completion surface them:

vyi
import { stdout } from "@io"

/// Clamps n to the 0..=255 range.
fn clamp(n: i32) i32 {
    if n < 0 { return 0 }
    if n > 255 { return 255 }
    return n
}

fn main() !u8 {
    stdout.writeInt(i64(clamp(300)))  // hover `clamp` to see the doc
    stdout.write("\n")
    return Ok(0)
}

The extension also understands package.vyipac manifests: unknown keys, malformed versions, and entry paths that don't exist are flagged in place, and key completion knows the manifest schema. .vyipac and .vyisum files get syntax highlighting too.

Feature toggles live under the vyi.* settings (inlay hints, semantic highlighting, diagnostics, and code actions can each be switched off). Set vyi.languageServer.target to wasm (default) or x86-64-linux so analysis matches the target you build and run with — the extension restarts the server when path or target changes. Vyi: Restart Language Server(s) is there when you need a manual restart.

The language server and the playground

All editor intelligence comes from one standalone binary, vyi-language-server, which speaks the Language Server Protocol over stdio (it accepts the conventional --stdio flag). It shares the compiler's front end, so what the editor tells you and what vyi check tells you never disagree. Any LSP-capable editor can use it — point your editor's LSP client at the binary for .vyi files.

The web playground wraps the same pieces in a browser page: a Monaco editor with diagnostics, hover, go-to-definition, outline, and semantic highlighting from the same language server, plus tabs showing the program's output, the generated WebAssembly text, and the AST. It runs locally from the repository (website/run.sh) and serves on one origin.

See also: reference/toolchain/install, reference/toolchain/cli, reference/toolchain/targets.