Transpilation
The sub command transpiles a single .sb source file into idiomatic code in any of 13 target languages. All backends share one AST, so the output is consistent and verifiable.
The sub Command
$ sub program.sb <target> # → program.<ext>
The target is a required positional argument. The output file is written to the same directory with the appropriate extension.
Available targets
| Target | Output extension | CI-verified |
|---|---|---|
python | .py | ✓ |
javascript | .js | ✓ |
typescript | .ts | ✓ |
c | .c | ✓ |
cpp | .cpp | ✓ |
rust | .rs | ✓ |
go | .go | ✓ |
java | .java | ✓ |
kotlin | .kt | ✓ |
swift | .swift | ✓ |
ruby | .rb | ✓ |
asm | .s | generated |
css | .css | generated |
Shared AST
All backends — transpiler, interpreter, and native compiler — operate on the same abstract syntax tree. The pipeline is:
- Lexer — tokenize the
.sbsource into tokens - Parser — build the AST from the token stream
- Backend — walk the AST and emit output in the target format
Because every backend sees the same tree, behavior is consistent. The CI suite runs each program through the interpreter and every transpiler backend, then diffs the output.
Type Inference
SUB has no type annotations. The codegen_infer.c module performs lightweight type inference by tracking the types of values as they flow through the program. This information is used by backends that need static types (Rust, Go, C, C++, TypeScript, Java, Kotlin, Swift) to emit correct declarations.
Note: Type inference is best-effort. For dynamic targets (Python, JavaScript, Ruby), types are erased and the runtime handles everything natively.
Type Mapping
Here is how SUB types map to each target language:
| SUB | Python | JavaScript | Rust | Go | C | Java |
|---|---|---|---|---|---|---|
int | int | number | i64 | int64 | long long | long |
float | float | number | f64 | float64 | double | double |
string | str | string | String | string | char* | String |
bool | bool | boolean | bool | bool | int | boolean |
null | None | null | Option | nil | NULL | null |
array | list | Array | Vec | []T | array | ArrayList |
object | dict | Object | HashMap | map | struct | HashMap |
Backend-Specific Notes
Python
Output is clean Python 3. Stdlib modules are translated to Python equivalents. Run with python3 program.py.
JavaScript
Outputs ES6+ JavaScript. Uses let/const, arrow functions where possible. Run with node program.js.
TypeScript
Full type annotations are inferred and emitted. Compile with tsc program.ts or run with ts-node program.ts.
C
Outputs a single .c file with a main() function. Compile with cc program.c -o program. The C backend includes a small runtime header for string and array operations.
C++
Uses std::string, std::vector, and std::cout. Compile with c++ program.cpp -o program -std=c++17.
Rust
Outputs idiomatic Rust with proper ownership. Types are fully inferred. Compile with rustc program.rs.
Limitation: The Rust backend does not yet handle all string mutation patterns. Some programs may need minor adjustments for borrow-checker compliance.
Go
Outputs a single .go file with a main package. Build with go build program.go.
Java
Outputs a single class with a main method. Compile with javac Program.java and run with java Program.
Kotlin
Outputs idiomatic Kotlin with a main function. Compile with kotlinc program.kt -include-runtime -d program.jar.
Swift
Outputs Swift 5+ code. Compile with swiftc program.swift.
Ruby
Outputs clean Ruby. Run with ruby program.rb.
x86-64 Assembly
Outputs NASM-syntax x86-64 assembly. This is a code-generation target — the assembly is not executed in CI.
CSS
The CSS backend is specialized for the UI DSL. It translates ui.* declarations into CSS rules. Not a general-purpose backend.
Running Transpiled Output
Each target produces a file you can run with the standard toolchain for that language:
# Transpile
$ sub hello.sb python # → hello.py
$ sub hello.sb rust # → hello.rs
$ sub hello.sb go # → hello.go
# Run
$ python3 hello.py
$ rustc hello.rs && ./hello
$ go run hello.go
Tip: Use the interpreter as the reference. If a transpiled program produces different output than subi, that's a bug — please report it.
Next Steps
- Native Compilation — learn about the
subccompiler - Contributing — add a new backend