Skip to content

thiremani/pluto

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,071 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The Pluto Programming Language

Taking programming to another planet 🚀

Pluto language logo

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.

Philosophy

  • 💎 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.


Highlights

  • 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)

Command name

Release artifacts ship both command names:

  • pluto
  • plt (conflict-safe alias)

If pluto conflicts on your system, use plt.


Quick example

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/helloworld

Output:

hello world
hello 🙏 👍

Pluto can compile a single .spt file or an entire directory — each .spt produces a native executable.


How Pluto programs are structured

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/func

Templates and specialization

Templates 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 * x

Inputs are read-only — they flow in. Outputs are writable — they flow out. Every function is a transformation.

Generics by use

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, k

Output:

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 as the execution primitive

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

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.


Function model

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.


Memory model

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.


Concurrency direction

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.


Building and Running

Build the compiler:

python3 build.py

If you prefer the conflict-safe command name locally:

python3 build.py -o plt

build.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 pluto

Compile a directory:

./pluto tests           # Compiles all .spt files in tests/
./tests/helloworld      # Run the compiled executable

Run 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 only

New 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 .ll files to the cache
  • PLUTO_TARGET_CPU defaults to native; set it to a CPU name or portable to override host CPU tuning

Requirements

  • 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

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)".


Installation

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:$PATH

Build and test:

python3 build.py
python3 test.py
macOS (Homebrew)
brew install llvm lld

Set PATH for your architecture:

# Apple Silicon
export PATH=/opt/homebrew/opt/llvm/bin:$PATH

# Intel
export PATH=/usr/local/opt/llvm/bin:$PATH

Build and test:

python3 build.py
python3 test.py
Windows (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.py

Manual build/test:

eval "$(python scripts/llvm_env.py --shell)"
go build -o pluto.exe
python test.py

Notes:

  • On Windows the produced binary is pluto.exe.
  • The runtime enables %n on UCRT to match POSIX printf behavior.

Status

Pluto is under active development.

Working today:

  • 🎯 Template specialization
  • 📊 Arrays and ranges
  • 🖥️ Native binaries
  • 🧞‍♂️ Cross-platform builds

In progress:

  • 🧵 Concurrency model
  • 🧰 Tooling
  • 📘 Documentation

Repository structure

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)

Architecture

  • Two phases: CodeCompiler parses .pt files and saves function templates and constants; ScriptCompiler compiles specialized versions for each usage in .spt files (if not already cached).
  • Automatic pipeline: generate IR → optimize -O3 and emit objects in-process with LLVM → link with cached runtime objects via clang — all handled transparently.
  • Module resolution: walks up from CWD to find pt.mod and 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_CPU builds: <PTCACHE>/<version>/target_cpu-<setting>/<module-path>/{code,script}

Troubleshooting

Common issues

undefined: llvmHostCPUName or undefined: llvmHostCPUFeatures during build:

  • Use python3 build.py / python build.py, or run eval "$(python3 scripts/llvm_env.py --shell)" before direct go build / go test

Missing LLVM tools:

  • Verify llvm-config and clang from 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
  • Override cache location with PTCACHE environment variable
  • Override host CPU tuning with PLUTO_TARGET_CPU (portable disables the default -mcpu=native path)

Encoding issues on Windows:

  • Run from the MSYS2 UCRT64 shell; the runner decodes output as UTF-8

Contributing

  • 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

License

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.

About

Pluto is a compiled language that aims for the simplicity, readability and expressiveness of scripting languages with the safety and performance of C/C++, Go. A Go front‑end lowers .pt/.spt to LLVM IR and native binaries. Features include range‑driven auto‑vectorization, scope‑based memory (no nulls, no OOB, no GC), and concurrency by construction.

Topics

Resources

License

Stars

2 stars

Watchers

2 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors