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
Metadata
Release files for pyjoy-lang 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pyjoy_lang-0.1.2.tar.gz | 190.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyjoy_lang-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 341.3 kB
Release files / pyjoy_lang-0.1.2.tar.gz
| Download URL | pyjoy_lang-0.1.2.tar.gz |
|---|---|
| Size | 190.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
343db87a349bebe264e579aa3c4279ea33fd26351f07766f646abe2fcb9bff6f
|
|
BLAKE2b-256 checksum How to use checksums |
1265afaf36ad8db538d0480697eb3d99ed4f98826a3aaded728efacd6151cd1f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Release files / pyjoy_lang-0.1.2-py3-none-any.whl
| Download URL | pyjoy_lang-0.1.2-py3-none-any.whl |
|---|---|
| Size | 150.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
69514d4c62bce4874b7305bcda7963ed4c8951f17a241ffeed23abee1def0a71
|
|
BLAKE2b-256 checksum How to use checksums |
e722c96e106cabbd9f9a7371abfae87f2b3f7ff524950fe1b43e8249029d0ec8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Security for pyjoy-lang 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.