Skip to content

mvm-sh/mvm

Repository files navigation

mvm

CI Go Reference Compat License: BSD-3-Clause

Mvm is a Go interpreter that compiles source to bytecode and runs it on a stack-based virtual machine. It ships as a single static binary with the full Go standard library bundled in, and embeds in Go or C host programs.

Status is alpha. Language coverage is broad but not yet complete, and the embedding API will still change. Pin a commit if you depend on it.

Want to kick the tires without installing anything? Try it in the browser at the mvm playground.

Features

  • Fast, portable bytecode virtual machine
  • Aims for full Go language compatibility
  • Embeddable in Go and C host programs (see examples/)
  • pure Go, zero dependency
  • Integrated REPL, debugger (trap() builtin), and a go test-compatible runner (tests, benchmarks, and examples)
  • One single static binary, batteries included (full stdlib)

Compatibility

Mvm aims to run real Go, so we measure it. A matrix refreshed weekly (and at every release) records how the standard library and a curated set of popular external packages fare when their own test suites run under mvm test. Each package gets a tier (all tests pass, some pass, fails to load, or no runnable tests) and a tests-passing ratio, with an aggregate trend over time.

Stdlib: 91/169 packages fully pass; external: 37/50 fully pass (as of 2026-06-11). See the full matrix at https://mvm.sh/compat.

The data lives in compat/ and is regenerated by make compat (which dogfoods mvm: the generator in compat/gen.go runs through the interpreter itself). The full matrix is published at mvm.sh/compat.

Performance

Mvm is a bytecode interpreter, not a JIT, but it competes with hand-tuned stock interpreters on tight-loop workloads.

sieve(10M): mvm 336 ms, lua5.4 379 ms, lua5.1 590 ms, python3 778 ms (linux/arm64, 2026-05-26). See the full table at https://mvm.sh/bench.

The scripts live in bench/ and the numbers are refreshed on demand by make bench (hyperfine + hand-committed JSON; CI hardware varies too much for meaningful microbench numbers).

Why mvm?

Disclosure: I (@mvertes) also created yaegi at Traefik, in addition to mvm.

There are several Go interpreters; mvm fills a different slot:

  • yaegi: the most mature option, AST tree-walking, focused on plugin-style use. Mvm makes a different bet: compile once to bytecode, then run on a small VM.
  • gomacro: also AST-based, with a strong REPL and macro story. Mvm has no macros and concentrates on running ordinary Go code through a compact VM.
  • neugram, igo: both no longer maintained.

If you want a small, embeddable runtime that runs idiomatic Go fast enough for non-trivial programs, mvm is built for that.

mvm starts with Go, but the design points further: the scanner/parser front-end is built to host other languages, the bytecode compiler is language-agnostic, and the VM leverages the Go runtime's memory management and concurrency. Useful well beyond scripting.

Usage

Install the mvm command:

go install github.com/mvm-sh/mvm@latest

Or from a clone of the repository:

go run .                            # start the REPL
go run . _samples/fib.go            # run a Go source file
go run . run _samples/fib.go        # same. "run" is the default subcommand
go run . run -e "fmt.Println(1+2)"  # evaluate an inline expression
go run . run -x _samples/fib.go     # run with execution tracing
go run . github.com/mvm-sh/mvm/cmd/mvmlint .  # fetch and run a remote main package
go run . test ./pkg                 # run TestX functions in a package directory
go run . test ./pkg -v              # ... verbose; -run REGEX selects tests
go run . test github.com/google/uuid  # fetch a remote module and run its tests
go run . help                       # list subcommands

See docs/usage.md for the full command reference, the -x / MVM_TRACE tracing modes, and the trap() debugger.

Remote import paths (run or test) are resolved through the Go module proxy and held in memory, so no module sources are written to disk. A trap() builtin drops the program into an interactive debug REPL where you can inspect the call stack and memory.

The repository contains two example trees: examples/ for embedding mvm in Go and C host programs, and _samples/ for Go programs you can run directly with mvm run.

A static file server in one line, using the inlined stdlib:

mvm -e 'http.ListenAndServe(":8080", http.FileServer(http.Dir(".")))'

Documentation

Build

make fast    # quick inner loop: interp suite, -short + race detector
make test    # full interp suite with the race detector
make cover   # cross-package coverage profile
make lint    # golangci-lint

Questions and discussions

GitHub Discussions is the preferred place to ask anything mvm-related: how to do something, whether a behavior is a bug or a misuse, or to float an idea for a new feature before it becomes an issue or a pull request. When in doubt, start a discussion.

Contributing

See the Contributing Guide

License

Mvm is distributed under the BSD-3-Clause license. See LICENSE for the full text. The bundled Go standard library sources retain their original BSD-3-Clause license.

Contributors

Languages