A statically-typed, Python-flavored programming language compiled ahead-of-time through LLVM.
gusty is a small, modern programming language that pairs Python's familiar
indentation-based syntax and ergonomic feel with the performance of native
compiled executables. Source goes through a clean, inspectable pipeline —
lex → parse → semantic (type inference) → codegen — down to LLVM IR, which
is verified, optimized, and lowered to a native binary, or JIT-executed in
the REPL.
The toolchain is built for both humans and agents: a friendly REPL/CLI for people, plus structured, machine-readable output (JSON diagnostics, JSON AST/IR dumps, a JSON Schema, stable flags, deterministic exit codes) so scripts and AI workflows can discover and consume the language without guessing.
print(40 + 2) # -> 42
x = 5
print(x + 1) # -> 6
if x < 2:
print(10)
else:
print(20)
s = 0
for i in range(5): # range(n) and range(a, b)
s = s + i
print(s) # -> 10
def double(x):
return x * 2
print(double(5)) # -> 10
x = 2
match x:
case 1:
print(1)
case 2:
print(2) # -> 2
i = 0
while i < 100:
i = i + 1
if i == 3:
break
print(i) # -> 3gusty ships an indentation-based syntax covering:
- Functions —
defwith default/keyword args, inferred return types, and anonymouslambdafunctions (lambda x: int: x + 1) lowered to closures exactly likedef. - Control flow —
if/elif/else,while,for ... in range(n)/range(a, b)/range(a, b, step),for x in [...], optional loopelse:clauses,break/continue, andpass. - Pattern matching —
matchwith integer-literal equality,_wildcards, and list-destructuring patterns (case [a, b]:). Matches also support guards (case x if cond:), or-patterns (case 1 | 2:), dict patterns (case {"k": v}:), and class patterns (case Point(x, y):with subclass walk + attribute binding). - Data structures — inline
list/dict/setliterals, indexing, and list / dict / set comprehensions. - Slicing —
s[a:b],s[::step], negative indices; supported in both the interpreter and the AOT backend (via thert_sliceruntime helper). - Generators —
def g(): yield a; yield bcollects yielded values. - Context managers —
with expr as name:/with expr:, dispatching__enter__/__exit__(including exception suppression); supported in both backends. - Exceptions —
try/except/finallywith typed built-in exception classes (Exception,ValueError,TypeError,KeyError,IndexError,RuntimeError,StopIteration,ZeroDivisionError) andraise. - Classes & inheritance —
class Name:with methods (self), instance attributes,__init__,class Child(Base):multi-level inheritance, andsuper()delegation. - Operator overloading — binary operators dispatch to dunder methods
(
__add__,__mul__,__lt__, ...) with reflected fallbacks (__radd__,__rmul__, swapped comparisons), in both the interpreter and AOT codegen. - Decorators —
@dec def f:→f = dec(f)at definition time; wrapping (fnptr-valued) decorators compile in AOT via compile-time specialization. - Modules —
import modloadsmod.gyand bindsmodas a namespace withmod.name/mod.fn(args)access. - Standard library — data-only on-disk modules folded as AOT constants:
import math—PI,E,TAU,PHI,SQRT2,LN2,LN10.import string—DIGITS,LOWERCASE,UPPERCASE,HEXDIGITS,WHITESPACE,PUNCT.import collections—EMPTY_DICT,EMPTY_LIST,ZERO,ONE.import json—NULL(None),TRUE(True),FALSE(False).
Optional annotations on variables, parameters, and returns are checked
statically by --verify, with any as the dynamic escape hatch; untyped code
falls back to dynamic dispatch.
- Union types —
int | str,int | float, andNone | intsugar forOptional; inferred and checked across assignments, call boundaries, and returns. In AOT, a union-annotated scalar variable gets a tagged%unionboxslot (runtime member tag 0=int, 1=float, 2=str) soprintdispatches on the live member — anintmember prints as%d, afloatas%f, astras%s, even after cross-member reassignment under branches/loops. - Literal types —
Literal[1, 2]annotations feedmatchexhaustiveness + narrowing on constants. - Type narrowing — after
if isinstance(x, int):, the checker narrowsxfromanytointin the then branch and away from it in the else branch;not isinstance(x, T)flips those; union complement narrowing usesdropType. - Walrus operator — assignment expressions
name := exprusable insideifconditions and comprehensions (if (n := len(x)) > 0:), scoped per Python 3.8+.
- Error-recovering lexer — on an unexpected character, emits a
TokErrortoken carrying the span + message and resumes instead of aborting the file, so the parser/semantic pass can report multiple diagnostics per run. - Rich token spans — each token carries
startANDend(byte + rune offsets) plus an optional multi-line flag, giving f-strings, slices, andmatchpatterns exact ranges for hover/diagnostics/formatting. - Unicode identifiers — identifiers scan by Unicode
ID_Start/ID_Continue(not just ASCII), NFC-normalized viagolang.org/x/textso decomposed and precomposed spellings are one symbol, and aTokWarningdiagnostic flags Greek/Cyrillic homoglyph lookalikes (e.g.ΟU+039F vs LatinO). - Numeric-literal modernization — hex (
0xFF), binary (0b101), octal (0o17), and_digit separators (1_000,0x_FF), with exact integer semantics and rejection of misplaced separators. - Raw & triple-quoted strings —
r"..."/R'...'raw strings and"""..."""/'''...'''multi-line strings; docstring extraction reuses both forms. - Line continuation — a trailing
\joins the next physical line into one logical line (Python-compatible), skipping the continued line's leading indentation and blank/comment-only continuation lines. - async/await + effectful syntax (L5.6) —
async def,async for,async with, andawait exprparse as first-class syntax; under the minimal synchronous-coroutine model (no suspension primitives yet) they lower identically to their sync counterparts in both the AST interpreter and the LLVM AOT/JIT backends, giving exact parity (seeasync_basic.gy). The cooperative event-loop runtime is Phase 7. - Pratt parser — a precedence-climbing expression parser keyed off a
precedence table (unary,
**right-assoc, multiplicative, additive, comparison,and/or, ternary) with panic-mode recovery (recoverStmt— nest-awareINDENT/DEDENTskipping) producing a forest of*ParseErrors and a partial AST. - Trailing commas —
f(a, b,),[1, 2,],{1: 2,}andmatchcase arg lists tolerated, and normalized away by the canonical formatter.
Every feature ships in both paths:
- Interpreter —
pkg/lang/jit.go, entryEvalExpr: the REPL /--eval/--verifypath. Fast feedback, rich diagnostics; a two-generation (nursery + old) tracing GC (ev.Collect()) reclaims unreachable pure-data heap objects; roots are top-level bindings, walking container elements, dict values, closure envs, and attr tables. Classes/methods/closures/imports are never freed. - LLVM AOT codegen —
pkg/lang/codegen.go+pkg/lang/closure.go, entryCompile: the--file/--emit-llvm/ link-and-run path. Emits deterministic opaque-pointer LLVM IR with realdoublefloat IR (float arithmetic viasitofppromotion,%.17gfloat print), tagged-union lowering,__doc__folding to string constants, string slicing viart_slice, literal-container membership viart_contains, andwith/yield-from runtime protocols.
- Constant folding — integer-literal binops fold at codegen time
(
x = 1 + 2emitsstore i32 3, noadd). - Escape-analysis heap elision — never-read top-level list literals skip
their runtime heap allocation (
[x * 2 for x in ...],{k: v for ...},{x for ...}). - Dead-global / dead-object elimination — unused
@.strN/@.lstNglobals and dead heap objects are pruned. - Real LLVM
optpipeline —pkg/lang/opt_llvm.godrives the externalopt-20tool over the raw module IR (instcombine, gvn, licm, sroa, simplifycfg, ...), so AOT emits verified, optimized IR — while preserving GC-correctness by rooting heap slots through module-global arrays (@gc.roots/@gc_roots_used).
gustyc is the command-line interface and REPL:
gustyc --eval "x = 2 + 3\nx" # evaluate source, print result
gustyc --file prog.gy # compile & run a source file
gustyc --build out a.gy b.gy # compile a set of files into a binary
gustyc --verify "def f(x): return x * 2" # static analysis only
gustyc --emit-llvm "x = 1 + 2" # print emitted LLVM IR
gustyc --emit-ast "x = 1" # print the AST as JSON
gustyc --emit-source-map --file src.gy # JSON source map (fn -> IR symbol+line)
gustyc --check <src> | check file1.gy ... # mypy-style type-check without executing
gustyc --json ... # machine-readable JSON output
gustyc --schema # print the JSON Schema for AST/IR dumps
gustyc --lang # self-describing feature list
gustyc --jit "..." # in-process dlopen JIT path
gustyc --bench '<src>' --bench-runs N --bench-opt L # wall-clock benchmark
gustyc --fmt <src> | --fmt-check <src> | --fmt-file <path> # canonical formatter
gustyc --lsp # stdio language server (hover, completion, diagnostics)
gustyc --stdlib <dir> # set stdlib root (default: ./stdlib)
gustyc --version # compiler version
gustyc # start the interactive REPL
Exit codes are deterministic:
| Code | Meaning |
|---|---|
| 0 | success / clean (check, fmt-check) |
| 1 | compile / type error, runtime/eval error |
| 2 | LLVM/verification failure or parse/usage error |
- Unit tests — lexer, parser, semantic, codegen, and runtime tests in
pkg/lang/; benchmarks (Benchmark*) and Go-native fuzz targets (Fuzz*) for the interpreter. - Integration suite —
integration/drives the full pipeline (lex → parse → typecheck → codegen → run) and asserts stdout matches expected output. - Conformance matrix —
integration/conformance_cases.go+conformance-matrix.json: 26 conformance cases, all passing (pass: 26, fail: 0) across both backends (interpreter and AOT). - Property testing — seeded deterministic whole-program generation.
- LLVM 20 —
llc-20for lowering andopt-20for the optimization pipeline (the AOT backend emits textual opaque-pointer IR verified by these external tools). - Go — build with the LLVM 20 tag:
go build -tags=llvm20 ./...
go test -tags=llvm20 ./pkg/... # unit tests
go test -tags=llvm20 ./integration/... # end-to-end pipeline- C linker —
cc/gccto link the emitted native object into a binary.
pkg/lang/ compiler: lexer, parser, semantic (types), codegen, jit runtime
cmd/gustyc/ the CLI + REPL
integration/ end-to-end pipeline + conformance suite
docs/ language spec (language.md) + operations guide (operations.md)
stdlib/ on-disk modules: math.gy, string.gy, collections.gy, json.gy
The language spec lives in docs/language.md; the toolchain & CLI reference
in docs/operations.md.