How unight0/stacklisp works
Purpose and Entry Points
Stacklisp is a small Lisp-to-stack-machine toolchain. It translates Lisp source into bytecode, persists that bytecode, and executes or disassembles it on a C-based VM.
Primary command-line entry points:
• t1.py: compiler/translator invoked as ./t1 <source>
• vm.c: VM executable supporting read <bytecode> and run <bytecode>
• Makefile: likely builds the native VM and supporting C objects
Lisp Front End
The Python front end converts source text into an internal AST before code generation:
• lexer.py: tokenizes Lisp syntax
• parser.py: parses tokens into syntax trees
• asttypes.py: AST node/type definitions
• t1.py: orchestrates source loading, parsing, code generation, display, and output writing
Example source programs are root-level .lisp files such as simple.lisp, arith.lisp, bool.lisp, and example.lisp.
Compiler Semantics and Built-ins
Compilation behavior is centered on recognized Lisp forms, commands, and runtime-visible functions:
• cmds.py: command/form definitions or translation dispatch
• binput.py: bytecode input/output representation or serialization support
• codegen.py: lowers parsed Lisp structures into stack-machine bytecode
• defs.h: shared opcode/instruction definitions for the native runtime
This is the principal language/runtime boundary: Python must emit bytecode compatible with the C VM’s opcode and value conventions.
Bytecode Artifact Boundary
The compiler writes a binary artifact, conventionally out.bin, which is later consumed independently by the VM:
• t1.py
• binput.py
• out.bin
The artifact boundary permits translation and execution to run as separate processes. The VM can also read and print bytecode without executing it.
Stack-Machine Runtime
The execution engine is implemented in C and operates over an explicit stack abstraction:
• vm.c: bytecode loading, instruction dispatch, read/disassembly mode, and run mode
• stack.c
• stack.h
vm.c is the runtime control loop: it reads serialized instructions, manipulates VM state through the stack, and invokes operation implementations.
Native Runtime Operations and Shared State
Runtime instruction behavior and system-facing functions are separated from the VM loop:
• sysfuncs.c
• sysfuncs.h
• utils.c
• utils.h
• globals.h
• globals_extern.h
These files define the native support layer for stack operations, built-in/system functions, utilities, and shared global state. They form the key infrastructure boundary beneath bytecode dispatch.
End-to-End Flow
1. A Lisp file is passed to t1.py.
2. lexer.py and parser.py construct AST values from source.
3. codegen.py, guided by cmds.py, emits VM-compatible bytecode.
4. The compiler displays and writes the bytecode artifact.
5. vm.c loads the artifact either to print decoded instructions (read) or execute them (run).
6. The VM dispatches operations against stack.c and native runtime services in sysfuncs.c.
Main entry points: vm.c VM control loop · codegen.py Code generator · sysfuncs.c Native operations · parser.py Parser
Components 17
Python compiler
- Compiler CLI · Loads source and coordinates compilation and output.
t1.py - Lexer
lexer.py - Parser
parser.py - AST types
asttypes.py - Code generator
codegen.py - Forms and commands
cmds.py - Bytecode I/O
binput.py
Bytecode boundary
- out.bin · Persisted compiler output consumed in a separate VM process.
out.bin
Native VM
- Native build
Makefile - VM control loop · Loads bytecode, disassembles it, or dispatches it for execution.
vm.c - Opcode definitions · Shared bytecode opcode and value conventions.
defs.h - Stack abstraction
stack.c - Stack interface
stack.h - Native operations · Built-ins and system-facing instruction behavior.
sysfuncs.c - Runtime service API
sysfuncs.h - Runtime support · Utilities plus global-state declarations used beneath dispatch.
utils.c
Other components
- Lisp source · Root-level example or user .lisp program.
Connections 18
- Lisp source → Compiler CLI: compile
- Compiler CLI → Lexer: tokenizes
- Lexer → Parser: tokens
- Parser → AST types: creates
- Parser → Code generator: AST
- Code generator → Forms and commands: recognized forms
- Code generator → Bytecode I/O: emits
- Bytecode I/O → out.bin: writes
- Opcode definitions → Code generator: bytecode contract
- Native build → VM control loop: builds
- out.bin → VM control loop: read or run
- Opcode definitions → VM control loop: instruction definitions
- VM control loop → Stack abstraction: manipulates state
- Stack interface → Stack abstraction: declares
- VM control loop → Native operations: dispatches operations
- Runtime service API → Native operations: declares
- Native operations → Stack abstraction: uses
- Native operations → Runtime support: uses