spython

A statically-typed subset of Python compiled to LLVM IR. Written in Go.

Go LLVM bdwgc multi-pass frontend

Overview

spython takes a strict, typed subset of Python, runs it through a clean multi-pass frontend, emits LLVM IR, and hands off to clang to produce a native binary. Heap memory is managed by the Boehm–Demers–Weiser conservative garbage collector.

The goal is a practical, readable compiler that’s small enough to reason about end-to-end — not a full CPython replacement. It’s opinionated: types are inferred from the RHS at first binding (annotations only required when the RHS is ambiguous, e.g. None or an empty container), dynamic features are intentionally absent, and the runtime is a single C file.

Pipeline

lexerparserloadertype checkercodegenclang

Each stage is isolated in its own Go package. No shared mutable state, no visitor patterns — just plain data flowing forward.

Example

class Shape:
    def __init__(self):
        self.name = "shape"
    def area(self) -> float:
        return 0.0

class Circle(Shape):
    def __init__(self, r: float):
        super().__init__()
        self.name = "circle"
        self.r = r
    def area(self) -> float:
        return 3.14 * self.r * self.r

class Rect(Shape):
    def __init__(self, w: float, h: float):
        super().__init__()
        self.name = "rect"
        self.w = w
        self.h = h
    def area(self) -> float:
        return self.w * self.h

shapes: list[Shape] = [Circle(2.0), Rect(3.0, 4.0), Circle(1.0)]
total: float = 0.0
for s in shapes:
    total = total + s.area()
print(total)  # 27.7

What’s supported

Types

  • int, float, bool
  • str
  • list[T]
  • dict[K, V]
  • set[T]
  • tuple
  • bytes, bytearray
  • None
  • user-defined classes

Control flow

  • if / elif / else
  • while
  • for … in range / list
  • for … in set / dict
  • for … in iterator
  • x in y / x not in y
  • break, continue
  • return

Functions

  • def with type annotations
  • recursion, early return
  • *args, **kwargs
  • keyword-only params
  • default arguments
  • kwargs & * / ** unpacking

Closures & generators

  • lambda expressions
  • nested def, by-value capture
  • Callable[[Args], Ret]
  • yield, yield from
  • Iterator[T], next()
  • StopIteration

Classes & OO

  • single inheritance
  • virtual dispatch (vtables)
  • super()
  • isinstance()
  • field inference from __init__
  • implicit upcasting

Dunder methods

  • __init__, __str__, __repr__
  • __eq__, __ne__
  • __lt__, __le__, __gt__, __ge__
  • __add__, __sub__, __mul__
  • __truediv__, __floordiv__, __mod__
  • __neg__, __pow__

Imports

  • import module
  • import module as alias
  • from module import name
  • multi-file projects

Exceptions

  • raise / try / except / finally
  • Exception, ArithmeticError
  • ZeroDivisionError, ValueError
  • TypeError, OSError, …
  • user-defined subclasses
  • propagation across calls

Container methods

  • str: upper/strip/split/…
  • list: append/sort(key=)/…
  • dict: keys/values/get/…
  • set: add/discard

Stdlib — 29 modules

  • math, random, time, sys
  • io, os, os.path, shutil
  • socket, ssl, requests, json
  • re, itertools, functools
  • heapq, bisect, hashlib, …
  • .spy shim + sibling .c, FFI

Operators

  • arithmetic: + - * / // % **
  • comparison: == != < > <= >=
  • logical: and, or, not
  • bitwise: & | ^ ~ << >>
  • augmented assign: += -= …

Runtime

  • print, range, len
  • int(), float(), str(), bool()
  • isinstance()
  • conservative GC (bdwgc)
  • list/str/map indexing

What’s not supported

Try it

1 / Prerequisites
# macOS
brew install go llvm bdw-gc

# Debian / Ubuntu
apt install golang clang libgc-dev
2 / Build
git clone https://github.com/hvuhsg/spython-llvm
cd spython-llvm
go build -o spython ./cmd/spython
3 / Run a program
./spython run testdata/class_polymorphism/main.spy