A small, readable scripting language with four execution engines.
Dynamic typing · try/oops exceptions · classes with shared identity · a tree-walk interpreter, a bytecode VM, an optional native-C compiler, and a WebAssembly target.
- Why QuantoScript?
- Installation
- Quick Start
- Language Tour
- Execution Engines
- Compiled Bytecode (
.qvm) - Native Compiler
- WebAssembly
- Standard Library
- Command-Line Interface
- Package Manager
- Building from Source
- Project Structure
- Known Limitations
- Roadmap
- Contributing
- License
QuantoScript is a small, embeddable scripting language designed for readability and simplicity — think Python's clarity with a simpler, brace-based syntax. It ships with four execution engines: a tree-walk interpreter for fast iteration, a bytecode VM for performance, an optional native-C compiler for compute-heavy numeric code, and a WebAssembly target for running under Node.js or any WASM host.
| Braces, not indentation | Blocks are delimited with { } — no significant whitespace. |
| Friendly keywords | maybe instead of elif, oops instead of except. |
| 1-based indexing | Lists and strings start at index 1. |
| Shared object identity | Class instances are references, not copies. |
| Matching engines | The same program runs on the interpreter or the bytecode VM with identical output. |
| Native fast-path | Arithmetic-heavy code can compile to C for a ~10× speedup. |
| Single-file core | The whole language is one C99 amalgamation — easy to embed and vendor. |
The fastest way to get QuantoScript is the one-line installer. It downloads a
prebuilt, self-contained binary (OpenSSL bundled in — no dependencies to install),
places it under ~/.quanto, and adds it to your PATH. No compiler and no sudo
required.
curl -fsSL https://raw.githubusercontent.com/PySudo/QuantoScript/main/install.sh | shirm https://raw.githubusercontent.com/PySudo/QuantoScript/main/install.ps1 | iexThen open a new terminal and run qs --help.
Prebuilt binaries are provided for macOS (Apple Silicon), Linux (x86-64), and Windows (x86-64). On Intel Macs and ARM64 Linux, the installer will ask you to build from source until binaries for those platforms are published.
Installer options
macOS / Linux (install.sh)
# Install a specific version
curl -fsSL https://raw.githubusercontent.com/PySudo/QuantoScript/main/install.sh | sh -s -- --version 2.5.1
# Custom install prefix (default: ~/.quanto)
QUANTO_INSTALL="$HOME/.local" curl -fsSL .../install.sh | sh
# Don't touch your shell profile
NO_MODIFY_PATH=1 curl -fsSL .../install.sh | sh
# Uninstall
curl -fsSL https://raw.githubusercontent.com/PySudo/QuantoScript/main/install.sh | sh -s -- --uninstallWindows (install.ps1)
# Install a specific version
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/PySudo/QuantoScript/main/install.ps1))) -Version 2.5.1
# Uninstall
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/PySudo/QuantoScript/main/install.ps1))) -UninstallPrefer to build it yourself? See Building from Source.
Already installed? Skip straight to Hello World. Otherwise build the interpreter with
make (see Building from Source for OpenSSL notes).
qs examples/full_tour.qs # run a script
qs # or drop into the REPLHello, World!
print("Hello, World!")
func greet(name) {
return "Hello, " + name + "!"
}
print(greet("QuantoScript"))
qs hello.qs # tree-walk interpreter
qs vm hello.qs # bytecode VM (identical output)x = 42 # integer
pi = 3.14 # float
name = "QS" # string
flag = true # boolean
nothing = null # null
items = [1, 2, 3] # list
user = {"name": "QS"} # map
func add(a, b) {
return a + b
}
print(add(3, 4)) # 7
double = fn(x) -> x * 2 # lambda expression
print(double(5)) # 10
if x > 10 {
print("big")
} maybe x > 5 { # "maybe" == "else if"
print("medium")
} else {
print("small")
}
repeat 5 { i = i + 1 } # counted loop
while x < 100 { x = x * 2 } # conditional loop
func safe_div(a, b) {
if b == 0 {
oops("division by zero") # raise
}
return a / b
}
try {
print(safe_div(10, 0))
} oops e { # catch, binding the error to `e`
print("caught: " + e)
}
class Person {
init(name) {
self.name = name
}
greet() {
print("Hi, I'm " + self.name)
}
}
p = Person("Alice")
p.greet() # Hi, I'm Alice
c = p # c and p reference the SAME object
c.name = "Bob"
p.greet() # Hi, I'm Bob
Note: Put each statement in a method body on its own line — semicolon-separated statements are not supported.
nums = [3, 1, 4, 1, 5]
print(len(nums)) # 5
nums.append(9)
print(nums.contains(4)) # true
user = {"name": "QS", "version": 1}
print(user["name"]) # QS
print(user.keys()) # [name, version]
Built-in methods
| Type | Methods |
|---|---|
| String | title · contains · replace · startsWith · endsWith · split · join · indexOf · substring · trimStart · trimEnd · repeat · reverse |
| List | append · extend · sort · contains · flatten · map · filter · find · find_type · sum |
| Map | keys · values · items · has · remove · extend |
len(value) is a built-in function (not a method) that works on strings and lists.
See The QuantoScript Guide for the complete language reference.
The interpreter and the bytecode VM run the same source and produce identical output.
| Engine | Command | Best for |
|---|---|---|
| Tree-walk interpreter | qs program.qs |
Development, debugging, quick scripts |
| Bytecode VM | qs vm program.qs |
Better runtime performance |
| Native C compiler | qs native program.qs |
~10× speedup on arithmetic-heavy code |
| WebAssembly | qs wasm program.qs |
Running in Node.js or a WASM host |
Compile QuantoScript to a stable binary bytecode format for faster startup and distribution without shipping source.
qs build program.qs # -> program.qvm
qs build program.qs -o custom.qvm # custom output path
qs vm program.qvm # run compiled bytecode
qs vm --dump-bytecode program.qs # disassemble for debuggingThe .qvm format (version 4) includes a magic-header check, version validation,
and a CRC32 checksum of the source, and it rejects malformed files. See
Bytecode and Native Builds for details.
For arithmetic-heavy workloads, QuantoScript can emit C and compile it to a native binary:
qs native program.qs # emit a_native.c
gcc -O2 a_native.c -o program_native
./program_nativeThe native backend covers the whole language: numbers, strings, lists, maps, control flow, functions, classes, exceptions, imports, and comprehensions all compile ahead of time.
QuantoScript also compiles to WebAssembly via Emscripten, so programs can run inside Node.js or any WASM host. A prebuilt runtime ships with the release, so you just run:
qs wasm program.qs # interpreter
qs wasm vm program.qs # bytecode VM
qs wasm build program.qs -o out.qvm
qs wasm run out.qvm
qs wasm examples/wasm_demo.qs # try the bundled demoThe qs wasm command needs a node executable (override with QS_NODE). It finds
the runtime that ships in <home>/wasm/, so no build step is required. To rebuild
the runtime from source, use make wasm (requires the Emscripten SDK so emcc is on
PATH; override with make EMCC=/path/to/emcc).
The language is identical; only the platform layer differs. Live networking, FFI,
POSIX regex, and process spawning are unavailable in the sandbox and report a clear
error. make wasm-test checks that every test and example matches the native
binary. See WebAssembly for details.
To hand a program to someone who only has Node.js installed (no qs binary, no
C compiler), bundle it into a self-contained folder:
qs wasm bundle program.qs # creates ./program_wasm/
qs wasm bundle program.qs -o dist/app # custom output directoryThe folder contains the JS glue, the .wasm module, the source, and a compiled
.qvm bytecode file, and runs with Node.js alone:
node dist/app/qs_node.js dist/app/program.qs # from source
node dist/app/qs_node.js run dist/app/program.qvm # from bytecode (faster)See WebAssembly for a full real-world example
(examples/wasm_demo.qs).
Import modules with from "<module>" import <names>. Public modules live in stdlib/;
internal runtime hooks (sys_*) stay hidden.
User code → stdlib modules → native runtime → OS
from "stdlib/http.qs" import get, post
from "stdlib/json.qs" import parse, stringify
from "stdlib/os.qs" import run, capture, cwd, chdir, exists
from "stdlib/fs.qs" import read, write, list_dir
from "stdlib/text.qs" import lower, upper, split
from "stdlib/time.qs" import now, localtime
from "stdlib/log.qs" import info, warn, error
from "stdlib/websocket.qs" import connect, send, recv, close
Examples
# HTTP
from "stdlib/http.qs" import get
resp = get("https://httpbin.org/get")
print(resp)
# JSON
from "stdlib/json.qs" import parse
data = parse("{\"key\": \"value\"}")
# Process execution
from "stdlib/os.qs" import run, capture, exists
run("echo Hello") # returns exit code
version = capture("git --version") # returns stdout
print(exists("gcc")) # true / false
| Module | Purpose |
|---|---|
core.qs |
Environment-variable helpers (env) |
math.qs |
Numeric utilities |
text.qs |
String processing |
fs.qs |
File system operations |
os.qs |
Process execution & working directory |
http.qs |
HTTP client (get, post, put, delete, patch) |
net.qs |
Low-level networking |
websocket.qs |
WebSocket client |
json.qs |
JSON parse / stringify |
url.qs |
URL percent-encoding (url_encode, url_decode) |
time.qs |
Time and clock operations |
log.qs |
Structured logging |
random.qs |
Pseudo-random numbers |
async.qs |
Task queue (experimental — see Known Limitations) |
At runtime, stdlib modules are resolved via the QUANTO_HOME environment variable.
Run qs home to print the configured location. See
Imports for how paths are resolved.
qs interactive REPL
qs <file.qs> run with the tree-walk interpreter
qs run <file.qs|.qvm> run with the bytecode VM
qs vm <file.qs|.qvm> run with the bytecode VM (alias for run)
qs build <file.qs> [-o out.qvm] compile to .qvm bytecode
qs native <file.qs> emit native C (a_native.c)
qs compile <file.qs> [out] compile to a native binary
qs wasm <file.qs> run under WebAssembly via Node
qs wasm bundle <file.qs> make a self-contained Node-runnable folder
qs wasm vm <file.qs|.qvm> run under WebAssembly with the bytecode VM
qs init scaffold a new project
qs check <file.qs> validate syntax without executing
qs fmt <file.qs> format source code
qs lint <file.qs> report common mistakes
qs doc <file.qs> generate documentation from comments
qs profile <file.qs> profile function calls
qs test [directory] run *_test.qs files
qs install <github-url> install a package
qs list list installed packages
qs remove <package> remove a package
qs home print the QUANTO_HOME directory
qs version print the version
Flags
--dump-bytecode print compiled bytecode to stderr
--trace print a per-instruction VM trace
--sandbox [path] restrict file access to the given path
QuantoScript has a built-in, git-backed package manager. Packages are cloned from
GitHub into a local packages/ directory, and their quanto.json dependencies are
resolved recursively.
qs install https://github.com/owner/repo
qs list
qs remove repofrom "packages/owner_repo/main.qs" import something
Scaffold a project with a manifest via qs init, which creates main.qs,
a test/ directory, and a quanto.json.
QuantoScript is a single C99 amalgamation (src/quanto.c). It requires a C compiler
and OpenSSL (used by the HTTP/WebSocket stack). Networking libraries differ per
platform.
| Platform | Toolchain | OpenSSL |
|---|---|---|
| macOS | Xcode Command Line Tools (clang) or gcc |
brew install openssl@3 |
| Linux | gcc / clang |
libssl-dev (Debian/Ubuntu) or openssl-devel (Fedora) |
| Windows | MSYS2 / MinGW-w64 gcc |
pacman -S mingw-w64-ucrt-x86_64-openssl |
Homebrew installs OpenSSL outside the default search path, so point the compiler at it:
SSL=$(brew --prefix openssl@3)
cc -std=c99 -O2 -Isrc -I"$SSL/include" src/quanto.c -o qs \
-L"$SSL/lib" -lssl -lcrypto -lpthread -lmgcc -std=c99 -O2 -Isrc src/quanto.c -o qs \
-lssl -lcrypto -lpthread -ldl -lmgcc -std=c99 -O2 -Isrc src/quanto.c -o qs.exe \
-lssl -lcrypto -lws2_32 -lwinhttp -lwininet -lcrypt32 -lbcrypt -lregexThe
-lregexflag (packagemingw-w64-ucrt-x86_64-libsystre) enables the real regular-expression back-end forregex_match/regex_find/regex_replace. It is optional: without it those built-ins fall back to literal substring matching. TheMakefiledetects it automatically.
The Makefile auto-detects OpenSSL (Homebrew on macOS, pkg-config elsewhere):
make # build build/qs
make test # build and run the regression suite
make wasm # build the WebAssembly runtime (needs Emscripten's emcc on PATH)
make wasm-test # compare the wasm build against the native binary
make clean # remove build artifactsUseful overrides:
make OPENSSL_DIR=/path/to/openssl # point at a specific OpenSSL prefix
make STATIC_SSL=1 # statically link OpenSSL (self-contained binary)
make CC=clang # choose the compilercmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
# Optional: self-contained binary and packaging
cmake -S . -B build -DQS_STATIC_OPENSSL=ON && cmake --build build
cpack --config build/CPackConfig.cmake # produce a .tar.gz / .zipBuilding with -DQS_PYTHON and the Python headers enables the Python FFI examples.
src/ C source (single-file amalgamation)
quanto.c Entry point that includes the parts below
parts/ Language modules (.inc): parser, VM, compiler, runtime, CLI, …
stdlib/ Standard library modules (.qs)
examples/ Example programs (see full_tour.qs)
tests/ Regression and stress test suite
docs/guide/ The QuantoScript Guide (language documentation)
tools/ Benchmarks, CMake helpers, packaging metadata
scripts/ Build and install scripts
build/ Build output (gitignored)
This is a v2.5.1 release. The following are known and tracked:
- Closures that capture an enclosing function's local variables work in the
tree-walk interpreter and in native builds, but not in the bytecode VM, which
reports an unknown variable. Nested
funcdeclarations are not supported. - Class method bodies take one statement per line; semicolon-separated statements are not supported.
- Return type annotations are parsed but not yet enforced.
- Async/await is not functional. The
stdlib/async.qstask queue hangs with two or more tasks and can crash non-deterministically under load. Single-taskspawn/run/resultwork. See Standard Library § async. - Python embedding requires the
-DQS_PYTHONbuild flag and Python headers.
| Version | Focus |
|---|---|
| v2.5.1 | WebAssembly bundle command (qs wasm bundle) and a shipped runtime |
| Planned | Closures in the bytecode VM, working async/await |
Issues and pull requests are welcome. See CONTRIBUTING.md for guidelines and CHANGELOG.md for release history. When submitting changes, please run the regression suite first:
make testReleased under the MIT License.