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

The commands, in the order nupp help lists them:

  • 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: migrate typed foreign source into Nupp
  • export-c: export canonical C declarations for Nupp structs
  • rock: create and package typed LuaRocks libraries
  • lsp: language-server and semantic source operations
  • version: print the compiler version
  • help: 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 --schema

init, 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: success
  • 1: the work was attempted and failed
  • 2: 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 check --colour
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.nupp
nupp.lua
return {
   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" },
      },
   },
}
src/greet.nupp
--- Greets someone by name.
local function greet(name: string): string
    return "Hello, " .. name
end

return {greet = greet}
src/main.nupp
local greet = require("greet")

print(greet.greet("world"))

Command reference#

init#

nupp init --help
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 output

The built-in templates travel inside the compiler, so this works with no network and no checkout:

nupp init --list
  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:

nupp init app greeter
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.lua

That 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 ${.

template.lua
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 --yes
Dive 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#

nupp ast --help
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 output

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

nupp ast --json-pretty src/tiny.nupp
{
  "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#

nupp aot --help
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 output

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

nupp aot bench/kernel-subset-spike/mandelbrot.nupp
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:

nupp aot --emit asm --function scale src/kernel.nupp
-- 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_6

total 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#

nupp bc --help
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 output

Generated 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:

nupp bc greet.nupp
  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 function

A 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#

nupp check --help
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 output

In 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:

nupp check
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:

nupp check --json
{
  "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 build

A 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.nupp
checked 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.3s

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

nupp check --json
{
  "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#

nupp fmt --help
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 output

With a file named, the formatted source goes to stdout. Given an unformatted src/messy.nupp:

src/messy.nupp
local function greet( name:string ):string
  return  "Hello, "..name
end
return {greet=greet}
nupp fmt 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:

nupp fmt --check
src/messy.nupp
nupp fmt --check --json
{
  "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#

nupp build --help
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 output

Output 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.nupp

Optimization 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    664ms

One 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:

nupp build --json
{
  "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#

nupp clean --help
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:

nupp clean --dry-run
would remove build
nupp clean
removed 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#

nupp bench --help
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#

nupp lints --help
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 output

The text table has no code column; --json includes code, default, and moved.

nupp lints
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 it

See Lints for moving one in nupp.lua, and Lints for waving one away at a statement.

ownership-audit#

nupp ownership-audit --help
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 output

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

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
    @unsafe do
        return b.size
    end
end

return {new = blockNew, free = blockFree, sizeOf = sizeOf}
nupp ownership-audit src/block.nupp
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 region

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

nupp explain --help
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 output
nupp explain NUPP2119
NUPP2119  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#nupp2119

reference#

nupp reference --help
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 output

With no chapter, it lists what there is to print:

nupp reference
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-resources

A 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#modules

That 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 NUPP2004

A 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#

nupp completions --help
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 output

The 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
nupp completions bash
# 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 nupp

test#

nupp test --help
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 output

These 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:

nupp test elseiftest
........

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               134ms

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

nupp test elseiftest --json
{
  "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#

nupp task --help
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 output

The list marks the default build target:

nupp task --list
task           kind     description
app (default)  modules  Build the greeter
greet          task     Print a greeting
test           test     Build and run project tests

Naming one after --list prints its effective configuration, inherited manifest defaults included:

nupp task --list app
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)
nupp task greet
Hello, world

Listing 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#

nupp doc --help
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 output

A successful run writes nothing to the terminal. --json names what it wrote:

nupp doc --kind markdown -o docs/api.md --json
{
  "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#

nupp fixpoint --help
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 output

Every 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-identically

Stage 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#

nupp run --help
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 output

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

nupp run src/main.nupp
Hello, world

Profiling 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.nupp

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

nupp import-c --help
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 output

It 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:

nupp import-c native/mini.h --lib mini -o src/mini.nupp
src/mini.nupp
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 --schema

Text 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.nupp

On 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#

nupp migrate --help
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 output

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

nupp export-c --help
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 output

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

nupp rock --help
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 output

Starting one is init with the built-in lib template:

nupp init lib string-tools
Created string-tools from built-in template lib

Next:
  cd string-tools
  nupp check
  nupp test

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

See LuaRocks for the declaration's contents and for publishing one.

lsp#

nupp lsp --help
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 output

inspect describes the symbol under a position:

nupp lsp inspect --json src/main.nupp 3 13
{
  "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:

nupp lsp definition --json src/main.nupp 3 7
{
  "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:

nupp lsp references --json --include-declaration src/greet.nupp 2 16
{
  "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:

nupp lsp rename src/greet.nupp 2 16 hello
--- src/greet.nupp
+++ src/greet.nupp
@@ 2:16 @@
-greet
+hello
@@ 6:17 @@
-greet
+hello

actions 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:

nupp lsp actions src/scratch.nupp 4 5
quickfix: write `elseif`

symbols searches the workspace, or one document with --file. Each line is the kind, the name, and where it was declared:

nupp lsp symbols greet
function greet src/greet.nupp:2:16

trace-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 16

nupp 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#

nupp version --help
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 output

The text form is one line and stays one line, in the shape an install script or a packaging recipe already expects:

nupp 1.2.3

A 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#

nupp help --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 output

Bare nupp prints the same list:

nupp help
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.