nupp command#
One executable holds every tool. Each command carries its own --help, which is the text nupp help <command> prints, and this page shows that text beside the output the command writes.
nupp check
nupp build
nupp run src/main.nuppThe commands, in the order nupp help lists them:
init: create a project from a templateast: dump a Nupp file's parsed syntax tree as JSONaot: show what the@aotfunctions in a file compile tobc: show the bytecode a Nupp file compiles tocheck: type-check source without emitting Luafmt: format Nupp sourcebuild: build source files or a configured project targetclean: remove build outputs configured innupp.luabench: run isolated benchmark programslints: list the lints and the level each runs atownership-audit: list foreign pointer contracts and unsafe assertion sitesexplain: describe a diagnostic code, with an example either wayreference: list or print a focused Nupp reference chaptercompletions: print a shell completion scripttest: build and run project teststask: list, inspect, or run project tasks fromnupp.luadoc: generate API documentation from source commentsfixpoint: verify a byte-identical self-hosting rebuildrun: compile and run a Nupp or Lua programimport-c: generate typed Nupp bindings from a C headermigrate: migrate typed foreign source into Nuppexport-c: export canonical C declarations for Nupp structsrock: create and package typed LuaRocks librarieslsp: language-server and semantic source operationsversion: print the compiler versionhelp: show general or command-specific help
Universal options#
Three options are appended to every command's grammar, so no command can be the one that forgot them:
--color[=WHEN]: always, never, or auto, the default--no-color: the same as--color=never-h,--help: show that command's help
--color never consumes the next argument. A bare --color means always, and a value is attached with =, which is what keeps the argument after it from being read as the value:
nupp check --color=never src/greet.nupp--color and --no-color land on one key, so asking for color and refusing it on the same command line is an error rather than last-wins.
A given --color decides on its own. With none, NO_COLOR refuses escapes, CLICOLOR_FORCE asks for them, and what is left is whether the stream is a terminal that understands them, which TERM=dumb says it is not. JSON output is never colored.
test, task, rock and the lsp group hand their arguments to another program or parse them per operation, so the universal options are not appended to them. Each takes -h, and lsp declares a grammar of its own.
JSON and schemas#
Every command that produces structured data takes --format json, shortened to --json, and --schema, which prints the JSON Schema of that output:
nupp check --json
nupp check --schemainit, aot, bc, check, fmt, build, clean, task, lints, ownership-audit, explain, doc, fixpoint, import-c, export-c and version take all three, and so does every lsp operation. reference names its formats markdown, skill and json instead. bench, test and run take --json and --schema with no --format, because the JSON each writes is one particular artifact rather than a rendering of the whole result. ast has no --format either: a lossless syntax tree has only the one form, so it takes --schema and prints JSON whatever else is asked. completions and rock produce no structured result and take neither.
A test runs each command for real and validates its output against that command's own --schema, so a schema cannot drift from what the command emits.
--format, --json and --text share one setting, and giving two of them is an error. No option repeats unless it says so, and three do: --set on init, and -Zno-opt on build and run.
A command writes its JSON as one line with no ordering guarantee across keys, and only ast offers an indented form, under --json-pretty. The json blocks on this page are indented so they can be read; the text blocks are the bytes the command wrote. A test runs each of them in the example project below and compares, apart from the few that write a project, need Nupp's own repository or a C toolchain, depend on the host, or are abridged.
Exit codes#
Every command returns one of three statuses:
0: success1: the work was attempted and failed2: usage error, such as an unknown option or the wrong argument count
A usage error is whatever the arguments decide on their own: a name checked against a set compiled into nupp (an explain code, a reference section, a shell, a built-in template) or against the argument alone (a rename's new identifier). A name checked against the project, such as a target, a task, a file or a line past its end, is a failure, status 1. A usage error is settled before any work starts, names the argument it could not use, and points at that command's help:
nupp: unknown option --colour
Try 'nupp help check' for usage.Example project#
Every example below runs in this project, except where a section says it runs in Nupp's own repository because it needs a test suite or a compiler to build:
greeter/
├── nupp.lua
└── src/
├── greet.nupp
└── main.nuppreturn {
include = { "src" },
build = {
outDir = "build",
default = "app",
targets = {
app = {
kind = "modules",
description = "Build the greeter",
entries = { "main" },
},
},
},
tasks = {
greet = {
description = "Print a greeting",
argv = { "nupp", "run", "src/main.nupp" },
},
},
}local greet = require("greet")
print(greet.greet("world"))Command reference#
init#
Create a project from a template.
Examples:
nupp init --list
nupp init app hello
nupp init lib math
Usage:
nupp init [options] [TEMPLATE] [DIRECTORY]
Arguments:
TEMPLATE Template name or repository.
DIRECTORY Destination directory.
Options:
--name NAME Project name; defaults to the directory basename.
--set KEY=VALUE Set a template variable.
--from PATH Use a template directory on disk.
--rev REV Commit, tag, or branch for a repository template.
--list List built-in templates and exit.
--yes Do not ask before writing a repository template.
--dry-run Print what would be written and write nothing.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe built-in templates travel inside the compiler, so this works with no network and no checkout:
built-in template what it writes
app A runnable program, with a test and a task to start it
browser A browser application using crypto and timers
browser-simd A browser application with scalar and SIMD128 AOT kernels
lib A typed library packaged as a LuaRocks rock
love A small LÖVE game using Nupp's LuaJIT compatibility target--list --json gives the same listing as templates, each with its name, description and kind.
nupp init with no arguments writes the app template into a directory named for it. Naming the directory names the project:
Created greeter from built-in template app
Next:
cd greeter
nupp check
nupp test greeter/
├── .gitignore
├── README.md
├── nupp.lua
├── src/
│ ├── greeting.nupp
│ └── main.nupp
└── tests/
└── run.luaThat project checks, builds, tests and runs as it stands, which is what the template is for.
Template sources#
A TEMPLATE argument is read as text, never by looking at the filesystem, so the same argument means the same thing in every directory.
| Argument | Resolves to |
|---|---|
app |
a built-in template of that name |
./x, ../x, /x, ~/x |
a directory on disk |
owner/repo |
https://github.com/owner/repo |
owner/repo@v1.2.0 |
the same, at that revision |
owner/repo/games/topdown |
that repository's games/topdown directory |
https://..., git@... |
used as given, with --rev for a revision |
--from PATH forces a directory, for the case where a local path looks like a repository name. A name with no slash that matches no built-in is refused by name rather than guessed at as a repository.
Writing a template#
A template is a directory tree with one template.lua at its root, which is not copied. Every other file is carried, .git at any depth is not, and ${name} is replaced in both file contents and path components, so src/${moduleName}.nupp becomes a file named for the project. Write $${ for a literal ${.
return {
description = "A runnable program",
variables = {
name = {pattern = "^[a-z0-9][a-z0-9_-]*$", invalid = "lowercase, please"},
author = {description = "Author", default = "unknown"},
},
raw = { "assets/**" },
after = { "git" },
}name, moduleName (the name with its hyphens and underscores removed) and directory are always defined. A template may declare name to constrain it, but its value comes from --name or the directory. Anything else is declared here or it cannot be used, and is supplied with --set KEY=VALUE.
raw names globs copied byte for byte, for assets that are not text. after names post-init steps from a closed set: git, check, build and test.
Fetched template limits#
A repository template is confirmed before anything is written, and a run with nothing at the terminal to answer is refused rather than assumed. --yes accepts it unread, and either way its after steps are reduced to git init, so the scaffolded project is read before any of it runs.
nupp init owner/repo@v1.2.0 game --yesDive deeper
The reduced step list is not caution about template.lua, which is loaded in a sandbox with no io, os, require or load in it, and with its own copies of string, table and math, so nothing it rebinds reaches the code that plans the scaffold after it. It is that check, build and test all load the nupp.lua that was just scaffolded, and a manifest is ordinary unrestricted Lua. A template allowed to ask for check could put its payload in the manifest instead, and the sandbox would be decoration.
ast#
Dump a Nupp file's parsed syntax tree as JSON.
The lossless tree includes structural children, tokens, trivia, locations,
and parse errors.
Examples:
nupp ast src/main.nupp
nupp ast --json-pretty src/main.nupp
Usage:
nupp ast [options] FILE
Arguments:
FILE Source file to parse.
Options:
--json-pretty Indent the JSON instead of printing it on one line.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe tree is the one Grammar defines, kept lossless down to trivia: every byte of the file is in exactly one token or one piece of trivia, so the report is the file in another shape rather than a summary of it. A file that does not parse still prints the tree recovery reached, with what went wrong in diagnostics, and then exits 1.
There is no text form and no --format. A tree this literal is read by a program, and --json-pretty indents the same document for the times it is read by a person:
{
"ok": true,
"file": "src/tiny.nupp",
"root": {
"tag": "node",
"kind": "chunk",
"children": [
{
"tag": "node",
"kind": "block",
"children": [
{
"tag": "node",
"kind": "returnStmt",
"children": [
{
"tag": "token",
"kind": "return",
"text": "return",
"offset": 1,
"line": 1,
"col": 1,
"trivia": []
},
{
"tag": "node",
"kind": "number",
"children": [
{
"tag": "token",
"kind": "number",
"text": "1",
"offset": 8,
"line": 1,
"col": 8,
"trivia": [
{"kind": "whitespace", "text": " ", "offset": 7, "line": 1, "col": 7}
]
}
]
}
]
}
]
},
{
"tag": "token",
"kind": "eof",
"text": "",
"offset": 10,
"line": 2,
"col": 1,
"trivia": [
{"kind": "whitespace", "text": "\n", "offset": 9, "line": 1, "col": 9}
]
}
]
},
"diagnostics": []
}That is the whole of return 1, down to the newline the file ends with.
aot#
Show what the @aot functions in a file compile to.
With no artifact, reports whether each function uses scalar, explicit SIMD, or GPU
execution. Select verified IR, LLVM IR, native GPU SPIR-V, browser GPU WGSL,
native assembly, or the generated Nupp binding with `--emit`.
Examples:
nupp aot bench/kernel-subset-spike/mandelbrot.nupp
nupp aot --emit ir src/kernel.nupp
nupp aot --emit llvm src/kernel.nupp
nupp aot --emit asm --function scale src/kernel.nupp
nupp aot --emit wgsl --function transform src/gpu.nupp
nupp aot --format json src/kernel.nupp
Usage:
nupp aot [options] FILE
Arguments:
FILE Source file to inspect.
Options:
--emit ARTIFACT Artifact to print: ir, llvm, spirv, wgsl, asm, or binding.
--function NAME Show only this function.
--triple TRIPLE Target triple to compile for.
--features TIER CPU feature tier to promise.
--library PATH Compiled object path used by a generated binding.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe bare command says what every @aot function in the file lowered to: its entry mode and whether its Nupp IR uses explicit SIMD, scalar control flow, or GPU invocations.
bench/kernel-subset-spike/mandelbrot.nupp: mandelbrot, kernel, scalar--emit prints one artifact. ir is the verified IR, llvm is the LLVM IR it lowers to, spirv is the native GPU module, wgsl is the browser WebGPU integer artifact, asm is the instructions that LLVM IR became, and binding is the Nupp module that stands in front of it.
The calling VM is always LuaJIT, and its numeric-loop semantics follow the target: dual-number on ARM64, single-number on x86 and for the browser guest's Wasm kernels. Other project settings remain in effect.
--emit asm compiles the LLVM IR with the options a build compiles this tier's unit with, and stops one step before the assembler encodes it. It is the answer to what the code generator did, which is the last thing between a lowering decision and the machine and the one thing nothing else in the tree reports. Each symbol is headed by what it is -- the compiled body, the forced-scalar oracle it is differentially tested against, a Lua wrapper, the registrar, a layout reporter, or a helper the compiler declined to inline -- and by a count of the listing under it:
-- src/kernel.nupp, aarch64-apple-darwin, neon, llvm 23.1.1; codegen 1
-- ks_scale (scale), kernel: 80 instructions, 31 vector, 4 loads, 4 stores, 11 branches, 0 calls, 0 stack
Lfunc_begin0:
cbz x2, LBB0_14
LBB0_6:
ldp q1, q2, [x9, #-32]
fmul.4s v1, v1, v0[0]
stp q1, q2, [x10, #-32]
subs x11, x11, #16
b.ne LBB0_6total is exact. The other counts are a rule per architecture over the mnemonic and the operand shape, coarse on purpose: what they support is comparing two runs of one kernel, not modelling the machine. Scalar floating point is deliberately not counted as vector on either architecture, since a loop that fell back to one element at a time is what a vector count is usually being read to detect, and stack counts instructions whose memory operand is the frame, which is the closest thing to a spill count that reading instructions can give.
--function narrows that to one body, named either as the source spells it or as the symbol does. With --features, which selects the tier the kernel is both lowered and compiled for, that is a repeatable command for one function at one tier -- so what a change did to the emitted instructions can be compared across runs rather than re-derived by hand. Two runs are comparable when the code generator is: --json reports it, alongside the options it was given and the per-symbol counts. There are instruction rules for aarch64 and x86-64; another architecture is refused rather than reported with empty counts.
The exit status is the one a build would give. An invalid explicit vector operation fails at its authored access. See SIMD programming for species and target tiers.
bc#
Show the bytecode a Nupp file compiles to.
Source lines are shown beside their instructions. `--check` identifies
operations which prevent a containing loop from being recorded by LuaJIT.
Examples:
nupp bc src/main.nupp
nupp bc --check src/hot-loop.nupp
nupp bc --json src/main.nupp
Usage:
nupp bc [options] FILE
Arguments:
FILE Source file to inspect.
Options:
--format FORMAT, --json, --text
Select the report representation.
-O0, -O1, -O2 Optimization level, matching run and build (default -O0).
--check Report bytecode a loop cannot compile.
--prologue Include the generated runtime preamble.
--width COLUMNS Columns the listing may use. Defaults to the terminal's
width.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputGenerated Lua keeps source line numbers one to one, so the listing shows the file that was written rather than the file that was generated. The file runs down the left and what each line compiled to runs down the right:
1 | local function greet(name: string): string | 0000 FUNCF 3
2 | return "Hello, " .. name | 0001 KSTR 1 0 ; "Hello, "
| | 0002 MOV 2 0
| | 0003 CAT 1 1 2
| | 0004 RET1 1 2
3 | end | 0046 FNEW 4 16 ; greet.nupp:1
4 |
5 | return {greet = greet} | 0047 TDUP 5 17
| | 0048 TSETS 4 5 18 ; "greet"
| | 0049 UCLO 0 => 0050
| | 0050 RET1 5 2
-- 65 instructions of runtime preamble, in 1 functionA line that compiled to several instructions keeps the first beside it and the rest on the rows below. A line that compiled to nothing -- line 4 above, and every comment and type annotation -- is a row with nothing beside it, which is the answer to what the annotations cost.
The row is divided by what each half needs: the file gets the width of its longest line where the terminal has room for it, and the source is what gets truncated when it does not, since a source line cut short is one you can still recognize where an instruction cut short is not. --width overrides the width, and redirected output uses a fixed one so the same command produces the same bytes on any machine.
The generated runtime preamble is folded into the count at the end rather than shown against line 1, where it would bury whatever the file has on that line under a runtime nobody wrote. --prologue shows it.
A terminal gets the listing colored. The left half carries the colors this documentation uses; the right half is muted, because a listing is mostly bytecode and coloring all of it leaves the source nothing to stand out against. The ; hint keeps its color, since that is where an instruction names something from the source -- the global it looked up, the string it loaded.
The escapes go inside lines and never change how many there are, so a listing stripped of them is the listing a pipe gets -- which is what redirected output is, as everywhere else, unless --color says otherwise.
Building a function is the usual thing --check finds. LuaJIT has no recording for it, so the loop holding one aborts recording, is blacklisted after enough attempts, and then runs interpreted however hot it gets. Nothing else reports that, because the program's results do not change.
It reads further than the two source lints, which see what was written rather than what was generated: loop-invariant-closure reports a function that could be lifted out of its loop unchanged, and jit-loop-closure, off until a project asks for it, reports one that reads the iteration and so cannot be. Neither says anything about a closure the compiler's own lowerings put in a loop, which is what this reads.
check#
Type-check source without emitting Lua.
With no files, checks the configured project target. Named files are
checked with project context when available.
Examples:
nupp check
nupp check src/main.nupp
nupp check --json
nupp check --target app
Usage:
nupp check [options] [FILE...]
Arguments:
FILE Source files to check.
Options:
--compat PROFILE Enforce the stock Lua 5.1 source subset without
compatibility lowering.
--target NAME Check a named manifest target.
--platform NAME Check one configured binary platform, or all.
--progress[=WHEN], -q, --quiet
When to report progress and timing.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputIn a .nupp file unknown variables are errors and module exports need annotations; a .lua file is checked as plain Lua. --target names a manifest target and cannot be combined with explicit files.
A clean project writes nothing and exits 0. With local shout: number = greet("world") added to src/greet.nupp:
src/greet.nupp:6:23: error: NUPP2001: cannot initialize shout: string is not a number
6 | local shout: number = greet("world")
| ^~~~~See File types for what each extension holds a file to.
Diagnostics as JSON#
Every diagnostic carries the position, the code, the severity, and the docs anchor that explain and the reference share:
{
"ok": false,
"diagnostics": [
{
"code": "NUPP2001",
"severity": "error",
"message": "cannot initialize shout: string is not a number",
"file": "src/greet.nupp",
"range": {
"start": {"line": 6, "column": 23, "offset": 128},
"end": {"line": 6, "column": 28, "offset": 133}
},
"notes": [],
"related": [],
"fixes": [],
"docs": "docs/reference/diagnostics.md#code-families"
}
]
}A lint carries its lint name and a help line as well, and a fix carries the edits that apply it. See Diagnostics for what a diagnostic holds and Lints for the levels.
ok says whether the check ran and found nothing wrong. It is false both for a project that reported an error and for a run that never got as far as checking: a manifest the command could not use ends the run before any file is read, and an empty diagnostics cannot tell that apart from a clean project on its own.
Parallel checking#
A cold whole-project check uses semantic workers when the project has at least 64 modules and 256 KiB of source. The checker uses the smaller of six workers and the available parallelism. Warm checks, checks of named files, smaller projects, and targets with an ahead-of-time policy stay in one process.
A cold nupp build uses the same workers on the same terms, and each worker also optimizes and generates the modules it checked; the coordinating process writes what they generated. The artifacts are byte-for-byte the ones a single process writes. One more case stays in one process: an optimized build of a project that declares a function with a const type parameter, because constant specialization is planned across every module at once. timing.parallel.reason says so when it happens.
NUPP_CHECK_JOBS sets the worker count, for checks and builds alike, and bypasses the workload floor. A count is capped at eight and at the number of modules. Set it to 1 to keep the work in one process:
NUPP_CHECK_JOBS=1 nupp check
NUPP_CHECK_JOBS=4 nupp buildA worker that stops is restarted once. A second failure returns the whole check or build to a single process, discarding every partial parallel record first. The timing.parallel object in --json reports parallel, serial, or serial-fallback, with worker, retry, and batch counts and a reason when a single process was selected. A parallel run also reports rechecked, the modules the coordinating process checked again because a worker's record of them no longer held once the whole project was in view.
NUPP_PARALLEL_CHECK_TRACE=1 is a diagnostic aid. It writes one line of scheduler counters to standard error when a parallel check finishes -- where the workers spent their time, how many bytes crossed the pipes, and each worker's batch count -- and the reason when a check falls back to the serial checker. Its format is not stable.
Check progress#
Checking a project from a terminal names the module it is working on and how far through the source set it is, on one line it rewrites in place, and finishes with how long it took and where that time went:
[ 62%] [251/405] checking src/nupp/compiler/lua/gen.nuppchecked compiler in 44.3s: 405 compiled, 0 reused
check 41.8s scan 1.9s
slowest
nupp.compiler.lua.gen 2.7s
nupp.compiler.syntax.parser 1.6s
nupp.compiler.aot.lower 1.3sThe count is what to compare between runs and the percentage is what says at a glance whether the wait is nearly over; neither passes its total, since a module can pull in a dependency the source set did not already hold.
Nothing is written unless standard error is a terminal, so a check driven by a script is as quiet as it has always been, and a check that finds something wrong prints the diagnostics rather than a summary. --progress=always reports anyway, -q reports nothing, and NUPP_PROGRESS says the same thing with always, never or auto. --json reports nothing unless --progress asks for it, and carries the same numbers in the timing object below instead.
Check timing#
With no files named, --json also carries a timing object, so a repeat check that feels slow can be read rather than waited out. It is the shape build publishes, minus the parts only generation charges time to:
{
"ok": true,
"diagnostics": [],
"timing": {
"totalMs": 8.4,
"compiledModules": 0,
"reusedModules": 2,
"phases": [{"name": "check", "durationMs": 3.1}],
"slowest": []
}
}compiledModules is how many modules this run actually reparsed and rechecked, and reusedModules is how many it answered from the last run's cache without looking at again. compiledModules = 0 is the answer to trust that nothing was redone.
slowest ranks modules by wall-clock time spent either way, longest first. Confirming a reused module's cache entry is still valid costs time too, so a check that stayed slow on an unchanged project still names a module to look at rather than asking to be trusted. timing is absent when a diagnostic stopped the check, the same as it is for build, since a run that did not finish has no account of itself to give.
fmt#
Format Nupp source.
With named files, formats each to stdout or rewrites it. With none, reports
files in the project which are not formatted.
Examples:
nupp fmt --check
nupp fmt --write src/main.nupp
nupp fmt --width 100 src/main.nupp
Usage:
nupp fmt [options] [FILE...]
Arguments:
FILE Source files to format.
Options:
--write, -w Rewrite files in place instead of writing to stdout.
--check Report files which are not formatted and write nothing.
--no-method-parens Preserve method-call sugar without parentheses.
--width N Code column after which a line may break.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputWith a file named, the formatted source goes to stdout. Given an unformatted src/messy.nupp:
local function greet(name: string): string
return "Hello, " .. name
end
return {greet = greet}With none, the project is the subject and the answer is the list of files that are not formatted, which is what a build gates on:
src/messy.nupp{
"ok": false,
"written": false,
"unformatted": ["src/messy.nupp"],
"failed": []
}See fmt.md for the rules the formatter applies and for the fmt table in nupp.lua.
build#
Build source files or a configured project target.
With files, emits those modules and their dependencies. Without files,
builds the manifest's default or selected target.
Manifest target options cannot be combined with explicit source files.
The optimization level is part of the build key, so changing it rebuilds
rather than mixing artifacts compiled at two levels. JSON output carries
diagnostics, written artifacts, materialization facts, and timing data.
Progress and timing stay on standard error so machine-readable standard
output remains one document.
Examples:
nupp build
nupp build --target app
nupp build -O2 --remarks
nupp build -o build/generated src/main.nupp
Usage:
nupp build [options] [FILE...]
Arguments:
FILE Source files to build.
Options:
--target NAME Build a named manifest target.
--platform NAME Build one configured binary platform, or all.
--standalone Link native FFI and AOT code into the binary host.
--output DIR, -o DIR Output directory, overriding the target's.
--host HOST Runtime host services.
--compat PROFILE Enforce the stock Lua 5.1 source subset without
compatibility lowering.
-O0, -O1, -O2 Optimization level.
--remarks Report what the optimizer did and what it declined to
do.
--remarks-for FILE Only report optimizer decisions for this source file.
--remarks-out[=PATH] Write the optimizer's account to a file,
build/remarks.json by default.
-Zno-opt=CODE Turn off one optimizer pass by stable code.
--progress[=WHEN], -q, --quiet
When to report progress and timing.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputOutput directories#
--output (-o) names the directory the build writes into. In a project build it overrides the manifest target's output directory; with explicit source files it is where their Lua lands, build when it is not given:
nupp build --target app --output dist
nupp build -o dist src/greet.nuppOptimization levels#
The default depends on what is being built. An ad-hoc file build stays at -O0, which performs no rewrite at all and maps output one-to-one to source. A binary, bundle, or component target is a release artifact and builds at -O2 unless its manifest entry declares its own level with optimize = 0|1|2; an explicit -O on the command line wins over both. The compiler builds itself optimized the same way, through optimize = 2 on its own target. -O1 and -O2 currently run the same eight passes, OPT-1 through OPT-8.
nupp build -O1 --remarks
nupp build -O1 -Zno-opt=OPT-3--remarks reports what the optimizer did and what it declined to do. -Zno-opt=CODE turns off one pass by its stable code, and the -Z prefix marks it as unstable: the option's name may change or go away. See performance.md for what each pass rewrites and what it measured.
Dive deeper
-Zno-opt reaches the build key as well, not only -O. That is what makes it a usable bisection tool: turning a pass off and building again produces a tree compiled entirely without it, rather than one where whichever modules happened to be cached still carry it.
Progress and timing#
To a terminal, a build says which module it is on, on one line it rewrites in place, and then how long the whole thing took, which activities that time went to, and the modules that cost the most of it:
built compiler in 18.9s: 164 compiled, 0 reused
check 16.1s generate 952ms
slowest
nupp.compiler.lua.gen 1.9s
nupp.mem.heap 699ms
nupp.compiler.check.calls 664msOne activity is current at a time, so the second line's parts add up to the whole rather than overlapping. A module is charged for its own checking and generation and not for the dependencies its check pulled in, so the list names where the time went rather than whichever module happened to be reached first.
To anything that is not a terminal a successful build still writes nothing, so a script reading its output sees what it always saw. --progress=always asks for the report anyway, -q refuses it, and NUPP_PROGRESS says the same thing for the builds nothing passes a flag to, including the rebuild bin/nupp runs before every other command.
JSON report#
--json says both what failed and what landed, and carries the same timing as data rather than as a report:
{
"ok": true,
"target": "app",
"written": ["build/greet.lua", "build/main.lua"],
"diagnostics": [],
"materializations": [],
"timing": {
"totalMs": 412.7,
"compiledModules": 2,
"reusedModules": 0,
"phases": [{"name": "check", "durationMs": 331.2}],
"slowest": [{"module": "main", "durationMs": 208.4}]
}
}It also reports bounded materialization facts under materializations: provider, schema, fingerprint, backend, sizes, runtime features, and ABI versions.
clean#
Remove build outputs configured in nupp.lua.
With no target, cleans every configured target output. Paths outside the
project and paths that resolve to the project root are always rejected.
Examples:
nupp clean --dry-run
nupp clean
nupp clean --target app
Usage:
nupp clean [options]
Options:
--target NAME Clean only the named build target.
--platform NAME Clean one configured binary platform, or all.
--dry-run Print output paths without removing them.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color output--dry-run names the paths and removes nothing, which is how to see what a clean is about to reach:
would remove buildremoved build--platform narrows one target's outputs and so requires --target; giving it alone is a usage error. See Build system for what a build leaves in the output directory.
bench#
Run isolated benchmark programs.
Selectors are Lua string patterns. Repeated patterns within one dimension
are alternatives; the command interprets and validates those strings.
Examples:
nupp bench --list
nupp bench --file bench/optparser.bench.nupp --forks 15
nupp bench --case mixed-argv --variant record-token --json
nupp bench --against ./build/baseline/bin/nupp --margin 2
Usage:
nupp bench [options]
Options:
--list List benchmark names and source files without running.
An aligned table, or the same listing as JSON under
`--json`.
--file PATH Inspect and run only one benchmark program.
--case PATTERN Run logical case names matching a Lua pattern.
--parameter PATTERN Run parameter text matching a Lua pattern.
--variant PATTERN Run variant names matching a Lua pattern.
--geo Include geometric-mean variant comparisons.
--forks INTEGER Processes to run each benchmark in.
--seed INTEGER Seed for the per-round permutation.
--margin PERCENT Practical equivalence margin, as a percentage, for
duration verdicts.
--pilot Estimate variance and report the forks a precision
would need.
--against PATH Another nupp executable to measure beside this one,
interleaved and paired.
--timeout-ms INTEGER Child deadline in milliseconds.
--gpu-costs DIR Write GPU JSONL cost records per case and fork in a
unique run directory.
--profile DIR Write one collapsed-stack profile per case under this
directory.
--profile-interval-ms INTEGER
Sampling interval in milliseconds.
--profile-zone PREFIX
Keep profile samples under this zone subtree.
--baseline PATH Compare deterministic counters with this baseline.
--accept Replace the named baseline after a complete run.
--history PATH Append the complete record as one NDJSON line.
--label TEXT Attach a label to the record and history entry.
--json Write only the merged JSON record to stdout, or the
listing under `--list`.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color output--forks runs each benchmark in that many separate processes, which is what an interval is computed across; below ten the harness reports a range and withholds every verdict, because no interval over fewer observations attains 95%. --pilot reports the count a target precision would need instead of reporting a result. --margin is required for duration verdicts and declares the practical equivalence margin as a percentage. --against measures a second executable interleaved with this one for a paired, causal comparison, where --baseline compares against a stored record and is observational.
Durations are reported and never gate. The exit status still comes only from the deterministic counters.
The measurement API and authoring guide are in Benchmarks.
lints#
List the lints and the level each runs at.
Levels are off, note, warning and error; only an error stops a build.
Examples:
nupp lints
nupp lints --json
Usage:
nupp lints [options]
Options:
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe text table has no code column; --json includes code, default, and moved.
lint category level summary
customary-operator style warning a customary operator where Lua has a word
deprecated suspicious warning use of an API marked deprecated
discarded-result suspicious warning a call with nothing to do but return has its result dropped
else-if style warning a conditional chain written as separate ifs
erased-projection suspicious warning an associated type was erased because inference did not reach its head
exhaustiveness correctness warning a dispatch leaves members of a closed set unhandled
exit-suffix-binding suspicious warning a binding list takes more than one value from an exit suffix
jit-boundary suspicious warning an FFI boundary cannot safely run on a compiled trace
jit-callback suspicious warning a C callback left on the JIT
jit-loop-closure performance off a loop builds a function and so never compiles
loop-invariant-closure suspicious warning a loop builds the same function every iteration
missing-require correctness error a project module is used without being required
positional-record-construction style warning a record built by field order rather than by naming its fields
prefer-comptime performance off a closed no-input function repeats work that comptime can erase
private-export-type suspicious warning an export exposes a private nominal type
reifiable-record performance off a record whose fields would all live in C memory
string-pointer suspicious warning a pointer taken from a Lua string
undocumented-raise suspicious warning a documented function raises without saying so
unused-binding suspicious warning a local is declared and nothing reads itSee Lints for moving one in nupp.lua, and Lints for waving one away at a statement.
ownership-audit#
List foreign pointer contracts and unsafe assertion sites.
With no files, scans Nupp sources under src. The report enumerates trusted
C contracts and explicit unsafe regions.
Examples:
nupp ownership-audit
nupp ownership-audit --regions src/native.nupp
nupp ownership-audit --json src/native.nupp
Usage:
nupp ownership-audit [options] [FILE...]
Arguments:
FILE Source files to inspect.
Options:
--regions Include automatic cleanup regions.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe report is the list of places where the checker is trusting something it cannot see: a foreign declaration's contract, and every @unsafe statement or expression. Given src/block.nupp:
cdef struct block
size: integer
end
cdef function blockCreate(size: integer): block*
cdef function blockFree(takes b: block*)
local function blockNew(size: integer): affine(block*, blockFree)
return blockCreate(size)
end
local function sizeOf(borrows b: block*): integer
do
return b.size
end
end
return {new = blockNew, free = blockFree, sizeOf = sizeOf}Foreign ownership contracts
src/block.nupp:5 blockCreate
result 1: block*
src/block.nupp:7 blockFree
parameter 1 b: block* takes
Unsafe assertion sites
src/block.nupp:14:5 unsafe assertion regionThe audit reports what the foreign boundary states. blockCreate returns a plain block* because a cdef declaration cannot say it produces an owner; the obligation begins at blockNew, which is ordinary Nupp and so is not a foreign contract to enumerate.
explain#
Describe a diagnostic code, with an example either way.
A code with no worked example still resolves through its family rather than
inventing an example to fit it.
Examples:
nupp explain NUPP2004
nupp explain --list
nupp explain NUPP2004 --json
Usage:
nupp explain [options] [CODE]
Arguments:
CODE Diagnostic code to explain.
Options:
--list List the codes with dedicated explanations.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputNUPP2119 A declaration does not say where it lives
A declaration is file-local (`local`), a member of a table (`record m.R`), or a
project global (`global`). Plain Lua would have made it a global silently; Nupp
asks instead, because a name that means one thing here and another elsewhere is
worth one word to prevent.
Reports it:
record Loose
id: integer
end
return Loose
Does not:
local record Loose
id: integer
end
return Loose
Related: NUPP2106, NUPP2120
Reference: docs/reference/diagnostics.md#nupp2119reference#
List or print a focused Nupp reference chapter.
With no chapter, lists the focused references and their sections. A
chapter is compiled into the executable and belongs to this compiler.
`--section` prints one section by heading or documentation pointer, and
`--for` prints the sections which explain one diagnostic code. Skill output
puts its short description in the always-loaded frontmatter and the full
reference in the lazily loaded body.
Examples:
nupp reference cli
nupp reference language
nupp reference --section affine-resources
nupp reference --section docs/learn/language/modules.md#modules
nupp reference --for NUPP2004
nupp reference cli --format skill -o .agents/skills/nupp-cli/SKILL.md
nupp reference performance --format skill -o .agents/skills/nupp-performance/SKILL.md
nupp reference --format skill -o .agents/skills/nupp/SKILL.md
Usage:
nupp reference [options] [CHAPTER]
Arguments:
CHAPTER Reference chapter to print.
Options:
--format FORMAT Output format.
--skill Shorthand for --format skill.
--json Shorthand for --format json.
--section NAME Print one section, by heading or documentation pointer.
--for CODE Print sections which explain a diagnostic code.
--output PATH, -o PATH
Write to this file instead of standard output.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputWith no chapter, it lists what there is to print:
Nupp reference chapters
language Nupp syntax, types, runtime constructs, lints, and diagnostics.
cli Nupp commands, JSON contracts, testing, and coverage workflows.
performance Nupp trace checking, sampling, abort analysis, zones, and benchmark workflow.
Language sections
nupp-and-luajit
declaring-things
types
functions
…
CLI sections
…
Performance sections
…
Run `nupp reference <chapter>` for one chapter, `nupp reference all` for the complete reference,
`nupp reference --section <name>` for one section, or `nupp reference --for <CODE>` for
whichever sections explain a diagnostic.Printing one section#
A chapter is thousands of words, and language is over thirteen thousand, so a reader who knows which construct they are asking about should not have to load the rest of it. --section prints one:
nupp reference --section types
nupp reference --section affine-resourcesA section is named by its heading or by any docs pointer at it, and everything before the # is ignored, so the anchor a diagnostic already carries can be followed straight through:
nupp reference --section docs/learn/language/modules.md#modulesThat prints the same section as --section modules. A pointer whose anchor is no heading of the reference is still followed. One at a code's entry in the diagnostic index, such as docs/reference/diagnostics.md#nupp2004, prints that entry. One at another page prints that page's section, read from the checkout the compiler runs from. Without a checkout, it prints the reference sections that cover the codes carrying the pointer. --for CODE goes the other way and prints whichever sections explain a diagnostic, which is what a reader holding one actually has:
nupp reference --for NUPP2004A code that no section covers says so and points at explain, which is where every code answers. The listing above names every section, so nothing has to be guessed at.
Chapter guarantees#
The chapters are generated from the compiler, so they cannot describe a construct the compiler does not have. Their examples compile in the test suite, and every cited diagnostic code resolves through nupp explain.
Dive deeper
The reference is compiled into the binary rather than fetched, which is the reason it is a command and not only a page on a website. A reader holding a reference from a different version is worse off than one holding none, because nothing in what they are reading tells them it does not match the compiler they are running. Printing it from the binary makes the two the same artifact.
completions#
Print a shell completion script.
Examples:
nupp completions bash
nupp completions zsh
nupp completions fish
Usage:
nupp completions SHELL
Arguments:
SHELL Shell whose completion script to print.
Options:
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe script asks nupp itself what fits at the cursor, through a hidden __complete command that reads the same derived command records that parse arguments and render help. A new command and its options appear without a second edit, each command offers only its own options, and a value that names a file or directory is handed to the shell's own path completion. Install it for the shell that runs nupp:
# Bash: add this to ~/.bashrc.
eval "$(nupp completions bash)"
# Zsh: write _nupp into a directory on fpath.
nupp completions zsh > "${fpath[1]}/_nupp"
# Fish
nupp completions fish > ~/.config/fish/completions/nupp.fish# Completion for nupp; generated from nupp.cli.
_nupp() {
local cur="${COMP_WORDS[COMP_CWORD]}"
local words=( "${COMP_WORDS[@]:1}" )
local IFS=$'\n'
local reply=( $(nupp __complete bash "$COMP_CWORD" "${words[@]}") )
case "${reply[0]}" in
:files) compopt -o filenames 2>/dev/null; COMPREPLY=( $(compgen -f -- "$cur") ) ;;
:dirs) compopt -o filenames 2>/dev/null; COMPREPLY=( $(compgen -d -- "$cur") ) ;;
*) COMPREPLY=( "${reply[@]}" ) ;;
esac
}
complete -F _nupp nupptest#
Build and run project tests.
Coverage options belong to Nupp. Every other argument is preserved in
order for the bundled runner or the project's configured test command.
The bundled runner accepts suite names, `--json`, `--verbose`, `--jobs=N`,
repeated `--group=NAME`, `--exclude=SUITE`, `--exclude-group=NAME`, one of
`--lane=shared|shell|isolated`, `--list-suites`, `--list-cases`,
`--list-groups`, `--case=SUITE/CASE`, `--rerun=REPORT`, and
`--timings[=N]`. Those remain forwarded arguments rather than fields of this
command because a project may configure a different test executable.
Coverage uses `build/coverage` without changing the ordinary build cache
and writes under `build/reports/coverage` by default. `--report-json` reads
that report without rebuilding or rerunning tests.
Examples:
nupp test
nupp test clitest doctest
nupp test --group=docs --json
nupp test --coverage clitest
Usage:
nupp test [options] [ARG...]
Arguments:
ARG Arguments owned by the selected test command.
Options:
--coverage Run against a separately instrumented build.
--coverage-out DIR Directory for coverage HTML, JSON, and LCOV files.
--report-json Print an existing coverage report without running tests.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThese examples run in Nupp's own repository, whose runner takes a suite name. Progress prints . when a unit passes, S when all of it is skipped, N when it could not execute, and E when it contains a failure. A parallel unit is a suite slice rather than one test, which keeps large runs compact; the failure report still names every failing test:
........
8 tests, 8 passed, 0 skipped, 0 failed (1972.3ms)
Timing: 2.0s wall, 2.0s of suite work
slowest suites wall load hooks cases tests
elseiftest 2.0s 24ms 0ms 1.9s 8
slowest tests wall
elseiftest / canBeAllowedByNameOrCode 427ms
elseiftest / allowsAdjacentConditionsThatCouldBothHol 411ms
elseiftest / offersAMachineApplicableFix 276ms
elseiftest / flagsAdjacentMutuallyExclusiveConditions 273ms
elseiftest / allowsAdditionalStatementsInTheElse 154ms
elseiftest / allowsAnAnnotatedNestedIf 137ms
elseiftest / canBeTurnedOff 135ms
elseiftest / flagsAnElseContainingOnlyAnIf 134msThe timing report is the slowest fifteen of each by default; --timings asks for all of them, --timings=N for N, and --timings=0 for none.
With --json the progress marks go to stderr and one document stays on stdout:
{
"schemaVersion": 1,
"ok": true,
"total": 8,
"passed": 8,
"skipped": 0,
"notExecuted": 0,
"failed": 0,
"durationMs": 1830.0378417969,
"tests": [
{"id": "elseiftest/allowsAnAnnotatedNestedIf",
"suite": "elseiftest", "name": "allowsAnAnnotatedNestedIf",
"file": "tests/elseiftest.lua", "line": 130,
"status": "passed", "durationMs": 142.4580078125}
],
"metrics": [],
"suites": [
{"suite": "elseiftest", "durationMs": 1830.037841796875,
"loadMs": 24.1259765625, "hooksMs": 0.394775390625,
"casesMs": 1805.449462890625, "tests": 8,
"slowestCase": "canBeAllowedByNameOrCode",
"slowestCaseMs": 384.158935546875}
],
"shards": []
}notExecuted counts cases the current runtime could not support. These records do not fail the run. --list-cases prints stable IDs, --case selects one, and --rerun reads the failed records from an earlier JSON report, and refuses one whose schemaVersion is not its own.
A failing record carries the message and the file and line the error came from. suites says what a suite cost beyond its cases -- compiling or loading it, and its beforeAll -- and shards is one record per worker process, empty here because a single named suite runs in one.
task#
List, inspect, or run project tasks from nupp.lua.
With `--list`, lists build targets plus configured actions, or inspects NAME.
Otherwise runs `tasks.<name>` from nupp.lua: builds `tasks.<name>.build` first when
it names one, then executes `tasks.<name>.argv` from `tasks.<name>.cwd`
(the project root by default) with every trailing argument appended.
Examples:
nupp task --list
nupp task --list app
nupp task serve --port 8080
Usage:
nupp task [options] [NAME] [ARG...]
Arguments:
NAME Configured task to run, or inspect with --list.
ARG Arguments appended to the configured command.
Options:
--list, -l List project tasks, or inspect NAME.
--format FORMAT, --json, --text
Select the list representation.
--schema Print the JSON Schema of list output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe list marks the default build target:
task kind description
app (default) modules Build the greeter
greet task Print a greeting
test test Build and run project testsNaming one after --list prints its effective configuration, inherited manifest defaults included:
Name: app
Default: yes
Description: Build the greeter
Kind: modules
Category: build
Command: nupp build --target app
Output directory: build
Entries:
- main
Resources:
(none)
Dependencies:
(none)Hello, worldListing options go before NAME, because everything after a task name is passed to that task. The exit code is the task command's own. See tasks.md for declaring a task and for the build it runs first.
doc#
Generate API documentation from source comments.
`--kind` names what to produce. Arguments are source paths; with none, the
manifest's configured sources are used.
Examples:
nupp doc
nupp doc --kind markdown -o docs/api.md
nupp doc --kind site --target library --output build/site
Usage:
nupp doc [options] [PATH...]
Arguments:
PATH Source paths to document.
Options:
--kind KIND What to produce: site, markdown, json, or both.
--output PATH, -o PATH
Output file or directory.
--target NAME Document a named manifest target.
--title TITLE Documentation title.
--include-private Include unexported, internal and private declarations.
--format FORMAT, --json, --text
Select this command's report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputA successful run writes nothing to the terminal. --json names what it wrote:
{
"ok": true,
"diagnostics": [],
"kind": "markdown",
"output": "docs/api.md",
"files": ["docs/api.md"]
}See doc.md for the docblock tags it reads and for configuring a documentation target.
fixpoint#
Verify a byte-identical self-hosting rebuild.
Examples:
nupp fixpoint
nupp fixpoint --binary
nupp fixpoint --emit-stage0 build/stage0/nupp.lua
Usage:
nupp fixpoint [options]
Options:
--emit-stage0 PATH Write the verified stage-zero bundle here.
--binary Verify the packaged binary instead of the compiler.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputEvery stage is built into its own directory, and the last two are compared file by file. The line names the stage zero the run started from, since which one that was is the part a reader cannot see:
fixpoint ok: /path/to/stage0/nupp.lua rebuilds itself byte-identicallyStage one is not compared with anything. Stage zero is a different compiler on purpose -- a published release, named by selfHost.bootstrap -- so what stage one contains is these sources as that release emits them, and the claim starts at stage two. A mismatch keeps every stage for inspection and exits 1.
--emit-stage0 additionally writes the stage-zero bundle the verified compiler composes. It is the artifact a release publishes for the next checkout to start from, and nothing here reads it back. See Build system for what selfHost configures.
--binary names the target it stamped and the size the two runs agreed on. A mismatch keeps both stages for inspection and exits 1. See Distribution for the payload format whose determinism this verifies.
run#
Compile and run a Nupp or Lua program.
The program path divides compiler options from arguments passed to the
loaded chunk. Profiling and trace-abort reports cover the program only.
Use `--` before a program path beginning with a dash. `--profile` writes
collapsed-stack samples, while `--jit-aborts` records where LuaJIT stopped
tracing. `--watch` is development-only, uses `-O0`, and applies valid
changed-body patches at cooperative poll boundaries.
Examples:
nupp run src/main.nupp
nupp run src/main.nupp first second
nupp run --profile --profile-interval-ms 2 --profile-out hot.txt src/main.nupp
nupp run --jit-aborts=jit-aborts.csv src/main.nupp
Usage:
nupp run [options] FILE [ARG...]
Arguments:
FILE Program to compile and run.
ARG Arguments passed to the program.
Options:
-O0, -O1, -O2 Optimization level.
--remarks Report what the optimizer did and what it declined to
do.
--remarks-for FILE Only report optimizer decisions for this source file.
--remarks-out[=PATH] Write the optimizer's account to a file,
build/remarks.json by default.
-Zno-opt=CODE Turn off one optimizer pass by stable code.
--watch Keep named function identities patchable at poll
points.
--profile Sample the program.
--profile-interval-ms MS
Sampling interval in milliseconds.
--profile-out PATH File which receives profiling samples.
--gpu-costs PATH Write GPU operation costs and device timestamps to this
JSONL file.
--jit-aborts[=PATH] Record JIT aborts, optionally naming an attached output
path.
--json Write JIT aborts as JSON rather than CSV.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe first non-option argument is the program; everything after it goes to the program, options included. Arguments arrive as the chunk's varargs and in the global arg, where arg[0] is the program and arg[1] onwards what followed it, as for a script luajit runs. A .nupp file is compiled first; anything else is loaded as Lua directly.
Hello, worldProfiling a run#
--jit-aborts and --remarks-out take an attached value or none, never the next argument, so a program name after either is still the program. --profile takes no value; --profile-interval-ms sets how often it samples, as it does for bench:
nupp run --profile --profile-interval-ms 2 --profile-out hot.txt src/main.nupp
nupp run --jit-aborts src/main.nuppThe defaults are 10 ms, profile.out, and jit-aborts.csv. With --json the abort default becomes jit-aborts.json, and every site carries both the raw VM detail and its stable normalized reason identity.
import-c#
Generate typed Nupp bindings from a C header.
Examples:
nupp import-c include/widget.h
nupp import-c -l widget -o src/widget.nupp include/widget.h
nupp import-c --inspect --json include/widget.h
Usage:
nupp import-c [options] HEADER
Arguments:
HEADER C header to import.
Options:
--output FILE, -o FILE
Write the generated module to this file.
--lib NAME, -l NAME Name the native library loaded by the bindings.
--bridge-out FILE Emit C wrappers for eligible static inline functions.
--inspect Report declaration dispositions without writing output.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputIt writes a committed, hand-editable module of cdef declarations, formatted as nupp fmt formats it. The output path names the module, so it must sit beneath an include root and spell each module segment in lowercase luacase; without -o it is the header's basename in that spelling. Given native/mini.h:
struct mini_point {
double x;
double y;
};
double mini_length(const struct mini_point *point);
int mini_version(void);The command reports the path it wrote:
src/mini.nupp-- generated by nupp import-c from mini.h
-- committed and hand-editable: fix or extend freely, re-import
-- only when the header changes.
module mini
cdef struct mini_point
x: number
y: number
end
cdef function mini_length(point: mini_point*?): number from"mini"
cdef function mini_version(): int32 from"mini"
export = {mini_point = mini_point, mini_length = mini_length, mini_version = mini_version}Every imported pointer is nullable unless you edit the generated module to add a stronger reviewed contract. Fixed arrays retain their bounds, function pointers remain typed in fields, parameters, results and pointer nesting, and a typedef-named anonymous aggregate keeps its typedef identity. A declaration the importer cannot type is left out with a readable -- import-c: skipped comment.
Inspecting a header#
--inspect reports what each declaration would lower to and writes nothing, neither the default module nor an -o path:
nupp import-c native/image.h --inspect
nupp import-c native/image.h --inspect --json
nupp import-c --schemaText inspection prints direct N, bridged N, skipped N. JSON also carries the warning strings and structured dispositions, where direct, type-only, bridge-inline, bridge-macro and skipped distinguish the lowering available. A skipped static inline function carries reason: "bridge-required".
Bridging static inline functions#
A static inline definition has no symbol for FFI to load, so ask for wrappers beside the editable module:
nupp import-c native/image.h \
--lib build/lib/libimage.so \
--bridge-out build/generated/image_bridge.c \
-o src/native/image.nuppOn success, text mode prints the Nupp output path. JSON reports this shape:
{
"ok": true,
"output": "src/native/image.nupp",
"bridgeOutput": "build/generated/image_bridge.c",
"warnings": [],
"direct": 1,
"bridged": 2,
"skipped": 0,
"dispositions": [
{"name": "image_version", "kind": "direct"},
{
"name": "image_triple",
"kind": "bridge-inline",
"symbol": "__nupp_bridge_..."
}
]
}The command emits C but does not compile it. Build that file into the exact library named by --lib, with the header's include paths, definitions and C flags. Function-like macros cannot be typed on this command line; configure their explicit signatures under a C dependency's bindings.macros. Combining --bridge-out with --inspect previews which inline functions would bridge but still writes no file.
See c-interop.md for complete direct and bridge headers, a runnable header-only manifest, macro recipe types, emitted C, ownership refinements, and supported limits.
migrate#
Migrates annotated Lua or retired serialization APIs.
Annotated Lua moves to a checked `.nupp` destination. Serialization
migrations update `.nupp` files only after the replacement type-checks.
Examples:
nupp migrate --dry-run src/legacy.lua
nupp migrate src/legacy.lua
nupp migrate --dialect luacats src/one.lua src/two.lua
Usage:
nupp migrate [options] FILE...
Arguments:
FILE Files to migrate.
Options:
--dry-run Print the migration plan without changing files.
--dialect DIALECT Select the source migration dialect.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe annotated-Lua guide describes always-on comment ingestion and the shared command/editor migration planner. --dry-run reports the complete plan without writing or removing files.
export-c#
Export canonical C declarations for Nupp structs.
Examples:
nupp export-c -o include/game.h src/game.nupp game.Position
nupp export-c --target library -o include/library.h src/library.nupp library.Widget
nupp export-c --json -o include/game.h src/game.nupp game.Position
Usage:
nupp export-c [options] INPUT...
Arguments:
INPUT Source files and declarations to export.
Options:
--output FILE, -o FILE
Write the generated header to this file.
--target NAME Use a named manifest build target.
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe command writes one target-specific header from selected exported structs and cdef functions. It includes transitive layout dependencies, stable C names, layout assertions, and the typed declarations used by native wrappers. See c-interop.md for what a struct has to be for a header to be exportable from it.
rock#
Package and check typed LuaRocks libraries.
A Nupp rock installs runtime Lua normally and carries matching public
declarations in its versioned `nupp/` directory. `pack` validates and
builds that layout; `test` installs the result into a fresh tree and checks
a fresh consumer. `nupp init lib <name>` writes a project in that shape.
Examples:
nupp rock pack
nupp rock test
Usage:
nupp rock
Commands:
pack Build and pack the current Nupp library.
test Install and check the rock from a clean consumer.
Options:
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputStarting one is init with the built-in lib template:
Created string-tools from built-in template lib
Next:
cd string-tools
nupp check
nupp testThe rock keeps its hyphens and the module drops them, since the module name is what require is given:
string-tools/
├── nupp.lua
├── string-tools-dev-1.rockspec
├── src/
│ └── stringtools.nupp
├── nupp/
│ └── stringtools.d.nupp
└── tests/
└── run.luaSee LuaRocks for the declaration's contents and for publishing one.
lsp#
Language-server and semantic source operations.
`nupp lsp serve [root]` runs the language server over stdio. Semantic
operations are `nupp lsp inspect`, `nupp lsp definition`,
`nupp lsp implementation`, `nupp lsp references`, `nupp lsp symbols`,
`nupp lsp rename`, `nupp lsp actions`, and `nupp lsp trace-check`.
Artifact operations are `nupp lsp artifacts` and `nupp lsp artifact`.
Operation-only options: references only: `--include-declaration`;
symbols only: `--file`; rename only: `--write`; actions only: `--only`;
artifact only: `--kind`, `-O0`, `-O1`, `-O2`.
Ask `nupp lsp <operation> --schema` for an operation's JSON schema.
Source positions are one-based byte line and column numbers. Rename
previews by default and changes files only with `--write`.
Examples:
nupp lsp serve
nupp lsp inspect src/main.nupp 12 8
nupp lsp references --include-declaration src/main.nupp 12 8
nupp lsp artifact --kind lua -O2 src/main.nupp
Usage:
nupp lsp
Commands:
serve Run the language server over stdio.
inspect Describe the symbol at a position.
definition Find where the symbol at a position is defined.
implementation Find registered implementations of a service member.
references Find semantic references to the symbol at a position.
symbols Search workspace or document symbols.
rename Rename a symbol and every semantic reference to it.
actions List code actions available at a position.
trace-check Check the function at a position for LuaJIT trace blockers.
artifacts List the compiled artifacts available at a position.
artifact Print what a file compiles to.
Options:
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputinspect describes the symbol under a position:
{
"symbol": {
"name": "greet",
"kind": "variable",
"type": "function(name: string): string",
"range": {
"start": {"line": 3, "column": 13, "offset": 45},
"end": {"line": 3, "column": 18, "offset": 50}
}
}
}definition returns the site a name was bound at:
{
"definition": {
"file": "src/main.nupp",
"range": {
"start": {"line": 1, "column": 7, "offset": 7},
"end": {"line": 1, "column": 12, "offset": 12}
}
}
}references returns semantic references rather than matching by name, and adds the declaration with --include-declaration:
{
"declarationIncluded": true,
"references": [
{"file": "src/greet.nupp",
"range": {"start": {"line": 2, "column": 16, "offset": 44},
"end": {"line": 2, "column": 21, "offset": 49}}},
{"file": "src/greet.nupp",
"range": {"start": {"line": 6, "column": 17, "offset": 122},
"end": {"line": 6, "column": 22, "offset": 127}}}
]
}rename previews as a diff and changes files only with --write:
--- src/greet.nupp
+++ src/greet.nupp
@@ 2:16 @@
-greet
+hello
@@ 6:17 @@
-greet
+helloactions lists what can be applied at a position, and --json carries the edits with each one. On the else of a file whose conditional the else-if lint reports:
quickfix: write `elseif`symbols searches the workspace, or one document with --file. Each line is the kind, the name, and where it was declared:
function greet src/greet.nupp:2:16trace-check asks the same question for one function that bc --check asks for a file, reporting the LuaJIT trace blockers and risks the reason catalog knows about it:
nupp lsp trace-check --json src/greet.nupp 2 16nupp help lsp shows a merged option list; each of --include-declaration, --file, --only and --write belongs to exactly one operation. Every operation prints its own schema for --schema.
version#
Print the compiler version.
The text form is the single line `nupp VERSION`, which is what an install
script should read. `nupp --version` prints the same line.
Examples:
nupp version
nupp --version
nupp version --json
Usage:
nupp version [options]
Options:
--format FORMAT, --json, --text
Select the report representation.
--schema Print the JSON Schema of JSON output and exit.
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputThe text form is one line and stays one line, in the shape an install script or a packaging recipe already expects:
nupp 1.2.3A version with a -dev suffix is a checkout between releases rather than a release. A release is a tag, the tag is built from the commit that set the number, and release CI runs the archived binary and compares what it prints to the tag before publishing it.
--json adds the LuaJIT interpreter underneath:
{"version": "1.2.3", "runtime": "LuaJIT 2.1.1234567890"}Answering costs nothing but starting the process: the number is a string in the compiler and reaching it loads no part of the compiler it names. That makes it the cheapest way to tell whether an install works at all.
help#
Show general or command-specific help.
With no command, prints the command list.
Examples:
nupp help
nupp help build
nupp help lsp rename
Usage:
nupp help [COMMAND...]
Arguments:
COMMAND Command path to describe.
Options:
-h, --help Show this help
--color[=WHEN] When to color output: always, never, or auto
--no-color Never color outputBare nupp prints the same list:
Nupp compiler and project tool.
Examples:
nupp check
nupp build
nupp run src/main.nupp
Usage:
nupp <command> [options]
nupp help [command ...]
nupp --version
Commands:
init Create a project from a template.
ast Dump a Nupp file's parsed syntax tree as JSON.
aot Show what the @aot functions in a file compile to.
bc Show the bytecode a Nupp file compiles to.
check Type-check source without emitting Lua.
fmt Format Nupp source.
build Build source files or a configured project target.
clean Remove build outputs configured in nupp.lua.
bench Run isolated benchmark programs.
lints List the lints and the level each runs at.
ownership-audit List foreign pointer contracts and unsafe assertion sites.
explain Describe a diagnostic code, with an example either way.
reference List or print a focused Nupp reference chapter.
completions Print a shell completion script.
test Build and run project tests.
task List, inspect, or run project tasks from nupp.lua.
doc Generate API documentation from source comments.
fixpoint Verify a byte-identical self-hosting rebuild.
run Compile and run a Nupp or Lua program.
import-c Generate typed Nupp bindings from a C header.
migrate Migrates annotated Lua or retired serialization APIs.
export-c Export canonical C declarations for Nupp structs.
rock Package and check typed LuaRocks libraries.
lsp Language-server and semantic source operations.
version Print the compiler version.
help Show general or command-specific help.
Run 'nupp help <command>' for command-specific help.