→ Forward · ← Backward · ◈ Container-first
Every PCB program passes through five stages before a single line executes. Click any stage for details.
A PCB program runs in both directions simultaneously — not as a mode but as two concurrent strands on shared data. KnotRunner uses an oscillation model: at each loop boundary the direction flips while the condition holds, so forward statements fire on forward passes and backward statements (tnirp, xob, rav, etc.) fire on backward passes — within the same loop body. Flow processing (tkp/pkt tick loops) runs independently on its own threads. Lexical reversal (keywords spelled backward, dot-chain mirrored) is the surface projection of directional traversal. The dot-chain is a directional composition operator — print.("x") and ("x").tnirp are the same execution graph node traversed from opposite ends.
Search and filter all PCB keywords. Click any card to see syntax, description, and examples. Cyan = forward form. Amber = reverse form.
PCB has six container types in two mirrored hierarchies. Forward: box → cup → pkt. Backward mirror: xob → puc → tkp. Each pair is not merely a syntactic mirror — each backward type has distinct execution semantics: xob is structurally opaque (inspection ops blocked); puc inverts every operation in its body at execution time; tkp has an independent tick-based lifecycle that seam-crosses to pkt. knt/tnk stand apart — they act as container, orientation, and operator simultaneously. Everything in PCB is a box — every container implements IBox, and every primitive is automatically wrapped in a single-item BoxInstance at storage boundaries. Variables are containers all the way down.
Primitives are never stored raw. At every storage boundary the runtime wraps them in a single-item BoxInstance. Computation may produce raw values, but storing them always boxes them. Reading them for arithmetic auto-unboxes transparently.
Boxing happens automatically when a value crosses any of these boundaries:
| Boundary | Where it fires |
|---|---|
| Container body evaluation | evaluateBody() in all six container types |
| Variable definition | var / rav declaration |
| Assignment | = (forward) and = (backward) |
| Routing injection | KnotRunner.injectIntoContainer() before addAll() |
| Bootstrap result | pkt/tkp tick() before inserting fn.call() result |
Input to Boxer.box() |
Result |
|---|---|
Double, Integer, String, Boolean | BoxInstance([value]) — single-item body |
null | BoxInstance([null]) — body size 1, distinct from empty BoxInstance |
Any Instance (container) | Passed through unchanged — containers are never re-wrapped |
| Non-boxable Java object | null; error routed to NON/LIMBO sink |
Binary operators (+ - * / % ^ and comparisons) automatically unwrap a BoxInstance on either operand before computing. The result is raw; it is re-boxed only at the next storage boundary. You never need to manually unbox for arithmetic.
Every container implements IBox. The marker interfaces form the runtime type tree:
IBox
├── ICup extends IBox
│ └── IPkt extends ICup
│ ├── ITkp extends IPkt → TkpInstance
│ ├── IKnt extends IPkt → KnotInstance
│ └── ITnk extends IPkt → TonkInstance
└── IXob extends IBox
└── IPuc extends IXob, ICup
BoxInstance implements IBox
CupInstance implements ICup
PocketInstance implements IPkt
XobInstance implements IXob (also extends BoxInstance)
PucInstance implements IPuc (also extends CupInstance)
pkt and tkp are self-contained execution ecosystems — they hold elements, run Flow tick loops on independent daemon threads, and manage their own lifecycle. They are the only containers with autonomous execution.
On each tick, every active Flow scans the body in its current direction. Priority order of what Flow acts on:
| Item in body | What Flow does |
|---|---|
Flow object | Scavenged — chain absorbed, item removed |
String(".") — standalone period | Flips active flow direction (FORWARD↔BACKWARD), consumed. Syntax: ( . "hello") |
Bare bracket string "(" "{" ")" "}" | Synthesizes period → new independent Flow promoted to flows list (open=fwd, close=bwd), consumed |
PocketOpen/CupOpen/PocketClosed/CupClosed expr | Same as bare bracket — new independent Flow, label discarded, consumed (knotted pocket) |
String with connectors "(." ".)" etc. | Chain absorbed, string added as cargo |
cup / puc — CupInstance | Bootstrapped — re-executes originalBody, cup consumed, token spent |
knt / tnk — KnotInstance/TonkInstance | Bootstrapped — KnotRunner.runWithRouting() runs knot and routes output to named target container; token spent |
Nested pkt / tkp | Ignored — Flow does not enter nested ecosystems |
These are separate concerns. Flow bootstraps — it starts execution. Routing is where the output goes — handled entirely by KnotRunner using interp.environment to look up the target container by name. Flow does not participate in routing.
A standalone . flips the active flow's direction in place. When a . appears inside a knt/tnk body, it becomes a routed result — injected into the target container by runWithRouting(). If the target is a pkt/tkp, the tick loop flips that container's flow direction. A knot can remotely flip flow direction in another pocket. In box/cup targets, "." lands inertly.
Heat death is reached when all Flow chain tokens are spent and no actionable items remain — no dormant Flow objects, no unexecuted cups or knots, no bracket signals. tkp at heat death seam-crosses to pkt. pkt at heat death simply stops ticking.
| Event | pkt result | tkp result |
|---|---|---|
| Heat death | Stops ticking (stays pkt) | → seam-crosses to pkt |
| Natural lifetime expiry | → seam-crosses to tkp | → seam-crosses to pkt |
User-kill (= null) | Destroyed, no transformation | Destroyed, no transformation |
| INDEFINITE lifetime | Never crosses naturally | Never crosses naturally |
Lifetime budget transfers through every crossing. TRAVERSAL carries remaining count. Body and flows transfer; seam-crossed container starts its own tick loop if it has flows.
Loops and conditionals have bidirectional forms. Labeled loops allow complex nested structures to reference each other.
PCB supports standard arithmetic, comparison, and logical operators plus two unique stream operators for direct file I/O.
Real programs from source files, plus authored examples demonstrating common patterns.
PCB has built-in trig, log, arithmetic, rounding, inverse trig, bitwise, and vector/matrix keywords. A separate symbolic math subsystem (BoxMath) with derivatives, integrals, and the Fresnel S/C integrals exists internally. The examples here cover all math keywords available in PCB programs.
PCB includes first-class commands for interacting with the FlatLand 2D game engine. Scripts attached to game objects can create, move, and destroy entities in the world.
Links are interfaces — named sets of method signatures declared with !-tagged cup labels. Templates are named types — cups with fields and methods, declared with #-tagged labels. Instances are created with @. When a template declares link conformance the runtime checks it at registration time and runs instantiation in a contract context.
A link is a cup whose open label is prefixed with ! and whose close label uses the reversed name suffixed with ! — e.g. !Drawable{ … }elbawarD!. Method bodies inside a link are omitted — they are signatures only. Calling an unimplemented method throws RuntimeError.
A template is a cup whose open label is prefixed with # and whose close label uses the reversed name suffixed with #. All names on the close label are reversed — the template name, any &Link names, and the <Base name. Conformance against declared links is checked at registration time.
TypeName @ instanceName creates a new instance of the template and binds it to instanceName. The backward form swaps the order.
| Situation | Result |
|---|---|
Template declares &Link and implements all signatures | Registered successfully. Methods are live implementations. |
Template declares &Link but is missing one or more methods | Throws RuntimeError at registration time. |
| Calling a null-body link method directly | Throws RuntimeError — link signatures are not callable. |
Template with no &Link | Plain template — no conformance check, no contract context. |
Template with >BaseName | Inherits base template's BoxClass as superclass. C3 rollback if instantiation fails. |
When a template declares at least one link, instantiation runs inside a contract context. Field initializers whose value chain-roots back to a seed global are preserved lazily — stored unevaluated rather than computed immediately. Literals and self-contained expressions evaluate eagerly as normal.
contractConfidence on the resulting instance is the ratio of lazy bindings to total bindings:
| Value | Meaning |
|---|---|
-1.0 | Outside contract context — not applicable. |
0.0 | All bindings are eager (fully self-contained). |
0.0 – 1.0 | Mixed — ratio of lazy to total bindings. |
1.0 | All bindings are lazy — fully data-driven from seed globals. |
| Construct | Open label | Close label |
|---|---|---|
| Link | !Name{ | }emaN! |
| Template (plain) | #Name{ | }emaN# |
| Template + link | #Name&Link{ | }emaN&kniL# |
| Template + link + base | #Name&Link>Base{ | }emaN<esaB&kniL# |
| Instantiation (forward) | TypeName @ instanceName | |
| Instantiation (backward) | instanceName @ TypeName | |