Skip to main content

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 include preprocessing
  • 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

License

MIT License - see LICENSE file for details.

References

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyjoy_lang-0.1.2.tar.gz (190.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyjoy_lang-0.1.2-py3-none-any.whl (150.7 kB view details)

Uploaded Python 3

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

Hashes for pyjoy_lang-0.1.2.tar.gz
Algorithm Hash digest
SHA256 343db87a349bebe264e579aa3c4279ea33fd26351f07766f646abe2fcb9bff6f
MD5 62612daf31083cc70b47f28dac1e25f1
BLAKE2b-256 1265afaf36ad8db538d0480697eb3d99ed4f98826a3aaded728efacd6151cd1f

See more details on using hashes here.

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

Hashes for pyjoy_lang-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 69514d4c62bce4874b7305bcda7963ed4c8951f17a241ffeed23abee1def0a71
MD5 f19f2da74e8488d669e1d9d6c36c0232
BLAKE2b-256 e722c96e106cabbd9f9a7371abfae87f2b3f7ff524950fe1b43e8249029d0ec8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page