Public output APIs

Source modules, evaluated runtime values and code-only function text now use one output pipeline: admitted EDAG → normalized FAST → common preparation → JavaScript syntax. Rust uses the same conversion and preparation with its own prepared serializer. The public source wrappers retain their existing result, chunk and marked-text APIs. They run source-spelling admission once after common placement; runtime value output accepts its wider evaluated-value domain.

Source logical operators are normalized to single-evaluation conditionals by AST-to-FAST sugar. Normalized/prepared FAST and canonical EDAG contain no &&, || or ?? operation. Checked graph inputs reject those former tags; the serializers spell the conditional form and provide no legacy graph adapter. This intentionally changes graph structure, hashes and function text while preserving source semantics and stable canonical round trips.

The common stages own evaluation regions, established captures, shared identity, lazy memo storage and loop reconstruction. These wrappers select an output form; they contain no target-specific graph placement algorithm. See edag/to_fast for conversion, normalization and the exact graph round-trip domain, and js/ast/serializer for JavaScript syntax.

Generated bytes intentionally follow the prepared serializer's canonical spelling. Names, grouping, single-use temporaries and function-body aliases can change from the former direct graph writers. Runtime functionText therefore has a new canonical code spelling while retaining code-only behavior: capture slots keep positional external names, captured values are neither rendered nor evaluated, and function arity, rest parameters, self identity, lazy behavior and failure behavior remain represented. Text stabilizes after normalization; raw pre-FAST graph hashes and the old code bytes are not compatibility promises.

Named scalar loop functions establish capture aliases in signature order before the condition. This removes the former inline writer's capture-order refusal using the existing source grammar.

Source loop output

General tail calls use shared loop conversion and preparation in code-only JavaScript/function text and Rust. Source output retains its admitted scalar, plain, structured and terminal invocation profiles through the separate source coordinator. It performs no second graph-placement or tail-eligibility analysis. The detailed source loop contract owns their exact admission, evaluation regions, fallback and graph normalization.

Canonical one-pass invocation joins also use common preparation's immutable continuation. Empty alternatives render as guards accepted by the existing FJS parser. The supplied K(z) and nested m cases support source parsing, execution and reserialization; this semantic contract does not require graph/hash equality. Genuine loop profiles retain their existing state/result plans. Inner decisions outside the terminating-guard subset retain their invocation loop under the current grammar.

Those profiles cover empty and multiple semantic inputs, observed rest, required normal-arm work, conditional results and transitions, mixed completion and continuation, and selected explicit throws. Common preparation retains exact loop targets and establishes reached arguments before simultaneous state updates. Immutable iteration/final snapshots preserve held captures and shared allocations; rest arrays remain fresh between invocations. A throw evaluates its selected payload once and leaves without a state or normal-result write. Public fixed arity and earlier scalar and zero-input divergent spellings retain their contracts.

Physical source packing does not change logical loop signatures or default JavaScript/function-text/Rust layouts. Parsing the first broader source output can introduce the canonical scalar-helper/result boundary, changing graph bytes/hashes and function text once under its existing exception. The graph from that first source parse and its canonical text then stabilize exactly, without accumulating helpers, projection demands or invocation wrappers. Existing raw scalar and exact-graph source fixtures retain their declared domains.

Reifiable roots outside the admitted source plans retain recursive spelling. Other supplied factored profiles return a factored terminal continuation outside the source loop profile through Result, preserving correct code-only JavaScript/Rust output. The remaining source-profile work tracks these explicit canonical-graph limits separately from the source grammar.

Discarded literal undefined anchors also normalize away during graph construction. They perform no failing work and create no identity; keeping them would accumulate unused declarations when source branch-result slots are reparsed. A supplied graph containing such an anchor can change its bytes/hash on the first source parse. The normalized graph and text then remain exact. Used undefined values, allocations, calls and other required work keep their existing behavior and identity. The generated round-trip law classifies only non-final literal undefined anchors with its other redundant prefixes.

Missing, extra and spread tail arguments normalize to fixed invocation inputs. Parsing the first source output can inline one-use argument setup; serialization of the normalized graph has that canonical spelling and reaches a source fixed point. This initial graph/text migration preserves evaluation and source admission; the existing exact-graph source domain keeps its round-trip checks.

Rest invocation loops use the shared fresh-rest argument recipe. Internal rest state does not add a public fixed parameter. Source reconstruction restores the original argument list instead of retaining private argument-vector wrappers, preserving fresh rest identity and stable normalized graphs. Default and destructuring setup lowered into the function body runs each iteration.

Source output continues to refuse frame layouts and lazy graphs the parser cannot represent. The existing source proofs retain every graph fixture and check graph normalization, text fixed points and agreement between token marks and tokenization in addition to representative exact output strings.

The shared return pass forwards the sole result of an immediately returned statement region into its normal leaves before graph construction. Terminal source guards and switches containing loop returns therefore keep their reached values within the selected region, without introducing source memo storage.

Demo

Compiles a FunctionalScript module without imports into JavaScript. Compare the output with the .js module written by fjs compile.

JavaScript module:

const $0=1+2;const $1=$0.x;const $2=$0*3;const $3=$0<4;export default [$0,$0,$2,$3?$0:$3,(...$4)=>$4,undefined];

Emergent Testing

Idle. Press Run to start the suite.

  • ./block_assignments.proof.f.mjs
  • ./function_text/proof.f.mjs
  • ./names/proof.f.mjs
  • ./primitive_captures.proof.f.mjs
  • ./proof.f.mjs
  • ./shared_comma.proof.f.mjs
  • ./value/proof.f.mjs