Go to file T
Code
.github ci: the explorer step learns the unified admission ladder — the E11xx fail files assert refusal-with-code at exit 2, the run files still explore to a closed frontier; local green both halves before push
assets assets: the wolf mark; readme carries it
docs str_from_utf8, and the escape design written down
examples readme: rewrite around a worked example — squares and overflow under examples/, output pasted from the release build; command tour table; scope section states the dynamic/static split
src fuzz: the harness sets its own rail
tests fuzz: the harness sets its own rail
vendor re-pin 4e316ad: corpus 283; a lu-cache purged from the vendored snapshot
.gitattributes seed: licenses, LF enforcement, readme
.gitignore ci: fmt/clippy/test/corpus/fixtures on three platforms plus an independence gate; conventions doc with the compiler track's platform lessons
.gitmodules org migration: tenseleyFlow -> wolffe-lang
CHANGELOG.md changelog: the fuzz-smoke measurement, in numbers
CONTRIBUTING.md contributing: drop the trailer clause, matching wolf-lang
Cargo.lock lupin 0.1.12 surfaces
Cargo.toml lupin 0.1.12 surfaces
LICENSE license: GPL-3.0-or-later (D41 as amended)
README.md lupin 0.1.12 surfaces
build.rs diff-run gains --counterparty-tier: the flagless lane compared 0 of 245 files at run, so the whole dynamic corpus was ledgered uncompared; now 107 checked / 107 native / 98 release execute both sides, and the release lane found nothing the optimizer changed
rust-toolchain.toml is00: crate sources — omitted from the skeleton commit by an aborted pathspec batch
upstream @ 4e316ad re-pin 4e316ad: corpus 283; a lu-cache purged from the vendored snapshot

wolf-interp

the wolf mark

The reference interpreter for the wolf language. It is an independent, executable reading of the specification, and the oracle the compiler (wolf-lang) is differentially tested against. The two implementations share no code. What they share is the pinned spec, the pinned corpus, and the observation protocol they are compared through. Wolf source files use the .lu extension. This repo builds a binary named lupin.

Licensed under GPL-3.0-or-later.

Building

git clone https://github.com/wolffe-lang/wolf-interp
cd wolf-interp
cargo build --release

The binary lands at target/release/lupin, which is how the transcripts below spell it. The toolchain is pinned by rust-toolchain.toml. The spec and corpus come from a pinned wolf-lang checkout: the upstream/ submodule when it is initialized, otherwise the tracked snapshot under vendor/upstream/. A bare clone works without touching submodules (manual). --version names the pairing, which is the binary, the package, and the posture, at the stated upstream pin:

$ lupin --version
lupin 0.1.12 (wolf-interp, reference interpreter at pin …)

Running a program

examples/squares.lu, in full:

//! check: run(exit=0, stdout="sum of squares: 30")
//! phase: run

struct Point { x: int, y: int }

fn main() -> !int {
    var sum = 0
    region frame {
        var pts = List[Point]()
        for i in 0..5 { pts.push(Point { x: i, y: i * i }) }
        for p in pts { sum += p.y }
    }
    print("sum of squares: {sum}")
    0
}

Running the file takes no subcommand. The program's output passes through, and its exit(N) becomes the process exit code:

$ lupin examples/squares.lu
sum of squares: 30

Honest failure output is part of the product. examples/overflow.lu overflows an i32. Arithmetic is checked in every build profile, so the program traps. The diagnostic goes to stderr and cites the spec clause it enforces. The process exits 3:

$ lupin examples/overflow.lu
examples/overflow.lu: trap(overflow): `+` produced 2147483648, outside `i32` — checked arithmetic traps in every profile (X3); spell intended overflow `wrapping[i32]` [arith.checked] at 107..113

The exit codes are documented in the manual: the program's own exit(N), 2 on a static-phase rejection, 3 on a trap, 4 on unsupported. lupin - reads a program from stdin the same way.

The //! header is a conformance directive. The file states its own expected outcome, in the grammar the corpus uses. conform-run is the protocol surface. It runs the program and reports what it observed:

$ lupin conform-run examples/squares.lu
examples/squares.lu: verdict=exit(0) phase_reached=run seeded=false
sum of squares: 30

The first line is the observation: the verdict, and the deepest pipeline phase that completed. The rest is the program's output.

The REPL

Bare lupin starts an interactive session, and lupin repl is the explicit spelling. Declarations persist, and a trap does not end the session. :mem, :regions and :trace show the memory model live. A walkthrough is in the manual. lupin eval 'CODE' (or -e) evaluates one snippet the same way and exits.

$ lupin
wolf> let s = "wolf"
wolf> let t = move s
wolf> s
trap(use-after-move): `s` was moved out and is uninitialized here [mem.tier0.move.2] at 0..1
  `s` moved here at 8..14
the session survives the trap; the world is as the fault left it [repl.trap.alive]
wolf> t
wolf : str
wolf> :quit

Commands

command what it does
run Run one program; output passes through live and the exit code is the program's (also lupin FILE.lu, no subcommand)
eval Evaluate a snippet in a fresh session and print its value as the REPL would
check Check files through the frontend only (lex, parse, resolve) and report diagnostics
repl Interactive session; --script replays a recorded transcript
conform-run Observe one program and emit a spec/06 observation record
corpus Walk the pinned corpus and check every directive against this implementation
lex, parse Run one frontend phase and dump its evidence
diff-run Compare this implementation against the pinned compiler, corpus-wide
conformance Export the versioned conformance bundle, or check an implementation against one
fuzz Differential testing over generated programs, with reduction of anything divergent
protocol Validate observation records against the spec/06 schema

A subcommand name wins over a file of the same name: a file literally named repl runs as lupin run repl. lupin <command> --help lists the flags; the manual covers each command with worked transcripts.

Scope

The interpreter implements the dynamic semantics in full, and only the static analysis needed to run programs. The type checker, borrow checker and region checker are the compiler's half. Every property they prove statically is enforced dynamically here, so an ownership violation surfaces as a runtime trap where the compiler would refuse the program outright (manual). Of the 241 entry files in the pinned corpus, 171 reach the run rung. The corpus walk (lupin corpus) prints the exact ledger.

Documentation

docs/README.md is the index. User-facing material lives in docs/manual/. The engineering documents (the approximation contract, the divergence log, the bundle format) live beside it in docs/. Every command/output pair in this README and in the manual is real output from the pinned build, enforced by a test. The spec is normative. This implementation is one reading of it.

See CONTRIBUTING.md for the independence doctrine, the gates, and the commit conventions.