A Python implementation of Manfred von Thun's Joy programming language with dual-mode architecture.
PyPI package: pyjoy-lang | Command: pyjoy
The primary aim of this project is to implement the Joy language in Python 3. This means the implementation should run Joy programs without issue. A secondary aim is to have the Python implementation generate C code which can then be compiled into machine code. This is consistent with the late Manfred von Thun's wish:
Several other people have published other more or less complete Joy interpreters, written in ML and in Scheme, in the "concatenative" mailing group. At this point in time I have no plans to write a full compiler. A first version of such a compiler would presumably use C as an intermediate language and leave the generation of machine code to the C compiler. I would very much welcome if somebody were to take up the task." A Conversation with Manfred von Thun
The implementation provides a dual-mode architecture: strict mode (strict=True) for Joy compliance, and pythonic mode (strict=False) for Python interoperability.
pip install pyjoy-langRequires Python 3.13+ and uv.
# Clone the repository
git clone https://github.com/shakfu/pyjoy-lang.git
cd pyjoy-lang
# Install dependencies
uv sync
# Run Python tests
uv run pytest
# Run Joy test suite
uv run pyjoy test tests/joy
# Run Joy tests with C compilation
uv run pyjoy test tests/joy --compileFor C compilation, you'll also need gcc or clang.
# Start the Joy REPL
uv run pyjoyExample session:
Joy> 2 3 + .
5
Joy> [1 2 3] [dup *] map .
[1 4 9]
Joy> DEFINE square == dup *.
Joy> 5 square .
25
Joy> quit
# Run a Joy source file
uv run pyjoy examples/factorial.joy
# Or using the run subcommand
uv run pyjoy run examples/factorial.joy
# Evaluate an expression
uv run pyjoy -e "5 [1] [*] primrec ."
120# Compile Joy source to executable
uv run pyjoy compile program.joy -o build -n myprogram
# Run the compiled program
./build/myprogram
# Or compile and run in one step
uv run pyjoy compile program.joy --run
# Generate C code only (no compilation)
uv run pyjoy compile program.joy --no-compile# Run all Joy tests
uv run pyjoy test tests/joy
# Run with verbose output
uv run pyjoy test tests/joy -v
# Run specific pattern
uv run pyjoy test tests/joy --pattern "fact*.joy"
# Also test C compilation
uv run pyjoy test tests/joy --compile| Backend | Passing | Total | Coverage |
|---|---|---|---|
| Python Interpreter | 194 | 215 | 90.2% |
| C Backend | 199 | 215 | 92.6% |
| pytest (unit tests) | 712 | 712 | 100% |
- 200+ primitives implemented in both Python and C backends
- Full support for Joy's core operations, combinators, and I/O
- Mode-aware primitives work with both strict (JoyValue) and pythonic (raw Python) values
- Some interpreter-specific primitives (
get,include) have limited C support
See TODO.md for detailed status and remaining work.
- Stack operations:
dup,pop,swap,rollup,rolldown,rotate, etc. - Arithmetic:
+,-,*,/,rem,div,abs,neg,sign, etc. - Comparison:
<,>,<=,>=,=,!=,equal,compare - Logic:
and,or,not,xor - Aggregates: lists
[...], sets{...}, strings"..." - Quotations and combinators
- Basic:
i,x,dip,dipd,dipdd - Conditionals:
ifte,cond,branch,iflist,ifinteger, etc. - Recursion:
linrec,binrec,genrec,primrec,tailrec - Tree recursion:
treerec,treegenrec,treestep - Conditional recursion:
condlinrec,condnestrec - Application:
app1,app2,app3,app4,map,filter,fold,step - Arity:
nullary,unary,binary,ternary,unary2,unary3,unary4 - Control:
cleave,construct,some,all,split
- Console:
put,putch,putchars,.(print with newline) - File I/O:
fopen,fclose,fread,fwrite,fgets,fput, etc. - System:
system,getenv,argc,argv - Time:
time,localtime,gmtime,mktime,strftime
When running with strict=False, Joy gains Python interoperability:
from pyjoy import Evaluator
ev = Evaluator(strict=False) # Enable pythonic mode
# Backtick expressions - evaluate Python and push result
ev.run("`2 + 3`") # Pushes 5
ev.run("`math.sqrt(16)`") # Pushes 4.0 (math is pre-imported)
# Dollar expressions - same as backticks, better for nested parens
ev.run("$(len([1,2,3]))") # Pushes 3
# Bang statements - execute Python without pushing
ev.run("!x = 42") # Sets x in Python namespace
ev.run("`x * 2`") # Pushes 84
# Access stack from Python
ev.run("1 2 3")
ev.run("`sum(stack)`") # Pushes 6
# Define Python functions
ev.run("!def square(n): return n * n")
ev.run("`square(7)`") # Pushes 49Pre-imported modules: math, json, os, sys, re, itertools, functools, collections
Available in namespace: stack (alias S), ctx, evaluator
See docs/pythonic-mode.md for the complete guide.
Define new Joy words using Python decorators:
from pyjoy.evaluator import joy_word, python_word, ExecutionContext
from pyjoy.types import JoyValue
# Simple functions: use @python_word (auto-pops args, pushes result)
@python_word(name="hypot", doc="X Y -> Z")
def hypot(x, y):
return (x**2 + y**2) ** 0.5
# Full stack control: use @joy_word
@joy_word(name="rot3", params=3, doc="X Y Z -> Y Z X")
def rot3(ctx: ExecutionContext):
c, b, a = ctx.stack.pop_n(3)
ctx.stack.push_value(b)
ctx.stack.push_value(c)
ctx.stack.push_value(a)
# Wrap existing Python functions
import math
@python_word(name="deg2rad")
def deg2rad(degrees):
return math.radians(degrees)
@python_word(name="factorial")
def factorial(n):
return math.factorial(int(n))When to use each decorator:
| Decorator | Use When |
|---|---|
@python_word |
Pure functions, fixed arity, single return value |
@joy_word |
Need stack access, multiple results, control flow, or ExecutionContext |
Example: HTTP fetch word
@joy_word(name="http-get", params=1, doc="URL -> RESPONSE")
def http_get(ctx: ExecutionContext):
import requests
url = ctx.stack.pop()
response = requests.get(url.value if hasattr(url, 'value') else url)
ctx.stack.push(response.text)- Compiles Joy to standalone C executables
- Compile-time
includepreprocessing - Recursive include with circular dependency detection
- Full runtime with garbage collection
pyjoy-lang/
src/pyjoy/
__init__.py # Public API
__main__.py # CLI entry point
types.py # Joy type system
stack.py # Stack implementation
scanner.py # Lexical analysis
parser.py # Parser
evaluator/ # Execution engine
backends/c/ # C code generator
stdlib/ # Joy standard library
tests/
joy/ # Joy language tests
test_*.py # pytest unit tests
docs/
pyjoy.md # Implementation spec
pythonic-mode.md # Python integration guide
comparison-with-joy.md # Word comparison tables
tutorial.md # Getting started- Implementation Spec - Architecture and design
- Pythonic Mode Guide - Python integration features
- Comparison with Joy - Word-by-word comparison
- Tutorial - Getting started with Joy
MIT License - see LICENSE file for details.
- Joy Language Home
- A Conversation with Manfred von Thun
- Joy42 - Reference C implementation