Skip to content

acompany-develop/PyReflect

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PyReflect

SemVer Python Version License

A transpiler for mutually-referential reflective programming in Python. It is available as a library (pyreflect) and a command-line tool (pyreflect).

Overview

PyReflect is a transpiler that makes mutually-referential programs — programs made of several nodes — possible. Every node can reference the source code of itself and of the other nodes, without relying on any external source (e.g. file, stdin, registry).

Suppose node A and node B each want to reference the other's code. The naïve approach is to embed A's code inside B and B's code inside A. But then A's code has changed, so the copy of A embedded in B must be updated; that changes B's code, so the copy of B embedded in A must be updated; that changes A's code again, ad infinitum. In general this "fixed point" problem has no solution: for A to contain B we need $\mathsf{len}\left(A\right) > \mathsf{len}\left(B\right)$, and for B to contain A we need $\mathsf{len}\left(B\right) > \mathsf{len}\left(A\right)$ — a contradiction.

This infinite regress can be resolved by Kleene's second recursion theorem. Instead of embedding the code, each node embeds a code generator together with its input data. The generator can reconstruct the exact code of every node (itself and its peers) from the embedded input data — derived intrinsically, never fetched from any external source.

Install

uv pip install git+https://github.com/acompany-develop/PyReflect

This exposes the pyreflect command and the importable pyreflect package.

Usage

Library API

from pyreflect import parse_template, transpile

with open(path, encoding="utf-8") as f:
    # Read
    text = f.read()
    # Parse
    template = parse_template(text)
    # Transpile
    nodes = transpile(template)

Transpiler CLI

# Input from file
pyreflect TEMPLATE.json OUTPUT_DIR

# Input from stdin
cat TEMPLATE.json | pyreflect - OUTPUT_DIR

It writes one file per node (node_<id>.py) with a manifest.json mapping node-id to filename.

Transpiler details

The pyreflect transpiler converts a template into N standalone Python programs. A template is a JSON list of node objects:

    [ {"node-id": "__NODE1", "code": "<python source>"},
      {"node-id": "__NODE2", "code": "<python source>"},
      ... ]

Each node has a node-id and a code body that may reference any subset of the node-ids (itself included).

The transpiler does two things:

  1. rewrites every node-id token in a code body into __pyreflect_render__("<target-node-id>");
  2. wraps each body with an identical framework, including the definition of the __pyreflect_render__ function.

The __pyreflect_render__ function reconstructs the exact source code of the specified node from data embedded in the emitted source code without any external source of information.

The framework exposes the following API to each body:

__pyreflect_SELF__              this node's id
__pyreflect_DATA__              Base64-encoded template
__pyreflect_self_id__()         this node's id
__pyreflect_node_ids__()        all node ids, in template order
__pyreflect_render__(target)    exact source code (string) of the target node

A body must not redefine the reserved names of the form __pyreflect_*__.

Example code

quine

Single node that prints its own code.

# Transpile
pyreflect examples/quine/template.json examples/quine/

# Run Node: Display its own code
uv run examples/quine/node___NODE.py

# Verify
cat examples/quine/node___NODE.py

mutual_quine

Two nodes, each of which prints the other's SHA-256 digest. The peer value is obtained intrinsically — node 1 reconstructs node 2's source from its own embedded data and hashes it, and vice versa — so node 1's self hash equals the value node 2 reports as its expected peer reference, and vice versa.

# Transpile
pyreflect examples/mutual_quine/template.json examples/mutual_quine/

# Run Node 1: Display the SHA-256 digest of Node 2
uv run examples/mutual_quine/node___NODE1.py
# Run Node 2: Display the SHA-256 digest of Node 1
uv run examples/mutual_quine/node___NODE2.py

# Verify
sha256sum examples/mutual_quine/*.py

trinity_quine

A variant of mutual_quine with three nodes wired into a cycle: node 1 prints node 2's digest, node 2 prints node 3's, node 3 prints node 1's (1 → 2 → 3 → 1). It demonstrates that the transpiler handles arbitrary n-node reference graphs, not just the symmetric two-node case.

# Transpile
pyreflect examples/trinity_quine/template.json examples/trinity_quine/

# Run Node 1: Display the SHA-256 digest of Node 2
uv run examples/trinity_quine/node___NODE1.py
# Run Node 2: Display the SHA-256 digest of Node 3
uv run examples/trinity_quine/node___NODE2.py
# Run Node 3: Display the SHA-256 digest of Node 1
uv run examples/trinity_quine/node___NODE3.py

# Verify
sha256sum examples/trinity_quine/*.py

About

A transpiler for mutually-referential reflective programming in Python

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages