A Python implementation of the Joy programming language with C code generation
Project description
pyjoy-lang
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.
Installation
From PyPI
pip install pyjoy-lang
From Source
Requires 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 --compile
For C compilation, you'll also need gcc or clang.
Usage
Interactive REPL
# Start the Joy REPL
uv run pyjoy
Example session:
Joy> 2 3 + .
5
Joy> [1 2 3] [dup *] map .
[1 4 9]
Joy> DEFINE square == dup *.
Joy> 5 square .
25
Joy> quit
Execute Joy files
# 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 to C
# 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 Test Suite
# 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
Status
Test Results
| Backend | Passing | Total | Coverage |
|---|---|---|---|
| Python Interpreter | 194 | 215 | 90.2% |
| C Backend | 199 | 215 | 92.6% |
| pytest (unit tests) | 712 | 712 | 100% |
Primitives
- 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.
Features
Core Language
- 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
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
I/O and System
- 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
Python Interop (Pythonic Mode)
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 49
Pre-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.
Extending Joy from Python
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)
C Backend Features
- Compiles Joy to standalone C executables
- Compile-time
includepreprocessing - Recursive include with circular dependency detection
- Full runtime with garbage collection
Project Structure
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
Documentation
- Implementation Spec - Architecture and design
- Pythonic Mode Guide - Python integration features
- Comparison with Joy - Word-by-word comparison
- Tutorial - Getting started with Joy
License
MIT License - see LICENSE file for details.
References
- Joy Language Home
- A Conversation with Manfred von Thun
- Joy42 - Reference C implementation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyjoy_lang-0.1.2.tar.gz.
File metadata
- Download URL: pyjoy_lang-0.1.2.tar.gz
- Upload date:
- Size: 190.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
343db87a349bebe264e579aa3c4279ea33fd26351f07766f646abe2fcb9bff6f
|
|
| MD5 |
62612daf31083cc70b47f28dac1e25f1
|
|
| BLAKE2b-256 |
1265afaf36ad8db538d0480697eb3d99ed4f98826a3aaded728efacd6151cd1f
|
File details
Details for the file pyjoy_lang-0.1.2-py3-none-any.whl.
File metadata
- Download URL: pyjoy_lang-0.1.2-py3-none-any.whl
- Upload date:
- Size: 150.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69514d4c62bce4874b7305bcda7963ed4c8951f17a241ffeed23abee1def0a71
|
|
| MD5 |
f19f2da74e8488d669e1d9d6c36c0232
|
|
| BLAKE2b-256 |
e722c96e106cabbd9f9a7371abfae87f2b3f7ff524950fe1b43e8249029d0ec8
|