Simple is a statically typed, general-purpose language built around a portable bytecode runtime. Source code is checked ahead of execution, lowered to Simple IR (SIR), encoded as Simple Bytecode (SBC), and run by the Simple VM.
The interpreter is the reference runtime on Linux, macOS, and Windows. An optional LLVM 18 ORC JIT accelerates supported bytecode without changing the portable SBC distribution format.
Simple is under active development. It can compile and run the programs in its test suite, but the language, libraries, bytecode format, and tooling are not yet stable.
- Releases
- Development dependencies
- Install and build
- Development expectations
- The language
- Command line
- Compiler and runtime
- Tests
- Repository layout
- Documentation
- Contributing
- License
Packages are published on the GitHub releases page.
Simple uses the release channel encoded by its version:
v0.X.0is a stable milestone release.v0.X.Y, whereYis nonzero, is an experimental development release toward the next stable milestone.
Experimental releases are marked as GitHub prereleases and do not replace the stable latest packages.
| Platform | Interpreter (int) |
LLVM 18 (llvm) |
Archive |
|---|---|---|---|
| Linux x86_64 | supported | supported | .tar.gz |
| macOS arm64/x86_64 | supported | supported | .tar.gz |
| Windows x86_64 | supported | experimental | .zip |
Use an int package for the interpreter-only runtime. Use an llvm package
to enable the LLVM ORC JIT; unsupported functions still run in the
interpreter. Windows LLVM support is experimental and may not be available for
every release.
Archive names include the version, platform, architecture, and runtime flavor:
simple-v0.5.0-linux-x86_64-int.tar.gz
simple-v0.5.0-linux-x86_64-llvm.tar.gz
simple-v0.5.0-darwin-arm64-int.tar.gz
simple-v0.5.0-windows-x86_64-int.zip
Aliases beginning with simple-latest- point to the newest stable v0.X.0 package for a given target and flavor. Experimental patch releases use only versioned archive names.
All source builds require:
- CMake 3.16 or newer;
- a C++17 compiler;
- libffi development headers and libraries;
- Git;
- Bash for the Unix build scripts, or Git Bash for Windows release scripts.
| Platform | Compiler | Interpreter dependencies | LLVM build |
|---|---|---|---|
| Ubuntu/Debian | GCC or Clang | libffi-dev, pkg-config |
llvm-18-dev |
| macOS | Xcode command-line tools | Homebrew libffi, pkg-config |
Homebrew llvm@18 |
| Windows | Visual Studio C++ tools | vcpkg libffi |
vcpkg LLVM 18 SDK |
Typical setup commands:
# Ubuntu/Debian interpreter build
sudo apt-get install cmake build-essential libffi-dev pkg-config
# Add this for LLVM support
sudo apt-get install llvm-18-dev# macOS
xcode-select --install
brew install cmake libffi pkg-config
# Add this for LLVM support
brew install llvm@18Windows development uses a Visual Studio developer environment. The vcpkg
manifests are under cmake/platform/vcpkg/, with the static release triplet in
cmake/platform/triplets/.
Download the package for your platform from the
latest release, extract
it, and add its bin directory to PATH.
Linux example:
curl -LO https://github.com/JJLDonley/Simple/releases/latest/download/simple-latest-linux-x86_64-int.tar.gz
tar -xzf simple-latest-linux-x86_64-int.tar.gz
./simple-*/bin/svm versionmacOS uses darwin-arm64 or darwin-x86_64 in the archive name.
Windows PowerShell example:
Invoke-WebRequest `
https://github.com/JJLDonley/Simple/releases/latest/download/simple-latest-windows-x86_64-int.zip `
-OutFile simple.zip
Expand-Archive simple.zip -DestinationPath simple
& .\simple\bin\svm.exe versionThe extracted package can be moved without rebuilding it.
Interpreter build on Linux or macOS:
./scripts/build/local.shManual CMake build:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel 2LLVM build:
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DSIMPLEVM_ENABLE_LLVM_JIT=ON
cmake --build build --parallel 2Homebrew may require its LLVM package path explicitly:
cmake -S . -B build \
-DSIMPLEVM_ENABLE_LLVM_JIT=ON \
-DLLVM_DIR="$(brew --prefix llvm@18)/lib/cmake/llvm"Windows interpreter build:
scripts\build\windows.batBuild output is written under build/bin/. Platform scripts copy the
user-facing commands to bin/:
bin/svm compiler, runtime, and language server
bin/simple embedded-program runtime stub
Install a local Unix build with LLVM 18 JIT enabled by default:
./scripts/install/unix.shUse ./scripts/install/unix.sh --no-llvm-jit for an interpreter-only local install. If changing compilers causes CMake to regenerate its cache, the installer reapplies the requested JIT mode before building.
On Windows:
scripts\install\windows.batThe path to a stable language is divided into a few broad stages:
| Stage | Expected outcome |
|---|---|
0.5 |
Establish the compiler, canonical System.*/Standard.* imports, interpreter, and optional JIT baseline. |
0.6 |
Complete the native runtime foundations and make the core libraries consistent across supported operating systems. |
0.7 |
Finish the intended core language surface and remove temporary language restrictions. |
0.8 |
Harden the VM, verifier, GC, JIT, ABI, and performance behavior. |
0.9 |
Stabilize projects, packages, editor tooling, documentation, and distribution. |
1.0 |
Freeze the documented language, bytecode, runtime, and compatibility contracts. |
These are expectations rather than release promises. A stage is complete only when its behavior is implemented, tested on supported platforms, documented, and represented consistently in the compiler, runtime, CLI, and LSP.
Every Simple source file begins with a required module declaration. That name is the source unit's runtime module namespace and named-import identity. The file may then define a main function or use top-level statements for short scripts.
module examples.hello
import Standard.IO
main :: () -> i32 {
Standard.IO.println("hello, world")
return 0
}
Run it with:
svm run hello.simple| Topic | Contents |
|---|---|
| Lexical rules | Keywords, comments, operators, numeric literals, strings, and character escapes. |
| Programs and modules | Entry points, top-level statements, source files, and module headers. |
| Declarations | Mutable and immutable variables, functions, and :: declarations. |
| Types | Primitive types, arrays, lists, generics, procedures, and pointers. |
| Literals | Numeric, boolean, character, string, array, list, and class values. |
| Expressions | Operators, calls, indexing, member access, assignment, and casts. |
| Control flow | if, else, while, for, switch, and return. |
| Functions | Parameters, return values, procedure types, and function literals. |
| Arrays and lists | Fixed-size arrays, growable lists, indexing, and list methods. |
| Classes | Structured values, fields, methods, initialization, and self. |
| Enums | Named integer-backed values and scoped members. |
Imports and using |
Named project modules, explicit quoted source paths, aliases, and canonical library imports. |
| System and Standard libraries | Runtime-facing System.* modules and higher-level Standard.* modules. |
| Extern and FFI | Native declarations, dynamic libraries, supported ABI types, and rejection rules. |
| Diagnostics | Compiler error classes and unsupported constructs. |
| Known limitations | Language features that are incomplete or deliberately restricted. |
count : i32 = 0
limit :: i32 = 10
add :: (a : i32, b : i32) -> i32 {
return a + b
}
count = add(count, 1)
Classes combine structured data and methods:
module Examples.Reference
Counter :: class {
value : i32
increment :: () -> void {
self.value += 1
}
}
main :: () -> i32 {
counter : Counter = { .value = 0 }
counter.increment()
return counter.value
}
coordinates : i32{3} = {10, 20, 30}
values : i32[] = [1, 2, 3]
values[1] = 9
A module declaration is required for scripts and declaration-oriented source files. Imports accept declared module names as well as quoted source paths.
module Tools.Math
MathTools :: namespace {
add :: (a : i32, b : i32) -> i32 {
return a + b
}
}
Another source unit can import its declared module name:
module App.Main
import Tools.Math
main :: () -> i32 {
return MathTools.add(40, 2)
}
Explicit local source imports are also supported when path-based resolution is intended:
module App.Local
import "./local_helpers"
import "./legacy_helpers.simple"
The complete language reference is docs/Language.md.
svm run <source.simple|module.sir|module.sbc>
svm check <source.simple|module.sir|module.sbc>
svm build <source.simple|module.sir> --out <program>
svm emit -ir <source.simple> --out <module.sir>
svm emit -sbc <source.simple|module.sir> --out <module.sbc>
svm lsp
svm version
Examples:
svm check hello.simple
svm emit -ir hello.simple --out hello.sir
svm emit -sbc hello.simple --out hello.sbc
svm run hello.sbc
svm build hello.simple --out hellosvm build uses the static runtime by default. -d requests a shared-runtime
development build.
.simple source
|
v
lexer / parser / resolver / type checker
|
v
Simple IR (SIR)
|
v
Simple Bytecode (SBC)
|
v
verifier -> interpreter or LLVM JIT
SIR is a readable intermediate form used to inspect compiler lowering. SBC is the serialized format consumed by the VM. The verifier rejects malformed or unsupported bytecode before execution.
Host-specific behavior is isolated under source/Platform/. Portable compiler
and VM code use that interface rather than embedding operating-system checks.
See docs/Portability.md for details.
cmake -S . -B build
cmake --build build --target simplevm_tests --parallel 2
./build/bin/simplevm_testsWindows:
build\bin\simplevm_tests.exeThe suite covers the language front end, IR, bytecode, VM, native modules, CLI, LSP, and end-to-end language fixtures.
source/ compiler, VM, CLI, LSP, and platform code
tests/ unit tests and language fixtures
docs/ public language and implementation documentation
editor/ editor integrations
cmake/ CMake and platform configuration
scripts/ build, install, audit, and editor scripts
Generated files belong in build/, bin/, and dist/. Personal planning and
audit notes belong in the ignored .notes/ directory.
Start with the documentation index.
- Language reference
- System library
- Standard library
- Command-line interface
- Simple IR
- Simple Bytecode
- Virtual machine
- Jobs and promises
- LLVM JIT
- Portability
- Coding standards
Follow the mandatory coding standards: use modern C++17 ownership and type-safety practices, keep platform behavior behind the platform interface, and leave no shims or dead code. Run the continuous cleanup gate after every commit-sized update. Add tests for observable changes, update affected public documentation, and run the required full-suite/configuration checks before submitting code.
Simple is distributed under the terms in LICENSE.