Taking programming to another planet 🚀
Pluto is a compiled language that combines the clarity of scripting languages with the type-safety, performance, and determinism of systems languages.
Functions in Pluto are templates defined in .pt files. Call a template with different argument types and Pluto generates specialized native code for each — no generics syntax, no runtime dispatch. Write concise programs that compile into efficient, fully specialized executables.
- 💎 Purity by design — functions have read-only inputs and writable outputs; structs contain raw data with no hidden pointers
- ✨ The simplicity of scripting
- ⚡ The type safety and speed of low-level languages
- 🧠 Deterministic memory
- 🔧 Automatic specialization
- 🔒 Predictable concurrency
Intended for performance-sensitive scripting, numerical work, simulation, and systems tools.
Design highlights:
Purity: Functions have read-only inputs and writable outputs; structs are raw data with no hidden pointers.
Range-driven auto-vectorization, safe arrays and slices.
Scope-based memory (no nulls, no out-of-bounds, no GC), and concurrency by construction.
- Go front-end, LLVM back-end; emits native binaries
- Template functions in
.pt: specialized per argument types (generics by use) - Range literals with auto-vectorized execution
- First-class arrays (safe slicing) and link semantics
- Scope-based memory: no nulls, no out-of-bounds, no garbage collector
- printf-style formatting; arrays/ranges printable
- Cross-platform (Linux/macOS/Windows)
Release artifacts ship both command names:
plutoplt(conflict-safe alias)
If pluto conflicts on your system, use plt.
Hello world (tests/helloworld.spt):
"hello world"
x = "hello 🙏"
x, "👍"No main(), no print(), no imports. Values on a line get printed.
Compile and run:
./pluto tests/helloworld.spt
./tests/helloworldOutput:
hello world
hello 🙏 👍
Pluto can compile a single .spt file or an entire directory — each .spt produces a native executable.
Pluto separates reusable templates from executable scripts:
| Extension | Purpose |
|---|---|
.pt |
Template files — functions, operators, structs, constants (act as libraries) |
.spt |
Script files — executable programs that compile to native binaries |
A directory is the unit of compilation:
math/
├── math.pt # defines Square, etc.
└── func.spt # uses them → compiles to ./math/func
Compile and run:
./pluto math
./math/funcTemplates are defined once with a clear input/output contract. The first line declares the output and input — the indented body describes the transformation.
Think of a template as a black box: data flows in through inputs, gets transformed, and flows out through outputs. Outputs work by reference — calling a template directly modifies the output variable in the caller's scope.
math.pt
# y is the output (writable), x is the input (read-only)
y = Square(x)
y = x * xInputs are read-only — they flow in. Outputs are writable — they flow out. Every function is a transformation.
Call Square with different types — Pluto compiles a specialized version for each:
func.spt
arr = [1 2 3 5]
# Basic specializations
a = Square(5) # int specialization
b = Square(2.2) # float specialization
c = Square(arr) # squares each array element
a, b, c
# Range, mask, and filter (non-accumulated)
d = Square(1:3) # range: final iteration result
e = Square(arr[1:3]) # array-range: final iteration result
f = Square(arr > 3) # element-wise mask: each element kept where > 3, else 0
g = Square(arr[1:3] > 3) # array-range filter: final iteration result
d, e, f, g
# Accumulation forms
h = [Square(1:3)] # range accumulation
i = [Square(arr[1:3])] # array-range accumulation
j = [Square((1:3) > 1)] # conditional range accumulation
k = [Square(arr[1:4] > 2)] # conditional array-range accumulation
h, i, j, kOutput:
25 4.84 [1 4 9 25]
4 9 [0 0 0 25] 0
[1 4] [4 9] [0 4] [0 9 25]
No generics syntax, no type parameters. Write the template once — call it with a scalar, an array, or a masked array. arr > 3 masks the array element-wise — each element kept where it's greater than 3, else 0 (same length) — and passes that through.
Generated code is equivalent to handwritten specialized code. There is no runtime overhead.
Ranges are first-class values:
Square(1:5)Passing a range to a template executes it across all values. The compiler can map these operations to SIMD instructions — this is range-driven auto-vectorization.
Data-parallel execution without explicit loop syntax.
Arrays use bracket syntax with no commas:
x = [1 2 3 4 5]
y = [1.1 2.2 3.3]Arrays are safe by construction — out-of-bounds access is not possible. Comparisons like arr > 2 produce element-wise masks (each element kept where it holds, else 0) that work anywhere an array does.
String-array elements print quoted so boundaries stay unambiguous:
words = ["hello" "hello world" ""]
words # ["hello" "hello world" ""]Compatible scalar format specifiers lift element-wise over arrays. For example,
"-ints%4d" pads every integer, "-values%7.2f" formats every float, and
"-ints%#x" prints every integer in hexadecimal. String arrays stay quoted by
default; an explicit "-words%s" prints their elements without quotes.
An unescaped % immediately after a resolved marker starts a strict format specifier. Use %% or
\% for a literal percent after a value, so "Progress: -n%%" prints a value followed by %;
-n%05d applies custom formatting and -n% d reserves a leading space for a positive number.
Dynamic width and precision identifiers such as (-width) must be defined I64 values. Scalar
strings remain unquoted by default. Use the Pluto %q formatting
conversion when a quoted, escaped scalar representation is needed: "-word%q". On Str, %x and
%X encode each byte as two hexadecimal digits; % x separates bytes and %#x adds the
0x prefix. Precision on %s, %q, and string %x/%X counts input bytes before quoting or hex
encoding. String markers
retain their normal scope semantics: -name interpolates when name is defined; use \-name when
literal marker-like text is required. Escape-produced characters remain literal during marker parsing,
so \x2dname is also literal. Supported escapes are \\, \", \-, \%, \n, \t, \r, \b,
\f, the fixed-width byte escape \xNN, and the fixed-width Unicode escapes \uNNNN and
\UNNNNNNNN. \xNN inserts one raw byte, while \u and \U encode a Unicode code point as UTF-8:
\xff is the byte ff, whereas \u00ff and \xc3\xbf both produce ÿ (bytes c3 bf). NUL is
rejected in every form. Octal, malformed, and unrecognized escapes are compile errors; Unicode escapes
also reject surrogate code points and values above U+10FFFF. See Pluto String and Formatting
Semantics for the complete grammar and
validation rules.
Functions are pure transformations:
- Inputs are read-only
- Outputs are writable
- Global constants allowed
- No hidden mutation
This keeps behavior predictable and easier to parallelize.
Pluto uses deterministic, scope-based memory:
- No garbage collector
- No null values
- No out-of-bounds access
- Memory freed when scope ends
Predictable performance with minimal runtime overhead.
Pluto is designed so that:
- Structs are pure data
- Functions are transformations
- Inputs are read-only
This enables strong guarantees around race-free execution. The concurrency model is evolving but built around deterministic behavior.
Build the compiler:
python3 build.pyIf you prefer the conflict-safe command name locally:
python3 build.py -o pltbuild.py sets the LLVM 22 byollvm environment for its own subprocess and does not mutate your shell. If you prefer to call Go directly:
eval "$(python3 scripts/llvm_env.py --shell)"
go build -o plutoCompile a directory:
./pluto tests # Compiles all .spt files in tests/
./tests/helloworld # Run the compiled executableRun tests:
python3 test.py # Full E2E suite
python3 test.py --leak-check # Full suite + memory leak detection
go test -race ./lexer ./parser ./compiler # Unit tests onlyNew project setup:
Add a pt.mod file at your project root to define the module path:
module github.com/you/yourproject
Pluto walks up from the working directory to find pt.mod and treats that directory as the project root.
Other commands:
./pluto -version(or-v) — show version./pluto -clean(or-c) — clear cache for current version./pluto -emit-ir [directory]— also write linked pre-optimization script.llfiles to the cachePLUTO_TARGET_CPUdefaults tonative; set it to a CPU name orportableto override host CPU tuning
- Go 1.26+
- LLVM 22 development libraries and tools on PATH:
llvm-config,clang - Python 3.x (for build/test helpers)
- Leak check tools (only for
python3 test.py --leak-check):- Linux:
valgrind - macOS:
leaks
- Linux:
Pluto builds against LLVM through CGO. python3 build.py and python3 test.py derive the required LLVM 22 byollvm flags from llvm-config for their subprocesses. Direct go build and go test are supported after eval "$(python3 scripts/llvm_env.py --shell)".
Linux
Install LLVM 22 from apt.llvm.org:
wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 22
sudo apt install llvm-22-dev clang-22 lld-22
export PATH=/usr/lib/llvm-22/bin:$PATHBuild and test:
python3 build.py
python3 test.pymacOS (Homebrew)
brew install llvm lldSet PATH for your architecture:
# Apple Silicon
export PATH=/opt/homebrew/opt/llvm/bin:$PATH
# Intel
export PATH=/usr/local/opt/llvm/bin:$PATHBuild and test:
python3 build.py
python3 test.pyWindows (MSYS2 UCRT64)
Install MSYS2 and use the "MSYS2 UCRT64" shell.
Install packages:
pacman -S --needed mingw-w64-ucrt-x86_64-{go,llvm,clang,lld,python}Quick build:
python build.pyManual build/test:
eval "$(python scripts/llvm_env.py --shell)"
go build -o pluto.exe
python test.pyNotes:
- On Windows the produced binary is
pluto.exe. - The runtime enables
%non UCRT to match POSIXprintfbehavior.
Pluto is under active development.
Working today:
- 🎯 Template specialization
- 📊 Arrays and ranges
- 🖥️ Native binaries
- 🧞♂️ Cross-platform builds
In progress:
- 🧵 Concurrency model
- 🧰 Tooling
- 📘 Documentation
| Path | Description |
|---|---|
main.go |
CLI entry point |
ast/ |
Abstract syntax tree definitions |
lexer/ |
Tokenizer with indentation-based syntax |
parser/ |
Recursive descent parser |
compiler/ |
Type solving, IR generation, LLVM emission |
runtime/ |
Embedded C runtime linked into executables |
tests/ |
End-to-end tests (.spt inputs, .exp expected outputs) |
- Two phases: CodeCompiler parses
.ptfiles and saves function templates and constants; ScriptCompiler compiles specialized versions for each usage in.sptfiles (if not already cached). - Automatic pipeline: generate IR → optimize
-O3and emit objects in-process with LLVM → link with cached runtime objects viaclang— all handled transparently. - Module resolution: walks up from CWD to find
pt.modand derives module path. - Cache layout (versioned to isolate different compiler versions):
<PTCACHE>/<version>/runtime/<hash>/for compiled runtime objects- Default host CPU builds:
<PTCACHE>/<version>/<module-path>/{code,script} - Non-default
PLUTO_TARGET_CPUbuilds:<PTCACHE>/<version>/target_cpu-<setting>/<module-path>/{code,script}
Common issues
undefined: llvmHostCPUName or undefined: llvmHostCPUFeatures during build:
- Use
python3 build.py/python build.py, or runeval "$(python3 scripts/llvm_env.py --shell)"before directgo build/go test
Missing LLVM tools:
- Verify
llvm-configandclangfrom LLVM 22 are on PATH
Stale cache behavior:
- Clear current version:
./pluto -clean - Clear all versions:
- macOS:
rm -rf "$HOME/Library/Caches/pluto" - Linux:
rm -rf "$HOME/.cache/pluto" - Windows:
rd /s /q %LocalAppData%\pluto
- macOS:
- Override cache location with
PTCACHEenvironment variable - Override host CPU tuning with
PLUTO_TARGET_CPU(portabledisables the default-mcpu=nativepath)
Encoding issues on Windows:
- Run from the MSYS2 UCRT64 shell; the runner decodes output as UTF-8
- Commit style: Conventional Commits (e.g.,
feat(parser): ...,fix(compiler): ...) - PRs: include a clear description, linked issues, and tests for changes
- Go formatting:
go fmt ./... - Run tests before submitting:
python3 test.py
Pluto is licensed under the Apache License 2.0 with LLVM Exceptions
(Apache-2.0 WITH LLVM-exception). See LICENSE.
The exception allows Pluto-owned runtime code embedded in compiler output to be redistributed without the requirements in Apache 2.0 Sections 4(a), 4(b), and 4(d). Compiler distributions and Pluto source remain subject to the full license. Third-party code always retains its own license; see the runtime dependency policy.