import type { TypeRef } from "./typed.js"; /** The static value kind of a compiled expression. `value` = a boxed escape (NOT allowed on the * covered plane — a compile that would need it fails closed). */ export type ExprType = TypeRef; /** A compiled native expression: hoisted fallible statements + the pure final expression + its type. * The native field type is ALWAYS `renderTypeRef(ref)` — since #173 the port boundary materializes to the * DECLARED port type (no backend-chosen override), so `ref` alone is the field-type SSoT. */ export interface Compiled { stmts: string[]; expr: string; ref: TypeRef; /** the SAME value as a PLACE the consumer may only BORROW from — set exactly when `expr` is an owning * COPY the head resolver made of that place (see {@link NativeExprBackend.resolveHead}'s `own`), absent * when `expr` is owned outright (a moved cell, a literal, an operator result). A consumer that * DECOMPOSES the value instead of keeping it — the wire box, which builds a NEW wire out of the parts * that cross — reads this and owns only those parts, so the whole value is never copied to read it * (#281). Every other consumer needs the owned `expr`. Ownership is a Rust concern: Go's value * semantics never set it. */ borrowed?: string; } /** * NativeExprBackend — the language-specific primitives. The traversal / type-inference / fail-closed * discipline live in this module (shared go+rust); the backend only renders syntax. Every method * returns RUNTIME-FREE native code (no bc-runtime type). */ export interface NativeExprBackend { /** language tag (for error messages). */ lang: "go" | "rust"; /** a fresh unique temp identifier for a hoisted fallible result. */ freshTemp(): string; /** render a TypeRef to the native type name (renderTypeRef). */ renderType(ref: TypeRef): string; /** the native scalar type name for a portable scalar. */ scalarType(s: "string" | "int" | "float" | "bool"): string; /** a string literal. */ strLit(s: string): string; /** a bool literal. */ boolLit(b: boolean): string; /** an i64 literal from a decimal string (validated in the safe/i64 range already). */ intLit(dec: string): string; /** an f64 literal. */ floatLit(n: number): string; /** field access: base expr of type baseRef, walk `path` → {expr, ref}. Mirrors typedFieldAccess. */ fieldAccess(baseExpr: string, baseRef: TypeRef, path: string[]): { expr: string; ref: TypeRef; }; /** the base expr + ref for a ref head: an input port field, an input array elem via $as, or a prior * node's typed cell. Returns null if the head is unknown (compile fails closed). `own`, when present, * renders the OWNED form of a resolved ref value at the LEAF (after the field path is walked) — a rust * clone / deref appropriate to the head's storage (a `RefCell` borrow vs a by-value field) and the * position (a port owns an array; a `len` borrows it). `fieldAccessed` is true when the ref walked * past the head into a struct field. It returns the owned `expr` plus, WHEN THAT OWNERSHIP IS A COPY, * the un-copied place as {@link Compiled.borrowed}. Go omits it (value semantics need no ownership). */ resolveHead(head: string): { expr: string; ref: TypeRef; own?: (expr: string, ref: TypeRef, fieldAccessed: boolean) => { expr: string; borrowed?: string; }; } | null; /** wrap a fallible helper call `call` (returns (T,err)/Result) as a hoisted temp of native type * `ty`, early-returning the failure. Returns the temp identifier. */ hoistFallible(temp: string, ty: string, call: string): string[]; /** the fixed helper-call syntax for a checked binary op. `helper` is the logical name * (addI64/subI64/mulI64/modI64/divF/lenArr/intLit). Returns the CALL expression (fallible). */ helperCall(helper: string, args: string[]): string; /** a native (infallible) binary numeric/compare operator expression. */ binOp(op: string, a: string, b: string, scalar: "int" | "float" | "string" | "bool"): string; /** native string concat of parts (all native strings). */ concat(parts: string[]): string; /** native `!a`. */ notOp(a: string): string; /** #309: the COMPLEMENT of an ordering compare on a PARTIALLY ordered scalar (float). Swapping the * operator is not the complement there (an incomparable pair makes both directions false), so the * backend spells "not " in whatever form its language accepts — see {@link NativeExprCompiler}'s * `compare`. Go has no lint to satisfy and keeps `(!(a > b))`; Rust cannot (`!(a > b)` on a * `PartialOrd`-but-not-`Ord` type is `clippy::neg_cmp_op_on_partial_ord`, an ERROR under a consumer's * `-D warnings` on a file `bc check` forbids them to edit) and matches the ORDERING instead. */ negPartialOrdCmp(op: "lt" | "le" | "gt" | "ge", a: string, b: string): string; /** a named struct literal with field inits (wire field names; the backend maps to native names). */ structLit(name: string, inits: { field: string; expr: string; }[]): string; /** a native array literal of element type `elemTy`. */ arrLit(elemTy: string, items: string[]): string; /** #209: build the opaque BC wire DIRECTLY from an inline composite literal materialized against an * expected `value` port — `{obj:{k:v,…}}` → a wire ROW, `{arr:[…]}` → a wire LIST, each member already * boxed to wire. A typed intermediate is impossible here (an inline literal at a `value` port has no * declared native shape to route through), so the wire is built structurally — the same wire the * interpreter's `evalPorts` hands the leaf. */ wireRowLit(entries: { key: string; expr: string; }[]): string; wireListLit(items: string[]): string; /** #209: a native string-keyed MAP literal of value type `valueTy` (go `map[string]V` / rust * `BTreeMap`), built from statically known entries (an `{obj:…}` materialized against a * declared `{map:V}` port — the dynamic-attribute port form #191 left after retiring additionalPorts). * Entry ORDER is not observable: go maps are unordered and the shared serializer sorts, rust uses the * key-ordered BTreeMap. */ mapLit(valueTy: string, entries: { key: string; expr: string; }[]): string; /** bc#156/#160: BOX one compiled element into the opaque BC wire (`WireValue`) for a `value[]` port — * a `value` element is identity (already wire); a native scalar is wrapped in the wire scalar ctor. The * input twin of the de-box: the covered plane stays typed and the datum crosses as wire ONLY here. */ wireBox(item: Compiled): string; /** bc#178: BOX EACH element of an OWNED typed array `srcExpr` (native `Vec`/`[]T`, element type * `elemRef`, T ≠ value) into an `arr` (native `Vec`/`[]WireValue`) — the element-wise * twin of {@link wireBox}, for a WHOLE-array reference flowing into a declared `arr` leaf port. It * reuses the SAME per-element box renderer (`renderRustWireBox`/`renderGoWireBox`) on `elemRef`, so this is * the box SSoT applied element-wise (box-once per element, no decode→re-box round-trip) — NOT a second box * path. An element type the box renderer does not cover (an opt-wrapped element) fails closed there, unchanged. * Takes the whole {@link Compiled} because this consumer DECOMPOSES the array: given `src.borrowed` it * iterates the place and owns only what crosses the wire, instead of copying the list first (#281). */ wireBoxEachElem(src: Compiled, elemRef: TypeRef): string; /** the native type name of the opaque BC wire (`WireValue`) — the element type of a boxed `value[]`. */ wireType(): string; /** the NONE value of an opt type whose inner native type is `innerTy` (a literal `null` materialized * against an `{opt:V}` expected type — go `nil` / rust `None`). #108 opt-null branch materialization. */ optNone(innerTy: string): string; /** a native bool: is the opt value `expr` PRESENT (non-null)? go `(expr != nil)` / rust `expr.is_some()`. * The null-presence test for `ne(, null)` — does NOT deref the opt. */ optIsSome(expr: string): string; /** a native bool: is the opt value `expr` ABSENT (null)? go `(expr == nil)` / rust `expr.is_none()`. * The null-presence test for `eq(, null)`. */ optIsNone(expr: string): string; /** #170: NARROW an opt value `optExpr` (native `Option` / `*T`) to its inner value of type `innerRef`, * in a scope a gate/guard `when(ne(, null), …)` has PROVEN non-null (a gated child / a guarded map * element only runs when the ref is non-null — byte-equal to run_behavior's skip/keep). go `(*optExpr)` / * rust `optExpr.unwrap()`. This is NOT a coalesce default — there is no fallback value; the absent branch * is statically unreachable in the proven scope. Reached ONLY for a path in {@link NativeExprCompiler}'s * narrowed set; every other opt-in-required position stays fail-closed. */ narrowUnwrap(optExpr: string, innerRef: TypeRef): string; /** #275: WRAP a PRESENT inner value `innerExpr` (native type `innerTy`, already materialized to the opt's * inner type) into the opt SOME form — the write-side twin of {@link optNone}, for a non-null value lowered * into an `{opt:V}` port/field/element (a string literal into an `opt` field, a `{obj}` into an * `opt`, a scalar into `opt`). rust: `Some(innerExpr)` (a pure expression). go: cannot take * the address of a temporary (`&`), so it HOISTS a temp (`_optN := innerExpr`) and returns `&_optN` — * hence `{ stmts, expr }` (mirrors how fallible sub-exprs hoist via {@link freshTemp}/{@link hoistFallible}; * the caller lays the stmts out before the expr, in evaluation order). NOT a default — `innerExpr` is the * present value; ABSENCE is represented by {@link optNone}, never reached here. */ optSome(innerExpr: string, innerTy: string): { stmts: string[]; expr: string; }; /** #275: CARRY an opt value `optExpr` (native `Option` / `*S`) through into `opt` * (`Option` / `*WireValue`), BOXING its PRESENT inner (native type `innerRef`, an `S`) into the * opaque BC wire and keeping ABSENT absent (None/nil) — the opt LIFT of {@link wireBox} (`opt → * opt`), reusing the SAME per-value box renderer (`renderRustWireBox`/`renderGoWireBox` on `innerRef`) * so this is the box SSoT applied under the option, NOT a second box path. rust: `optExpr.map(|v| )`; * go: an IIFE that nil-checks and boxes the deref'd inner (`func() *WireValue { … }()`). NOT a coalesce — * there is no fallback; absent stays absent (byte-equal to run_behavior passing a null `Value` through). * Takes the whole {@link Compiled} for the same reason as {@link wireBoxEachElem}: it DECOMPOSES the * present inner, so `src.borrowed` lets it box out of the place instead of copying the option first. */ optMapWireBox(src: Compiled, innerRef: TypeRef): string; /** the opt value `optExpr` (native `Option` / `*T`) defaulted to a PURE `defaultExpr` when ABSENT, * as a native EXPRESSION of the inner type `innerTy`. `coalesce(, )` where the default * hoists no statements. * * The default here is PURE, so whether a backend evaluates it eagerly or lazily is UNOBSERVABLE: it * cannot fail and has no side effect, and the compiler routes a fallible default to * {@link NativeExprBackend.optCoalesceGuard} instead. Backends are therefore free either way — rust's * `unwrap_or` is eager (it evaluates its argument) while `unwrap_or_else` is lazy, and both are * correct for this input. Laziness is a REQUIREMENT only on the fallible path, where it is * observable; see `optCoalesceGuard`. */ optUnwrapOr(optExpr: string, defaultExpr: string, innerTy: string): string; /** the same coalesce when the default is FALLIBLE (it hoists statements carrying early error-returns): * emit STATEMENTS binding `temp` (native type `ty`) to the opt's inner value when present, else * running `dStmts` and taking `dExpr`. The default's statements run ONLY on the absent branch * (short-circuit ≡ evaluate). This cannot be a closure: an early error-return propagates out of a * rust `match` arm / a go `if` block, but NOT out of `unwrap_or_else`'s closure. `bindTemp` is a fresh * name the backend may bind the unwrapped value to. */ optCoalesceGuard(temp: string, ty: string, optExpr: string, bindTemp: string, dStmts: string[], dExpr: string): string[]; /** a ternary/if-expression producing `t` when `cond` else `e`, both of native type `ty`. */ ternary(cond: string, t: string, e: string, ty: string): string; /** short-circuit and/or with a FALLIBLE right side: emit statements binding `temp` (native bool) * such that the right side's `rStmts` (+ its early error-returns) run ONLY when the left `left` * does not settle the result. `and` → temp=left && rExpr (rExpr computed only if left true); * `or` → temp=left || rExpr (rExpr computed only if left false). */ shortCircuitBool(op: "and" | "or", temp: string, left: string, rStmts: string[], rExpr: string): string[]; /** cond with a FALLIBLE branch: emit statements binding `temp` (native type `ty`) by an if/else on * `cond` where ONLY the taken branch's stmts+expr are evaluated (short-circuit ≡ evaluate). */ condGuard(temp: string, ty: string, cond: string, tStmts: string[], tExpr: string, eStmts: string[], eExpr: string): string[]; } /** * NativeExprCompiler — compiles Expression IR to native code via a backend. One instance per * expression-compilation context (it accumulates hoisted statements through `compile`). */ export declare class NativeExprCompiler { private readonly be; /** #170: ref-path keys ({@link refPathKey}) a dominating gate/guard `when(ne(, null), …)` has PROVEN * non-null in the scope this compiler lowers (a gated child's / a guarded map element's ports). At an * opt-in-required position a path in this set is NARROWED to its inner value (see `ref`); every other opt * stays fail-closed. Empty = no narrowing (the default for cond/guard/output compilation). */ private readonly narrowed; constructor(be: NativeExprBackend, narrowed?: ReadonlySet); /** compile a node to {stmts, expr, ref}. Fallible sub-exprs are hoisted into stmts. */ compile(node: unknown): Compiled; /** * compilePortTo — lower a node at PORT position to its DECLARED port type `expected` (#173). The port * boundary is now declared-type-driven, symmetric with the output boundary (`compileTo`): there is NO * element-kind inference here anymore — the leaf's `portSchemas` supplies `expected` and the datum is * MATERIALIZED to it (a `{arr:{value}}` boxes every element via `wireBox`; a `{arr:T}` materializes each * element to T and keeps an empty array typed; a scalar `value` boxes the scalar; a scalar/obj/named/map * lowers exactly as the output boundary does). All lowering routes through {@link compileTo} — one * materialization path, no duplicated emit logic. * * There is NO port-specific rule: {@link compileTo}'s `{opt:V}` branch IS the SSoT for lowering into an * optional slot (a native `Option` / `*T`) — a null literal → NONE, an opt-typed leaf access carried * through (identity or an `opt → opt` box crossing), or a non-null value wrapped in SOME. That * branch admits the opt-typed access a required cond/guard slot rejects, so an optional leaf port receives * its value/null exactly as run_behavior's `evalPorts` would pass it — with no second, narrower opt path * here (this method previously duplicated the opt-ref case and rejected the `opt → opt` crossing). * #170 gate/guard narrowing still applies through `compile`'s `narrowed` set (a proven-non-null opt in a * required operand of a non-opt port lowers to its inner value). */ compilePortTo(node: unknown, expected: TypeRef, resolveDecl: (name: string) => { name: string; type: TypeRef; }[]): Compiled; /** compile a node and require it to produce a native `bool` (the cond `if` / guard `when` gate). A * non-bool result is a GENERATION-TIME fail-closed reject (strict-bool, mirrors requireBool). */ compileBool(node: unknown): Compiled; /** * compileTo — compile a node to the given expected TypeRef, MATERIALIZING an {obj}/{arr}/scalar * literal against `expected` (the cond-branch / boundary shape — mirrors emitTypedValue). A ref / * operator sub-node is compiled by `compile` and must already carry the expected type. `named` * targets require the plan-resolved decl fields (passed via `resolveDecl`). */ compileTo(node: unknown, expected: TypeRef, resolveDecl: (name: string) => { name: string; type: TypeRef; }[]): Compiled; private numberLit; private intLit; private floatLit; private ref; private obj; private arr; private binArgs; private arith; private neg; private div; /** widen an int/float compiled value to a fallible f64 (PRECISION_LOSS for int out of ±2^53). */ private widenToFloat; private mod; private concat; private eqNe; /** * optLeafAccess — resolve a node whose VALUE is an `{opt:V}`, WITHOUT the fail-closed opt reject that * `ref()` enforces (a required scalar slot cannot hold "absent"; the opt-aware callers — the * null-compare and `coalesce` — consume the opt itself). Returns the opt-typed access expr + its * TypeRef, or null when the node is not one of the two covered opt shapes. * * The covered shapes, each byte-equal to `refCore` (expr-eval.ts): * - `{ref:[head, …path]}` whose LEAF resolves to an `{opt:V}` field (an opt field on a typed input / * prior-node struct). * - `{refOpt:[head]}` — a SINGLE-SEGMENT `opt($.x)` (authoring.ts `opt`). With an empty rest-path * `refCore` never reaches its null-intermediate branch, so `refOpt` and `ref` observe the SAME * value here; the head's own `{opt:V}` type is the result. * * A MULTI-segment `refOpt` (`opt($.x).y`) walks intermediates with null-propagation and is NOT covered * (it would need opt-nil chaining) — null, so the caller fails closed. */ private optLeafAccess; /** is `node` a bare INTEGRAL JS number literal (the IR form of an int literal, e.g. `2`)? Used to * widen an int literal to float when compared against a float operand (run_behavior value parity). */ private isIntegralNumberLiteral; private compare; private andOr; private not; /** * lowerBool — the ONE place a native bool and its NEGATION are lowered (#299). `negated` asks for the * boolean complement of `node`; `ctxOp` names the operator that asked, for the fail-closed message. * * A negation is pushed INTO the operand wherever the complement has an EXACT native spelling, so the * emitted code carries no reducible `!`: * - a bool LITERAL folds to the opposite literal (`!(true)` is `clippy::nonminimal_bool`); * - `not(not(x))` is `x` (`!(!x)` is `nonminimal_bool`); * - `eq`/`ne` swap (exact for every scalar AND for the null-presence fold — `!(x.is_some())` is * `nonminimal_bool`, `x.is_none()` is the reduced form the lint asks for); * - an ORDERING compare swaps its operator on a TOTAL order — see {@link compare}, which owns that * decision because it is the only place the operand's scalar type is known. * Everything else takes the backend's `!`. This is a spelling fold, not a semantic one: each rewrite is * the complement for EVERY value the operand can take, so the module computes exactly what it did. * * `negated === false` is the plain "compile this as a bool" path, which every caller needs anyway (the * bool-literal compare fold in {@link eqNe} lands on either side depending on the literal), so both * signs live in one method rather than a second entry point that could drift from this one. */ private lowerBool; /** * coalesce — `{coalesce:[a,b]}` = `a ?? b`. run_behavior (expr-eval.ts §coalesce): evaluate `a`; if it * is null evaluate and return `b`, else return `a` (the right side is LAZY). * * Two covered cases, each byte-equal to the interpreter: * - `a` is an `{opt:U}` (an optional input port / an opt struct field) and `b` is a `U`: the native * `unwrap_or` (rust `a.unwrap_or(b)` / the go twin). This is the ORM default — `coalesce(opt($.limit), 20)`. * Its result type is `U`, matching the declared `coalesce-join` rule (expr-operator-types.ts). * - `a` is a REQUIRED native value (never null — the type says so) and `b` has `a`'s type: the value * is CONSTANT-folded to `a`, and `b` is never evaluated (exactly the interpreter's short-circuit). * Mirrors the same reasoning the null-compare fold uses. * * The interpreter evaluates the right side LAZILY. That is only OBSERVABLE when the default can fail, * so the two forms split on exactly that: a PURE default takes the expression form (its evaluation * order cannot be observed — it neither fails nor has an effect), and a default that hoists statements * (a fallible sub-expression carrying an early error-return) takes the STATEMENT form, which evaluates * it only on the absent branch. The statement form is required because an early return propagates out * of a rust `match` arm / a go `if` block, but not out of a closure. * Whether a caller can host those statements is the CALLER's constraint, not this compiler's: a * cond/guard position hoists them; a port built inline in a ports-struct literal cannot, and rejects * there. */ private coalesce; private cond; private len; } /** structural TypeRef equality (for cond-branch join + expected checks). */ export declare function typeRefEqual(a: TypeRef, b: TypeRef): boolean; //# sourceMappingURL=native-expr.d.ts.map