pub enum Node {
Show 70 variants
Lit(i64),
FloatLit(u64),
FloatOp {
op: BinOp,
lhs: NodeHash,
rhs: NodeHash,
},
IntToFloat(NodeHash),
FloatToInt(NodeHash),
DecimalLit(i64),
DecimalOp {
op: BinOp,
lhs: NodeHash,
rhs: NodeHash,
},
IntToDecimal(NodeHash),
DecimalToInt(NodeHash),
DecimalRaw(NodeHash),
Bool(bool),
Not(NodeHash),
Str(String),
StrLen(NodeHash),
StrLower(NodeHash),
StrFromCode(NodeHash),
StrConcat(NodeHash, NodeHash),
StrSlice {
s: NodeHash,
start: NodeHash,
len: NodeHash,
},
StrEq(NodeHash, NodeHash),
StrContains {
haystack: NodeHash,
needle: NodeHash,
},
StrStartsWith {
s: NodeHash,
prefix: NodeHash,
},
StrIndexOf {
haystack: NodeHash,
needle: NodeHash,
},
NumberToStr(NodeHash),
StrToNumber(NodeHash),
StrToNumberOpt(NodeHash),
Now,
List(Vec<NodeHash>),
ListEmpty {
elem: Type,
},
ListCons {
head: NodeHash,
tail: NodeHash,
},
OptionSome(NodeHash),
OptionNone {
elem: Type,
},
OptionElse {
opt: NodeHash,
default: NodeHash,
},
OptionMatch {
opt: NodeHash,
some_bind: String,
some_body: NodeHash,
none_body: NodeHash,
},
ListTryGet {
list: NodeHash,
index: NodeHash,
},
ListLen(NodeHash),
ListGet {
list: NodeHash,
index: NodeHash,
},
Map(Vec<(NodeHash, NodeHash)>),
MapGet {
map: NodeHash,
key: NodeHash,
},
MapTryGet {
map: NodeHash,
key: NodeHash,
},
MapLen(NodeHash),
Log(NodeHash),
Publish(NodeHash),
SetHeader {
name: NodeHash,
value: NodeHash,
},
Rand,
MutNew(NodeHash),
MutGet(NodeHash),
MutSet {
cell: NodeHash,
value: NodeHash,
},
DiskWrite {
path: NodeHash,
content: NodeHash,
},
DiskRead(NodeHash),
NetGet(NodeHash),
DbQuery {
sql: NodeHash,
params: NodeHash,
},
Ref(String),
Call {
func: String,
args: Vec<NodeHash>,
},
FuncRef(String),
CallValue {
callee: NodeHash,
args: Vec<NodeHash>,
},
Lambda {
params: Vec<Param>,
body: NodeHash,
},
Hole {
expects: String,
},
BinOp {
op: BinOp,
lhs: NodeHash,
rhs: NodeHash,
},
Fail(String),
Handle {
body: NodeHash,
handlers: Vec<(String, NodeHash)>,
},
If {
cond: NodeHash,
then_branch: NodeHash,
else_branch: NodeHash,
},
Step {
binding: String,
value: NodeHash,
},
Function {
name: String,
type_params: Vec<String>,
params: Vec<Param>,
produces: Produces,
requires: BTreeSet<Effect>,
on_failure: Vec<String>,
body: Vec<NodeHash>,
result: NodeHash,
},
Module {
name: String,
types: Vec<NodeHash>,
functions: Vec<NodeHash>,
},
RecordDef {
name: String,
fields: Vec<(String, Type)>,
},
Record {
type_name: String,
fields: Vec<(String, NodeHash)>,
},
Field {
base: NodeHash,
type_name: String,
field: String,
},
VariantDef {
name: String,
cases: Vec<(String, Vec<(String, Type)>)>,
},
Variant {
type_name: String,
case: String,
fields: Vec<(String, NodeHash)>,
},
Match {
scrutinee: NodeHash,
type_name: String,
arms: Vec<MatchArm>,
},
}Expand description
A stored AST node. Compound variants reference children by NodeHash.
Variants§
Lit(i64)
An integer literal. (A single unified numeric type is the language direction; the seed model only needs a concrete numeric leaf.)
FloatLit(u64)
An IEEE-754 double, stored as its bit pattern so the node stays
Eq/hashable and content-addresses deterministically. The uniform
i64 slot is these bits; only FloatOp/conversions reinterpret.
FloatOp
Arithmetic or comparison on two Floats. op is restricted to
+ - * / and == < <= > >= (no % != && || on Float).
IntToFloat(NodeHash)
Number → Float.
FloatToInt(NodeHash)
Float → Number: truncates toward zero; traps on NaN/overflow
(a clean trap, not silent UB — like the bounds checks).
DecimalLit(i64)
A Decimal literal: the value pre-scaled by 10_000 (so 1.25 is
12500). Exact; no rounding at use.
DecimalOp
Arithmetic or comparison on two Decimals. + - are plain i64;
* is (a*b)/10000, / is (a*10000)/b (rescaling). All six
comparisons are exact. % && || are rejected by the checker.
IntToDecimal(NodeHash)
Number → Decimal (multiplies by 10_000).
DecimalToInt(NodeHash)
Decimal → Number (integer part; truncates toward zero).
DecimalRaw(NodeHash)
Decimal → Number: the raw scaled mantissa (the value ×10_000,
Decimal’s documented representation). Identity at runtime; the
seam that lets decimal_to_str be written in Cairn.
Bool(bool)
A boolean literal. Bool was previously only producible by a
comparison; the literal makes Bool a first-class value the
logical operators can stand on.
Not(NodeHash)
Logical negation of a Bool, yielding Bool. The one unary
operator (Principle 9); &&/|| are the binary BinOps.
Str(String)
A UTF-8 string literal.
StrLen(NodeHash)
Length (in bytes) of a string. The first string operation; more follow with the stdlib.
StrLower(NodeHash)
ASCII-lowercase a string (A–Z → a–z, other bytes
unchanged). The minimal primitive for case-insensitive
matching — needed because some platforms (e.g. Cloudflare)
lowercase HTTP header names, so the header/cookie lookup
cannot be case-sensitive (design.md §9).
StrFromCode(NodeHash)
A one-byte string from a code point 0–255 (the low byte of the
Number; the inverse of indexing a byte out). The minimal
primitive for percent-decoding — added when a real HTML form
(the shipped blog’s create) forced form_value to URL-decode
%XX/+, which is otherwise inexpressible in pure Cairn
(design.md §9; Principle 10 — a real need, not speculation).
StrConcat(NodeHash, NodeHash)
a ++ b — concatenate two strings.
StrSlice
Byte substring s[start .. start+len]. Bounds-checked: a
negative index/length or start+len > len(s) traps (not silent
UB), proven by out_of_bounds_list_get_and_str_slice_trap.
Byte-indexed by design (decided, not a gap): a Cairn String
is byte-addressable for predictable, AI-author-friendly indexing;
codepoint-aware slicing is a stdlib concern, not a core operator
(Principle 9). Slicing across a UTF-8 boundary therefore yields
bytes the host renders lossily — the documented, intended
semantics of a byte slice.
StrEq(NodeHash, NodeHash)
Content equality of two strings, yielding Bool.
StrContains
Whether needle occurs in haystack, yielding Bool.
StrStartsWith
Whether s begins with prefix, yielding Bool. With
StrSlice/StrLen this is enough for prefix routing and
path-parameter extraction (e.g. /customers/{id}).
StrIndexOf
0-based byte index of the first occurrence of needle in
haystack, or -1 if absent (an empty needle is at 0). Yields
Number. With StrSlice this is enough to split a delimited
string — e.g. parsing db_query rows into typed records.
NumberToStr(NodeHash)
Decimal rendering of a Number as a String (e.g. for HTML
output). Correct over the full i64 range, including i64::MIN
(rendered in the non-positive domain — no magnitude overflow).
StrToNumber(NodeHash)
Parse a String to a Number, leniently: an optional leading -
then the leading decimal digits; a non-numeric/empty string yields
0. The unchecked fast path (cf. StrToNumberOpt).
StrToNumberOpt(NodeHash)
Checked parse: Some(n) only if the whole string is a valid
integer (optional - then ≥1 digits, nothing else), else None.
The handle-able counterpart to StrToNumber, paralleling
ListGet/ListTryGet.
Now
Read the clock (milliseconds). The first effectful primitive: it
performs the Time effect and yields a Number at external
confidence (the value comes from outside the program). Lowers to a
host import; other effects follow the same pattern.
List(Vec<NodeHash>)
A non-empty list literal. Elements share a type T; the list’s type
is List<T>. (Empty literals need a type annotation — unsupported in
v0.3.)
ListEmpty
The empty List<elem>. The typed counterpart to a List literal,
needed because the element type can’t be inferred with no elements.
With ListCons this lets a recursive function build a list of
runtime length.
ListCons
Prepend head to tail (a List<T>), yielding a new List<T>.
v0.3: an O(n) fresh-array copy — immutable, no structural sharing,
consistent with the bump allocator’s documented simplicity.
OptionSome(NodeHash)
Some(value) — a present Option<T> where T is the value’s
type. The typed result for operations that may have no answer.
OptionNone
None : Option<elem> — the absent case (element type can’t be
inferred with no value, like ListEmpty).
OptionElse
Eliminate an Option<T>: the contained value if Some, else
default. Both arms share T, the expression’s type. This is the
noise-free recovery path — no on_failure threading.
OptionMatch
Case analysis over an Option<T>: bind the payload to
some_bind in some_body, else evaluate none_body. Both bodies
share a type (the expression’s type). Unlike OptionElse (value
or default) this runs different logic per case — the construct
that makes a returned Option usable for control flow.
ListTryGet
Checked indexing: Some(elem) if index is in bounds, else
None. The handle-able counterpart to ListGet (which traps);
neither pollutes signatures with a failure.
ListLen(NodeHash)
Number of elements in a list.
ListGet
Element of a list at a (0-based Number) index. No runtime bounds
check in v0.3 (a known simplification, like the bump allocator).
Map(Vec<(NodeHash, NodeHash)>)
A non-empty map literal of key/value pairs. Keys share a type K,
values a type V; the type is Map<K, V>.
MapGet
Value for a key (linear scan). Key equality is i64/identity —
exact for Number keys, which is Map’s decided domain
(Principle 9: one canonical form). A missing key yields 0;
MapTryGet is the handle-able form. Structural keying of any
type (incl. String) is not a second mechanism here — it is
find over a List of key/value records (stdlib, first-class
functions), which is structural for any K. Decided, not
deferred: adding key-kind dispatch to Map would be the
speculative generality Principle 10 forbids when find already
covers it (see string_keyed_lookup_is_find_over_a_list).
MapTryGet
Checked lookup: Some(V) for the first matching key, else
None — the handle-able counterpart to MapGet (which yields 0
on a miss, indistinguishable from a real 0). Same decided
Number/identity key domain as MapGet.
MapLen(NodeHash)
Number of entries in a map.
Log(NodeHash)
Observe a value for diagnostics (the Log effect), passing it
through unchanged (same type and confidence as the argument). Lowers
to a host::log import.
Publish(NodeHash)
Announce that topic (a String) changed — the Live effect
(design.md §10). The live runtime re-renders and pushes to every
connection subscribed to that topic. Yields Number (0). Lowers
to a host::publish import. Liveness is this, explicit and
effect-typed — never an implicit default.
SetHeader
Emit an extra HTTP response header — the Resp effect. name
and value are Strings (e.g. "Set-Cookie",
"sid=abc; HttpOnly; Path=/"); the host buffers it per request
and writes it after the standard headers. Yields Number (0),
so it sequences in a step like publish. Lowers to a
host::set_header import. Response headers are this, explicit
and effect-typed — not a hidden Response field.
Rand
Draw a random Number (the Rand effect), at external confidence
(it comes from outside the program). Lowers to a host::rand import.
MutNew(NodeHash)
Allocate a mutable cell holding value (the Mut effect). Type is
Cell<T> where T is the value’s type.
MutGet(NodeHash)
Read a mutable cell’s current value.
MutSet
Write value into a cell (the Mut effect); passes value through.
DiskWrite
Write a String to a path (the Disk effect); returns the number of
bytes written.
DiskRead(NodeHash)
Read the file at a String path (the Disk effect), returning its
contents as a String (host→wasm allocation). A missing file yields
the empty string in v0.3.
NetGet(NodeHash)
HTTP(S) GET a String URL (the Net effect), returning the
HTTP status as a Number (-1 on transport error). Backed by a
real ureq + TLS client — no stub path exists (proven by
real_net_get_returns_the_http_status).
DbQuery
Run a SQL statement (the Db effect), returning the result as a
String at persisted confidence (read back from the system of
record). params is a List<String> bound to the statement’s ?
placeholders by the driver — the only query form, so user input
never reaches SQL by concatenation (use an empty list for static
SQL). Backed by real embedded SQLite only (rusqlite —
in-memory by default, a file when a path is given). There is no
canned/stub path: every query hits the engine (wasm::run_sql;
the v0.3 removal of the old canned path is complete).
Ref(String)
A reference to an in-scope binding, by name.
Call
A call: a callee function name plus argument nodes.
FuncRef(String)
A named module function used as a first-class value. Its type is the
function’s signature as a Fn. Anonymous lambdas with transitive
closure capture are shipped (K2 — see Lambda/CallValue; the
stdlib map/filter/fold and every app pass closures). FuncRef
is the named-function value form; Lambda is the anonymous/closure
form — both lower through the funcref table + call_indirect.
CallValue
Apply a function value to arguments. callee evaluates to a
Fn; args are checked against its parameter types and its
effects union into the caller. Lowers to call_indirect.
Lambda
An anonymous closure. params are its inputs (typed, like a
function’s); body is a single result expression. Names it
references that are neither its params nor module functions are
captured by value from the enclosing scope at creation. Its type
is a Fn; its body’s effects live in that Fn (a closure’s
effects fire when it is called, not when it is made). v0.4: a
single-expression body and no uncaught Fail (documented — write a
named function and FuncRef it for step bodies or failures).
Hole
A typed hole: an unfilled position carrying what it expects.
BinOp
A binary operation over two sub-expressions.
Fail(String)
Raise a typed failure, short-circuiting the reasoning chain. Its type
is Never. The named variant must be covered by the enclosing
function’s on_failure unless a Handle catches it.
Handle
Run body; if it raises one of the handled failure variants, evaluate
that handler’s recover expression instead. A handled failure does not
propagate. handlers maps a failure-variant name to its recovery
expression (which yields the value, or itself fails).
If
A conditional expression. cond must be Bool; the two branches
share a type, which is the expression’s type. There is no statement
form — Cairn has expressions and reasoning-chain steps, not imperative
control flow.
Step
A reasoning-chain step: binding = value.
Function
A function: the full Section 4 contract plus an ordered body and a result expression.
Fields
Module
A module: named type definitions and function definitions, referenced by hash. The unit a checker resolves calls and type names within.
RecordDef
A record (product) type definition: named fields, each with a type.
Record
Construct a record value of a named record type.
Field
Access a field of a record value. type_name is the record type the
base must have; it makes the node self-describing for type-directed
lowering (the checker verifies it; the projection elides it).
VariantDef
A variant (sum) type definition: named cases, each with named payload fields (an empty payload is a nullary case).
Variant
Construct a value of a named variant type, in one of its cases.
Match
Case analysis over a variant value. Must be exhaustive. type_name
is the scrutinee’s variant type (self-describing for type-directed
lowering; the checker verifies it, the projection elides it).
Implementations§
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Node
impl<'de> Deserialize<'de> for Node
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for Node
impl StructuralPartialEq for Node
Auto Trait Implementations§
impl Freeze for Node
impl RefUnwindSafe for Node
impl Send for Node
impl Sync for Node
impl Unpin for Node
impl UnsafeUnpin for Node
impl UnwindSafe for Node
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more