9.7 KiB
ƿit (pit) Language Project
Building
Build (or rebuild after changes): make
Install to system: make install
Run cell --help to see all CLI flags.
Code Style
All code uses 2 spaces for indentation. K&R style for C and Javascript.
ƿit Script Quick Reference
ƿit script files: .ce (actors) and .cm (modules). The syntax is similar to JavaScript with important differences listed below.
Key Differences from JavaScript
var(mutable) anddef(constant) — noletorconst==and!=are strict (no===or!==)- No
undefined— onlynull - No classes — only objects and prototypes (
meme(),proto(),isa()) - No
switch/case— use record dispatch (a record keyed by case, values are functions or results) instead of if/else chains - No
for...in,for...of, spread (...), rest params, or default params - Functions have a maximum of 4 parameters — use a record for more
- Variables must be declared at function body level only (not in if/while/for/blocks)
- All variables must be initialized at declaration (
var xalone is an error; usevar x = null) - No
try/catch/throw— usedisrupt/disruption - No arraybuffers — only
blob(works with bits; muststone(blob)before reading) - Identifiers can contain
?and!(e.g.,nil?,set!,is?valid) - Prefer backticks for string interpolation; otherwise use
text()to convert non-strings - Everything should be lowercase
Intrinsic Functions (always available, no use() needed)
The creator functions are polymorphic — behavior depends on argument types:
-
array(number)— create array of size N filled with null -
array(number, value_or_fn)— create array with initial values -
array(array)— copy array -
array(array, fn)— map -
array(array, array)— concatenate -
array(array, from, to)— slice -
array(record)— get keys as array of text -
array(text)— split text into individual characters (e.g.,array("hello")→["h","e","l","l","o"]) -
array(text, separator)— split by separator -
array(text, length)— split into chunks of length -
text(array, separator)— join array into text -
text(number)ortext(number, radix)— number to text -
text(text, from, to)— substring -
number(text)ornumber(text, radix)— parse text to number -
number(logical)— boolean to number -
record(record)— copy -
record(record, another)— merge -
record(array_of_keys)— create record from keys
Other key intrinsics: length(), stone(), is_stone(), print(), filter(), find(), reduce(), sort(), reverse(), some(), every(), starts_with(), ends_with(), meme(), proto(), isa(), splat(), apply(), extract(), replace(), search(), format(), lower(), upper(), trim()
Sensory functions: is_array(), is_text(), is_number(), is_object(), is_function(), is_null(), is_logical(), is_integer(), is_stone(), etc.
Standard Library (loaded with use())
blob— binary data (bits, not bytes)time— time constants and conversionsmath— trig, logarithms, roots (math/radians,math/turns)json— JSON encoding/decodingrandom— random number generation
Actor Model
.cefiles are actors (independent execution units, don't return values).cmfiles are modules (return a value, cached and frozen)- Actors never share memory; communicate via
$send()message passing - Actor intrinsics start with
$:$me,$stop(),$send(),$start(),$delay(),$receiver(),$clock(),$portal(),$contact(),$couple(),$unneeded(),$connection(),$time_limit()
Requestors (async composition)
sequence(), parallel(), race(), fallback() — compose asynchronous operations. See docs/requestors.md.
Error Handling
var fn = function() {
disrupt // bare keyword, no value
} disruption {
// handle error; can re-raise with disrupt
}
Push/Pop Syntax
var a = [1, 2]
a[] = 3 // push: [1, 2, 3]
var v = a[] // pop: v is 3, a is [1, 2]
C Integration
- Declare everything
staticthat can be - Most files don't have headers; files in a package are not shared between packages
- No undefined in C API: use
JS_IsNullandJS_NULLonly - A C file with correct macros (
CELL_USE_FUNCSetc) is loaded as a module by its name (e.g.,png.cin a package →use('<package>/png')) - C symbol naming:
js_<pkg>_<file>_use(e.g.,js_core_math_radians_useforcore/math/radians) - Core is the
corepackage — its symbols follow the samejs_core_<name>_usepattern as all other packages - Package directories should contain only source files (no
.mach/.mcodealongside source) - Build cache files in
build/are bare hashes (no extensions)
MANDATORY: GC Rooting for C Functions
This project uses a copying garbage collector. ANY JS allocation (JS_NewObject, JS_NewString, JS_NewArray, JS_NewInt32, JS_SetPropertyStr, js_new_blob_stoned_copy, etc.) can trigger GC, which invalidates all unrooted JSValue locals. This is not theoretical — it causes real crashes.
Before writing or modifying ANY C function, apply this checklist:
- Count the number of
JS_New*,JS_SetProperty*, andjs_new_blob*calls in the function - If there are 2 or more, the function MUST use
JS_FRAME/JS_ROOT/JS_RETURN - Every JSValue that is held across an allocating call must be rooted
Pattern — object with properties:
JS_FRAME(js);
JS_ROOT(obj, JS_NewObject(js));
JS_SetPropertyStr(js, obj.val, "x", JS_NewInt32(js, 42));
JS_SetPropertyStr(js, obj.val, "name", JS_NewString(js, "hello"));
JS_RETURN(obj.val);
Pattern — array with loop:
JS_FRAME(js);
JS_ROOT(arr, JS_NewArray(js));
for (int i = 0; i < count; i++) {
JS_ROOT(item, JS_NewObject(js));
JS_SetPropertyStr(js, item.val, "v", JS_NewInt32(js, i));
JS_SetPropertyNumber(js, arr.val, i, item.val);
}
JS_RETURN(arr.val);
Rules:
- Access rooted values via
.val(e.g.,obj.val, notobj) - Error returns before
JS_FRAMEuse plainreturn - Error returns after
JS_FRAMEmust useJS_RETURN_EX()orJS_RETURN_NULL() - When calling a helper that itself returns a JSValue, that return value is safe to pass directly into
JS_SetPropertyStr— no need to root temporaries that aren't stored in a local
Common mistake — UNSAFE (will crash under GC pressure):
JSValue obj = JS_NewObject(js); // NOT rooted
JS_SetPropertyStr(js, obj, "pixels", js_new_blob_stoned_copy(js, data, len));
// ^^^ blob allocation can GC, invalidating obj
return obj; // obj may be a dangling pointer
See docs/c-modules.md for the full GC safety reference.
Project Layout
source/— C source for the cell runtime and CLIdocs/— master documentation (Markdown), reflected on the websitewebsite/— Hugo site; theme atwebsite/themes/knr/internal/— internal ƿit scripts (engine.cm etc.)packages/— core packagesMakefile— build system (maketo rebuild,make bootstrapfor first build)
Package Management (Shop CLI)
When running locally with ./cell --dev, these commands manage packages:
./cell --dev add <path> # add a package (local path or remote)
./cell --dev remove <path> # remove a package (cleans lock, symlink, dylibs)
./cell --dev build <path> # build C modules for a package
./cell --dev test package <path> # run tests for a package
./cell --dev list # list installed packages
Local paths are symlinked into .cell/packages/. The build step compiles C files to .cell/lib/<pkg>/<stem>.dylib. C files in src/ are support files linked into module dylibs, not standalone modules.
Debugging Compiler Issues
When investigating bugs in compiled output (wrong values, missing operations, incorrect comparisons), start from the optimizer down, not the VM up. The compiler inspection tools will usually identify the problem faster than adding C-level tracing:
./cell --dev streamline --types <file> # show inferred slot types — look for wrong types
./cell --dev ir_report --events <file> # show every optimization applied and why
./cell --dev ir_report --types <file> # show type inference results per function
./cell --dev mcode --pretty <file> # show raw IR before optimization
./cell --dev streamline --ir <file> # show human-readable optimized IR
Triage order:
streamline --types— are slot types correct? Wrong type inference causes wrong optimizations.ir_report --events— are type checks being incorrectly eliminated? Look forknown_type_eliminates_guardon slots that shouldn't have known types.mcode --pretty— is the raw IR correct before optimization? If so, the bug is in streamline.- Only dig into
source/mach.cif the IR looks correct at all levels.
See docs/compiler-tools.md for the full tool reference and docs/spec/streamline.md for pass details.
Testing
After any C runtime changes, run all three test suites before considering the work done:
make # rebuild
./cell --dev vm_suite # VM-level tests (641 tests)
./cell --dev test suite # language-level tests (493 tests)
./cell --dev fuzz # fuzzer (100 iterations)
All three must pass with 0 failures.
Documentation
The docs/ folder is the single source of truth. The website at website/ mounts it via Hugo. Key files:
docs/language.md— language syntax referencedocs/functions.md— all built-in intrinsic functionsdocs/actors.md— actor model and actor intrinsicsdocs/requestors.md— async requestor patterndocs/library/*.md— intrinsic type reference (text, number, array, object) and standard library modules