ChrysaLisp Coding Skill
ChrysaLisp is a unique, LISP-like, distributed, message-passing, parallel
processing MIMD OS and language. It features a portable Virtual Processor (VP)
architecture, its own build tools, native translators, a hyper-fast Lisp
interpreter, and extensive class libraries at both the VP assembler and Lisp
levels.
The system is a direct evolution of its bare-metal predecessor, Taos OS.
Its features and syntax emerged from first-principles engineering focused on
creating a high-performance, distributed system; convergence with Lisp was a
discovery, not a design goal. It fundamentally rejects traditional Lisp
implementation details (such as cons cells and tracing garbage collection) in
favor of vector primitives that map directly to high-performance hardware.
ChrysaLisp treats the host OS as a set of drivers via its Platform
Implementation Interface (PII), rather than acting as a runtime dependent on
one.
Documentation Index & AI Digest
For comprehensive architectural guides, design rationale, and system
deep-dives, consult LLM.md in the workspace root. LLM.md is the master
index and reading guide for all 70 technical documents in docs/ai_digest/,
organized by category:
Core Architecture: Genesis, philosophies, memory model, and object
hierarchies (0–8).
Virtual Processor (VP): Translation, classes, functions, SIMD, and
emulator (9–13).
Lisp Language & Primitives: Modern Lisp, Four Horsemen sequence
transformations, flow-through, and built-ins (14–20).
Advanced Lisp: Dual vtables, closure-less design, O(1) calling,
modules, REPL/JIT, and CScript compiler (21–28).
Data Types & Streams: Numerics, text parsing, regexps, streams, pipes,
and slicing (29–36).
GUI Framework: Views, widgets, compositor, vector graphics, and text
stack (37–44).
Distributed OS & Networking: Fault tolerance, dynamic code, IPC, task
farming, and streaming pipelines (45–50, 68–69).
Core Architectural Philosophies
Philosophy 1: "Well, Don't Do That Then!"
ChrysaLisp pragmatically avoids common systems programming problems rather
than engineering complex machinery to manage them:
Concurrency Without Race Conditions: Sidesteps shared-memory race
conditions by running completely isolated tasks communicating via
message passing rather than shared-memory threads.
Performance Without GC Pauses: Eliminates garbage collection pauses
entirely by using reference counting and a memory model built strictly
on vector primitives instead of traditional cons cells.
Security Without Complex Memory Protection: Avoids self-modifying
native code and complex W^X policies. The native code engine is
immutable and ROMable. Dynamic optimizations and runtime changes occur
by patching Lisp data structures (the "script") in RAM, which the engine
executes.
Radical Simplicity and Speed: The entire boot image for a RISC CPU
is around 200 KB (fitting inside L1 cache), and a full OS rebuild
completes in under 0.1 seconds on a modern laptop.
Philosophy 2: "Be Formless, Shapeless, Like Water"
The system is engineered for fluid adaptability and distributed scalability:
Formless Network: The network is an emergent entity defined solely
by active nodes and links. Communication is completely location-
transparent: (mail-send) behaves identically whether the target task
is on the same core, a separate core, or a remote machine.
Dual-Mode Task Placement:
- Application-Directed Placement: Programs query
(lisp-nodes)
and use libraries like `lib/task/farm.inc` to explicitly distribute
workloads (e.g. random or round-robin placement).
- Kernel-Assisted Emergent Placement: When spawning a task with
`+kn_call_run`, the kernel initiates a decentralized load-
balancing search. It compares its local `task_count` against
immediate network neighbors. If a neighbor is less loaded, the
entire spawn request flows "downhill" to that node without
spawning locally. This cascades across hops until settling in a
local minimum ("valley"), where the task finally spawns.
Philosophy 3: "Know Thyself" — Cooperative Internals
Internal primitives are designed with intimate awareness of the cooperative
execution model:
Cooperative Tasks and Small Stacks: Tasks are cooperatively
scheduled and non-preemptible, yielding only at explicit points (e.g.
task-sleep, mail-read, task-slice). This guarantee allows tasks to
operate safely with small, fixed stacks (~8 KB), minimizing footprint
and enabling high concurrency.
The Iterative Idiom: Deep recursion on machine stacks is strictly
prohibited by the small stack size. ChrysaLisp pervasively uses
iteration with an explicit heap-allocated list as a work stack
(used in lisp :read, host_gui :composite, etc.).
Synergy with O(1) Cache Performance: Flatter iterative lexical
scopes keep symbol bindings stable, preventing the churn of deep
scope chains and maximizing cache hits in the symbol engine.
Dual HMap Tree Duality: Every object instance is fundamentally an
hmap participating simultaneously in two hierarchies:
- The dynamic Containment Hierarchy traversed at runtime via the
`:parent` key for inherited appearance attributes (such as
`:color` or `:font`).
- The static Class Hierarchy resolved via the
:vtable key
pointing to a compile-time-composed, flattened `hmap` of method
pointers. Method dispatch (`(. obj :method)`) is an immediate
two-step O(1) lookup without any runtime inheritance traversal.
Lock-Free, State-Aware Algorithms: Non-preemption permits safe,
lock-free data updates:
- Atomic Swap Pattern: Shared caches (such as
font :flush)
prepare changes on a private copy and commit via an atomic pointer
swap across non-yielding sequences.
- Robust Iterator Pattern: Iterators like
hmap :each support
in-place deletion of the current item via swap-and-pop `erase`,
intelligently resynchronizing state after callback execution.
The Unambiguous, Ephemeral netid
Network identity is based on the tuple (mailbox_id, node_id):
node_id (Ephemeral Node Identity): Generated randomly each time a
node boots. If a node restarts, its node_id changes, preventing
messages meant for a previous incarnation from ever delivering to the
new one.
mailbox_id (Disposable Mailbox Identity): A 64-bit monotonically
increasing counter allocated via (mail-alloc-mbox). Mailbox IDs are
never reused. When (mail-free-mbox) is invoked, the ID is permanently
invalidated; mail:validate drops subsequent messages addressed to it.
Practical Pattern: For any distinct conversation, transaction, or
request-response cycle, allocate a fresh mailbox. Freeing the mailbox
instantly drops late-arriving responses, eliminating stale messages,
sequence-number tracking, and zombie tasks.
The Lisp Implementation & Symbol Engine
ChrysaLisp's interpreter is self-hosted (class/lisp/, root environment in
class/lisp/root.inc) and built for maximum throughput:
Vector-Based Architecture: The system entirely dispenses with cons
cells, car, and cdr. All Lisp sequences are vectors operated on via
indexed primitives ((!) forms).
No-Layer FFI: Direct zero-overhead calling between Lisp forms and
underlying Virtual Processor (VP) machine instructions.
O(1) Symbol Lookup & Single-Bucket HMaps:
- Environments are trees of single-bucket
hmap objects. Single-bucket
tables avoid hash division/modulo math and minimize allocation
costs.
Interned symbol objects carry a cached field: str_hashslot.
When a symbol is bound (via defq or argument binding), the
runtime stores the binding and immediately writes the slot index
into the symbol's `str_hashslot`. Lookups do not perform initial
linear scans; they execute as direct indexed reads.
- If shadowing invalidates a cached slot,
hmap:find performs a
one-time linear recovery scan upon exiting the shadow, and
immediately rewrites `str_hashslot` with the recovered index to
resume O(1) performance.
Linkerless VP Code Generation: VP compilation emits symbolic
dependency paths in a links section. The boot-image tool resolves
these to relative offsets, and sys/load/init performs runtime
rebinding to absolute addresses in fractions of a second.
Core Lisp Disciplines (Must Follow)
Tab Style: Always use leading 4-space tab characters for indentation
in source code and documentation, with spaces afterwards if needed.
Type-Dependent Equality with eql:
eql performs deep content comparison on scalar numbers, strings,
and typed numeric vectors (`nums`, `fixeds`, `reals`).
eql performs pointer identity comparison on general containers
(`list`, `hmap`). To test list content equivalence, use:
(and (= (length l1) (length l2)) (every eql l1 l2))
No Lexical Closures: lambda forms are pure code templates that
execute strictly inside the environment of their caller. They do not
capture defining scopes. All context must be supplied explicitly via
arguments or pre-exist in the caller's environment.
Static '() vs Independent (list): '() evaluates to a shared,
static empty list instance. Mutating '() (e.g. via push) corrupts
every reference in the system. Always use (list) to create a fresh,
independent, mutable empty list.
Control Flow and Error Prevention: There is no return keyword;
a function yields the result of its last evaluated expression.
Non-local exits (catch, throw) are strictly for debugging. Code must
prioritize input validation to prevent invalid states from ever
occurring, rather than catching errors after the fact.
Lists as LIFO Stacks:
(push list elem) appends to the end of list.
(pop list) removes and returns the final element.
(first list) is (elem-get list 0).
(last list) is (elem-get list -2).
(slice list 1 -2) returns the list minus first and last items.
Pragmatic Lambda Usage (# vs lambda):
- Use
(# ...) for compact, performance-critical callbacks. Positional
symbols `%0`, `%1`, etc., are globally interned with permanent
`str_hashslot` cache indices.
- Use
(lambda ...) when destructuring arguments or when explicit
naming clarifies complex logic.
- NEVER use
(bind ...) inside an anaphoric # lambda
(anti-pattern: `(# (bind '(k v) %0) ...)`). Doing so introduces
local symbols that destroy the cache benefits of `%0` while adding
`bind` overhead. Use `(lambda ((k v)) ...)` instead.
Variable Binding and Shadowing:
- Variable Naming (Snake_case Only): Variables, arguments, and
parameters must ALWAYS use snake_case with underscores (`_`), never
hyphens (`-`). Hyphenated/kebab-case symbols are strictly reserved
for callable forms (functions and macros).
- Do not nest
let or let*. Use defq to declare and bind multiple
variables simultaneously in the current scope:
(defq a 1 b 2 c (list))
- Never use built-in function names as variables (e.g.
path, str).
Functions and variables share the same symbol environment.
- Use
bind for destructuring sequences and return tuples:
(bind '(w h) (. view :get_size))
(bind '(x y &rest tail) my_list)
(bind '(x y &ignore) my_list)
(bind '((x0 x1 &ignore) (y0 y1 &ignore) &ignore) nested_list)
Object Syntax:
(.-> *canvas* (:set_color +argb_black) (:fill 0))
Property access: (get :prop obj).
Property create or mutate: (def obj :prop val).
Property mutate only: (set obj :prop val).
Anaphoric Loop Index (!): In each, each!, map, and lines!,
the form (!) evaluates to the current zero-based loop index:
(each (# (print "Item " (!) ": " %0)) my_list)
Compile-Time Constants: Force compile-time arithmetic or lookups
using (const ...):
(* x (const (/ 180.0 +fp_pi)))
GUI Application Event Loop Pattern:
(defun main ()
(defq select (task-mboxes +select_size) running :t)
; ... setup widgets / window ...
(gui-add-front-rpc window)
(while running
(defq msg (mail-read (elem-get select (defq idx (mail-select select)))))
(cond
((= idx +select_main)
(if (= (getf msg +ev_msg_target_id) +event_close)
(setq running :nil)
(. window :event msg)))
(:t (. window :event msg))))
(gui-sub-rpc window))
Short-Circuiting, Embedded Binding, and Branching (and / or):
and expands to condn (testing for :nil), and or expands to
`cond`. Write predicates for zero wasted interpreter evaluations.
- Embed
defq Inside Consumer Predicates: Do not write standalone
`(defq ...)` clauses in an `and` chain. Bind variables directly
inside the comparison that uses them:
(eql (third %1) (defq d (third %0)))
- Order Clauses for Fastest Failure: Place the most discriminative
test earliest to lazily abort execution before further bindings.
- Leverage
cond Branch Conditions: Put (and ...) directly as
the `cond` branch condition; do not wrap the body in `(when ...)`.
- Use
when Naturally for Multi-Statement Blocks: Inside case or
unconditional blocks, use `(when tst a1 a2)` directly.
(cond
((eql op0 'emit-cpy-rr)
(when (and (defq cpy_info (pfind +map (first %1)))
(defq d (third %0))
(eql (third %1) d)
(defq s (second %0))
(nql s d))
(case (first cpy_info)
(:cr
(elem-set emit_list (!) (list (second cpy_info) (second %1) s d))
(elem-set emit_list (inc (!)) '(emit-nop)))))))
(cond
((and (eql op0 'emit-cpy-rr)
(defq cpy_info (pfind +map (first %1)))
(eql (third %1) (defq d (third %0)))
(nql d :rsp)
(nql (defq s (second %0)) d)
(nql s :rsp))
(case (first cpy_info)
(:cr
(elem-set emit_list (!) (list (second cpy_info) (second %1) s d))
(elem-set emit_list (inc (!)) '(emit-nop))))))
Virtual Processor (VP) Assembler & CScript Guide
When writing or modifying .vp files, you target ChrysaLisp's register and
execution model.
Register Architecture
15 General-Purpose registers: :r0 through :r14.
1 Dedicated Stack Pointer: :rsp.
16 Floating-Point registers: :f0 through :f15.
ABSOLUTE VOLATILITY (No Callee-Saved Registers): ChrysaLisp has NO
callee-saved registers. Any call may trash registers. The ;trashes
header above each function is the sole source of truth.
VP Method Definition Boilerplate
Every method must follow standard boundary and scoping conventions:
(def-method :class :method)
;inputs
;:r0 = this (ptr)
;:r1 = arg (num)
;outputs
;:r0 = result (num)
;trashes
;:r1-:r3
(vp-rdef (this arg res))
(entry :class :method '(:r0 :r1))
; ... method body ...
(exit :class :method '(:r0))
(vp-ret)
(def-func-end)
The assign Macro
(assign ...) evaluates expressions, moves data, and loads/stores memory
simultaneously:
(assign '((:r0 +str_length)) '(:r1))
CScript Integration & Memory Rules
CScript provides high-level typed variables and pointer expressions within
{...} blocks:
Typed Declarations: Define stack variables with (def-vars ...).
Supported types include ptr, pubyte, long, uint, etc.
Dereferencing {*src_ptr} generates appropriate byte or word transfers
based on variable type.
CScript Scope Discipline (CRITICAL):
macros automatically emit scope unwinding instructions. Calling
`pop-scope` manually creates a double-free of the stack frame.
- Place
(pop-scope-syms) at the end of the def-func block (after
the `errorcase` and `signature` sections) to cleanly purge compiler
tracking.
CScript Stack Packing & Unions:
- Order variables in
def-vars from largest (long, ptr, netid)
to smallest (`uint`, `ubyte`) to prevent alignment padding waste.
- Use
(union ...) to share stack space between variables with
mutually exclusive lifetimes. Do not create redundant variables
merely to avoid type casts.
Assignment Fusion & Hazards:
Fuse assignments to minimize temporaries: (assign {a, b} {x, y}).
Evaluation Order: Sources compile left-to-right (pushed to
value stack); destinations compile right-to-left (popped from value
stack).
- Mutation Hazard: Never mutate a variable in a fused assignment
if that variable calculates the address of a destination further to
the left (e.g. `(assign {val, p + 1} {p[0], p})` is invalid). Split
such updates into sequential `assign` statements.
Raw VP Loop Optimization (Bypassing CScript in Hot Paths)
When CScript's value stack overhead is unacceptable in inner loops, drop to
pure VP assembler:
Declare raw registers: (vp-rdef (r_buf r_mask r_i)).
Extract CScript variables into registers with mixed assign:
(assign {buf, mask, ptr_i} (,r_buf ,r_mask ,r_i))`.
Hand-code the loop using loop-start, loop-until, breakif, and
raw instructions (vp-cpy-dr-ub, vp-add-cr, etc.).
Restore results to CScript variables: (assign (,r_i) {ptr_i})`.
If a call inside a loop trashes registers needed for loop control,
keep loop counters in registers outside the call's ;trashes set, or
advance loop counters before the call, or spill persistent registers
(this, args) to stack slots.
Mixed VP Assembler & CScript Syntax Duality
String Literal Equivalence: Curly braces {...} and double quotes
"..." are identical string constructors in ChrysaLisp. {...} is
favored for CScript code to allow embedded quotes (\q) without escapes.
Three Symbol Contexts:
- Raw Symbols (
(vp-cpy-ir adr 80 cnt)): Resolve directly to VP
registers (`:r0` - `:r14`).
- CScript Strings (
{adr, cnt}): Resolve to stack frame offsets
(`[rsp + offset]`).
- Quasi-Quoted Lists (
`(,adr ,cnt)): Commas explicitly evaluate
register symbols inside macros.
Bridging Pattern:
; 1. Define CScript stack variables
(def-vars
(pubyte buf)
(long pos))
; 2. Define VP registers with matching names
(vp-rdef (buf pos))
; 3. Extract inputs into VP registers
(list-bind-args args `(,buf ,pos) '(:obj :num))
; 4. Map VP registers to CScript variables
(assign `(,buf ,pos) {buf, pos})
Calling Conventions & Register Introspection
Inspect register contracts dynamically at compile time:
Dynamic Call Mapping: Pass (method-input ...) directly to vp-rdef
to bind argument aliases without hardcoding volatile registers:
(vp-rdef (cp_src cp_dst cp_len) (method-input :sys_mem :copy_to_ring))
Assign loop state into these argument aliases immediately before the call.
If the call trashes registers used for loop progression, keep counters in
registers outside the call's ;trashes set, or advance them before the call.
Preserve live state across calls via explicit (vp-push reg) /
(vp-pop reg) or by saving to CScript stack slots.
Systematic VP Lowering Workflow
Follow this 7-step discipline when designing VP routines:
Draft the Algorithm: Specify control flow and data transitions.
Identify Call Boundaries: Enumerate every sub-call in the logic.
Inspect Call Contracts & Trashed Sets: Review ;trashes and
(method-input ...) for each dependency.
Evaluate Stack Spill Necessity: Check if persistent state can fit
in registers above the trashed set, avoiding stack manipulation.
Map Variable Lifespans: Group variables into persistent (must
survive calls) and ephemeral (local to call intervals).
Formulate Compact vp-rdef Mappings: Assign persistent state to
safe registers and reuse volatile low registers across intervals.
CScript vs. Pure VP Selection: Use pure VP when state fits
entirely in registers; use CScript when named stack slots are required.
Numerical Representations & Systems
ChrysaLisp natively supports three numerical types:
Integers (num): 64-bit signed integers.
Fixed-Point Reals (fixed, fixeds): 48.16 signed fixed-point
values (64-bit word with 16 fractional bits). Uses +fp_frac_mask and
+fp_shift 16. Trigonometric and transcendental math operate on fixed.
Double-Precision Reals (real, reals): Standard 64-bit IEEE 754
double-precision floating-point values.
Conversions:
(n2i x): Converts fixed or real to integer num.
(n2f x): Converts num or real to 48.16 fixed.
(n2r x): Converts num or fixed to IEEE double real.
Subsystems & Architecture Reference
Shared Memory (SHMEM) Link Protocol
Independent ChrysaLisp instances communicate over shared memory using
sys_link ring buffers:
Negotiation Towel: Nodes race to write their node_id into the
host_a field of chan_1. After (task-sleep 100), the surviving ID
becomes the owner (transmits on chan_1, receives on chan_2). The
other node writes to host_b (transmits on chan_2, receives on
chan_1).
Buffer Slot Statuses: lk_chan_status_ready (free),
lk_chan_status_ping (routing heartbeat), lk_chan_status_frag
(message data fragment), and lk_chan_status_skip (buffer wrap marker).
Distributed JIT Compilation Pipeline
Dynamic VP compilation (lisp.vp) is protected by network locking:
(lock-claim-rpc obj_prefix)
(when (some (# (> file_age (age (cat obj_prefix %0)))) products)
(catch (within-compile-env (# (include file))) :nil))
(lock-release-rpc obj_prefix)
Two-Pass GUI Layout System
GUI rendering executes in two deterministic passes:
Constraint Pass (:constraint): Traverses top-down to compute
minimum bounding dimensions based on content.
Layout Pass (:layout): Traverses bottom-up to assign final
coordinates and bounds to widgets.
- Flags like
+flow_stack_fill and +flow_down_fill use lastw and
lasth to absorb remaining container dimensions.
File & Naming Conventions
.lisp: Executable ChrysaLisp programs.
.inc: Library files included via (import ...).
.vp: Virtual Processor assembly source.
.tre: Application configuration and state trees.
class.inc: VP assembler include definitions ((include ...)).
class.vp: VP assembler method implementations.
lisp.inc: Lisp FFI bindings and Lisp versions of VP structures.
lisp.vp: VP implementations of :lisp_xxx static primitives.
Identifier Naming Rules:
- Functions and Macros: Kebab-case with hyphens (
foo-bar). ONLY
callable code (functions and macros) may use kebab style.
- Variables and Parameters: Snake_case with underscores (
foo_bar).
NEVER use hyphens in variable or parameter names.
Constants: Plus prefix (+foo_bar).
Globals: Asterisk-wrapped (*foo_bar*).
Properties and Keywords: Colon prefix (:foo_bar).
Output Directives (Mandatory)
Do not emit entire unmodified source files. Provide focused diffs or
concise cut-and-paste snippets indicating exact locations.
Always use 4-space tab indentation in ChrysaLisp source code and
documentation.
Always place a blank line between all documentation elements in .md
files, including between individual bullet points and sub-bullets.
Always wrap ChrysaLisp .md documentation at 80 columns (do not wrap
source code blocks).
1---2name: chrysalisp3description: Use when writing, reviewing, or modifying ChrysaLisp code (Lisp .lisp/.inc, VP assembler .vp, CScript). Covers idioms, primitives, and architecture.4---56# ChrysaLisp Coding Skill78ChrysaLisp is a unique, LISP-like, distributed, message-passing, parallel9processing MIMD OS and language. It features a portable Virtual Processor (VP)10architecture, its own build tools, native translators, a hyper-fast Lisp11interpreter, and extensive class libraries at both the VP assembler and Lisp12levels.1314The system is a direct evolution of its bare-metal predecessor, Taos OS.15Its features and syntax emerged from first-principles engineering focused on16creating a high-performance, distributed system; convergence with Lisp was a17discovery, not a design goal. It fundamentally rejects traditional Lisp18implementation details (such as cons cells and tracing garbage collection) in19favor of vector primitives that map directly to high-performance hardware.20ChrysaLisp treats the host OS as a set of drivers via its Platform21Implementation Interface (PII), rather than acting as a runtime dependent on22one.2324## Documentation Index & AI Digest2526For comprehensive architectural guides, design rationale, and system27deep-dives, consult `LLM.md` in the workspace root. `LLM.md` is the master28index and reading guide for all 70 technical documents in `docs/ai_digest/`,29organized by category:3031* **Core Architecture:** Genesis, philosophies, memory model, and object32 hierarchies (`0–8`).3334* **Virtual Processor (VP):** Translation, classes, functions, SIMD, and35 emulator (`9–13`).3637* **Lisp Language & Primitives:** Modern Lisp, Four Horsemen sequence38 transformations, flow-through, and built-ins (`14–20`).3940* **Advanced Lisp:** Dual vtables, closure-less design, O(1) calling,41 modules, REPL/JIT, and CScript compiler (`21–28`).4243* **Data Types & Streams:** Numerics, text parsing, regexps, streams, pipes,44 and slicing (`29–36`).4546* **GUI Framework:** Views, widgets, compositor, vector graphics, and text47 stack (`37–44`).4849* **Distributed OS & Networking:** Fault tolerance, dynamic code, IPC, task50 farming, and streaming pipelines (`45–50`, `68–69`).5152## Core Architectural Philosophies5354### Philosophy 1: "Well, Don't Do That Then!"5556ChrysaLisp pragmatically avoids common systems programming problems rather57than engineering complex machinery to manage them:5859* **Concurrency Without Race Conditions:** Sidesteps shared-memory race60 conditions by running completely isolated tasks communicating via61 message passing rather than shared-memory threads.6263* **Performance Without GC Pauses:** Eliminates garbage collection pauses64 entirely by using reference counting and a memory model built strictly65 on vector primitives instead of traditional `cons` cells.6667* **Security Without Complex Memory Protection:** Avoids self-modifying68 native code and complex W^X policies. The native code engine is69 immutable and ROMable. Dynamic optimizations and runtime changes occur70 by patching Lisp data structures (the "script") in RAM, which the engine71 executes.7273* **Radical Simplicity and Speed:** The entire boot image for a RISC CPU74 is around 200 KB (fitting inside L1 cache), and a full OS rebuild75 completes in under 0.1 seconds on a modern laptop.7677### Philosophy 2: "Be Formless, Shapeless, Like Water"7879The system is engineered for fluid adaptability and distributed scalability:8081* **Formless Network:** The network is an emergent entity defined solely82 by active nodes and links. Communication is completely location-83 transparent: `(mail-send)` behaves identically whether the target task84 is on the same core, a separate core, or a remote machine.8586* **Dual-Mode Task Placement:**8788 * **Application-Directed Placement:** Programs query `(lisp-nodes)`89 and use libraries like `lib/task/farm.inc` to explicitly distribute90 workloads (e.g. random or round-robin placement).9192 * **Kernel-Assisted Emergent Placement:** When spawning a task with93 `+kn_call_run`, the kernel initiates a decentralized load-94 balancing search. It compares its local `task_count` against95 immediate network neighbors. If a neighbor is less loaded, the96 entire spawn request flows "downhill" to that node without97 spawning locally. This cascades across hops until settling in a98 local minimum ("valley"), where the task finally spawns.99100### Philosophy 3: "Know Thyself" — Cooperative Internals101102Internal primitives are designed with intimate awareness of the cooperative103execution model:104105* **Cooperative Tasks and Small Stacks:** Tasks are cooperatively106 scheduled and non-preemptible, yielding only at explicit points (e.g.107 `task-sleep`, `mail-read`, `task-slice`). This guarantee allows tasks to108 operate safely with small, fixed stacks (~8 KB), minimizing footprint109 and enabling high concurrency.110111* **The Iterative Idiom:** Deep recursion on machine stacks is strictly112 prohibited by the small stack size. ChrysaLisp pervasively uses113 **iteration with an explicit heap-allocated `list` as a work stack**114 (used in `lisp :read`, `host_gui :composite`, etc.).115116* **Synergy with O(1) Cache Performance:** Flatter iterative lexical117 scopes keep symbol bindings stable, preventing the churn of deep118 scope chains and maximizing cache hits in the symbol engine.119120* **Dual HMap Tree Duality:** Every object instance is fundamentally an121 `hmap` participating simultaneously in two hierarchies:122123 * The dynamic **Containment Hierarchy** traversed at runtime via the124 `:parent` key for inherited appearance attributes (such as125 `:color` or `:font`).126127 * The static **Class Hierarchy** resolved via the `:vtable` key128 pointing to a compile-time-composed, flattened `hmap` of method129 pointers. Method dispatch (`(. obj :method)`) is an immediate130 two-step O(1) lookup without any runtime inheritance traversal.131132* **Lock-Free, State-Aware Algorithms:** Non-preemption permits safe,133 lock-free data updates:134135 * *Atomic Swap Pattern:* Shared caches (such as `font :flush`)136 prepare changes on a private copy and commit via an atomic pointer137 swap across non-yielding sequences.138139 * *Robust Iterator Pattern:* Iterators like `hmap :each` support140 in-place deletion of the current item via swap-and-pop `erase`,141 intelligently resynchronizing state after callback execution.142143### The Unambiguous, Ephemeral `netid`144145Network identity is based on the tuple `(mailbox_id, node_id)`:146147* **`node_id` (Ephemeral Node Identity):** Generated randomly each time a148 node boots. If a node restarts, its `node_id` changes, preventing149 messages meant for a previous incarnation from ever delivering to the150 new one.151152* **`mailbox_id` (Disposable Mailbox Identity):** A 64-bit monotonically153 increasing counter allocated via `(mail-alloc-mbox)`. **Mailbox IDs are154 never reused.** When `(mail-free-mbox)` is invoked, the ID is permanently155 invalidated; `mail:validate` drops subsequent messages addressed to it.156157* **Practical Pattern:** For any distinct conversation, transaction, or158 request-response cycle, allocate a fresh mailbox. Freeing the mailbox159 instantly drops late-arriving responses, eliminating stale messages,160 sequence-number tracking, and zombie tasks.161162## The Lisp Implementation & Symbol Engine163164ChrysaLisp's interpreter is self-hosted (`class/lisp/`, root environment in165`class/lisp/root.inc`) and built for maximum throughput:166167* **Vector-Based Architecture:** The system entirely dispenses with cons168 cells, `car`, and `cdr`. All Lisp sequences are vectors operated on via169 indexed primitives (`(!)` forms).170171* **No-Layer FFI:** Direct zero-overhead calling between Lisp forms and172 underlying Virtual Processor (VP) machine instructions.173174* **O(1) Symbol Lookup & Single-Bucket HMaps:**175176 * Environments are trees of single-bucket `hmap` objects. Single-bucket177 tables avoid hash division/modulo math and minimize allocation178 costs.179180 * Interned symbol objects carry a cached field: `str_hashslot`.181182 * When a symbol is bound (via `defq` or argument binding), the183 runtime stores the binding and immediately writes the slot index184 into the symbol's `str_hashslot`. Lookups do not perform initial185 linear scans; they execute as direct indexed reads.186187 * If shadowing invalidates a cached slot, `hmap:find` performs a188 one-time linear recovery scan upon exiting the shadow, and189 immediately rewrites `str_hashslot` with the recovered index to190 resume O(1) performance.191192* **Linkerless VP Code Generation:** VP compilation emits symbolic193 dependency paths in a links section. The `boot-image` tool resolves194 these to relative offsets, and `sys/load/init` performs runtime195 rebinding to absolute addresses in fractions of a second.196197## Core Lisp Disciplines (Must Follow)198199* **Tab Style:** Always use leading 4-space tab characters for indentation200 in source code and documentation, with spaces afterwards if needed.201202* **Type-Dependent Equality with `eql`:**203204 * `eql` performs deep content comparison on scalar numbers, strings,205 and typed numeric vectors (`nums`, `fixeds`, `reals`).206207 * `eql` performs pointer identity comparison on general containers208 (`list`, `hmap`). To test list content equivalence, use:209210 (and (= (length l1) (length l2)) (every eql l1 l2))211212* **No Lexical Closures:** `lambda` forms are pure code templates that213 execute strictly inside the environment of their caller. They do not214 capture defining scopes. All context must be supplied explicitly via215 arguments or pre-exist in the caller's environment.216217* **Static `'()` vs Independent `(list)`:** `'()` evaluates to a shared,218 static empty list instance. Mutating `'()` (e.g. via `push`) corrupts219 every reference in the system. Always use `(list)` to create a fresh,220 independent, mutable empty list.221222* **Control Flow and Error Prevention:** There is no `return` keyword;223 a function yields the result of its last evaluated expression.224 Non-local exits (`catch`, `throw`) are strictly for debugging. Code must225 prioritize input validation to prevent invalid states from ever226 occurring, rather than catching errors after the fact.227228* **Lists as LIFO Stacks:**229230 * `(push list elem)` appends to the end of `list`.231232 * `(pop list)` removes and returns the final element.233234 * `(first list)` is `(elem-get list 0)`.235236 * `(last list)` is `(elem-get list -2)`.237238 * `(slice list 1 -2)` returns the list minus first and last items.239240* **Pragmatic Lambda Usage (`#` vs `lambda`):**241242 * Use `(# ...)` for compact, performance-critical callbacks. Positional243 symbols `%0`, `%1`, etc., are globally interned with permanent244 `str_hashslot` cache indices.245246 * Use `(lambda ...)` when destructuring arguments or when explicit247 naming clarifies complex logic.248249 * **NEVER** use `(bind ...)` inside an anaphoric `#` lambda250 (anti-pattern: `(# (bind '(k v) %0) ...)`). Doing so introduces251 local symbols that destroy the cache benefits of `%0` while adding252 `bind` overhead. Use `(lambda ((k v)) ...)` instead.253254* **Variable Binding and Shadowing:**255256 * **Variable Naming (Snake_case Only):** Variables, arguments, and257 parameters must ALWAYS use snake_case with underscores (`_`), never258 hyphens (`-`). Hyphenated/kebab-case symbols are strictly reserved259 for callable forms (functions and macros).260261 * Do not nest `let` or `let*`. Use `defq` to declare and bind multiple262 variables simultaneously in the current scope:263264 (defq a 1 b 2 c (list))265266 * Never use built-in function names as variables (e.g. `path`, `str`).267 Functions and variables share the same symbol environment.268269 * Use `bind` for destructuring sequences and return tuples:270271 (bind '(w h) (. view :get_size))272 (bind '(x y &rest tail) my_list)273 (bind '(x y &ignore) my_list)274 (bind '((x0 x1 &ignore) (y0 y1 &ignore) &ignore) nested_list)275276* **Object Syntax:**277278 * Method call: `(. obj :method arg1 arg2)`.279280 * Chained method call returning `this`: `.->` macro:281282 (.-> *canvas* (:set_color +argb_black) (:fill 0))283284 * Property access: `(get :prop obj)`.285286 * Property create or mutate: `(def obj :prop val)`.287288 * Property mutate only: `(set obj :prop val)`.289290* **Anaphoric Loop Index `(!)`:** In `each`, `each!`, `map`, and `lines!`,291 the form `(!)` evaluates to the current zero-based loop index:292293 (each (# (print "Item " (!) ": " %0)) my_list)294295* **Compile-Time Constants:** Force compile-time arithmetic or lookups296 using `(const ...)`:297298 (* x (const (/ 180.0 +fp_pi)))299300* **GUI Application Event Loop Pattern:**301302 (defun main ()303 (defq select (task-mboxes +select_size) *running* :t)304 ; ... setup widgets / window ...305 (gui-add-front-rpc *window*)306 (while *running*307 (defq msg (mail-read (elem-get select (defq idx (mail-select select)))))308 (cond309 ((= idx +select_main)310 (if (= (getf msg +ev_msg_target_id) +event_close)311 (setq *running* :nil)312 (. *window* :event msg)))313 (:t (. *window* :event msg))))314 (gui-sub-rpc *window*))315316* **Short-Circuiting, Embedded Binding, and Branching (`and` / `or`):**317318 * `and` expands to `condn` (testing for `:nil`), and `or` expands to319 `cond`. Write predicates for zero wasted interpreter evaluations.320321 * *Embed `defq` Inside Consumer Predicates:* Do not write standalone322 `(defq ...)` clauses in an `and` chain. Bind variables directly323 inside the comparison that uses them:324325 (eql (third %1) (defq d (third %0)))326327 * *Order Clauses for Fastest Failure:* Place the most discriminative328 test earliest to lazily abort execution before further bindings.329330 * *Leverage `cond` Branch Conditions:* Put `(and ...)` directly as331 the `cond` branch condition; do not wrap the body in `(when ...)`.332333 * *Use `when` Naturally for Multi-Statement Blocks:* Inside `case` or334 unconditional blocks, use `(when tst a1 a2)` directly.335336 * *Anti-Pattern:*337338 (cond339 ((eql op0 'emit-cpy-rr)340 (when (and (defq cpy_info (pfind +map (first %1)))341 (defq d (third %0))342 (eql (third %1) d)343 (defq s (second %0))344 (nql s d))345 (case (first cpy_info)346 (:cr347 (elem-set emit_list (!) (list (second cpy_info) (second %1) s d))348 (elem-set emit_list (inc (!)) '(emit-nop)))))))349350 * *Correct Pattern:*351352 (cond353 ((and (eql op0 'emit-cpy-rr)354 (defq cpy_info (pfind +map (first %1)))355 (eql (third %1) (defq d (third %0)))356 (nql d :rsp)357 (nql (defq s (second %0)) d)358 (nql s :rsp))359 (case (first cpy_info)360 (:cr361 (elem-set emit_list (!) (list (second cpy_info) (second %1) s d))362 (elem-set emit_list (inc (!)) '(emit-nop))))))363364## Virtual Processor (VP) Assembler & CScript Guide365366When writing or modifying `.vp` files, you target ChrysaLisp's register and367execution model.368369### Register Architecture370371* 15 General-Purpose registers: `:r0` through `:r14`.372373* 1 Dedicated Stack Pointer: `:rsp`.374375* 16 Floating-Point registers: `:f0` through `:f15`.376377* **ABSOLUTE VOLATILITY (No Callee-Saved Registers):** ChrysaLisp has NO378 callee-saved registers. Any call may trash registers. The `;trashes`379 header above each function is the sole source of truth.380381### VP Method Definition Boilerplate382383Every method must follow standard boundary and scoping conventions:384385(def-method :class :method)386 ;inputs387 ;:r0 = this (ptr)388 ;:r1 = arg (num)389 ;outputs390 ;:r0 = result (num)391 ;trashes392 ;:r1-:r3393394 (vp-rdef (this arg res))395396 (entry :class :method '(:r0 :r1))397398 ; ... method body ...399400 (exit :class :method '(:r0))401 (vp-ret)402403(def-func-end)404405### The `assign` Macro406407`(assign ...)` evaluates expressions, moves data, and loads/stores memory408simultaneously:409410(assign '((:r0 +str_length)) '(:r1))411412### CScript Integration & Memory Rules413414CScript provides high-level typed variables and pointer expressions within415`{...}` blocks:416417* **Typed Declarations:** Define stack variables with `(def-vars ...)`.418 Supported types include `ptr`, `pubyte`, `long`, `uint`, etc.419 Dereferencing `{*src_ptr}` generates appropriate byte or word transfers420 based on variable type.421422* **CScript Scope Discipline (CRITICAL):**423424 * Open scope with `(push-scope)` at function start.425426 * **NEVER call `(pop-scope)` before `(return)` or `(jump)`**. Both427 macros automatically emit scope unwinding instructions. Calling428 `pop-scope` manually creates a double-free of the stack frame.429430 * Place `(pop-scope-syms)` at the end of the `def-func` block (after431 the `errorcase` and `signature` sections) to cleanly purge compiler432 tracking.433434* **CScript Stack Packing & Unions:**435436 * Order variables in `def-vars` from largest (`long`, `ptr`, `netid`)437 to smallest (`uint`, `ubyte`) to prevent alignment padding waste.438439 * Use `(union ...)` to share stack space between variables with440 mutually exclusive lifetimes. Do not create redundant variables441 merely to avoid type casts.442443* **Assignment Fusion & Hazards:**444445 * Fuse assignments to minimize temporaries: `(assign {a, b} {x, y})`.446447 * **Evaluation Order:** Sources compile left-to-right (pushed to448 value stack); destinations compile right-to-left (popped from value449 stack).450451 * **Mutation Hazard:** Never mutate a variable in a fused assignment452 if that variable calculates the address of a destination further to453 the left (e.g. `(assign {val, p + 1} {p[0], p})` is invalid). Split454 such updates into sequential `assign` statements.455456### Raw VP Loop Optimization (Bypassing CScript in Hot Paths)457458When CScript's value stack overhead is unacceptable in inner loops, drop to459pure VP assembler:4604611. Declare raw registers: `(vp-rdef (r_buf r_mask r_i))`.4624632. Extract CScript variables into registers with mixed `assign`:464 `(assign {buf, mask, ptr_i} `(,r_buf ,r_mask ,r_i))`.4654663. Hand-code the loop using `loop-start`, `loop-until`, `breakif`, and467 raw instructions (`vp-cpy-dr-ub`, `vp-add-cr`, etc.).4684694. Restore results to CScript variables: `(assign `(,r_i) {ptr_i})`.4704715. If a call inside a loop trashes registers needed for loop control,472 keep loop counters in registers outside the call's `;trashes` set, or473 advance loop counters *before* the call, or spill persistent registers474 (`this`, `args`) to stack slots.475476### Mixed VP Assembler & CScript Syntax Duality477478* **String Literal Equivalence:** Curly braces `{...}` and double quotes479 `"..."` are identical string constructors in ChrysaLisp. `{...}` is480 favored for CScript code to allow embedded quotes (`\q`) without escapes.481482* **Three Symbol Contexts:**483484 * *Raw Symbols* (`(vp-cpy-ir adr 80 cnt)`): Resolve directly to VP485 registers (`:r0` - `:r14`).486487 * *CScript Strings* (`{adr, cnt}`): Resolve to stack frame offsets488 (`[rsp + offset]`).489490 * *Quasi-Quoted Lists* (`` `(,adr ,cnt) ``): Commas explicitly evaluate491 register symbols inside macros.492493* **Bridging Pattern:**494495 ; 1. Define CScript stack variables496 (def-vars497 (pubyte buf)498 (long pos))499500 ; 2. Define VP registers with matching names501 (vp-rdef (buf pos))502503 ; 3. Extract inputs into VP registers504 (list-bind-args args `(,buf ,pos) '(:obj :num))505506 ; 4. Map VP registers to CScript variables507 (assign `(,buf ,pos) {buf, pos})508509### Calling Conventions & Register Introspection510511* Inspect register contracts dynamically at compile time:512513 * `(method-input :class :method)`: Returns expected input registers.514515 * `(method-output :class :method)`: Returns output registers.516517* **Dynamic Call Mapping:** Pass `(method-input ...)` directly to `vp-rdef`518 to bind argument aliases without hardcoding volatile registers:519520 (vp-rdef (cp_src cp_dst cp_len) (method-input :sys_mem :copy_to_ring))521522 Assign loop state into these argument aliases immediately before the call.523 If the call trashes registers used for loop progression, keep counters in524 registers outside the call's `;trashes` set, or advance them before the call.525526* Preserve live state across calls via explicit `(vp-push reg)` /527 `(vp-pop reg)` or by saving to CScript stack slots.528529### Systematic VP Lowering Workflow530531Follow this 7-step discipline when designing VP routines:5325331. **Draft the Algorithm:** Specify control flow and data transitions.5345352. **Identify Call Boundaries:** Enumerate every sub-call in the logic.5365373. **Inspect Call Contracts & Trashed Sets:** Review `;trashes` and538 `(method-input ...)` for each dependency.5395404. **Evaluate Stack Spill Necessity:** Check if persistent state can fit541 in registers above the trashed set, avoiding stack manipulation.5425435. **Map Variable Lifespans:** Group variables into persistent (must544 survive calls) and ephemeral (local to call intervals).5455466. **Formulate Compact `vp-rdef` Mappings:** Assign persistent state to547 safe registers and reuse volatile low registers across intervals.5485497. **CScript vs. Pure VP Selection:** Use pure VP when state fits550 entirely in registers; use CScript when named stack slots are required.551552## Numerical Representations & Systems553554ChrysaLisp natively supports three numerical types:555556* **Integers (`num`):** 64-bit signed integers.557558* **Fixed-Point Reals (`fixed`, `fixeds`):** 48.16 signed fixed-point559 values (64-bit word with 16 fractional bits). Uses `+fp_frac_mask` and560 `+fp_shift 16`. Trigonometric and transcendental math operate on fixed.561562* **Double-Precision Reals (`real`, `reals`):** Standard 64-bit IEEE 754563 double-precision floating-point values.564565* **Conversions:**566567 * `(n2i x)`: Converts `fixed` or `real` to integer `num`.568569 * `(n2f x)`: Converts `num` or `real` to 48.16 `fixed`.570571 * `(n2r x)`: Converts `num` or `fixed` to IEEE double `real`.572573## Subsystems & Architecture Reference574575### Shared Memory (SHMEM) Link Protocol576577Independent ChrysaLisp instances communicate over shared memory using578`sys_link` ring buffers:579580* **Negotiation Towel:** Nodes race to write their `node_id` into the581 `host_a` field of `chan_1`. After `(task-sleep 100)`, the surviving ID582 becomes the owner (transmits on `chan_1`, receives on `chan_2`). The583 other node writes to `host_b` (transmits on `chan_2`, receives on584 `chan_1`).585586* **Buffer Slot Statuses:** `lk_chan_status_ready` (free),587 `lk_chan_status_ping` (routing heartbeat), `lk_chan_status_frag`588 (message data fragment), and `lk_chan_status_skip` (buffer wrap marker).589590### Distributed JIT Compilation Pipeline591592Dynamic VP compilation (`lisp.vp`) is protected by network locking:593594(lock-claim-rpc obj_prefix)595(when (some (# (> file_age (age (cat obj_prefix %0)))) products)596 (catch (within-compile-env (# (include file))) :nil))597(lock-release-rpc obj_prefix)598599### Two-Pass GUI Layout System600601GUI rendering executes in two deterministic passes:6026031. **Constraint Pass (`:constraint`):** Traverses top-down to compute604 minimum bounding dimensions based on content.6056062. **Layout Pass (`:layout`):** Traverses bottom-up to assign final607 coordinates and bounds to widgets.608609* Flags like `+flow_stack_fill` and `+flow_down_fill` use `lastw` and610 `lasth` to absorb remaining container dimensions.611612## File & Naming Conventions613614* `.lisp`: Executable ChrysaLisp programs.615616* `.inc`: Library files included via `(import ...)`.617618* `.vp`: Virtual Processor assembly source.619620* `.tre`: Application configuration and state trees.621622* `class.inc`: VP assembler include definitions (`(include ...)`).623624* `class.vp`: VP assembler method implementations.625626* `lisp.inc`: Lisp FFI bindings and Lisp versions of VP structures.627628* `lisp.vp`: VP implementations of `:lisp_xxx` static primitives.629630* **Identifier Naming Rules:**631632 * **Functions and Macros:** Kebab-case with hyphens (`foo-bar`). ONLY633 callable code (functions and macros) may use kebab style.634635 * **Variables and Parameters:** Snake_case with underscores (`foo_bar`).636 NEVER use hyphens in variable or parameter names.637638 * **Constants:** Plus prefix (`+foo_bar`).639640 * **Globals:** Asterisk-wrapped (`*foo_bar*`).641642 * **Properties and Keywords:** Colon prefix (`:foo_bar`).643644## Output Directives (Mandatory)645646* Do not emit entire unmodified source files. Provide focused diffs or647 concise cut-and-paste snippets indicating exact locations.648649* Always use 4-space tab indentation in ChrysaLisp source code and650 documentation.651652* Always place a blank line between all documentation elements in `.md`653 files, including between individual bullet points and sub-bullets.654655* Always wrap ChrysaLisp `.md` documentation at 80 columns (do not wrap656 source code blocks).