From cff80d2efd7cb705d29e4d449c8449c8255cf7ed Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 19:21:20 +0000 Subject: [PATCH 1/6] Import JS WAM cut-semantics hardening from the coordinator tip. Pulls the barrier-model runtime, lowered-emitter fixes, cut-semantics suite, and status/convention docs so pkg_resolver P2 builds against the post-P0.5 runtime. No resolver changes in this commit. Co-authored-by: johns243a --- docs/WAM_BACKEND_CONVENTIONS.md | 161 +++++++- docs/WAM_JAVASCRIPT_STATUS.md | 93 ++++- .../targets/wam_javascript_lowered_emitter.pl | 255 +++++++++--- .../javascript_wam/runtime.js.mustache | 187 ++++++++- tests/test_wam_javascript_cut_semantics.pl | 389 ++++++++++++++++++ 5 files changed, 1016 insertions(+), 69 deletions(-) create mode 100644 tests/test_wam_javascript_cut_semantics.pl diff --git a/docs/WAM_BACKEND_CONVENTIONS.md b/docs/WAM_BACKEND_CONVENTIONS.md index 00747469e..d78e319b0 100644 --- a/docs/WAM_BACKEND_CONVENTIONS.md +++ b/docs/WAM_BACKEND_CONVENTIONS.md @@ -22,7 +22,11 @@ are deliberately chosen to exercise every convention below. --- -## TL;DR — the six things that bite +## TL;DR — the eight things that bite + +*(§1–§6 were found by the conformance fixtures; §7–§8 were found by +running a whole real program — the A2 exercise — and the fixtures do +not catch them.)* | # | Convention | Symptom if you get it wrong | Conformance program that catches it | |---|---|---|---| @@ -32,6 +36,8 @@ are deliberately chosen to exercise every convention below. | 4 | **`deref` before every type test** (`is_var`, `is_list`, …) | a *bound* variable is mistaken for unbound → write-mode corruption | `member`, `reverse` | | 5 | `is/2` must produce an **integer** for integral results (if unify is type-strict) | `R is N+1` fails when `R` is a ground integer | `fib`, `ack` | | 6 | **Never drop or throw on an unhandled instruction** — emit a real no-op so PC/label alignment is preserved | indexing hints (`switch_on_term*`, `switch_on_constant_fallthrough`) vanish from the code vector, shifting every later label by one; backtracking skips `retry_me_else`/`trust_me` and loops or mis-clauses | `fib`, `ack`, `append`, `reverse` | +| 7 | **`Execute` of a runtime-implemented builtin must run it and then take `Proceed`'s return path** — never halt, never silently fail, never jump to PC 0 | a clause whose *last* goal is a builtin outside `is_builtin_pred/2` (`sub_string/5`, `catch/3`, any runtime-extended builtin) binds its outputs and then kills or fails the whole call | none yet — found by the whole-program benchmark (`examples/cli_args/`), not the fixture suite | +| 8 | **Do not assume `Allocate` framing protects Y registers** — with the numeric `X→+100 / Y→+200` encoding, X101 aliases Y1, and the shared fact compiler emits >99 X placeholders with **no** `Allocate` | calling a large ground-fact predicate silently corrupts the caller's permanent variables | none yet — same benchmark (a ~7-entry nested registry fact triggers it) | --- @@ -228,6 +234,88 @@ fallback) through it. --- +## 7. `Execute` of a builtin must return to the continuation + +*Adopted 2026-09 from the JS-runtime findings of the whole-program +(A2) exercise — see [`WAM_FLEET_GAPS.md`](WAM_FLEET_GAPS.md). Unlike +§1–§6, the conformance fixtures do **not** catch violations; the +`examples/cli_args/` benchmark does.* + +The compiler emits a clause's last goal as `deallocate` + `execute +P/N` (TCO). When `P/N` is in `is_builtin_pred/2` it becomes +`builtin_call` + `proceed` instead — but many predicates a runtime +implements are **not** in that shared table (`sub_string/5`, +`catch/3`, `call/N`, every runtime-extended builtin), so they reach +the backend as `execute` of a name **with no label**. + +**Rule:** the `Execute` arm, after failing label lookup, must try the +runtime's own builtin/foreign dispatch, and on success perform exactly +what `Proceed` would: restore the caller and jump to CP (halting only +when CP is the top-level sentinel). Three observed failure modes, all +wrong: + +- **halt** — the JS runtime set `halt = true` after the builtin + succeeded, dropping the rest of the caller (`substring_from/3` + ending in `Execute sub_string/5` left `parse_args/3`'s result + unbound); fixed by routing through the shared `proceed_to_cp`. +- **silent failure** — most runtimes (Lua, Python, C, Clojure, and the + general case in Rust/Haskell/F#/Elixir) fail the goal when the label + is missing, so a *succeeding* builtin reports failure. +- **PC 0** — WAT's `resolve_label` encodes an unknown label as PC 0: a + silent jump to instruction 0. + +**Precedents:** Go's dedicated `BuiltinExecute` instruction +(`instructions.go.mustache` — run the builtin, then Proceed's return +step; kept as one instruction so PC/label alignment survives, per §6); +C++'s `Execute` fallback-then-proceed (`wam_cpp_target.pl:8035-8040`); +R's `call_library` + Proceed protocol (`runtime.R.mustache:1424-1467`); +JS post-A2 (`proceed_to_cp`). A builtin the runtime genuinely does not +implement should still **fail loudly enough to find** (a diagnostic on +stderr costs nothing); it must never be encoded as a jump. + +--- + +## 8. Y registers are not protected by `Allocate` framing — the X window aliases into Y space + +*Adopted 2026-09 from the same exercise. This is a property of the +**shared bytecode**, so every backend must pick a defence.* + +The shared fact compiler emits a large ground fact (e.g. a 7-entry +nested registry, `default_registry/1` in `examples/cli_args/`) as a +plain `get_*`/`unify_*` sequence with **no `Allocate`** and with more +than 99 X-register placeholders. Under the common numeric register +encoding — `A_n→n, X_n→n+100, Y_n→n+200` (or 0-based / 128-slot +variants) — **X101 and Y1 are the same slot**. Calling such a fact +therefore overwrites the caller's permanent (Y) registers, and the +callee's missing `Allocate` means no frame discipline ever saves them. +Frame-based runtimes are *not* exempt: a runtime that routes "id ≥ +200" to the topmost environment frame (Haskell, Python at ≥ 301, R, +F#) writes the fact's spilled X registers into the **caller's** frame. + +**Rule — one of, in order of preference:** + +1. **Non-aliasing register spaces.** String-named registers or + segregated banks (`"X101"` ≠ `"Y1"`): Rust, Kotlin, C++, Clojure, C + are structurally immune. If the bank is a fixed-size array (C's + `WAM_MAX_REGS 256`), bounds-check it — an oversized fact must fail + loudly, not corrupt memory. +2. **Save/restore the Y range across `Call`.** The JS workaround: + `Call`/`CallPc` snapshot registers ≥ Y-base, `Proceed` restores + (`push_y_save` / `proceed_to_cp` in `runtime.js.mustache`); the + snapshot must also be captured into choice points. +3. *(Upstream, preferred long-term)*: make `wam_target.pl`'s fact + compilation stay inside the X window (spill to a dedicated range), + at which point this section demotes to a historical note. Until + then, assume any fact with >99 placeholders is hostile to your Y + registers. + +**Test:** call a ground fact whose single argument needs ≥ 120 +placeholder registers from a clause that holds live Y registers across +the call, and check the Y values afterwards. `probe_y_preserve/0` in +`tests/test_wam_javascript_builtins.pl` is the model. + +--- + ## New-backend checklist Before declaring a WAM backend conformant, confirm each of these against a @@ -250,8 +338,77 @@ expression (the conformance fixtures do both): `append`). (§6) - [ ] operator functors are escaped for the host string syntax (`=\=` must survive as `=\\=` in a C/Java/… literal). (§2) -- [ ] `CONFORMANCE_TARGETS=` is green with no `ct_xfail` entries. +- [ ] `execute` of an unlabelled name falls back to the runtime's builtin + dispatch and then takes `Proceed`'s return path — test a clause whose + *last* goal is a builtin outside `is_builtin_pred/2`. (§7) +- [ ] Y registers survive a call to a no-`Allocate` ground fact with ≥ 120 + placeholder registers (non-aliasing register spaces, or a Y snapshot + on `Call`). (§8) +- [ ] `CONFORMANCE_TARGETS=` is green with no `ct_xfail` entries — + and remember the fixtures do **not** cover §7/§8; the + `examples/cli_args/` whole-program benchmark does + ([`WAM_FLEET_GAPS.md`](WAM_FLEET_GAPS.md), Class C). The first backend that hits a *new* class of divergence should add a row to the table in `WAM_CROSS_TARGET_CONFORMANCE.md` and, if it is a general convention rather than a one-off, a section here. + +### §9 — Cut is a barrier, never a stack wipe + +*(Adopted 2026-09 from the JS-backend cut-semantics audit: twelve divergences, +every one invisible to the 48-query conformance set.)* + +A cut prunes choice points back to a **barrier**, never to zero. Every backend +must maintain an explicit barrier and every runtime that can nest one execution +inside another must maintain two: + +1. **`cut_barrier` (WAM `B0`)** — the choice-point-stack height recorded when + the current predicate activation was entered. `!` truncates back to it and + no further. +2. **`cp_barrier` (isolation floor)** — the height at which a nested run driven + from host code begins. Neither backtracking nor cutting may cross it. + +The effective floor for any cut is `max(cut_barrier, cp_barrier)`. Implementing +`!` as "clear the choice-point stack" is a defect even when the test suite +passes: it only shows up once a nondeterministic caller sits below a cutting +callee. + +**Both `call` and `execute` set `B0 <- B`.** Last-call optimization reuses the +caller's frame slot, so `execute` must *replace* the barrier rather than push a +new one — but it must still replace it. A backend that rebases on `call` only +will let a `!` in a tail-called predicate destroy its caller's clause +alternatives. + +**Every choice point must snapshot and restore the barrier state** +(`cut_barrier` and the saved-`B0` stack) alongside the trail mark and +registers. A hand-written choice-point record that omits them silently deletes +the barrier regime on backtrack. Backends should funnel all choice-point +creation through a single snapshot routine rather than open-coding the record. + +**Barrier-raising contexts.** The condition of `( C -> T ; E )`, the argument +of `\+`, the goal of `call/1`, and the inner goal of +`findall`/`bagof`/`setof`/`aggregate_all` are each opaque cut scopes: a `!` +inside one may prune only that scope's own choice points. In particular, +entering the *then* branch of an if-then-else must cut the condition's choice +points, and an inlined aggregate must raise the barrier above its own aggregate +choice point so an inner `!` cannot strand the collection. The *then* and +*else* branches are **not** opaque — a `!` there cuts the enclosing clause +(ISO). + +**Lowered tiers are first-solution.** A lowered host function that returns +through the host stack cannot resume a callee's choice point. A backend with a +lowered tier must therefore *decline to lower* any predicate that (a) reaches a +choice-point-creating builtin outside a commit wrapper, (b) calls a user +predicate that can succeed more than once outside a commit wrapper, (c) can +itself succeed more than once, or (d) contains a commit-less disjunction. The +set in (a) is the set of builtins that actually push on that backend's +choice-point stack — it is backend-specific and must be derived from the +runtime, not copied. Where a lowered call cannot avoid leaving a choice point +behind, the leftover must be **dropped**, not left to be resumed incoherently: +honestly first-solution beats silently wrong. + +**Conformance.** A backend claiming §9 must ship a cut-semantics probe corpus +run against a reference Prolog, covering each context above in *both* the +interpreted and lowered tiers and at the boundary between them. +Forty-eight-query conformance suites do not exercise this: the JS backend +passed all of them while `!` was wiping the entire choice-point stack. diff --git a/docs/WAM_JAVASCRIPT_STATUS.md b/docs/WAM_JAVASCRIPT_STATUS.md index d11779bc2..2254ae6e7 100644 --- a/docs/WAM_JAVASCRIPT_STATUS.md +++ b/docs/WAM_JAVASCRIPT_STATUS.md @@ -347,7 +347,92 @@ large term on first construction. Next lever: skip `snapshot_lite` when the ITE condition is read-only, and/or intern `default_registry/1` at emit time so the first `parse_args/2` is not a full construction. -## Remaining / partial +## Cut and choice-point barriers + +This section is normative for the JS WAM backend. It is pinned down by +`tests/test_wam_javascript_cut_semantics.pl` (35 probes, each run against +SWI-Prolog as the oracle in four emit modes). + +### What a barrier is + +`state.cps` is one flat array of choice points, shared by the +interpreter, the lowered JS functions and every nested run. A **barrier** +is an index into that array below which a given operation may not +truncate. Two barriers exist and they mean different things: + +| field | meaning | set by | consumed by | +|---|---|---|---| +| `state.cut_barrier` | the WAM **B0** of the *current predicate activation*: `cps.length` recorded when this predicate was entered. `!` prunes back to it. | `push_cut_barrier` (Call), `enter_execute` (Execute) | `neck_cut` | +| `state.cp_barrier` | a **hard isolation floor**: a nested `Runtime.run` driven from a lowered frame may neither backtrack below it nor cut below it. | `Runtime.run_isolated` | `Runtime.backtrack`, `neck_cut`, `Cut Yn` | + +`state.cut_stack` is the saved-B0 stack: Call pushes the caller's +`cut_barrier`, Proceed pops it. `snapshot_machine` captures **both** +`cut_barrier` and `cut_stack`, and `restore_machine` restores them, so +backtracking into any choice point restores the barrier regime that was +live when the choice point was created. **Any hand-rolled choice-point +object must therefore be built by `Runtime.snapshot_machine`** — a +literal that omits those two fields makes `restore_machine` delete the +barrier and empty the stack. + +The effective floor for a cut is + +```js +cut_floor(state) = max(state.cut_barrier ?? 0, state.cp_barrier ?? 0) +``` + +The `max` matters: an isolated sub-run inherits a *lower* `cut_barrier` +from an outer frame, and without the clamp a `!` inside it would destroy +choice points its caller still owns. + +### Barrier discipline per context + +| context | barrier | +|---|---| +| Query top level | No frame is pushed; `cut_barrier` is undefined, so `cut_floor` is 0. A top-level `!` prunes everything, which is correct — nothing outside the query owns a choice point. | +| Interpreted `Call` | `push_call_frame` = Y-save + `push_cut_barrier`. `cut_barrier := cps.length` **before** the callee's `try_me_else`, so `!` leaves the caller's alternatives and cuts the callee's own clause choice point. `state.cp` is set to the return address first. | +| Interpreted `Execute` (LCO) | `enter_execute`: `cut_barrier := cps.length` **without** pushing. WAM does `B0 <- B` on `execute` as well as on `call`; the callee reuses the caller's `cut_stack` slot (its Proceed pops the entry the caller's Call pushed) but still gets its own B0. | +| Lowered `Call` (JS return = Proceed) | `Runtime.push_cut_barrier` / `pop_cut_barrier` around the direct JS call (`push_call_frame` / `pop_call_frame` when the caller's Y is live across it). Choice points the callee leaves above the entry mark are dropped — see *first-solution contract* below. | +| Lowered `Execute` | `Runtime.enter_execute` before the tail call, then `return`. | +| `Runtime.execute_user_isolated` | `cp := 0` so the interpreted callee's Proceed halts the isolated run rather than jumping the lowered caller's continuation. It pushes a call frame (so the callee's `pop_call_frame` is balanced) and repairs with `call_frame_mark` / `call_frame_release` on the failure path. | +| `Runtime.run_isolated` / `cp_barrier` | Raises the hard floor to `cps.length`, runs, then **drops every choice point left above the floor**. Those choice points are not resumable — their snapshot carries `cp = 0` and a pc inside the callee — so keeping them produced incoherent resumes. Dropping them makes an isolated call honestly first-solution. | +| `\+ G` | Compiled (`inline_not_as_failure`, default) to the soft-cut form `(G -> fail ; true)` with `get_level`/`cut Yn`, so it is an opaque barrier scoped to the negation alone. Metacalled `\+ /1` runs `G` in a **fresh sub-state** with its own empty `cps`, which is opaque by construction. | +| `findall` / `bagof` / `setof` / `aggregate_all` inner goal | `BeginAggregate` pushes the aggregate choice point and then `push_cut_barrier`, so `cut_barrier` sits **above** the aggregate CP. A `!` in the inner goal prunes only the inner goal's own alternatives and can never destroy the aggregate CP (which would strand `EndAggregate`). The barrier and `cut_stack` are restored by `restore_cp_frame` when `backtrack` reaches the aggregate — its snapshot was taken before the push. This is the ISO "inner goal is call/1-like" rule. | +| `once/1` | Compiled to `(G -> true ; fail)`. Entering the then-branch commits: the condition's choice points are cut. `once` therefore does **not** cut the enclosing predicate's clause alternatives. | +| `( C -> T ; E )` | `get_level Yn` before `try_me_else`, `cut Yn` at the commit. Entering the then-branch cuts **C's** choice points only; T's and E's belong to the enclosing clause and are **not** cut. A `!` written *in* C is opaque to the clause — the compiler retargets it to a second Y holding the condition's own entry level. A `!` in T or E cuts the **enclosing clause** (ISO). | +| `call/1` | ISO: a cut inside `call/1` is **local**. The metacall builds a fresh sub-state whose `cps` is empty, so `!` there cannot reach the caller. Control constructs (`,`, `;`, `->`, `\+`, `!`, `once`, `ignore`, `call/N`, `^`) are interpreted structurally inside the metacall. | +| `-> ` inside `\+` | Two nested opaque scopes; each gets its own Y level. Verified by probe P23. | +| `catch/3` | **Not implemented** in this backend (no `catch`/`throw`), so it has no barrier. Listed here so the omission is explicit rather than assumed. | +| `Cut Yn` (Y-level soft cut) | Truncates to the level `get_level` recorded, clamped at `cp_barrier`. | + +### The first-solution contract (and what the emitter refuses) + +A lowered JS function yields **at most one solution**: its body is +straight-line, so when a goal fails there is no machinery to retry an +earlier goal, and any choice point a callee leaves is not resumable from +a lowered frame. The emitter enforces the contract by *declining to +lower* rather than by emitting first-solution code where more is needed: + +- a predicate whose body reaches a **CP-creating builtin** outside a + commit wrapper (`member/2`, `between/3` — exactly the builtins that + push onto `state.cps`; every other library predicate in this runtime is + semi-deterministic and cannot leak one); +- a predicate whose body **calls a user predicate with more than one + distinct clause** (counted up to variant equality) outside a commit + wrapper — the caller could never reach that callee's second solution; +- a predicate that can itself **yield more than one solution**: more than + one distinct clause, heads not first-argument mutually exclusive, and + not every clause but the last committed by a top-level `!`. T5/T6 + (`clause_chain`) are exempt — their unbound-A1 path pushes a real + interpreter choice point and hands the alternatives back; +- a **plain disjunction** `( A ; B )`. It compiles to a commit-less + `try_me_else` block; folding that into `ite(A, [], B)` silently deletes + B as a retry alternative and makes A first-solution. + +Commit wrappers that *do not* taint the caller: `findall`, `bagof`, +`setof`, `aggregate_all`, `once`, `forall`, `\+`, and the **condition** +of an if-then-else. + + | Builtin | Status | |---|---| @@ -364,7 +449,7 @@ emit time so the first `parse_args/2` is not a full construction. | `library(assoc)` | **Implemented** as a Prolog `assoc/1` list of Key-Value pairs (not SWI's AVL tree). get/put/list/keys match SWI for unique-key maps. | | First-arg indexing | **Implemented.** `switch_on_constant` / `_fallthrough` / `_a2`, `switch_on_structure` / `_a2`, and `switch_on_term` / `_a2` jump to the matching clause group. Ground first-arg with a unique clause leaves no choice point (`deterministic/0`). Unbound first arg falls through to the try/retry/trust chain (no lost solutions). Exclusive miss fails; fallthrough variants keep the chain for variable-headed clauses. Dedicated `try`/`retry`/`trust` dispatch chains are emitted for multi-clause groups. | | Second-arg / deep indexing | A2 switches are implemented; deep (argument >2) indexing is not. | -| Lowered / functions emit mode | **Implemented.** `javascript_wam_resolve_emit_mode/2` accepts `interpreter` (default), `functions` (lower every eligible predicate), `mixed` (lower every eligible predicate, interpret the rest), and `mixed([P/A, ...])` (lower only the named ones). Eligible shapes: single-clause deterministic bodies; T4 all-clauses-inline (including nested `\+` via a depth-aware ITE fold, and nil/cons list recursion without a bound-A1 snapshot); T5 first-arg constant dispatch; T6 hash dispatch (≥8 atom keys); structured ITE / negation / once. Ground facts intern via `copy_term` into `program.ground_memo`. Execute of a user predicate preserves CP (`execute_user_isolated` or JS `return`). Execute/Call of a JS WAM builtin is `op_builtin`. Unsupported ops fall back to the interpreter rather than emitting wrong code. Interpreter-mode bytecode and wrappers are unchanged. | +| Lowered / functions emit mode | **Implemented.** `javascript_wam_resolve_emit_mode/2` accepts `interpreter` (default), `functions` (lower every eligible predicate), `mixed` (lower every eligible predicate, interpret the rest), and `mixed([P/A, ...])` (lower only the named ones). Eligible shapes: single-clause deterministic bodies; T4 all-clauses-inline (including nested `\+` via a depth-aware ITE fold, and nil/cons list recursion without a bound-A1 snapshot); T5 first-arg constant dispatch; T6 hash dispatch (≥8 atom keys); structured ITE / negation / once. Ground facts intern via `copy_term` into `program.ground_memo`. Execute of a user predicate preserves CP (`execute_user_isolated` or JS `return`). Execute/Call of a JS WAM builtin is `op_builtin`. Unsupported ops fall back to the interpreter rather than emitting wrong code. Interpreter-mode bytecode and wrappers are unchanged. Shapes that would need a resumable choice point are **refused** (CP-creating builtin, multi-clause user callee, self can-yield-many, plain disjunction) — see [Cut and choice-point barriers](#cut-and-choice-point-barriers). | | CLI / runtime term parser | **Implemented.** Pratt reader: int/float/atom (incl. quoted)/var/list/`[H\|T]`/compound. CLI argv + `read_term_from_atom` / `atom_to_term` / `term_to_atom`. **`op/3`** updates the live infix/prefix/postfix tables (defaults cloned from ISO). Compile-time ops via `javascript_wam_ops/1`. Capability `native(parse_term)` via `INTEGRATION_PATCH.md` §7. | | Interpreter profiling | **Implemented.** Off by default (`Runtime._prof === null`). `UW_PROFILE=1` / `json` or `Runtime.profile(...)` writes a per-predicate table or JSON to **stderr**. Lowered tier: call counts only. See [Profiling (GP-PROF)](#profiling-gp-prof). | | `op/3` | **Implemented.** Infix `xfx`/`xfy`/`yfx`, prefix `fx`/`fy`, postfix `xf`/`yf`. Priority 0 removes. Name = atom or list of atoms. `current_op/3` is not implemented; ops are process-global. | @@ -378,6 +463,10 @@ mkdir -p output/advanced # Dedicated probe + local 48-query suite (does not edit the shared harness): swipl -q -g run_tests -t halt tests/test_wam_javascript_builtins.pl +# Cut / choice-point barrier conformance: 35 probes x 4 emit modes, +# every probe's stdout compared byte-for-byte with SWI-Prolog. +swipl -q -g run_tests -t halt tests/test_wam_javascript_cut_semantics.pl + # File-backed P/2 fact sources (CSV/TSV/JSONL) + indexed/lmdb stores: swipl -q -g run_tests -t halt tests/test_wam_javascript_fact_sources.pl # Backend B builder (zero deps): node scripts/js_wam/uw_fact_index.js build edges.tsv store_prefix diff --git a/src/unifyweaver/targets/wam_javascript_lowered_emitter.pl b/src/unifyweaver/targets/wam_javascript_lowered_emitter.pl index d4ed97049..1f59f4358 100644 --- a/src/unifyweaver/targets/wam_javascript_lowered_emitter.pl +++ b/src/unifyweaver/targets/wam_javascript_lowered_emitter.pl @@ -314,9 +314,15 @@ js_split_commit_(R, D1, [trust_me|Acc], Cond, Then). js_split_commit_([Commit|Then], 0, Acc, Acc, Then) :- is_commit(Commit), !. -% Disjunction (A ; B) has no cut on the then-path. Treat the whole -% path as the condition and an empty then — ite(A, [], B). -js_split_commit_([], 0, Acc, Acc, []) :- !. +% A try_me_else block with NO commit on the then-path is a plain +% disjunction (A ; B), not an if-then-else. It used to be folded into +% ite(A, [], B) — i.e. compiled as (A -> true ; B) — which silently +% deleted B as a retry alternative and made A first-solution-only. +% `( Goal, write(X), nl, fail ; true )` (the standard failure-driven +% enumeration loop) then printed only the first solution. Cut-semantics +% probe D-DISJ. Refuse: js_split_commit_/5 now fails on a commit-less +% path, js_structure_ite/2 fails with it, and the predicate falls back +% to the interpreter, which has real choice points. js_split_commit_([I|R], D, Acc, Cond, Then) :- js_split_commit_(R, D, [I|Acc], Cond, Then). @@ -408,6 +414,18 @@ % the caller's next instruction rather than the alt clause. Wrong-but- % fast is a failure; leave these on the interpreter until T4+ITE fits. Reason \== multi_clause_1, + % Every lowered plan except clause_chain returns the FIRST clause that + % succeeds and pushes no choice point, so it is only sound when at + % most one clause CAN succeed: either the heads are first-argument + % mutually exclusive, or every clause but the last commits with a + % top-level cut. `p(X) :- once(d(X)). p(9).` is neither, and a caller + % could never reach the second solution (probes P10/P18). T5/T6 + % (clause_chain) are exempt: their unbound-A1 path pushes a real + % interpreter choice point and hands the alternatives back. + ( Reason == clause_chain + -> true + ; \+ js_pi_may_yield_many(PI) + ), ( Reason == ite -> forall(member(I, Payload), js_struct_supported(I)) ; Reason == clause_chain @@ -697,6 +715,14 @@ format("~w return true;~n", [Ind]), format("~w })();~n", [Ind]), format("~w if (_ite_cond) {~n", [Ind]), + % ISO: entering the then-branch COMMITS to the condition. The + % condition is a once-like scope, so its choice points must be cut + % here. Without this, `once(d(X))` (compiled to (d(X) -> true ; fail)) + % left d/1's clause choice point live and the caller backtracked into + % it -- probe P18 produced 1, 2, 3 where SWI gives 1. The then- and + % else-branches' own choice points are NOT cut (they belong to the + % enclosing clause). + format("~w while (state.cps.length > _ite_cps) state.cps.pop();~n", [Ind]), emit_struct_js(Then, Ind4), format("~w } else {~n", [Ind]), format("~w Runtime.restore_machine(state, _ite_snap);~n", [Ind]), @@ -759,20 +785,12 @@ function ~w(program, state) { const alt_pc = program.labels[~w]; if (alt_pc === undefined || alt_pc === null) return false; - const _cp = { - next_pc: alt_pc, - regs: Runtime.copy_table(state.regs), - cp: state.cp, - trail_len: state.trail.length, - var_counter: state.var_counter, - mode: state.mode, - build_stack: state.build_stack.slice(), - stack: state.stack.slice(), - read_stack: (state.read_stack || []).slice(), - read_args: state.read_args, - read_cursor: state.read_cursor, - y_save: (state.y_save || []).slice() - }; + // Runtime.snapshot_machine (not a hand-rolled literal): it also + // captures cut_barrier / cut_stack. Without them restore_machine + // DELETED the barrier and emptied the stack on backtrack into this + // choice point, so the alt clause ran with no cut barrier at all. + const _cp = Runtime.snapshot_machine(state); + _cp.next_pc = alt_pc; state.cps.push(_cp); const ok = (function () { ~w return false; @@ -806,20 +824,10 @@ if (t5a1 && typeof t5a1 === "object" && t5a1.tag !== "unbound") return false; const alt_pc = program.labels[~w]; if (alt_pc === undefined || alt_pc === null) return false; - const _cp = { - next_pc: alt_pc, - regs: Runtime.copy_table(state.regs), - cp: state.cp, - trail_len: state.trail.length, - var_counter: state.var_counter, - mode: state.mode, - build_stack: state.build_stack.slice(), - stack: state.stack.slice(), - read_stack: (state.read_stack || []).slice(), - read_args: state.read_args, - read_cursor: state.read_cursor, - y_save: (state.y_save || []).slice() - }; + // snapshot_machine also captures cut_barrier / cut_stack; a literal + // omitting them made restore_machine wipe the barrier stack. + const _cp = Runtime.snapshot_machine(state); + _cp.next_pc = alt_pc; state.cps.push(_cp); const ok = (function () { ~w return false; @@ -846,20 +854,12 @@ } const alt_pc = program.labels[~w]; if (alt_pc === undefined || alt_pc === null) return false; - const _cp = { - next_pc: alt_pc, - regs: Runtime.copy_table(state.regs), - cp: state.cp, - trail_len: state.trail.length, - var_counter: state.var_counter, - mode: state.mode, - build_stack: state.build_stack.slice(), - stack: state.stack.slice(), - read_stack: (state.read_stack || []).slice(), - read_args: state.read_args, - read_cursor: state.read_cursor, - y_save: (state.y_save || []).slice() - }; + // Runtime.snapshot_machine (not a hand-rolled literal): it also + // captures cut_barrier / cut_stack. Without them restore_machine + // DELETED the barrier and emptied the stack on backtrack into this + // choice point, so the alt clause ran with no cut barrier at all. + const _cp = Runtime.snapshot_machine(state); + _cp.next_pc = alt_pc; state.cps.push(_cp); const ok = (function () { ~w return false; @@ -1061,15 +1061,36 @@ % Direct JS call: the callee's Proceed is `return`, so Call does not % touch cp/pc. typeof is false when mixed([subset]) left it interpreted. format("~wif (typeof ~w === \"function\") {~n", [I, FuncName]), + % Choice points a lowered callee leaves (T5/T6 unbound-A1 dispatch) + % are not resumable from a lowered frame: their snapshot carries the + % LOWERED CALLER's cp, so backtracking into one resumes the + % interpreter past this call site and prints garbage (probe P01 in + % emit_mode(mixed) gave `r(1,one) _V2 _V2`). Drop them, matching + % Runtime.run_isolated: an inner lowered call is honestly + % first-solution rather than silently wrong. + format("~w const _cpd = state.cps.length;~n", [I]), ( Protect == true -> % Caller's Y is live across this Call (parse_args/2 after % default_registry). Match invoke_lowered_call: Y-snapshot + % mode/build/read restore so intern write-mode cannot clobber Y. - format("~w Runtime.push_y_save(state);~n", [I]), + % push_call_frame = Y-save + cut barrier (WAM call: B0 <- B), so + % a `!` in the callee prunes only the callee's alternatives. + format("~w Runtime.push_call_frame(state);~n", [I]), format("~w const _ok = Runtime.run_lowered_body(program, state, ~w);~n", [I, FuncName]), - format("~w Runtime.pop_y_save(state);~n", [I]), + format("~w Runtime.pop_call_frame(state);~n", [I]), + format("~w while (state.cps.length > _cpd) state.cps.pop();~n", [I]), format("~w if (_ok !== true) return false;~n", [I]) - ; format("~w if (~w(program, state) !== true) return false;~n", [I, FuncName]) + ; % WAM call: B0 <- B. Without the barrier a neck cut in the callee + % prunes the CALLER's choice points (the D44 bug shape, lowered + % side). Two array ops; the interpreter Call pays the same. + format("~w Runtime.push_cut_barrier(state);~n", [I]), + format("~w if (~w(program, state) !== true) {~n", [I, FuncName]), + format("~w Runtime.pop_cut_barrier(state);~n", [I]), + format("~w while (state.cps.length > _cpd) state.cps.pop();~n", [I]), + format("~w return false;~n", [I]), + format("~w }~n", [I]), + format("~w Runtime.pop_cut_barrier(state);~n", [I]), + format("~w while (state.cps.length > _cpd) state.cps.pop();~n", [I]) ), format("~w} else {~n", [I]), format("~w const saved_cp = state.cp;~n", [I]), @@ -1077,12 +1098,18 @@ format("~w const target = program.labels[~w];~n", [I, Q]), format("~w let _ok = true;~n", [I]), format("~w if (target !== undefined && target !== null) {~n", [I]), - format("~w Runtime.push_y_save(state);~n", [I]), + % push_call_frame (not bare push_y_save): the interpreted callee's + % Proceed runs pop_call_frame, which pops a cut_stack entry too. With + % only a Y-save pushed it popped THIS frame's barrier, so a `!` after + % the call pruned the caller's choice points (probe P28). + format("~w const _fm = Runtime.call_frame_mark(state);~n", [I]), + format("~w Runtime.push_call_frame(state);~n", [I]), format("~w state.cp = 0;~n", [I]), format("~w state.pc = target;~n", [I]), format("~w state.program = program;~n", [I]), format("~w _ok = Runtime.run_isolated(program, state) === true;~n", [I]), format("~w state.halt = false;~n", [I]), + format("~w Runtime.call_frame_release(state, _fm);~n", [I]), format("~w } else if (Runtime.step(program, state, I.Call(~w, ~w)) !== true) {~n", [I, PQ, Arity]), format("~w _ok = false;~n", [I]), format("~w }~n", [I]), @@ -1130,8 +1157,13 @@ wam_javascript_target:js_string_literal(PredName, PQ), % Lowered-to-lowered Execute: JS return IS Proceed. Do not proceed_to_cp % and do not touch cp (the caller's continuation stays in state.cp). - format("~wif (typeof ~w === \"function\") return ~w(program, state) === true;~n", - [I, FuncName, FuncName]), + % WAM execute still does B0 <- B: enter_execute rebases the cut + % barrier onto the tail-called predicate so its `!` cannot prune the + % choice points the CALLER left behind (probes P01/P33/P34). + format("~wif (typeof ~w === \"function\") {~n", [I, FuncName]), + format("~w Runtime.enter_execute(state);~n", [I]), + format("~w return ~w(program, state) === true;~n", [I, FuncName]), + format("~w}~n", [I]), format("~w{~n", [I]), format("~w const target = program.labels[~w];~n", [I, Q]), format("~w if (target !== undefined && target !== null) {~n", [I]), @@ -1240,8 +1272,18 @@ wam_javascript_explain_lower(_, _, fallback('not classified')). %% js_pi_needs_naked_member_cps(+PI) -% True when this predicate (or a user callee) uses member/2 outside -% findall/bagof/setof/once. Lowering would keep only the first witness. +% True when this predicate (or a user callee) uses a NONDETERMINISTIC +% BUILTIN outside findall/bagof/setof/once/aggregate_all/forall. +% Lowering would keep only the first witness. +% +% A lowered JS body is straight-line: when a goal fails there is no +% machinery to retry an earlier goal's choice point, and any choice +% point the goal left on state.cps is unreachable from the lowered +% frame (Runtime.run_isolated now drops those rather than letting a +% later backtrack resume the interpreter incoherently). The rule used +% to name member/2 only; every builtin in js_nondet_builtin/2 has the +% same shape, and `between(1, 3, X), X > 1, !` was the probe (P20) that +% showed member/2 was not special. js_pi_needs_naked_member_cps(PI) :- js_pi_functor_arity(PI, F, A), js_functor_needs_naked_member(F, A, []). @@ -1249,23 +1291,113 @@ js_pi_functor_arity(_M:F/A, F, A) :- !. js_pi_functor_arity(F/A, F, A). +% A CALLEE with more than one distinct clause can succeed more than once, +% so calling it leaves a choice point. A lowered body cannot resume one: +% either the callee is itself lowered and its T5/T6 unbound-A1 choice +% point snapshots the LOWERED CALLER's continuation, or it is interpreted +% and Runtime.run_isolated drops what it left. Either way the caller only +% ever sees the first solution, so the caller must stay on the +% interpreter. Visited == [] is the predicate being classified: its own +% clause count is fine (T4/T5/T6 dispatch it), only its CALLEES matter. +% +% Clauses are counted up to variant equality: a fixture that re-asserts +% the same fact must not look nondeterministic. +js_functor_needs_naked_member(F, A, Visited) :- + Visited \== [], + \+ memberchk(F/A, Visited), + js_distinct_clause_count(F, A, N), + N > 1, !. js_functor_needs_naked_member(F, A, Visited) :- \+ memberchk(F/A, Visited), functor(Head, F, A), catch(clause(user:Head, Body), _, fail), js_body_naked_member(Body, [F/A|Visited]). +js_distinct_clause_count(F, A, N) :- + js_distinct_clauses(F, A, Uniq), + length(Uniq, N). + +% Deduplicated but ORDER-PRESERVING: js_clauses_cut_committed/1 reads +% "every clause but the last", which is only meaningful in source order. +js_distinct_clauses(F, A, Uniq) :- + functor(Head, F, A), + findall(C, + ( catch(clause(user:Head, Body), _, fail), + copy_term(Head-Body, C), + numbervars(C, 0, _) + ), Cs), + js_dedupe_ordered(Cs, [], Uniq). + +js_dedupe_ordered([], _, []). +js_dedupe_ordered([C|Cs], Seen, Out) :- + ( memberchk(C, Seen) + -> js_dedupe_ordered(Cs, Seen, Out) + ; Out = [C|More], + js_dedupe_ordered(Cs, [C|Seen], More) + ). + +%% js_pi_may_yield_many(+PI) +% True when more than one clause of PI can succeed for the same call, so +% a first-solution lowering would hide answers. False (safe to lower) +% when the heads are first-argument mutually exclusive, or when every +% clause but the last commits with a top-level cut. +js_pi_may_yield_many(PI) :- + js_pi_functor_arity(PI, F, A), + js_distinct_clauses(F, A, Clauses), + Clauses = [_, _ | _], + \+ js_clauses_first_arg_exclusive(Clauses), + \+ js_clauses_cut_committed(Clauses). + +js_clauses_first_arg_exclusive(Clauses) :- + findall(K, ( member(H-_, Clauses), js_first_arg_key(H, K) ), Keys), + length(Clauses, N), + length(Keys, N), + sort(Keys, Sorted), + length(Sorted, N). + +js_first_arg_key(Head, Key) :- + compound(Head), + arg(1, Head, A1), + nonvar(A1), + % js_distinct_clauses/3 numbervars its output, so a clause-head + % variable arrives as '$VAR'(N) -- still a variable for indexing. + A1 \= '$VAR'(_), + ( compound(A1) + -> functor(A1, KF, KA), Key = KF/KA + ; Key = c(A1) + ). + +% Every clause but the last reaches a top-level `!` (a conjunction chain +% cut, not one buried in call/1, \+ or an if-then-else branch). +js_clauses_cut_committed(Clauses) :- + append(AllButLast, [_], Clauses), + AllButLast \== [], + forall(member(_-B, AllButLast), js_body_top_level_cut(B)). + +js_body_top_level_cut(B) :- var(B), !, fail. +js_body_top_level_cut(!) :- !. +js_body_top_level_cut((A, B)) :- !, + ( js_body_top_level_cut(A) + ; js_body_top_level_cut(B) + ). +js_body_top_level_cut(_) :- fail. + js_body_naked_member(Goal, _) :- var(Goal), !, fail. js_body_naked_member(_Mod:G, V) :- !, js_body_naked_member(G, V). -js_body_naked_member(member(_, _), _) :- !. -js_body_naked_member(lists:member(_, _), _) :- !. js_body_naked_member(findall(_, _, _), _) :- !, fail. js_body_naked_member(bagof(_, _, _), _) :- !, fail. js_body_naked_member(setof(_, _, _), _) :- !, fail. +js_body_naked_member(aggregate_all(_, _, _), _) :- !, fail. +js_body_naked_member(aggregate_all(_, _, _, _), _) :- !, fail. js_body_naked_member(once(_), _) :- !, fail. +js_body_naked_member(forall(_, _), _) :- !, fail. +js_body_naked_member(G, _) :- + nonvar(G), + functor(G, F, A), + js_nondet_builtin(F, A), !. js_body_naked_member((A, B), V) :- !, ( js_body_naked_member(A, V) ; js_body_naked_member(B, V) @@ -1286,6 +1418,19 @@ \+ js_body_skip_functor(F), js_functor_needs_naked_member(F, A, V). +%% js_nondet_builtin(+Name, +Arity) +% Library builtins that can leave a choice point on state.cps. A lowered +% body cannot resume one, so any predicate reaching one of these outside +% a commit wrapper stays on the interpreter. +% The list is exactly the builtins that push onto state.cps in +% runtime.js.mustache. Every other library predicate there is +% implemented semi-deterministically (select/3, nth0/3, append/3, +% sub_atom/5, ... return one solution and push nothing), so they cannot +% leak a choice point into a lowered body and must NOT be listed -- +% listing them only refuses lowering that is in fact safe. +js_nondet_builtin(member, 2). +js_nondet_builtin(between, 3). + js_body_skip_functor(','). js_body_skip_functor(';'). js_body_skip_functor('->'). diff --git a/templates/targets/javascript_wam/runtime.js.mustache b/templates/targets/javascript_wam/runtime.js.mustache index 2d203591d..875b92c36 100644 --- a/templates/targets/javascript_wam/runtime.js.mustache +++ b/templates/targets/javascript_wam/runtime.js.mustache @@ -508,17 +508,52 @@ function pop_y_save(state) { restore_yregs(state, state.y_save.pop()); } -// Neck-cut barrier: Call (and lowered Call) records cps.length so !/0 -// prunes only this predicate's alternatives, not the caller's member/2 -// (or other) choice points. A previous implementation did state.cps = [] -// which made every lowered helper with a neck cut (satisfies/2) steal -// search from the interpreter. +// --------------------------------------------------------------------- +// Choice-point barrier model (see docs/WAM_JAVASCRIPT_STATUS.md §"Cut and +// choice-point barriers"). +// +// state.cut_barrier -- the WAM B0 of the CURRENT predicate activation: +// the cps.length recorded when this predicate was +// entered. `!` prunes back to it. Saved on +// state.cut_stack by Call, restored by Proceed, +// REPLACED (not stacked) by Execute, because a +// last-call reuses the caller's frame slot but +// still gets its own B0 (WAM: both call and +// execute do B0 <- B). +// state.cp_barrier -- a HARD isolation floor: a nested Runtime.run +// driven from a lowered body (run_isolated) must +// neither backtrack below it nor cut below it. +// +// The effective floor for any cut is max(cut_barrier, cp_barrier): a +// stale (lower) cut_barrier inherited from an outer frame must never let +// an isolated sub-run destroy its caller's choice points. +// +// A previous implementation did state.cps = [] for `!`, which made every +// lowered helper with a neck cut (satisfies/2) steal search from the +// interpreter. +function cut_floor(state) { + let bar = 0; + if (typeof state.cut_barrier === "number") bar = state.cut_barrier; + if (typeof state.cp_barrier === "number" && state.cp_barrier > bar) { + bar = state.cp_barrier; + } + return bar; +} + function push_cut_barrier(state) { state.cut_stack = state.cut_stack || []; state.cut_stack.push(state.cut_barrier); state.cut_barrier = state.cps.length; } +// WAM `execute P`: B0 <- B without pushing a frame. The callee reuses the +// caller's cut_stack slot (its Proceed pops the entry the caller's Call +// pushed) but must get its OWN barrier, or a `!` in the tail-called +// predicate prunes the CALLER's clause alternatives. +function enter_execute(state) { + state.cut_barrier = state.cps.length; +} + function pop_cut_barrier(state) { const stack = state.cut_stack; if (!stack || stack.length === 0) { @@ -541,9 +576,7 @@ function pop_call_frame(state) { } function neck_cut(state) { - let bar = 0; - if (typeof state.cut_barrier === "number") bar = state.cut_barrier; - else if (typeof state.cp_barrier === "number") bar = state.cp_barrier; + const bar = cut_floor(state); while (state.cps.length > bar) state.cps.pop(); return true; } @@ -560,6 +593,35 @@ function proceed_to_cp(state) { Runtime.push_y_save = push_y_save; Runtime.pop_y_save = pop_y_save; +Runtime.push_cut_barrier = push_cut_barrier; +Runtime.pop_cut_barrier = pop_cut_barrier; +Runtime.push_call_frame = push_call_frame; +Runtime.pop_call_frame = pop_call_frame; +Runtime.enter_execute = enter_execute; +Runtime.cut_floor = cut_floor; + +// Frame-stack repair for nested interpreter runs driven from a lowered +// body. The callee's Proceed does pop_call_frame unconditionally; when a +// lowered frame drove that run, the pop can consume an entry the nested +// call never pushed, leaving state.cut_barrier restored to the LOWERED +// CALLER'S caller. A `!` after the call then prunes choice points the +// caller still owns (probe P28: `findall(Y, (e(_), p28_h(Y)), L)` lost +// e/1's second solution). Mark before the nested run, release after. +Runtime.call_frame_mark = function (state) { + return { + y: (state.y_save || []).length, + c: (state.cut_stack || []).length, + b: state.cut_barrier + }; +}; +Runtime.call_frame_release = function (state, m) { + const ys = state.y_save || (state.y_save = []); + while (ys.length > m.y) ys.pop(); + const cs = state.cut_stack || (state.cut_stack = []); + while (cs.length > m.c) cs.pop(); + if (m.b === undefined) delete state.cut_barrier; + else state.cut_barrier = m.b; +}; Runtime.snapshot_machine = snapshot_machine; Runtime.restore_machine = restore_machine; @@ -679,6 +741,13 @@ Runtime.run_lowered_body = run_lowered_body; function invoke_lowered_call(program, state, fn) { const retPc = state.pc + 1; const savedCp = state.cp; + // Interpreter Call sets cp to the return address before entering the + // callee. Do the same for a lowered callee: a choice point the lowered + // body pushes (T5/T6 unbound-A1 dispatch) snapshots state.cp, and on + // backtrack the alt clause's Proceed jumps there. With the caller's + // OLD cp it returned past this call site entirely, printing garbage + // for the second solution of a lowered fact predicate (probe P01). + state.cp = retPc; push_call_frame(state); const ok = run_lowered_body(program, state, fn); pop_call_frame(state); @@ -689,6 +758,7 @@ function invoke_lowered_call(program, state, fn) { } function invoke_lowered_execute(program, state, fn) { + enter_execute(state); const ok = run_lowered_body(program, state, fn); if (ok !== true) return false; state.indexed_entry = false; @@ -3064,7 +3134,68 @@ function run_builtin_or_label(program, parent, goal, shareBindings) { if (pred === "true") return true; if (pred === "fail" || pred === "false") return false; if (pred === "^" && arity >= 2) { - return invoke_goal(program, parent, args[1], share === true); + return invoke_goal(program, parent, args[1], shareBindings === true); + } + // Control constructs reached through call/1 (and through \+ /1, which + // shares this entry point). ISO: a cut inside call/1 is LOCAL to the + // call, so `!` here commits the metacall and does not touch the + // caller's choice points. `call((G, !))` used to fail outright because + // ,/2 has no label and no builtin (probe P10). + if (pred === "," && arity === 2) { + if (invoke_goal(program, parent, args[0], shareBindings) !== true) return false; + return invoke_goal(program, parent, args[1], shareBindings) === true; + } + if (pred === ";" && arity === 2) { + const left = Runtime.deref(parent, args[0]); + const lname = (typeof left === "object" && left !== null && left.tag === "struct") + ? strip_trailing_arity(Runtime.string_of(intern, left.fid)) : null; + const largs = (left && left.args) || []; + if ((lname === "->" || lname === "*->") && largs.length === 2) { + if (goal_succeeds(program, parent, largs[0]) === true) { + // Re-run the condition for its bindings, then the then-branch. + if (invoke_goal(program, parent, largs[0], shareBindings) !== true) return false; + return invoke_goal(program, parent, largs[1], shareBindings) === true; + } + return invoke_goal(program, parent, args[1], shareBindings) === true; + } + if (invoke_goal(program, parent, args[0], shareBindings) === true) return true; + return invoke_goal(program, parent, args[1], shareBindings) === true; + } + if ((pred === "->" || pred === "*->") && arity === 2) { + if (invoke_goal(program, parent, args[0], shareBindings) !== true) return false; + return invoke_goal(program, parent, args[1], shareBindings) === true; + } + if ((pred === "\\+" || pred === "not") && arity === 1) { + return goal_succeeds(program, parent, args[0]) !== true; + } + if (pred === "!" && arity === 0) { + // Opaque: the metacall is already first-solution, so committing is a + // no-op. It must NOT prune the caller's choice points. + return true; + } + if (pred === "once" && arity === 1) { + return invoke_goal(program, parent, args[0], shareBindings) === true; + } + if (pred === "ignore" && arity === 1) { + invoke_goal(program, parent, args[0], shareBindings); + return true; + } + if (pred === "call" && arity >= 1) { + const inner = Runtime.deref(parent, args[0]); + if (arity === 1) return invoke_goal(program, parent, inner, shareBindings) === true; + const extra = args.slice(1); + let fid; + let base = []; + if (typeof inner === "object" && inner !== null && inner.tag === "struct") { + fid = inner.fid; + base = (inner.args || []).slice(); + } else if (typeof inner === "object" && inner !== null && inner.tag === "atom") { + fid = inner.id; + } else { + return false; + } + const built = V.Struct(fid, base.concat(extra)); + return invoke_goal(program, parent, built, shareBindings) === true; } function fresh_sub() { @@ -3987,8 +4118,14 @@ Runtime.step = function (program, state, inst) { return true; } if (op === "Cut") { + // Y-level (soft) cut: prune back to the level get_level recorded. + // Clamp at cp_barrier so an isolated sub-run can never destroy the + // choice points of the lowered frame that drove it. let lvl = Runtime.get_reg(state, inst.yn); if (typeof lvl !== "number") lvl = 0; + if (typeof state.cp_barrier === "number" && state.cp_barrier > lvl) { + lvl = state.cp_barrier; + } while (state.cps.length > lvl) state.cps.pop(); state.pc += 1; return true; @@ -4055,6 +4192,10 @@ Runtime.step = function (program, state, inst) { return false; } state.pc = target; + // WAM: execute does B0 <- B. Without this the tail-called predicate + // inherits the caller's barrier and its `!` prunes the CALLER's + // clause alternatives (probes P01/P04/P22/P33/P34/P35). + enter_execute(state); if (Runtime._prof) { prof_leave(); prof_enter(call_key(inst), true); @@ -4065,6 +4206,7 @@ Runtime.step = function (program, state, inst) { const lowered = lowered_fn_at_pc(program, inst.pc); if (lowered) return invoke_lowered_execute(program, state, lowered); state.pc = inst.pc; + if (state.pc !== undefined && state.pc !== null) enter_execute(state); if (Runtime._prof && state.pc !== undefined && state.pc !== null) { prof_leave(); prof_enter(pred_key_from_pc(program, state.pc), true); @@ -4101,6 +4243,14 @@ Runtime.step = function (program, state, inst) { }); agg.next_pc = endPc + 1; state.cps.push(agg); + // The inner goal of findall/bagof/setof/aggregate_all is an opaque + // cut scope (ISO: like call/1). Raise the barrier above the aggregate + // choice point so a `!` in the inner goal prunes only the inner + // goal's own alternatives and cannot destroy the aggregate CP itself + // (which would strand EndAggregate). The barrier and cut_stack are + // restored by restore_cp_frame when backtrack reaches `agg` -- its + // snapshot was taken before this push. + push_cut_barrier(state); state.pc += 1; return true; } @@ -4163,8 +4313,18 @@ Runtime.run = function (program, state) { // but backtrack must stop at the pre-call length (see cp_barrier). Runtime.run_isolated = function (program, state) { const prev = state.cp_barrier; - state.cp_barrier = state.cps.length; + const floor = state.cps.length; + state.cp_barrier = floor; const ok = Runtime.run(program, state) === true; + // Choice points the isolated run leaves behind are NOT resumable: the + // lowered frame that drove this run returns through JS, so a later + // backtrack into one of them would restart the interpreter at a pc + // inside the callee with a cp captured mid-isolation (it was forced to + // 0), producing an incoherent resume rather than a solution -- probe + // P20 printed `0 a 2 a 3` for `between(1,3,X), X > 1, !`. Dropping them + // makes an isolated call honestly first-solution, which is the lowered + // contract, instead of silently wrong. + while (state.cps.length > floor) state.cps.pop(); if (prev === undefined) delete state.cp_barrier; else state.cp_barrier = prev; return ok === true; @@ -4180,12 +4340,19 @@ Runtime.run_isolated = function (program, state) { // (the lowered wrapper, or invoke_lowered_execute) the actual Proceed. Runtime.execute_user_isolated = function (program, state, target) { const saved_cp = state.cp; + // The isolated callee's Proceed runs pop_call_frame; balance it with a + // frame of our own (and repair on the failure path) so it cannot pop + // the lowered caller's barrier. cut_barrier = cps.length here is also + // exactly the WAM `execute` B0 <- B for the tail-called predicate. + const mark = Runtime.call_frame_mark(state); + push_call_frame(state); state.cp = 0; state.pc = target; state.program = program; state.halt = false; const ok = Runtime.run_isolated(program, state) === true; state.halt = false; + Runtime.call_frame_release(state, mark); state.cp = saved_cp; return ok === true; }; diff --git a/tests/test_wam_javascript_cut_semantics.pl b/tests/test_wam_javascript_cut_semantics.pl new file mode 100644 index 000000000..d9d91a97a --- /dev/null +++ b/tests/test_wam_javascript_cut_semantics.pl @@ -0,0 +1,389 @@ +:- encoding(utf8). +% SPDX-License-Identifier: MIT OR Apache-2.0 +% Copyright (c) 2026 John William Creighton (@s243a) +% +% test_wam_javascript_cut_semantics.pl +% +% Cut and choice-point barrier conformance for the JS WAM backend. +% +% Every probe pNN is a failure-driven loop that prints ALL solutions of +% pNN_t/1. The SAME clauses run under SWI-Prolog (the oracle) and under +% Node, in four emit modes, and the two stdout streams must match exactly: +% +% interpreter -- everything on the WAM interpreter +% mixed -- every eligible predicate lowered to a JS function +% functions -- same, forced +% mixed(Helpers) -- only the pNN_h/_a/_b/_c/_g/_r helpers lowered, so +% the interpreted caller / lowered callee boundary +% (the shape the D44 cut bug lived in) is exercised +% in BOTH directions +% +% The barrier model these probes pin down is written up in +% docs/WAM_JAVASCRIPT_STATUS.md, section "Cut and choice-point barriers". +% +% swipl -q -g run_tests -t halt tests/test_wam_javascript_cut_semantics.pl + +:- module(test_wam_javascript_cut_semantics, + [test_wam_javascript_cut_semantics/0]). + +:- use_module(library(plunit)). +:- use_module(library(lists)). +:- use_module(library(filesex), [make_directory_path/1, directory_file_path/3]). +:- use_module(library(process)). +:- use_module('../src/unifyweaver/targets/wam_javascript_target', + [write_wam_javascript_project/3]). +:- use_module('../src/unifyweaver/targets/wam_javascript_lowered_emitter', + [wam_javascript_explain_lower/3]). +:- use_module('../src/unifyweaver/targets/wam_target', + [compile_predicate_to_wam_text/3]). + +% --------------------------------------------------------------------- +% The probe program. Kept as ONE source of truth: the same clause terms +% are asserted into user: (so SWI runs them) and handed to the JS WAM +% emitter (so Node runs them). There is no second copy to drift. +% --------------------------------------------------------------------- + +%% cut_probe(-Name, -Context) +% Name is pNN; Context names the barrier context the probe pins down. +cut_probe(p01, 'neck cut in a callee reached by Execute (D44 shape)'). +cut_probe(p02, 'mid-body cut'). +cut_probe(p03, 'last-goal cut'). +cut_probe(p04, 'cut in a callee tail-called from a nondet caller'). +cut_probe(p05, 'cut in an if-then-else CONDITION (condition CPs only)'). +cut_probe(p06, 'cut in an ITE condition that then fails'). +cut_probe(p07, 'cut in the THEN branch cuts the enclosing clause'). +cut_probe(p08, 'cut in the ELSE branch cuts the enclosing clause'). +cut_probe(p09, 'cut inside call/1 is local'). +cut_probe(p10, 'cut inside call((G, !)) is local to the call'). +cut_probe(p11, 'cut inside \\+ is local to the negation'). +cut_probe(p12, 'cut inside a findall inner goal'). +cut_probe(p13, 'cut inside findall; caller still nondeterministic after'). +cut_probe(p14, 'cut inside a bagof inner goal'). +cut_probe(p15, 'cut inside a setof inner goal'). +cut_probe(p16, 'cut inside an aggregate_all inner goal'). +cut_probe(p17, 'once/1 after a nondeterministic goal'). +cut_probe(p18, 'once/1 does not cut the enclosing predicate clauses'). +cut_probe(p19, 'deep recursion: cut binds only that activation'). +cut_probe(p20, 'cut after between/3 (a CP-creating builtin)'). +cut_probe(p21, 'cut after member/2'). +cut_probe(p22, 'caller keeps member/2 alternatives across a cutting callee'). +cut_probe(p23, '-> inside \\+'). +cut_probe(p24, 'nested ITE with a cut in the inner condition'). +cut_probe(p25, 'disjunction with a cut in the left branch'). +cut_probe(p26, 'disjunction with a cut in the right branch'). +cut_probe(p27, 'cut followed by more nondeterminism in the same clause'). +cut_probe(p28, 'cutting helper invoked from inside findall'). +cut_probe(p29, 'findall with a cut, nested inside \\+'). +cut_probe(p30, 'cut in an ITE condition inside a nondeterministic caller'). +cut_probe(p31, 'forall/2 (soft-cut rewrite)'). +cut_probe(p32, 'cut inside the ACTION of forall/2'). +cut_probe(p33, 'cutting callee two frames deep'). +cut_probe(p34, 'cut in the first clause guards the later clauses'). +cut_probe(p35, 'cut with a choice point created before the guard'). + +cut_probe_clause(d(1)). +cut_probe_clause(d(2)). +cut_probe_clause(d(3)). +cut_probe_clause(e(a)). +cut_probe_clause(e(b)). + +cut_probe_clause((p01_h(X) :- !, X = one)). +cut_probe_clause((p01_h(_) :- fail)). +cut_probe_clause((p01_t(r(X, Y)) :- d(X), p01_h(Y))). + +cut_probe_clause((p02_h(X, Y) :- d(X), X > 1, !, Y = big)). +cut_probe_clause(p02_h(_, small)). +cut_probe_clause((p02_t(r(X, Y)) :- p02_h(X, Y))). + +cut_probe_clause((p03_h(X) :- d(X), !)). +cut_probe_clause(p03_h(99)). +cut_probe_clause((p03_t(X) :- p03_h(X))). + +cut_probe_clause((p04_h(X) :- d(X), !)). +cut_probe_clause(p04_h(0)). +cut_probe_clause((p04_c(Y) :- e(_), p04_h(Y))). +cut_probe_clause((p04_t(Y) :- p04_c(Y))). + +cut_probe_clause((p05_h(X, R) :- ( d(X), ! -> R = then ; R = else ))). +cut_probe_clause((p05_t(r(X, R)) :- p05_h(X, R))). + +cut_probe_clause((p06_h(R) :- ( d(_), !, fail -> R = then ; R = else ))). +cut_probe_clause(p06_h(second)). +cut_probe_clause((p06_t(R) :- p06_h(R))). + +cut_probe_clause((p07_h(X, R) :- d(X), ( X > 1 -> !, R = big ; R = small ))). +cut_probe_clause(p07_h(_, none)). +cut_probe_clause((p07_t(r(X, R)) :- p07_h(X, R))). + +cut_probe_clause((p08_h(X, R) :- d(X), ( X > 2 -> R = big ; !, R = small ))). +cut_probe_clause(p08_h(_, none)). +cut_probe_clause((p08_t(r(X, R)) :- p08_h(X, R))). + +cut_probe_clause((p09_h(X) :- d(X), call(!))). +cut_probe_clause((p09_t(X) :- p09_h(X))). + +cut_probe_clause((p10_h(X) :- call((d(X), !)))). +cut_probe_clause(p10_h(9)). +cut_probe_clause((p10_t(X) :- p10_h(X))). + +cut_probe_clause((p11_h(X) :- d(X), \+ ( e(_), !, fail ))). +cut_probe_clause((p11_t(X) :- p11_h(X))). + +cut_probe_clause((p12_h(L) :- findall(X, (d(X), !), L))). +cut_probe_clause((p12_t(L) :- p12_h(L))). + +cut_probe_clause((p13_h(r(Y, L)) :- e(Y), findall(X, (d(X), !), L))). +cut_probe_clause((p13_t(R) :- p13_h(R))). + +cut_probe_clause((p14_h(L) :- bagof(X, (d(X), !), L))). +cut_probe_clause((p14_t(L) :- p14_h(L))). + +cut_probe_clause((p15_h(L) :- setof(X, (d(X), !), L))). +cut_probe_clause((p15_t(L) :- p15_h(L))). + +cut_probe_clause((p16_h(N) :- aggregate_all(count, (d(_), !), N))). +cut_probe_clause((p16_t(N) :- p16_h(N))). + +cut_probe_clause((p17_h(X) :- d(X), once(e(_)))). +cut_probe_clause((p17_t(X) :- p17_h(X))). + +cut_probe_clause((p18_h(X) :- once(d(X)))). +cut_probe_clause(p18_h(9)). +cut_probe_clause((p18_t(X) :- p18_h(X))). + +cut_probe_clause(p19_r([], [])). +cut_probe_clause((p19_r([H|T], [H2|T2]) :- + ( H > 1 -> H2 = big ; H2 = H ), !, p19_r(T, T2))). +cut_probe_clause((p19_t(L) :- p19_r([1, 2, 3], L))). + +cut_probe_clause((p20_h(X) :- between(1, 3, X), X > 1, !)). +cut_probe_clause(p20_h(0)). +cut_probe_clause((p20_t(X) :- p20_h(X))). + +cut_probe_clause((p21_h(X) :- member(X, [a, b, c]), !)). +cut_probe_clause(p21_h(z)). +cut_probe_clause((p21_t(X) :- p21_h(X))). + +cut_probe_clause(p22_g(a, one)). +cut_probe_clause(p22_g(b, two)). +cut_probe_clause((p22_h(K, V) :- p22_g(K, V), !)). +cut_probe_clause((p22_t(r(K, V)) :- member(K, [a, b]), p22_h(K, V))). + +cut_probe_clause((p23_h(X) :- d(X), \+ ( X > 1 -> fail ; true ))). +cut_probe_clause((p23_t(X) :- p23_h(X))). + +cut_probe_clause((p24_h(X, R) :- d(X), ( ( X > 1, ! ) -> R = hi ; R = lo ))). +cut_probe_clause((p24_t(r(X, R)) :- p24_h(X, R))). + +cut_probe_clause((p25_h(X) :- ( d(X), ! ; X = z ))). +cut_probe_clause(p25_h(9)). +cut_probe_clause((p25_t(X) :- p25_h(X))). + +cut_probe_clause((p26_h(X) :- ( fail ; d(X), ! ))). +cut_probe_clause(p26_h(9)). +cut_probe_clause((p26_t(X) :- p26_h(X))). + +cut_probe_clause((p27_h(r(X, Y)) :- d(X), !, e(Y))). +cut_probe_clause((p27_t(R) :- p27_h(R))). + +cut_probe_clause((p28_h(X) :- d(X), !)). +cut_probe_clause((p28_c(L) :- findall(Y, (e(_), p28_h(Y)), L))). +cut_probe_clause((p28_t(L) :- p28_c(L))). + +cut_probe_clause((p29_h :- \+ ( findall(X, (d(X), !), L), L == [] ))). +cut_probe_clause((p29_t(ok) :- p29_h)). + +cut_probe_clause((p30_h(X, R) :- ( d(X), !, X > 1 -> R = yes ; R = no ))). +cut_probe_clause((p30_c(r(Y, R)) :- e(Y), p30_h(_X, R))). +cut_probe_clause((p30_t(R) :- p30_c(R))). + +cut_probe_clause((p31_h(ok) :- forall(d(X), X > 0))). +cut_probe_clause((p31_t(R) :- p31_h(R))). + +cut_probe_clause((p32_h(ok) :- forall(d(X), (e(_), !, X > 0)))). +cut_probe_clause((p32_t(R) :- p32_h(R))). + +cut_probe_clause((p33_a(X) :- d(X), !)). +cut_probe_clause((p33_b(X) :- p33_a(X))). +cut_probe_clause((p33_c(r(Y, X)) :- e(Y), p33_b(X))). +cut_probe_clause((p33_t(R) :- p33_c(R))). + +cut_probe_clause((p34_h(X, one) :- X == 1, !)). +cut_probe_clause((p34_h(X, two) :- X == 2, !)). +cut_probe_clause(p34_h(_, many)). +cut_probe_clause((p34_t(r(X, R)) :- d(X), p34_h(X, R))). + +cut_probe_clause((p35_h(X, Y) :- member(Y, [p, q]), X > 1, !)). +cut_probe_clause(p35_h(_, none)). +cut_probe_clause((p35_t(r(X, Y)) :- d(X), p35_h(X, Y))). + +% --------------------------------------------------------------------- +% Installation +% --------------------------------------------------------------------- + +cut_probe_head(Clause, F/A) :- + ( Clause = (H :- _) -> true ; H = Clause ), + functor(H, F, A). + +install_cut_probes :- + findall(PI, (cut_probe_clause(C), cut_probe_head(C, PI)), PIs0), + sort(PIs0, PIs), + forall(member(F/A, PIs), + ( functor(H, F, A), catch(retractall(user:H), _, true) )), + forall(cut_probe(N, _), + ( functor(D, N, 0), catch(retractall(user:D), _, true) )), + forall(cut_probe_clause(C), assertz(user:C)), + % Failure-driven driver: print every solution of pNN_t/1, then stop. + % The driver itself holds no cut, so what the probe measures is the + % cut inside pNN_t's callees. + forall(cut_probe(N, _), + ( atom_concat(N, '_t', TName), + TG =.. [TName, R], + assertz(user:(N :- ( TG, write(R), nl, fail ; true ))) )). + +cut_probe_preds(Preds) :- + findall(user:PI, (cut_probe_clause(C), cut_probe_head(C, PI)), P0), + findall(user:(N/0), cut_probe(N, _), P1), + append(P0, P1, P2), + sort(P2, Preds). + +% pNN_h / _a / _b / _c / _g / _r are the "helper" tier: lowering only +% those puts a lowered callee under an interpreted caller AND an +% interpreted callee under a lowered caller. +cut_probe_helper_preds(Hot) :- + findall(F/A, + ( cut_probe_clause(C), cut_probe_head(C, F/A), + atom_concat(_, Suffix, F), + atom_length(Suffix, 2), + memberchk(Suffix, ['_h', '_a', '_b', '_c', '_g', '_r']) + ), Hot0), + sort(Hot0, Hot). + +% --------------------------------------------------------------------- +% Running +% --------------------------------------------------------------------- + +swi_probe_output(N, Out) :- + with_output_to(string(Raw), + ( catch(user:N, _, true) -> true ; true )), + normalize_output(Raw, Out). + +node_probe_output(Dir, N, Out) :- + directory_file_path(Dir, 'js', JsDir), + format(atom(Key), '~w/0', [N]), + process_create(path(node), ['generated_program.js', Key], + [cwd(JsDir), stdout(pipe(O)), stderr(pipe(E)), + process(Pid)]), + read_string(O, _, S1), + read_string(E, _, S2), + close(O), close(E), + process_wait(Pid, exit(_)), + atomic_list_concat([S1, S2], Raw), + normalize_output(Raw, Out). + +% The generated main prints an A-register dump plus a trailing +% true/false; strip those and all whitespace so only the probe's own +% write/1 lines are compared. +normalize_output(Raw, Out) :- + split_string(Raw, "\n", "\r", Lines0), + exclude(cut_probe_noise_line, Lines0, Lines), + atomic_list_concat(Lines, "\n", Joined), + split_string(Joined, " \t", "", Parts), + atomic_list_concat(Parts, '', Out). + +cut_probe_noise_line(""). +cut_probe_noise_line("true"). +cut_probe_noise_line("false"). +cut_probe_noise_line(L) :- + string_concat("A", Rest, L), + sub_string(Rest, B, _, _, " = "), + sub_string(Rest, 0, B, _, Num), + number_string(_, Num). + +compile_cut_probes(Mode, Dir) :- + cut_probe_preds(Preds), + ( Mode == helpers + -> cut_probe_helper_preds(Hot), Opts = [emit_mode(mixed(Hot))] + ; Opts = [emit_mode(Mode)] + ), + format(atom(Dir), 'output/js_wam_cut_semantics_~w', [Mode]), + make_directory_path(Dir), + write_wam_javascript_project(Preds, Opts, Dir). + +%% run_cut_probe_mode(+Mode) +% Compile in Mode, then require an EXACT match with SWI for every probe. +run_cut_probe_mode(Mode) :- + compile_cut_probes(Mode, Dir), + findall(fail(N, Ctx, Swi, Node), + ( cut_probe(N, Ctx), + swi_probe_output(N, Swi), + node_probe_output(Dir, N, Node), + Swi \== Node + ), Fails), + ( Fails == [] + -> true + ; forall(member(fail(N, Ctx, S, J), Fails), + format(user_error, + 'CUT PROBE DIVERGENCE [~w] ~w (~w)~n swi : ~q~n node: ~q~n', + [Mode, N, Ctx, S, J])), + fail + ). + +test_wam_javascript_cut_semantics :- + run_tests(js_wam_cut_semantics). + +:- begin_tests(js_wam_cut_semantics). + +test(interpreter_matches_swi, [setup(install_cut_probes)]) :- + run_cut_probe_mode(interpreter). + +test(helper_lowering_matches_swi, [setup(install_cut_probes)]) :- + run_cut_probe_mode(helpers). + +test(mixed_matches_swi, [setup(install_cut_probes)]) :- + run_cut_probe_mode(mixed). + +test(functions_matches_swi, [setup(install_cut_probes)]) :- + run_cut_probe_mode(functions). + +% A commit-less try_me_else block is a plain disjunction (A ; B), not an +% if-then-else. Lowering it as ite(A, [], B) deletes B as a retry +% alternative; the emitter must decline. +test(plain_disjunction_not_lowered_as_ite, [setup(install_cut_probes)]) :- + compile_predicate_to_wam_text(user:p25_h/1, + [ite_use_y_level(true), inline_bagof_setof(true)], Wam), + wam_javascript_explain_lower(user:p25_h/1, Wam, Decision), + assertion(Decision = fallback(_)). + +% A predicate whose clauses are neither first-argument exclusive nor +% cut-committed can yield more than one solution; a first-solution +% lowering would hide the rest. +test(multi_solution_predicate_not_lowered, [setup(install_cut_probes)]) :- + compile_predicate_to_wam_text(user:p18_h/1, + [ite_use_y_level(true), inline_bagof_setof(true)], Wam), + wam_javascript_explain_lower(user:p18_h/1, Wam, Decision), + assertion(Decision = fallback(_)). + +% A CP-creating builtin outside a commit wrapper keeps the predicate on +% the interpreter (the rule that used to name only member/2). +test(nondet_builtin_taints_caller, [setup(install_cut_probes)]) :- + compile_predicate_to_wam_text(user:p20_h/1, + [ite_use_y_level(true), inline_bagof_setof(true)], Wam), + wam_javascript_explain_lower(user:p20_h/1, Wam, Decision), + assertion(Decision = fallback(_)). + +% Cut-committed clause chains and first-argument-exclusive fact tables +% must STILL lower: the refusals above are targeted, not blanket. +test(committed_clause_chain_still_lowers, [setup(install_cut_probes)]) :- + compile_predicate_to_wam_text(user:p34_h/2, + [ite_use_y_level(true), inline_bagof_setof(true)], Wam), + wam_javascript_explain_lower(user:p34_h/2, Wam, Decision), + assertion(Decision = lower(_)). + +test(first_arg_exclusive_facts_still_lower, [setup(install_cut_probes)]) :- + compile_predicate_to_wam_text(user:d/1, + [ite_use_y_level(true), inline_bagof_setof(true)], Wam), + wam_javascript_explain_lower(user:d/1, Wam, Decision), + assertion(Decision = lower(_)). + +:- end_tests(js_wam_cut_semantics). From d1cbb5f0ae9ad54794f1475464a3efa1b9b2c68a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 19:22:39 +0000 Subject: [PATCH 2/6] Rebuild pkg_resolver wamjs against the cut-semantics runtime. Confirms P0.5 gates still hold on the coordinator tip: 38/38 SWI, 39/39 wamjs corpus, 2400/0 term-catalog differential. Co-authored-by: johns243a --- .../wamjs/js/generated_program.js | 3034 +++-------------- examples/pkg_resolver/wamjs/js/wam_runtime.js | 187 +- 2 files changed, 716 insertions(+), 2505 deletions(-) diff --git a/examples/pkg_resolver/wamjs/js/generated_program.js b/examples/pkg_resolver/wamjs/js/generated_program.js index 64a1aa46e..604011521 100644 --- a/examples/pkg_resolver/wamjs/js/generated_program.js +++ b/examples/pkg_resolver/wamjs/js/generated_program.js @@ -822,7 +822,7 @@ const shared_instructions = [ I.GetVariable(204, 3), I.GetVariable(208, 4), I.GetLevel(209), - I.TryMeElse("L_ite_else_43"), + I.TryMeElse("L_ite_else_45"), I.PutValue(201, 1), I.PutValue(206, 2), I.BuiltinCall("==/2", 2), @@ -835,7 +835,7 @@ const shared_instructions = [ I.SetValue(202), I.SetValue(203), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_43"), + I.Jump("L_ite_cont_45"), I.TrustMe(), I.PutVariable(207, 1), I.PutValue(204, 2), @@ -968,7 +968,7 @@ const shared_instructions = [ I.GetVariable(205, 3), I.GetVariable(207, 4), I.GetLevel(209), - I.TryMeElse("L_ite_else_45"), + I.TryMeElse("L_ite_else_47"), I.PutValue(205, 1), I.PutValue(203, 2), I.PutVariable(201, 3), @@ -979,10 +979,10 @@ const shared_instructions = [ I.PutValue(205, 3), I.PutValue(207, 4), I.Call("first_broken", 4), - I.Jump("L_ite_cont_45"), + I.Jump("L_ite_cont_47"), I.TrustMe(), I.GetLevel(210), - I.TryMeElse("L_ite_else_46"), + I.TryMeElse("L_ite_else_48"), I.PutValue(202, 1), I.PutValue(203, 2), I.PutValue(204, 3), @@ -996,7 +996,7 @@ const shared_instructions = [ I.SetValue(204), I.SetValue(206), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_46"), + I.Jump("L_ite_cont_48"), I.TrustMe(), I.PutValue(208, 1), I.PutValue(202, 2), @@ -1033,7 +1033,7 @@ const shared_instructions = [ I.GetVariable(204, 2), I.GetVariable(205, 3), I.GetLevel(206), - I.TryMeElse("L_ite_else_49"), + I.TryMeElse("L_ite_else_53"), I.PutValue(201, 1), I.PutValue(204, 2), I.BuiltinCall("==/2", 2), @@ -1041,7 +1041,7 @@ const shared_instructions = [ I.PutValue(205, 1), I.PutValue(202, 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_49"), + I.Jump("L_ite_cont_53"), I.TrustMe(), I.PutValue(203, 1), I.PutValue(204, 2), @@ -1091,7 +1091,7 @@ const shared_instructions = [ I.GetVariable(212, 5), I.GetVariable(213, 6), I.GetLevel(214), - I.TryMeElse("L_ite_else_51"), + I.TryMeElse("L_ite_else_55"), I.PutValue(210, 1), I.PutValue(211, 2), I.BuiltinCall("member/2", 2), @@ -1103,7 +1103,7 @@ const shared_instructions = [ I.PutValue(212, 5), I.PutValue(213, 6), I.Call("inst_walk", 6), - I.Jump("L_ite_cont_51"), + I.Jump("L_ite_cont_55"), I.TrustMe(), I.PutVariable(205, 205), I.PutVariable(201, 201), @@ -1262,7 +1262,7 @@ const shared_instructions = [ I.GetVariable(203, 3), I.GetVariable(207, 4), I.GetLevel(208), - I.TryMeElse("L_ite_else_54"), + I.TryMeElse("L_ite_else_58"), I.PutValue(205, 1), I.PutValue(201, 2), I.PutValue(202, 3), @@ -1276,7 +1276,7 @@ const shared_instructions = [ I.SetValue(201), I.SetValue(202), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_54"), + I.Jump("L_ite_cont_58"), I.TrustMe(), I.PutVariable(206, 1), I.PutValue(203, 2), @@ -1310,7 +1310,7 @@ const shared_instructions = [ I.GetVariable(204, 3), I.GetVariable(205, 4), I.GetLevel(206), - I.TryMeElse("L_ite_else_57"), + I.TryMeElse("L_ite_else_61"), I.PutValue(202, 1), I.PutValue(203, 2), I.PutVariable(201, 3), @@ -1322,7 +1322,7 @@ const shared_instructions = [ I.PutValue(205, 1), I.PutValue(201, 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_57"), + I.Jump("L_ite_cont_61"), I.TrustMe(), I.PutValue(202, 1), I.PutValue(203, 2), @@ -1363,7 +1363,7 @@ const shared_instructions = [ I.GetVariable(204, 2), I.GetVariable(205, 3), I.GetLevel(206), - I.TryMeElse("L_ite_else_60"), + I.TryMeElse("L_ite_else_64"), I.PutValue(201, 1), I.PutValue(204, 2), I.PutVariable(202, 3), @@ -1372,7 +1372,7 @@ const shared_instructions = [ I.PutValue(205, 1), I.PutValue(202, 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_60"), + I.Jump("L_ite_cont_64"), I.TrustMe(), I.PutValue(203, 1), I.PutValue(204, 2), @@ -1425,7 +1425,7 @@ const shared_instructions = [ I.GetVariable(208, 3), I.GetVariable(205, 4), I.GetLevel(210), - I.TryMeElse("L_ite_else_62"), + I.TryMeElse("L_ite_else_67"), I.PutValue(201, 1), I.PutValue(207, 2), I.BuiltinCall("==/2", 2), @@ -1441,7 +1441,7 @@ const shared_instructions = [ I.SetValue(203), I.SetValue(204), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_62"), + I.Jump("L_ite_cont_67"), I.TrustMe(), I.PutValue(205, 1), I.PutVariable(209, 2), @@ -1471,7 +1471,7 @@ const shared_instructions = [ I.GetVariable(206, 3), I.GetVariable(203, 4), I.GetLevel(208), - I.TryMeElse("L_ite_else_64"), + I.TryMeElse("L_ite_else_69"), I.PutValue(201, 1), I.PutValue(205, 2), I.BuiltinCall("==/2", 2), @@ -1484,7 +1484,7 @@ const shared_instructions = [ I.SetValue(202), I.SetVariable(207), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_64"), + I.Jump("L_ite_cont_69"), I.TrustMe(), I.PutValue(203, 1), I.PutVariable(207, 2), @@ -1566,7 +1566,7 @@ const shared_instructions = [ I.UnifyVariable(202), I.UnifyVariable(206), I.GetLevel(207), - I.TryMeElse("L_ite_else_66"), + I.TryMeElse("L_ite_else_72"), I.PutValue(203, 1), I.PutValue(204, 2), I.PutValue(205, 3), @@ -1574,11 +1574,11 @@ const shared_instructions = [ I.Call("conflicts_in", 4), I.Cut(207), I.BuiltinCall("fail/0", 0), - I.Jump("L_ite_cont_66"), + I.Jump("L_ite_cont_72"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.GetLevel(208), - I.TryMeElse("L_ite_else_67"), + I.TryMeElse("L_ite_else_73"), I.PutValue(203, 1), I.PutValue(201, 2), I.PutValue(202, 3), @@ -1586,7 +1586,7 @@ const shared_instructions = [ I.Call("conflicts_in", 4), I.Cut(208), I.BuiltinCall("fail/0", 0), - I.Jump("L_ite_cont_67"), + I.Jump("L_ite_cont_73"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.PutValue(203, 1), @@ -1659,7 +1659,7 @@ const shared_instructions = [ I.GetVariable(205, 6), I.GetVariable(206, 7), I.GetLevel(207), - I.TryMeElse("L_ite_else_72"), + I.TryMeElse("L_ite_else_78"), I.PutValue(202, 1), I.PutValue(203, 2), I.PutVariable(201, 3), @@ -1674,7 +1674,7 @@ const shared_instructions = [ I.PutValue(206, 1), I.PutConstant(V.Atom(31), 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_72"), + I.Jump("L_ite_cont_78"), I.TrustMe(), I.PutValue(202, 1), I.PutValue(203, 2), @@ -1714,20 +1714,20 @@ const shared_instructions = [ I.PutVariable(206, 2), I.Call("installed_list", 2), I.GetLevel(213), - I.TryMeElse("L_ite_else_75"), + I.TryMeElse("L_ite_else_81"), I.PutValue(210, 1), I.PutValue(207, 2), I.PutVariable(201, 3), I.Call("installed_ver", 3), I.Cut(213), I.BuiltinCall("true/0", 0), - I.Jump("L_ite_cont_75"), + I.Jump("L_ite_cont_81"), I.TrustMe(), I.PutVariable(201, 1), I.PutConstant(V.Atom(19), 2), I.BuiltinCall("=/2", 2), I.GetLevel(214), - I.TryMeElse("L_ite_else_76"), + I.TryMeElse("L_ite_else_82"), I.PutValue(201, 1), I.PutConstant(V.Atom(19), 2), I.BuiltinCall("==/2", 2), @@ -1735,7 +1735,7 @@ const shared_instructions = [ I.PutValue(212, 1), I.PutConstant(V.Atom(2), 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_76"), + I.Jump("L_ite_cont_82"), I.TrustMe(), I.PutValue(210, 1), I.PutValue(206, 2), @@ -1771,23 +1771,23 @@ const shared_instructions = [ I.PutValue(208, 2), I.BuiltinCall("member/2", 2), I.GetLevel(215), - I.TryMeElse("L_ite_else_77"), + I.TryMeElse("L_ite_else_83"), I.PutValue(204, 1), I.PutValue(209, 2), I.BuiltinCall("member/2", 2), I.Cut(215), I.BuiltinCall("fail/0", 0), - I.Jump("L_ite_cont_77"), + I.Jump("L_ite_cont_83"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.GetLevel(216), - I.TryMeElse("L_ite_else_78"), + I.TryMeElse("L_ite_else_84"), I.PutValue(210, 1), I.PutValue(204, 2), I.Call("base_name", 2), I.Cut(216), I.BuiltinCall("fail/0", 0), - I.Jump("L_ite_cont_78"), + I.Jump("L_ite_cont_84"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.PutStructure(22, 1, 2), @@ -1829,7 +1829,7 @@ const shared_instructions = [ I.UnifyVariable(204), I.GetVariable(205, 2), I.GetLevel(206), - I.TryMeElse("L_ite_else_87"), + I.TryMeElse("L_ite_else_93"), I.PutValue(205, 1), I.PutValue(201, 2), I.PutVariable(202, 3), @@ -1838,7 +1838,7 @@ const shared_instructions = [ I.PutValue(202, 1), I.PutValue(203, 2), I.Call("satisfies", 2), - I.Jump("L_ite_cont_87"), + I.Jump("L_ite_cont_93"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.PutValue(204, 1), @@ -1852,7 +1852,7 @@ const shared_instructions = [ I.UnifyVariable(204), I.UnifyVariable(205), I.GetLevel(206), - I.TryMeElse("L_ite_else_89"), + I.TryMeElse("L_ite_else_96"), I.PutValue(203, 1), I.PutStructure(15, 2, 2), I.SetVariable(201), @@ -1863,7 +1863,7 @@ const shared_instructions = [ I.PutValue(201, 2), I.PutValue(204, 3), I.Call("canonicalize_name", 3), - I.Jump("L_ite_cont_89"), + I.Jump("L_ite_cont_96"), I.TrustMe(), I.PutValue(202, 1), I.PutValue(203, 2), @@ -1958,7 +1958,7 @@ const shared_instructions = [ I.GetVariable(208, 4), I.GetVariable(209, 5), I.GetLevel(212), - I.TryMeElse("L_ite_else_91"), + I.TryMeElse("L_ite_else_99"), I.PutValue(208, 1), I.PutValue(210, 2), I.PutVariable(211, 3), @@ -1973,7 +1973,7 @@ const shared_instructions = [ I.PutValue(208, 4), I.PutValue(209, 5), I.Call("resolve_pending", 5), - I.Jump("L_ite_cont_91"), + I.Jump("L_ite_cont_99"), I.TrustMe(), I.PutValue(205, 1), I.PutValue(206, 2), @@ -1993,7 +1993,7 @@ const shared_instructions = [ I.PutVariable(207, 3), I.BuiltinCall("append/3", 3), I.GetLevel(213), - I.TryMeElse("L_ite_else_92"), + I.TryMeElse("L_ite_else_100"), I.PutValue(204, 1), I.PutConstant(V.Atom(31), 2), I.BuiltinCall("=/2", 2), @@ -2004,7 +2004,7 @@ const shared_instructions = [ I.PutValue(208, 4), I.PutValue(209, 5), I.Call("resolve_pending", 5), - I.Jump("L_ite_cont_92"), + I.Jump("L_ite_cont_100"), I.TrustMe(), I.PutValue(206, 1), I.PutValue(210, 2), @@ -2077,42 +2077,42 @@ const shared_instructions = [ I.PutVariable(202, 3), I.Call("canonicalize_name", 3), I.GetLevel(207), - I.TryMeElse("L_ite_else_97"), + I.TryMeElse("L_ite_else_105"), I.GetLevel(208), - I.TryMeElse("L_ite_else_98"), + I.TryMeElse("L_ite_else_106"), I.PutValue(201, 1), I.PutValue(202, 2), I.PutValue(206, 3), I.Call("package_in", 3), I.Cut(208), I.BuiltinCall("fail/0", 0), - I.Jump("L_ite_cont_98"), + I.Jump("L_ite_cont_106"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.Cut(207), I.PutValue(204, 1), I.PutConstant(V.Atom(33), 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_97"), + I.Jump("L_ite_cont_105"), I.TrustMe(), I.GetLevel(208), - I.TryMeElse("L_ite_else_99"), + I.TryMeElse("L_ite_else_107"), I.GetLevel(209), - I.TryMeElse("L_ite_else_100"), + I.TryMeElse("L_ite_else_108"), I.PutValue(201, 1), I.PutValue(202, 2), I.PutVariable(203, 3), I.Call("base_reason", 3), I.Cut(209), I.BuiltinCall("fail/0", 0), - I.Jump("L_ite_cont_100"), + I.Jump("L_ite_cont_108"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.Cut(208), I.PutValue(204, 1), I.PutConstant(V.Atom(33), 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_99"), + I.Jump("L_ite_cont_107"), I.TrustMe(), I.PutValue(201, 1), I.PutValue(202, 2), @@ -2198,13 +2198,13 @@ const shared_instructions = [ I.GetStructure(42, 2, 1), I.UnifyVariable(102), I.GetLevel(201), - I.TryMeElse("L_ite_else_109"), + I.TryMeElse("L_ite_else_117"), I.PutValue(101, 1), I.PutValue(102, 2), I.Call("version_lt", 2), I.Cut(201), I.BuiltinCall("fail/0", 0), - I.Jump("L_ite_cont_109"), + I.Jump("L_ite_cont_117"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.Proceed(), @@ -2224,13 +2224,13 @@ const shared_instructions = [ I.UnifyVariable(103), I.UnifyVariable(202), I.GetLevel(203), - I.TryMeElse("L_ite_else_110"), + I.TryMeElse("L_ite_else_118"), I.PutValue(201, 1), I.PutValue(103, 2), I.Call("version_lt", 2), I.Cut(203), I.BuiltinCall("fail/0", 0), - I.Jump("L_ite_cont_110"), + I.Jump("L_ite_cont_118"), I.TrustMe(), I.BuiltinCall("true/0", 0), I.PutValue(201, 1), @@ -2251,7 +2251,7 @@ const shared_instructions = [ I.GetVariable(205, 2), I.GetVariable(211, 3), I.GetLevel(212), - I.TryMeElse("L_ite_else_113"), + I.TryMeElse("L_ite_else_123"), I.PutValue(202, 1), I.PutStructure(26, 2, 2), I.SetConstant(V.Atom(25)), @@ -2262,10 +2262,10 @@ const shared_instructions = [ I.PutValue(205, 2), I.PutVariable(210, 3), I.Call("scan_base_holds", 3), - I.Jump("L_ite_cont_113"), + I.Jump("L_ite_cont_123"), I.TrustMe(), I.GetLevel(213), - I.TryMeElse("L_ite_else_114"), + I.TryMeElse("L_ite_else_124"), I.PutValue(202, 1), I.PutStructure(26, 2, 2), I.SetVariable(203), @@ -2275,10 +2275,10 @@ const shared_instructions = [ I.PutVariable(210, 1), I.PutValue(205, 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_114"), + I.Jump("L_ite_cont_124"), I.TrustMe(), I.GetLevel(214), - I.TryMeElse("L_ite_else_115"), + I.TryMeElse("L_ite_else_125"), I.PutValue(202, 1), I.PutStructure(25, 2, 2), I.SetVariable(113), @@ -2297,10 +2297,10 @@ const shared_instructions = [ I.SetValue(207), I.SetValue(208), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_115"), + I.Jump("L_ite_cont_125"), I.TrustMe(), I.GetLevel(215), - I.TryMeElse("L_ite_else_116"), + I.TryMeElse("L_ite_else_126"), I.PutValue(202, 1), I.PutStructure(22, 2, 2), I.SetVariable(206), @@ -2316,7 +2316,7 @@ const shared_instructions = [ I.SetValue(207), I.SetConstant(V.Atom(9)), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_116"), + I.Jump("L_ite_cont_126"), I.TrustMe(), I.PutVariable(210, 1), I.PutValue(205, 2), @@ -2332,13 +2332,13 @@ const shared_instructions = [ I.UnifyVariable(202), I.GetVariable(203, 2), I.GetLevel(204), - I.TryMeElse("L_ite_else_121"), + I.TryMeElse("L_ite_else_131"), I.PutValue(201, 1), I.PutValue(203, 2), I.BuiltinCall("==/2", 2), I.Cut(204), I.BuiltinCall("true/0", 0), - I.Jump("L_ite_cont_121"), + I.Jump("L_ite_cont_131"), I.TrustMe(), I.PutValue(202, 1), I.PutValue(203, 2), @@ -2352,7 +2352,7 @@ const shared_instructions = [ I.GetVariable(203, 2), I.GetVariable(204, 3), I.GetLevel(205), - I.TryMeElse("L_ite_else_123"), + I.TryMeElse("L_ite_else_133"), I.PutValue(201, 1), I.PutStructure(22, 2, 2), I.SetValue(203), @@ -2360,7 +2360,7 @@ const shared_instructions = [ I.BuiltinCall("=/2", 2), I.Cut(205), I.BuiltinCall("true/0", 0), - I.Jump("L_ite_cont_123"), + I.Jump("L_ite_cont_133"), I.TrustMe(), I.PutValue(202, 1), I.PutValue(203, 2), @@ -2395,7 +2395,7 @@ const shared_instructions = [ I.GetVariable(205, 2), I.GetVariable(206, 3), I.GetLevel(207), - I.TryMeElse("L_ite_else_125"), + I.TryMeElse("L_ite_else_135"), I.PutValue(201, 1), I.PutValue(206, 2), I.BuiltinCall("\\==/2", 2), @@ -2409,7 +2409,7 @@ const shared_instructions = [ I.Call("tight_constraint", 1), I.Cut(207), I.BuiltinCall("true/0", 0), - I.Jump("L_ite_cont_125"), + I.Jump("L_ite_cont_135"), I.TrustMe(), I.PutValue(204, 1), I.PutValue(205, 2), @@ -2480,7 +2480,7 @@ const shared_instructions = [ I.GetVariable(214, 6), I.GetVariable(213, 7), I.GetLevel(215), - I.TryMeElse("L_ite_else_128"), + I.TryMeElse("L_ite_else_138"), I.PutStructure(22, 1, 2), I.SetValue(211), I.SetVariable(207), @@ -2521,7 +2521,7 @@ const shared_instructions = [ I.PutValue(210, 1), I.PutValue(209, 2), I.BuiltinCall("=/2", 2), - I.Jump("L_ite_cont_128"), + I.Jump("L_ite_cont_138"), I.TrustMe(), I.PutValue(210, 1), I.PutStructure(5, 2, 2), @@ -2590,7 +2590,7 @@ const shared_instructions = [ I.PutVariable(202, 3), I.Call("canonicalize_name", 3), I.GetLevel(205), - I.TryMeElse("L_ite_else_131"), + I.TryMeElse("L_ite_else_141"), I.PutValue(201, 1), I.PutValue(202, 2), I.PutValue(203, 3), @@ -2605,7 +2605,7 @@ const shared_instructions = [ I.SetValue(203), I.PutValue(204, 3), I.Call("close_moving", 3), - I.Jump("L_ite_cont_131"), + I.Jump("L_ite_cont_141"), I.TrustMe(), I.PutValue(204, 1), I.PutConstant(V.Atom(33), 2), @@ -2623,16 +2623,16 @@ const shared_instructions = [ I.UnifyVariable(204), I.UnifyVariable(206), I.GetLevel(207), - I.TryMeElse("L_ite_else_134"), + I.TryMeElse("L_ite_else_144"), I.PutValue(201, 1), I.PutValue(202, 2), I.BuiltinCall(" _cpd) state.cps.pop(); + return false; + } + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); } else { const saved_cp = state.cp; const saved_pc = state.pc; const target = program.labels["selected_ver/3"]; let _ok = true; if (target !== undefined && target !== null) { - Runtime.push_y_save(state); + const _fm = Runtime.call_frame_mark(state); + Runtime.push_call_frame(state); state.cp = 0; state.pc = target; state.program = program; _ok = Runtime.run_isolated(program, state) === true; state.halt = false; + Runtime.call_frame_release(state, _fm); } else if (Runtime.step(program, state, I.Call("selected_ver", 3)) !== true) { _ok = false; } @@ -3208,19 +3218,29 @@ function lowered_seen_name_2(program, state) { Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); if (typeof lowered_seen_name_2 === "function") { - if (lowered_seen_name_2(program, state) !== true) return false; + const _cpd = state.cps.length; + Runtime.push_cut_barrier(state); + if (lowered_seen_name_2(program, state) !== true) { + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); + return false; + } + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); } else { const saved_cp = state.cp; const saved_pc = state.pc; const target = program.labels["seen_name/2"]; let _ok = true; if (target !== undefined && target !== null) { - Runtime.push_y_save(state); + const _fm = Runtime.call_frame_mark(state); + Runtime.push_call_frame(state); state.cp = 0; state.pc = target; state.program = program; _ok = Runtime.run_isolated(program, state) === true; state.halt = false; + Runtime.call_frame_release(state, _fm); } else if (Runtime.step(program, state, I.Call("seen_name", 2)) !== true) { _ok = false; } @@ -3270,19 +3290,29 @@ function lowered_scan_base_holds_3(program, state) { Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); { const v = Runtime.new_var(state); Runtime.put_reg(state, 210, v); Runtime.put_reg(state, 3, v); } if (typeof lowered_scan_base_holds_3 === "function") { - if (lowered_scan_base_holds_3(program, state) !== true) return false; + const _cpd = state.cps.length; + Runtime.push_cut_barrier(state); + if (lowered_scan_base_holds_3(program, state) !== true) { + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); + return false; + } + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); } else { const saved_cp = state.cp; const saved_pc = state.pc; const target = program.labels["scan_base_holds/3"]; let _ok = true; if (target !== undefined && target !== null) { - Runtime.push_y_save(state); + const _fm = Runtime.call_frame_mark(state); + Runtime.push_call_frame(state); state.cp = 0; state.pc = target; state.program = program; _ok = Runtime.run_isolated(program, state) === true; state.halt = false; + Runtime.call_frame_release(state, _fm); } else if (Runtime.step(program, state, I.Call("scan_base_holds", 3)) !== true) { _ok = false; } @@ -3374,7 +3404,10 @@ function lowered_scan_base_holds_3(program, state) { Runtime.put_reg(state, 2, Runtime.get_reg(state, 210)); Runtime.put_reg(state, 3, Runtime.get_reg(state, 211)); if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_scan_base_holds_3 === "function") return lowered_scan_base_holds_3(program, state) === true; + if (typeof lowered_scan_base_holds_3 === "function") { + Runtime.enter_execute(state); + return lowered_scan_base_holds_3(program, state) === true; + } { const target = program.labels["scan_base_holds/3"]; if (target !== undefined && target !== null) { @@ -3434,19 +3467,29 @@ function lowered_scan_base_holds_3(program, state) { Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); { const v = Runtime.new_var(state); Runtime.put_reg(state, 210, v); Runtime.put_reg(state, 3, v); } if (typeof lowered_scan_base_holds_3 === "function") { - if (lowered_scan_base_holds_3(program, state) !== true) return false; + const _cpd = state.cps.length; + Runtime.push_cut_barrier(state); + if (lowered_scan_base_holds_3(program, state) !== true) { + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); + return false; + } + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); } else { const saved_cp = state.cp; const saved_pc = state.pc; const target = program.labels["scan_base_holds/3"]; let _ok = true; if (target !== undefined && target !== null) { - Runtime.push_y_save(state); + const _fm = Runtime.call_frame_mark(state); + Runtime.push_call_frame(state); state.cp = 0; state.pc = target; state.program = program; _ok = Runtime.run_isolated(program, state) === true; state.halt = false; + Runtime.call_frame_release(state, _fm); } else if (Runtime.step(program, state, I.Call("scan_base_holds", 3)) !== true) { _ok = false; } @@ -3538,7 +3581,10 @@ function lowered_scan_base_holds_3(program, state) { Runtime.put_reg(state, 2, Runtime.get_reg(state, 210)); Runtime.put_reg(state, 3, Runtime.get_reg(state, 211)); if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_scan_base_holds_3 === "function") return lowered_scan_base_holds_3(program, state) === true; + if (typeof lowered_scan_base_holds_3 === "function") { + Runtime.enter_execute(state); + return lowered_scan_base_holds_3(program, state) === true; + } { const target = program.labels["scan_base_holds/3"]; if (target !== undefined && target !== null) { @@ -3552,9 +3598,16 @@ function lowered_scan_base_holds_3(program, state) { } lowered_dispatch["scan_base_holds/3"] = function (program, state) { return lowered_scan_base_holds_3(program, state); }; -// Lowered: satisfies/2 (T4 all-clauses inline) -function lowered_satisfies_2(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("satisfies/2"); +// wamjs lower fallback: satisfies/2 fallback(multi_clause_n) +// wamjs lower fallback: safe_upgrade_reason/5 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: safe_upgrade/4 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: roots_to_pairs/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: resolve_pending/5 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: resolve_layered/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: resolve/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// Lowered: requested_list/2 (T4 all-clauses inline) +function lowered_requested_list_2(program, state) { + if (Runtime._prof) Runtime.prof_lowered_call("requested_list/2"); const _t4_trail = state.trail.length; const _t4_regs = Runtime.copy_table(state.regs); const _t4_vc = state.var_counter; @@ -3566,8 +3619,14 @@ function lowered_satisfies_2(program, state) { const _t4_rargs = state.read_args; const _t4_rcur = state.read_cursor; if ((function () { - Runtime.put_reg(state, 101, Runtime.get_reg(state, 1)); - if (Runtime.op_get_constant(program, state, 2, V.Atom(32)) !== true) return false; + if (Runtime.op_get_structure(program, state, 6, 1, 6) !== true) return false; + if (Runtime.op_unify_variable(state, 101) !== true) return false; + if (Runtime.op_unify_variable(state, 102) !== true) return false; + if (Runtime.op_unify_variable(state, 103) !== true) return false; + if (Runtime.op_unify_variable(state, 104) !== true) return false; + if (Runtime.op_unify_variable(state, 105) !== true) return false; + if (Runtime.op_unify_variable(state, 106) !== true) return false; + if (Runtime.op_get_value(program, state, 106, 2) !== true) return false; return true; return false; })()) return true; @@ -3582,68 +3641,52 @@ function lowered_satisfies_2(program, state) { state.read_args = _t4_rargs; state.read_cursor = _t4_rcur; if ((function () { - Runtime.put_reg(state, 101, Runtime.get_reg(state, 1)); - if (Runtime.op_get_structure(program, state, 41, 2, 1) !== true) return false; + if (Runtime.op_get_structure(program, state, 6, 1, 9) !== true) return false; + if (Runtime.op_unify_variable(state, 101) !== true) return false; if (Runtime.op_unify_variable(state, 102) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 101)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 102)); - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; + if (Runtime.op_unify_variable(state, 103) !== true) return false; + if (Runtime.op_unify_variable(state, 104) !== true) return false; + if (Runtime.op_unify_variable(state, 105) !== true) return false; + if (Runtime.op_unify_variable(state, 106) !== true) return false; + if (Runtime.op_unify_variable(state, 107) !== true) return false; + if (Runtime.op_unify_variable(state, 108) !== true) return false; + if (Runtime.op_unify_variable(state, 109) !== true) return false; + if (Runtime.op_get_value(program, state, 106, 2) !== true) return false; return true; return false; })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; + return false; +} + +lowered_dispatch["requested_list/2"] = function (program, state) { return lowered_requested_list_2(program, state); }; +// wamjs lower fallback: request_to_req/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: reqs_ok_moving/2 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: repairs_moving/4 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: removal_orphans/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: pick_repair/4 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: pick/7 fallback(naked member/2 (or callee) needs interpreter choice points) +// Lowered: packages/2 (T4 all-clauses inline) +function lowered_packages_2(program, state) { + if (Runtime._prof) Runtime.prof_lowered_call("packages/2"); + const _t4_trail = state.trail.length; + const _t4_regs = Runtime.copy_table(state.regs); + const _t4_vc = state.var_counter; + const _t4_stack = state.stack.slice(); + const _t4_ysave = (state.y_save || []).slice(); + const _t4_mode = state.mode; + const _t4_build = state.build_stack.slice(); + const _t4_rstack = (state.read_stack || []).slice(); + const _t4_rargs = state.read_args; + const _t4_rcur = state.read_cursor; if ((function () { - Runtime.put_reg(state, 101, Runtime.get_reg(state, 1)); - if (Runtime.op_get_structure(program, state, 42, 2, 1) !== true) return false; + if (Runtime.op_get_structure(program, state, 6, 1, 6) !== true) return false; + if (Runtime.op_unify_variable(state, 101) !== true) return false; if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_get_level(state, 201) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 101)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 102)); - if (typeof lowered_version_lt_2 === "function") { - if (lowered_version_lt_2(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["version_lt/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("version_lt", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - if (Runtime.op_builtin(program, state, "fail/0", 0) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - if (Runtime.op_builtin(program, state, "true/0", 0) !== true) return false; - } - } + if (Runtime.op_unify_variable(state, 103) !== true) return false; + if (Runtime.op_unify_variable(state, 104) !== true) return false; + if (Runtime.op_unify_variable(state, 105) !== true) return false; + if (Runtime.op_unify_variable(state, 106) !== true) return false; + if (Runtime.op_get_value(program, state, 101, 2) !== true) return false; return true; return false; })()) return true; @@ -3658,104 +3701,63 @@ function lowered_satisfies_2(program, state) { state.read_args = _t4_rargs; state.read_cursor = _t4_rcur; if ((function () { - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 101, Runtime.get_reg(state, 1)); - if (Runtime.op_get_structure(program, state, 43, 2, 1) !== true) return false; + if (Runtime.op_get_structure(program, state, 6, 1, 9) !== true) return false; + if (Runtime.op_unify_variable(state, 101) !== true) return false; if (Runtime.op_unify_variable(state, 102) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 101)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 102)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_version_lt_2 === "function") return lowered_version_lt_2(program, state) === true; - { - const target = program.labels["version_lt/2"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "version_lt", 2) === true; - } + if (Runtime.op_unify_variable(state, 103) !== true) return false; + if (Runtime.op_unify_variable(state, 104) !== true) return false; + if (Runtime.op_unify_variable(state, 105) !== true) return false; + if (Runtime.op_unify_variable(state, 106) !== true) return false; + if (Runtime.op_unify_variable(state, 107) !== true) return false; + if (Runtime.op_unify_variable(state, 108) !== true) return false; + if (Runtime.op_unify_variable(state, 109) !== true) return false; + if (Runtime.op_get_value(program, state, 101, 2) !== true) return false; + return true; return false; })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { + return false; +} + +lowered_dispatch["packages/2"] = function (program, state) { return lowered_packages_2(program, state); }; +// wamjs lower fallback: package_in/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: no_acc_conflicts/4 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: needed_names/4 fallback(naked member/2 (or callee) needs interpreter choice points) +// Lowered: names_of/2 (T4 nil/cons dispatch; no snapshot on bound A1) +function lowered_names_of_2(program, state) { + if (Runtime._prof) Runtime.prof_lowered_call("names_of/2"); + const _a1 = Runtime.deref(state, Runtime.get_reg(state, 1)); + if (Runtime.term_is_nil(program, _a1)) { + if (Runtime.op_get_constant(program, state, 2, V.Atom(2)) !== true) return false; + return true; + return true; + } + if (Runtime.term_is_cons(program, _a1)) { if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 201, Runtime.get_reg(state, 1)); - if (Runtime.op_get_structure(program, state, 44, 2, 2) !== true) return false; + if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; + if (Runtime.op_unify_variable(state, 101) !== true) return false; + if (Runtime.op_get_structure(program, state, 22, 101, 2) !== true) return false; + if (Runtime.op_unify_variable(state, 102) !== true) return false; if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 202) !== true) return false; - if (Runtime.op_get_level(state, 203) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 103)); - if (typeof lowered_version_lt_2 === "function") { - if (lowered_version_lt_2(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["version_lt/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("version_lt", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - if (Runtime.op_builtin(program, state, "fail/0", 0) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - if (Runtime.op_builtin(program, state, "true/0", 0) !== true) return false; - } - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); + if (Runtime.op_unify_variable(state, 104) !== true) return false; + if (Runtime.op_get_list(program, state, 2, 5) !== true) return false; + if (Runtime.op_unify_value(program, state, 102) !== true) return false; + if (Runtime.op_unify_variable(state, 105) !== true) return false; + Runtime.put_reg(state, 1, Runtime.get_reg(state, 104)); + Runtime.put_reg(state, 2, Runtime.get_reg(state, 105)); if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_version_lt_2 === "function") return lowered_version_lt_2(program, state) === true; + if (typeof lowered_names_of_2 === "function") { + Runtime.enter_execute(state); + return lowered_names_of_2(program, state) === true; + } { - const target = program.labels["version_lt/2"]; + const target = program.labels["names_of/2"]; if (target !== undefined && target !== null) { return Runtime.execute_user_isolated(program, state, target) === true; } - return Runtime.op_builtin(program, state, "version_lt", 2) === true; + return Runtime.op_builtin(program, state, "names_of", 2) === true; } - return false; - })()) return true; - return false; -} - -lowered_dispatch["satisfies/2"] = function (program, state) { return lowered_satisfies_2(program, state); }; -// wamjs lower fallback: safe_upgrade_reason/5 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: safe_upgrade/4 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: roots_to_pairs/3 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: resolve_pending/5 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: resolve_layered/3 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: resolve/3 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: requested_list/2 (T4 all-clauses inline) -function lowered_requested_list_2(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("requested_list/2"); + return true; + } const _t4_trail = state.trail.length; const _t4_regs = Runtime.copy_table(state.regs); const _t4_vc = state.var_counter; @@ -3767,14 +3769,8 @@ function lowered_requested_list_2(program, state) { const _t4_rargs = state.read_args; const _t4_rcur = state.read_cursor; if ((function () { - if (Runtime.op_get_structure(program, state, 6, 1, 6) !== true) return false; - if (Runtime.op_unify_variable(state, 101) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - if (Runtime.op_unify_variable(state, 106) !== true) return false; - if (Runtime.op_get_value(program, state, 106, 2) !== true) return false; + if (Runtime.op_get_constant(program, state, 1, V.Atom(2)) !== true) return false; + if (Runtime.op_get_constant(program, state, 2, V.Atom(2)) !== true) return false; return true; return false; })()) return true; @@ -3789,708 +3785,38 @@ function lowered_requested_list_2(program, state) { state.read_args = _t4_rargs; state.read_cursor = _t4_rcur; if ((function () { - if (Runtime.op_get_structure(program, state, 6, 1, 9) !== true) return false; + if (Runtime.op_allocate(state) !== true) return false; + if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; if (Runtime.op_unify_variable(state, 101) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - if (Runtime.op_unify_variable(state, 106) !== true) return false; - if (Runtime.op_unify_variable(state, 107) !== true) return false; - if (Runtime.op_unify_variable(state, 108) !== true) return false; - if (Runtime.op_unify_variable(state, 109) !== true) return false; - if (Runtime.op_get_value(program, state, 106, 2) !== true) return false; - return true; - return false; - })()) return true; - return false; -} - -lowered_dispatch["requested_list/2"] = function (program, state) { return lowered_requested_list_2(program, state); }; -// Lowered: request_to_req/3 (if-then-else / negation / once) -function lowered_request_to_req_3(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("request_to_req/3"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 202, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 203, Runtime.get_reg(state, 2)); - if (Runtime.op_get_structure(program, state, 15, 3, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 204) !== true) return false; - if (Runtime.op_unify_variable(state, 205) !== true) return false; - if (Runtime.op_get_level(state, 206) !== true) return false; - { - const _ite_lite = Runtime.snapshot_lite(state); - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 203)); - if (Runtime.op_put_structure(program, state, 15, 2, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 201) !== true) return false; - if (Runtime.op_unify_value(program, state, 205) !== true) return false; - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 204)); - if (typeof lowered_canonicalize_name_3 === "function") { - if (lowered_canonicalize_name_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["canonicalize_name/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("canonicalize_name", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - } else { - Runtime.restore_lite(state, _ite_lite); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 204)); - if (typeof lowered_canonicalize_name_3 === "function") { - if (lowered_canonicalize_name_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["canonicalize_name/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("canonicalize_name", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 2, V.Atom(32)); - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } - } - if (Runtime.op_deallocate(state) !== true) return false; - return true; - return true; -} - -lowered_dispatch["request_to_req/3"] = function (program, state) { return lowered_request_to_req_3(program, state); }; -// Lowered: reqs_ok_moving/2 (T4 nil/cons dispatch; no snapshot on bound A1) -function lowered_reqs_ok_moving_2(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("reqs_ok_moving/2"); - const _a1 = Runtime.deref(state, Runtime.get_reg(state, 1)); - if (Runtime.term_is_nil(program, _a1)) { - Runtime.put_reg(state, 101, Runtime.get_reg(state, 2)); - return true; - return true; - } - if (Runtime.term_is_cons(program, _a1)) { - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 106) !== true) return false; - if (Runtime.op_get_structure(program, state, 15, 106, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 201) !== true) return false; - if (Runtime.op_unify_variable(state, 203) !== true) return false; - if (Runtime.op_unify_variable(state, 204) !== true) return false; - Runtime.put_reg(state, 205, Runtime.get_reg(state, 2)); - if (Runtime.op_get_level(state, 206) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 201)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 202, v); Runtime.put_reg(state, 3, v); } - if (typeof lowered_selected_ver_3 === "function") { - if (lowered_selected_ver_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["selected_ver/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("selected_ver", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - if (typeof lowered_satisfies_2 === "function") { - if (lowered_satisfies_2(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["satisfies/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("satisfies", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - if (Runtime.op_builtin(program, state, "true/0", 0) !== true) return false; - } - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_reqs_ok_moving_2 === "function") return lowered_reqs_ok_moving_2(program, state) === true; - { - const target = program.labels["reqs_ok_moving/2"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "reqs_ok_moving", 2) === true; - } - return true; - } - const _t4_trail = state.trail.length; - const _t4_regs = Runtime.copy_table(state.regs); - const _t4_vc = state.var_counter; - const _t4_stack = state.stack.slice(); - const _t4_ysave = (state.y_save || []).slice(); - const _t4_mode = state.mode; - const _t4_build = state.build_stack.slice(); - const _t4_rstack = (state.read_stack || []).slice(); - const _t4_rargs = state.read_args; - const _t4_rcur = state.read_cursor; - if ((function () { - if (Runtime.op_get_constant(program, state, 1, V.Atom(2)) !== true) return false; - Runtime.put_reg(state, 101, Runtime.get_reg(state, 2)); - return true; - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 106) !== true) return false; - if (Runtime.op_get_structure(program, state, 15, 106, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 201) !== true) return false; - if (Runtime.op_unify_variable(state, 203) !== true) return false; - if (Runtime.op_unify_variable(state, 204) !== true) return false; - Runtime.put_reg(state, 205, Runtime.get_reg(state, 2)); - if (Runtime.op_get_level(state, 206) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 201)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 202, v); Runtime.put_reg(state, 3, v); } - if (typeof lowered_selected_ver_3 === "function") { - if (lowered_selected_ver_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["selected_ver/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("selected_ver", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - if (typeof lowered_satisfies_2 === "function") { - if (lowered_satisfies_2(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["satisfies/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("satisfies", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - if (Runtime.op_builtin(program, state, "true/0", 0) !== true) return false; - } - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_reqs_ok_moving_2 === "function") return lowered_reqs_ok_moving_2(program, state) === true; - { - const target = program.labels["reqs_ok_moving/2"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "reqs_ok_moving", 2) === true; - } - return false; - })()) return true; - return false; -} - -lowered_dispatch["reqs_ok_moving/2"] = function (program, state) { return lowered_reqs_ok_moving_2(program, state); }; -// Lowered: repairs_moving/4 (deterministic) -function lowered_repairs_moving_4(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("repairs_moving/4"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 103, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 104, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 105, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 202, Runtime.get_reg(state, 4)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 103)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 104)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 105)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 201, v); Runtime.put_reg(state, 4, v); } - if (typeof lowered_collect_deps_4 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_collect_deps_4); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["collect_deps/4"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("collect_deps", 4)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_reqs_ok_moving_2 === "function") return lowered_reqs_ok_moving_2(program, state) === true; - { - const target = program.labels["reqs_ok_moving/2"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "reqs_ok_moving", 2) === true; - } - return true; -} - -lowered_dispatch["repairs_moving/4"] = function (program, state) { return lowered_repairs_moving_4(program, state); }; -// wamjs lower fallback: removal_orphans/3 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: pick_repair/4 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: pick/7 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: packages/2 (T4 all-clauses inline) -function lowered_packages_2(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("packages/2"); - const _t4_trail = state.trail.length; - const _t4_regs = Runtime.copy_table(state.regs); - const _t4_vc = state.var_counter; - const _t4_stack = state.stack.slice(); - const _t4_ysave = (state.y_save || []).slice(); - const _t4_mode = state.mode; - const _t4_build = state.build_stack.slice(); - const _t4_rstack = (state.read_stack || []).slice(); - const _t4_rargs = state.read_args; - const _t4_rcur = state.read_cursor; - if ((function () { - if (Runtime.op_get_structure(program, state, 6, 1, 6) !== true) return false; - if (Runtime.op_unify_variable(state, 101) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - if (Runtime.op_unify_variable(state, 106) !== true) return false; - if (Runtime.op_get_value(program, state, 101, 2) !== true) return false; - return true; - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_get_structure(program, state, 6, 1, 9) !== true) return false; - if (Runtime.op_unify_variable(state, 101) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - if (Runtime.op_unify_variable(state, 106) !== true) return false; - if (Runtime.op_unify_variable(state, 107) !== true) return false; - if (Runtime.op_unify_variable(state, 108) !== true) return false; - if (Runtime.op_unify_variable(state, 109) !== true) return false; - if (Runtime.op_get_value(program, state, 101, 2) !== true) return false; - return true; - return false; - })()) return true; - return false; -} - -lowered_dispatch["packages/2"] = function (program, state) { return lowered_packages_2(program, state); }; -// wamjs lower fallback: package_in/3 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: no_acc_conflicts/4 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: needed_names/4 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: names_of/2 (T4 nil/cons dispatch; no snapshot on bound A1) -function lowered_names_of_2(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("names_of/2"); - const _a1 = Runtime.deref(state, Runtime.get_reg(state, 1)); - if (Runtime.term_is_nil(program, _a1)) { - if (Runtime.op_get_constant(program, state, 2, V.Atom(2)) !== true) return false; - return true; - return true; - } - if (Runtime.term_is_cons(program, _a1)) { - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 101) !== true) return false; - if (Runtime.op_get_structure(program, state, 22, 101, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_get_list(program, state, 2, 5) !== true) return false; - if (Runtime.op_unify_value(program, state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 104)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 105)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_names_of_2 === "function") return lowered_names_of_2(program, state) === true; - { - const target = program.labels["names_of/2"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "names_of", 2) === true; - } - return true; - } - const _t4_trail = state.trail.length; - const _t4_regs = Runtime.copy_table(state.regs); - const _t4_vc = state.var_counter; - const _t4_stack = state.stack.slice(); - const _t4_ysave = (state.y_save || []).slice(); - const _t4_mode = state.mode; - const _t4_build = state.build_stack.slice(); - const _t4_rstack = (state.read_stack || []).slice(); - const _t4_rargs = state.read_args; - const _t4_rcur = state.read_cursor; - if ((function () { - if (Runtime.op_get_constant(program, state, 1, V.Atom(2)) !== true) return false; - if (Runtime.op_get_constant(program, state, 2, V.Atom(2)) !== true) return false; - return true; - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 101) !== true) return false; - if (Runtime.op_get_structure(program, state, 22, 101, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_get_list(program, state, 2, 5) !== true) return false; - if (Runtime.op_unify_value(program, state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 104)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 105)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_names_of_2 === "function") return lowered_names_of_2(program, state) === true; - { - const target = program.labels["names_of/2"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "names_of", 2) === true; - } - return false; - })()) return true; - return false; -} - -lowered_dispatch["names_of/2"] = function (program, state) { return lowered_names_of_2(program, state); }; -// wamjs lower fallback: member_selected/3 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: matching_versions/4 (T4 nil/cons dispatch; no snapshot on bound A1) -function lowered_matching_versions_4(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("matching_versions/4"); - const _a1 = Runtime.deref(state, Runtime.get_reg(state, 1)); - if (Runtime.term_is_nil(program, _a1)) { - Runtime.put_reg(state, 101, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 102, Runtime.get_reg(state, 3)); - if (Runtime.op_get_constant(program, state, 4, V.Atom(2)) !== true) return false; - return true; - return true; - } - if (Runtime.term_is_cons(program, _a1)) { - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 108) !== true) return false; - if (Runtime.op_get_structure(program, state, 27, 108, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 201) !== true) return false; - if (Runtime.op_unify_variable(state, 202) !== true) return false; - if (Runtime.op_unify_variable(state, 204) !== true) return false; - Runtime.put_reg(state, 205, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 206, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 203, Runtime.get_reg(state, 4)); - if (Runtime.op_get_level(state, 208) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - if (Runtime.op_builtin(program, state, "==/2", 2) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 206)); - if (typeof lowered_satisfies_2 === "function") { - if (lowered_satisfies_2(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["satisfies/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("satisfies", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 203)); - if (Runtime.op_put_structure(program, state, 5, 2, 2) !== true) return false; - if (Runtime.op_unify_value(program, state, 202) !== true) return false; - if (Runtime.op_unify_variable(state, 207) !== true) return false; - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 203)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 207, v); Runtime.put_reg(state, 2, v); } - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 206)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 207)); + if (Runtime.op_get_structure(program, state, 22, 101, 2) !== true) return false; + if (Runtime.op_unify_variable(state, 102) !== true) return false; + if (Runtime.op_unify_variable(state, 103) !== true) return false; + if (Runtime.op_unify_variable(state, 104) !== true) return false; + if (Runtime.op_get_list(program, state, 2, 5) !== true) return false; + if (Runtime.op_unify_value(program, state, 102) !== true) return false; + if (Runtime.op_unify_variable(state, 105) !== true) return false; + Runtime.put_reg(state, 1, Runtime.get_reg(state, 104)); + Runtime.put_reg(state, 2, Runtime.get_reg(state, 105)); if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_matching_versions_4 === "function") return lowered_matching_versions_4(program, state) === true; - { - const target = program.labels["matching_versions/4"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "matching_versions", 4) === true; - } - return true; - } - const _t4_trail = state.trail.length; - const _t4_regs = Runtime.copy_table(state.regs); - const _t4_vc = state.var_counter; - const _t4_stack = state.stack.slice(); - const _t4_ysave = (state.y_save || []).slice(); - const _t4_mode = state.mode; - const _t4_build = state.build_stack.slice(); - const _t4_rstack = (state.read_stack || []).slice(); - const _t4_rargs = state.read_args; - const _t4_rcur = state.read_cursor; - if ((function () { - if (Runtime.op_get_constant(program, state, 1, V.Atom(2)) !== true) return false; - Runtime.put_reg(state, 101, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 102, Runtime.get_reg(state, 3)); - if (Runtime.op_get_constant(program, state, 4, V.Atom(2)) !== true) return false; - return true; - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 108) !== true) return false; - if (Runtime.op_get_structure(program, state, 27, 108, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 201) !== true) return false; - if (Runtime.op_unify_variable(state, 202) !== true) return false; - if (Runtime.op_unify_variable(state, 204) !== true) return false; - Runtime.put_reg(state, 205, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 206, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 203, Runtime.get_reg(state, 4)); - if (Runtime.op_get_level(state, 208) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - if (Runtime.op_builtin(program, state, "==/2", 2) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 206)); - if (typeof lowered_satisfies_2 === "function") { - if (lowered_satisfies_2(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["satisfies/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("satisfies", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 203)); - if (Runtime.op_put_structure(program, state, 5, 2, 2) !== true) return false; - if (Runtime.op_unify_value(program, state, 202) !== true) return false; - if (Runtime.op_unify_variable(state, 207) !== true) return false; - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 203)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 207, v); Runtime.put_reg(state, 2, v); } - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } + if (typeof lowered_names_of_2 === "function") { + Runtime.enter_execute(state); + return lowered_names_of_2(program, state) === true; } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 206)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 207)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_matching_versions_4 === "function") return lowered_matching_versions_4(program, state) === true; { - const target = program.labels["matching_versions/4"]; + const target = program.labels["names_of/2"]; if (target !== undefined && target !== null) { return Runtime.execute_user_isolated(program, state, target) === true; } - return Runtime.op_builtin(program, state, "matching_versions", 4) === true; + return Runtime.op_builtin(program, state, "names_of", 2) === true; } return false; })()) return true; return false; } -lowered_dispatch["matching_versions/4"] = function (program, state) { return lowered_matching_versions_4(program, state); }; +lowered_dispatch["names_of/2"] = function (program, state) { return lowered_names_of_2(program, state); }; +// wamjs lower fallback: member_selected/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: matching_versions/4 fallback(naked member/2 (or callee) needs interpreter choice points) // Lowered: matching_deps/4 (T4 nil/cons dispatch; no snapshot on bound A1) function lowered_matching_deps_4(program, state) { if (Runtime._prof) Runtime.prof_lowered_call("matching_deps/4"); @@ -4550,7 +3876,10 @@ function lowered_matching_deps_4(program, state) { Runtime.put_reg(state, 3, Runtime.get_reg(state, 208)); Runtime.put_reg(state, 4, Runtime.get_reg(state, 209)); if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_matching_deps_4 === "function") return lowered_matching_deps_4(program, state) === true; + if (typeof lowered_matching_deps_4 === "function") { + Runtime.enter_execute(state); + return lowered_matching_deps_4(program, state) === true; + } { const target = program.labels["matching_deps/4"]; if (target !== undefined && target !== null) { @@ -4606,213 +3935,55 @@ function lowered_matching_deps_4(program, state) { const _ite_trail = state.trail.length; const _ite_args = Runtime.capture_a_regs(state, 8); const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 207)); - if (Runtime.op_builtin(program, state, "==/2", 2) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 208)); - if (Runtime.op_builtin(program, state, "==/2", 2) !== true) return false; - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); - if (Runtime.op_put_structure(program, state, 5, 2, 2) !== true) return false; - if (Runtime.op_unify_variable(state, 112) !== true) return false; - if (Runtime.op_unify_variable(state, 209) !== true) return false; - if (Runtime.op_put_structure(program, state, 15, 112, 2) !== true) return false; - if (Runtime.op_unify_value(program, state, 203) !== true) return false; - if (Runtime.op_unify_value(program, state, 204) !== true) return false; - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } else { - Runtime.undo_trail(state, _ite_trail); - Runtime.restore_a_regs(state, _ite_args); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 209, v); Runtime.put_reg(state, 2, v); } - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 206)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 207)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 208)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 209)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_matching_deps_4 === "function") return lowered_matching_deps_4(program, state) === true; - { - const target = program.labels["matching_deps/4"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "matching_deps", 4) === true; - } - return false; - })()) return true; - return false; -} - -lowered_dispatch["matching_deps/4"] = function (program, state) { return lowered_matching_deps_4(program, state); }; -// Lowered: map_requests/3 (T4 all-clauses inline) -function lowered_map_requests_3(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("map_requests/3"); - const _t4_trail = state.trail.length; - const _t4_regs = Runtime.copy_table(state.regs); - const _t4_vc = state.var_counter; - const _t4_stack = state.stack.slice(); - const _t4_ysave = (state.y_save || []).slice(); - const _t4_mode = state.mode; - const _t4_build = state.build_stack.slice(); - const _t4_rstack = (state.read_stack || []).slice(); - const _t4_rargs = state.read_args; - const _t4_rcur = state.read_cursor; - if ((function () { - Runtime.put_reg(state, 101, Runtime.get_reg(state, 1)); - if (Runtime.op_get_constant(program, state, 2, V.Atom(2)) !== true) return false; - if (Runtime.op_get_constant(program, state, 3, V.Atom(2)) !== true) return false; - return true; - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 201, Runtime.get_reg(state, 1)); - if (Runtime.op_get_list(program, state, 2, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_unify_variable(state, 202) !== true) return false; - if (Runtime.op_get_list(program, state, 3, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - if (Runtime.op_unify_variable(state, 203) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 104)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 105)); - if (typeof lowered_request_to_req_3 === "function") { - if (lowered_request_to_req_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["request_to_req/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("request_to_req", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 203)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_map_requests_3 === "function") return lowered_map_requests_3(program, state) === true; - { - const target = program.labels["map_requests/3"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "map_requests", 3) === true; - } - return false; - })()) return true; - return false; -} - -lowered_dispatch["map_requests/3"] = function (program, state) { return lowered_map_requests_3(program, state); }; -// Lowered: lookup_held/3 (if-then-else / negation / once) -function lowered_lookup_held_3(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("lookup_held/3"); - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 201) !== true) return false; - if (Runtime.op_unify_variable(state, 203) !== true) return false; - Runtime.put_reg(state, 204, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 205, Runtime.get_reg(state, 3)); - if (Runtime.op_get_level(state, 206) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 204)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 202, v); Runtime.put_reg(state, 3, v); } - if (typeof lowered_item_ver_3 === "function") { - if (lowered_item_ver_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["item_ver/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("item_ver", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 203)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 205)); - if (typeof lowered_lookup_held_3 === "function") { - if (lowered_lookup_held_3(program, state) !== true) return false; + Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); + Runtime.put_reg(state, 2, Runtime.get_reg(state, 207)); + if (Runtime.op_builtin(program, state, "==/2", 2) !== true) return false; + Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); + Runtime.put_reg(state, 2, Runtime.get_reg(state, 208)); + if (Runtime.op_builtin(program, state, "==/2", 2) !== true) return false; + return true; + })(); + if (_ite_cond) { + Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); + if (Runtime.op_put_structure(program, state, 5, 2, 2) !== true) return false; + if (Runtime.op_unify_variable(state, 112) !== true) return false; + if (Runtime.op_unify_variable(state, 209) !== true) return false; + if (Runtime.op_put_structure(program, state, 15, 112, 2) !== true) return false; + if (Runtime.op_unify_value(program, state, 203) !== true) return false; + if (Runtime.op_unify_value(program, state, 204) !== true) return false; + if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["lookup_held/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("lookup_held", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; + Runtime.undo_trail(state, _ite_trail); + Runtime.restore_a_regs(state, _ite_args); + Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); + { const v = Runtime.new_var(state); Runtime.put_reg(state, 209, v); Runtime.put_reg(state, 2, v); } + if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; } } - } - if (Runtime.op_deallocate(state) !== true) return false; - return true; - return true; + Runtime.put_reg(state, 1, Runtime.get_reg(state, 206)); + Runtime.put_reg(state, 2, Runtime.get_reg(state, 207)); + Runtime.put_reg(state, 3, Runtime.get_reg(state, 208)); + Runtime.put_reg(state, 4, Runtime.get_reg(state, 209)); + if (Runtime.op_deallocate(state) !== true) return false; + if (typeof lowered_matching_deps_4 === "function") { + Runtime.enter_execute(state); + return lowered_matching_deps_4(program, state) === true; + } + { + const target = program.labels["matching_deps/4"]; + if (target !== undefined && target !== null) { + return Runtime.execute_user_isolated(program, state, target) === true; + } + return Runtime.op_builtin(program, state, "matching_deps", 4) === true; + } + return false; + })()) return true; + return false; } -lowered_dispatch["lookup_held/3"] = function (program, state) { return lowered_lookup_held_3(program, state); }; +lowered_dispatch["matching_deps/4"] = function (program, state) { return lowered_matching_deps_4(program, state); }; +// wamjs lower fallback: map_requests/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: lookup_held/3 fallback(naked member/2 (or callee) needs interpreter choice points) // Lowered: layers_list/2 (T4 all-clauses inline) function lowered_layers_list_2(program, state) { if (Runtime._prof) Runtime.prof_lowered_call("layers_list/2"); @@ -4945,7 +4116,10 @@ function lowered_item_ver_3(program, state) { Runtime.put_reg(state, 2, Runtime.get_reg(state, 103)); Runtime.put_reg(state, 3, Runtime.get_reg(state, 104)); if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_lookup_held_3 === "function") return lowered_lookup_held_3(program, state) === true; + if (typeof lowered_lookup_held_3 === "function") { + Runtime.enter_execute(state); + return lowered_lookup_held_3(program, state) === true; + } { const target = program.labels["lookup_held/3"]; if (target !== undefined && target !== null) { @@ -5051,383 +4225,47 @@ function lowered_hold_reason_3(program, state) { Runtime.put_reg(state, 2, Runtime.get_reg(state, 204)); Runtime.put_reg(state, 3, Runtime.get_reg(state, 205)); if (typeof lowered_hold_reason_3 === "function") { - if (lowered_hold_reason_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["hold_reason/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("hold_reason", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - } - } - if (Runtime.op_deallocate(state) !== true) return false; - return true; - return true; -} - -lowered_dispatch["hold_reason/3"] = function (program, state) { return lowered_hold_reason_3(program, state); }; -// wamjs lower fallback: freeze_audit/2 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: first_broken/4 (T4 nil/cons dispatch; no snapshot on bound A1) -function lowered_first_broken_4(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("first_broken/4"); - const _a1 = Runtime.deref(state, Runtime.get_reg(state, 1)); - if (Runtime.term_is_nil(program, _a1)) { - Runtime.put_reg(state, 101, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 102, Runtime.get_reg(state, 3)); - if (Runtime.op_get_constant(program, state, 4, V.Atom(19)) !== true) return false; - return true; - return true; - } - if (Runtime.term_is_cons(program, _a1)) { - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 109) !== true) return false; - if (Runtime.op_get_structure(program, state, 8, 109, 3) !== true) return false; - if (Runtime.op_unify_variable(state, 203) !== true) return false; - if (Runtime.op_unify_variable(state, 204) !== true) return false; - if (Runtime.op_unify_variable(state, 110) !== true) return false; - if (Runtime.op_unify_variable(state, 208) !== true) return false; - Runtime.put_reg(state, 202, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 205, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 207, Runtime.get_reg(state, 4)); - if (Runtime.op_get_level(state, 209) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 201, v); Runtime.put_reg(state, 3, v); } - if (typeof lowered_selected_ver_3 === "function") { - if (lowered_selected_ver_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["selected_ver/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("selected_ver", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 208)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 207)); - if (typeof lowered_first_broken_4 === "function") { - if (lowered_first_broken_4(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["first_broken/4"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("first_broken", 4)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - if (Runtime.op_get_level(state, 210) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 205)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 206, v); Runtime.put_reg(state, 5, v); } - if (typeof lowered_dep_breaks_moving_5 === "function") { - if (lowered_dep_breaks_moving_5(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["dep_breaks_moving/5"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("dep_breaks_moving", 5)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 207)); - if (Runtime.op_put_structure(program, state, 21, 2, 3) !== true) return false; - if (Runtime.op_unify_value(program, state, 203) !== true) return false; - if (Runtime.op_unify_value(program, state, 204) !== true) return false; - if (Runtime.op_unify_value(program, state, 206) !== true) return false; - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 208)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 207)); - if (typeof lowered_first_broken_4 === "function") { - if (lowered_first_broken_4(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["first_broken/4"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("first_broken", 4)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - } - } - } - } - if (Runtime.op_deallocate(state) !== true) return false; - return true; - return true; - } - const _t4_trail = state.trail.length; - const _t4_regs = Runtime.copy_table(state.regs); - const _t4_vc = state.var_counter; - const _t4_stack = state.stack.slice(); - const _t4_ysave = (state.y_save || []).slice(); - const _t4_mode = state.mode; - const _t4_build = state.build_stack.slice(); - const _t4_rstack = (state.read_stack || []).slice(); - const _t4_rargs = state.read_args; - const _t4_rcur = state.read_cursor; - if ((function () { - if (Runtime.op_get_constant(program, state, 1, V.Atom(2)) !== true) return false; - Runtime.put_reg(state, 101, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 102, Runtime.get_reg(state, 3)); - if (Runtime.op_get_constant(program, state, 4, V.Atom(19)) !== true) return false; - return true; - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 109) !== true) return false; - if (Runtime.op_get_structure(program, state, 8, 109, 3) !== true) return false; - if (Runtime.op_unify_variable(state, 203) !== true) return false; - if (Runtime.op_unify_variable(state, 204) !== true) return false; - if (Runtime.op_unify_variable(state, 110) !== true) return false; - if (Runtime.op_unify_variable(state, 208) !== true) return false; - Runtime.put_reg(state, 202, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 205, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 207, Runtime.get_reg(state, 4)); - if (Runtime.op_get_level(state, 209) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 201, v); Runtime.put_reg(state, 3, v); } - if (typeof lowered_selected_ver_3 === "function") { - if (lowered_selected_ver_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["selected_ver/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("selected_ver", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 208)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 207)); - if (typeof lowered_first_broken_4 === "function") { - if (lowered_first_broken_4(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["first_broken/4"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("first_broken", 4)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; + const _cpd = state.cps.length; + Runtime.push_cut_barrier(state); + if (lowered_hold_reason_3(program, state) !== true) { + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); + return false; } + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - if (Runtime.op_get_level(state, 210) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 205)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 206, v); Runtime.put_reg(state, 5, v); } - if (typeof lowered_dep_breaks_moving_5 === "function") { - if (lowered_dep_breaks_moving_5(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["dep_breaks_moving/5"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("dep_breaks_moving", 5)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 207)); - if (Runtime.op_put_structure(program, state, 21, 2, 3) !== true) return false; - if (Runtime.op_unify_value(program, state, 203) !== true) return false; - if (Runtime.op_unify_value(program, state, 204) !== true) return false; - if (Runtime.op_unify_value(program, state, 206) !== true) return false; - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 208)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 207)); - if (typeof lowered_first_broken_4 === "function") { - if (lowered_first_broken_4(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["first_broken/4"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("first_broken", 4)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - } + const saved_cp = state.cp; + const saved_pc = state.pc; + const target = program.labels["hold_reason/3"]; + let _ok = true; + if (target !== undefined && target !== null) { + const _fm = Runtime.call_frame_mark(state); + Runtime.push_call_frame(state); + state.cp = 0; + state.pc = target; + state.program = program; + _ok = Runtime.run_isolated(program, state) === true; + state.halt = false; + Runtime.call_frame_release(state, _fm); + } else if (Runtime.step(program, state, I.Call("hold_reason", 3)) !== true) { + _ok = false; } + state.cp = saved_cp; + state.pc = saved_pc; + state.halt = false; + if (!_ok) return false; } } - if (Runtime.op_deallocate(state) !== true) return false; - return true; - return false; - })()) return true; - return false; + } + if (Runtime.op_deallocate(state) !== true) return false; + return true; + return true; } -lowered_dispatch["first_broken/4"] = function (program, state) { return lowered_first_broken_4(program, state); }; +lowered_dispatch["hold_reason/3"] = function (program, state) { return lowered_hold_reason_3(program, state); }; +// wamjs lower fallback: freeze_audit/2 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: first_broken/4 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: explain_blocked_list/3 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: explain_blocked/3 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: excluded_name/2 fallback(naked member/2 (or callee) needs interpreter choice points) @@ -5485,95 +4323,7 @@ function lowered_excluded_list_2(program, state) { } lowered_dispatch["excluded_list/2"] = function (program, state) { return lowered_excluded_list_2(program, state); }; -// Lowered: exclude_name/3 (T4 all-clauses inline) -function lowered_exclude_name_3(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("exclude_name/3"); - const _t4_trail = state.trail.length; - const _t4_regs = Runtime.copy_table(state.regs); - const _t4_vc = state.var_counter; - const _t4_stack = state.stack.slice(); - const _t4_ysave = (state.y_save || []).slice(); - const _t4_mode = state.mode; - const _t4_build = state.build_stack.slice(); - const _t4_rstack = (state.read_stack || []).slice(); - const _t4_rargs = state.read_args; - const _t4_rcur = state.read_cursor; - if ((function () { - Runtime.put_reg(state, 101, Runtime.get_reg(state, 1)); - if (Runtime.op_get_constant(program, state, 2, V.Atom(2)) !== true) return false; - if (Runtime.op_get_constant(program, state, 3, V.Atom(2)) !== true) return false; - return true; - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 201, Runtime.get_reg(state, 1)); - if (Runtime.op_get_list(program, state, 2, 5) !== true) return false; - if (Runtime.op_unify_value(program, state, 201) !== true) return false; - if (Runtime.op_unify_variable(state, 202) !== true) return false; - Runtime.put_reg(state, 203, Runtime.get_reg(state, 3)); - if (Runtime.op_builtin(program, state, "!/0", 0) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 203)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_exclude_name_3 === "function") return lowered_exclude_name_3(program, state) === true; - { - const target = program.labels["exclude_name/3"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "exclude_name", 3) === true; - } - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 101, Runtime.get_reg(state, 1)); - if (Runtime.op_get_list(program, state, 2, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_get_list(program, state, 3, 5) !== true) return false; - if (Runtime.op_unify_value(program, state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 101)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 103)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 104)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_exclude_name_3 === "function") return lowered_exclude_name_3(program, state) === true; - { - const target = program.labels["exclude_name/3"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "exclude_name", 3) === true; - } - return false; - })()) return true; - return false; -} - -lowered_dispatch["exclude_name/3"] = function (program, state) { return lowered_exclude_name_3(program, state); }; +// wamjs lower fallback: exclude_name/3 fallback(multi_clause_n) // Lowered: direct_on/4 (T4 nil/cons dispatch; no snapshot on bound A1) function lowered_direct_on_4(program, state) { if (Runtime._prof) Runtime.prof_lowered_call("direct_on/4"); @@ -5630,7 +4380,10 @@ function lowered_direct_on_4(program, state) { Runtime.put_reg(state, 3, Runtime.get_reg(state, 207)); Runtime.put_reg(state, 4, Runtime.get_reg(state, 208)); if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_direct_on_4 === "function") return lowered_direct_on_4(program, state) === true; + if (typeof lowered_direct_on_4 === "function") { + Runtime.enter_execute(state); + return lowered_direct_on_4(program, state) === true; + } { const target = program.labels["direct_on/4"]; if (target !== undefined && target !== null) { @@ -5713,7 +4466,10 @@ function lowered_direct_on_4(program, state) { Runtime.put_reg(state, 3, Runtime.get_reg(state, 207)); Runtime.put_reg(state, 4, Runtime.get_reg(state, 208)); if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_direct_on_4 === "function") return lowered_direct_on_4(program, state) === true; + if (typeof lowered_direct_on_4 === "function") { + Runtime.enter_execute(state); + return lowered_direct_on_4(program, state) === true; + } { const target = program.labels["direct_on/4"]; if (target !== undefined && target !== null) { @@ -5721,358 +4477,71 @@ function lowered_direct_on_4(program, state) { } return Runtime.op_builtin(program, state, "direct_on", 4) === true; } - return false; - })()) return true; - return false; -} - -lowered_dispatch["direct_on/4"] = function (program, state) { return lowered_direct_on_4(program, state); }; -// Lowered: depends_list/2 (T4 all-clauses inline) -function lowered_depends_list_2(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("depends_list/2"); - const _t4_trail = state.trail.length; - const _t4_regs = Runtime.copy_table(state.regs); - const _t4_vc = state.var_counter; - const _t4_stack = state.stack.slice(); - const _t4_ysave = (state.y_save || []).slice(); - const _t4_mode = state.mode; - const _t4_build = state.build_stack.slice(); - const _t4_rstack = (state.read_stack || []).slice(); - const _t4_rargs = state.read_args; - const _t4_rcur = state.read_cursor; - if ((function () { - if (Runtime.op_get_structure(program, state, 6, 1, 6) !== true) return false; - if (Runtime.op_unify_variable(state, 101) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - if (Runtime.op_unify_variable(state, 106) !== true) return false; - if (Runtime.op_get_value(program, state, 102, 2) !== true) return false; - return true; - return false; - })()) return true; - while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } - state.regs = Runtime.copy_table(_t4_regs); - state.var_counter = _t4_vc; - state.stack = _t4_stack.slice(); - state.y_save = _t4_ysave.slice(); - state.mode = _t4_mode; - state.build_stack = _t4_build.slice(); - state.read_stack = _t4_rstack.slice(); - state.read_args = _t4_rargs; - state.read_cursor = _t4_rcur; - if ((function () { - if (Runtime.op_get_structure(program, state, 6, 1, 9) !== true) return false; - if (Runtime.op_unify_variable(state, 101) !== true) return false; - if (Runtime.op_unify_variable(state, 102) !== true) return false; - if (Runtime.op_unify_variable(state, 103) !== true) return false; - if (Runtime.op_unify_variable(state, 104) !== true) return false; - if (Runtime.op_unify_variable(state, 105) !== true) return false; - if (Runtime.op_unify_variable(state, 106) !== true) return false; - if (Runtime.op_unify_variable(state, 107) !== true) return false; - if (Runtime.op_unify_variable(state, 108) !== true) return false; - if (Runtime.op_unify_variable(state, 109) !== true) return false; - if (Runtime.op_get_value(program, state, 102, 2) !== true) return false; - return true; - return false; - })()) return true; - return false; -} - -lowered_dispatch["depends_list/2"] = function (program, state) { return lowered_depends_list_2(program, state); }; -// wamjs lower fallback: depends_in/5 fallback(naked member/2 (or callee) needs interpreter choice points) -// wamjs lower fallback: dependents_installed/3 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: dependents/3 (deterministic) -function lowered_dependents_3(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("dependents/3"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 201, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 106, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 205, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 106)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 203, v); Runtime.put_reg(state, 3, v); } - if (typeof lowered_canonicalize_name_3 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_canonicalize_name_3); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["canonicalize_name/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("canonicalize_name", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 202, v); Runtime.put_reg(state, 2, v); } - if (typeof lowered_depends_list_2 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_depends_list_2); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["depends_list/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("depends_list", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - Runtime.put_reg(state, 3, V.Atom(2)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 204, v); Runtime.put_reg(state, 4, v); } - if (typeof lowered_direct_on_4 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_direct_on_4); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["direct_on/4"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("direct_on", 4)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - if (Runtime.op_builtin(program, state, "sort/2", 2) !== true) return false; - if (Runtime.op_builtin(program, state, "!/0", 0) !== true) return false; - if (Runtime.op_deallocate(state) !== true) return false; - return true; - return true; -} - -lowered_dispatch["dependents/3"] = function (program, state) { return lowered_dependents_3(program, state); }; -// Lowered: dep_breaks_moving/5 (deterministic) -function lowered_dep_breaks_moving_5(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("dep_breaks_moving/5"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 106, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 202, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 203, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 204, Runtime.get_reg(state, 4)); - Runtime.put_reg(state, 205, Runtime.get_reg(state, 5)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 106)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 201, v); Runtime.put_reg(state, 2, v); } - if (typeof lowered_depends_list_2 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_depends_list_2); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["depends_list/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("depends_list", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 203)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 5, Runtime.get_reg(state, 205)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_dep_breaks_5 === "function") return lowered_dep_breaks_5(program, state) === true; - { - const target = program.labels["dep_breaks/5"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "dep_breaks", 5) === true; - } - return true; -} - -lowered_dispatch["dep_breaks_moving/5"] = function (program, state) { return lowered_dep_breaks_moving_5(program, state); }; -// Lowered: dep_breaks/5 (if-then-else / negation / once) -function lowered_dep_breaks_5(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("dep_breaks/5"); - if (Runtime.op_allocate(state) !== true) return false; - if (Runtime.op_get_list(program, state, 1, 5) !== true) return false; - if (Runtime.op_unify_variable(state, 111) !== true) return false; - if (Runtime.op_get_structure(program, state, 24, 111, 4) !== true) return false; - if (Runtime.op_unify_variable(state, 201) !== true) return false; - if (Runtime.op_unify_variable(state, 202) !== true) return false; - if (Runtime.op_unify_variable(state, 203) !== true) return false; - if (Runtime.op_unify_variable(state, 205) !== true) return false; - if (Runtime.op_unify_variable(state, 206) !== true) return false; - Runtime.put_reg(state, 207, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 208, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 209, Runtime.get_reg(state, 4)); - Runtime.put_reg(state, 210, Runtime.get_reg(state, 5)); - if (Runtime.op_get_level(state, 211) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 207)); - if (Runtime.op_builtin(program, state, "==/2", 2) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 208)); - if (Runtime.op_builtin(program, state, "==/2", 2) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 209)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 204, v); Runtime.put_reg(state, 3, v); } - if (typeof lowered_selected_ver_3 === "function") { - if (lowered_selected_ver_3(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["selected_ver/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("selected_ver", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - if (Runtime.op_get_level(state, 212) !== true) return false; - { - const _ite_snap = Runtime.snapshot_machine(state); - const _ite_cps = state.cps.length; - const _ite_cond = (function () { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - if (typeof lowered_satisfies_2 === "function") { - if (lowered_satisfies_2(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["satisfies/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("satisfies", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - return true; - })(); - if (_ite_cond) { - if (Runtime.op_builtin(program, state, "fail/0", 0) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - if (Runtime.op_builtin(program, state, "true/0", 0) !== true) return false; - } - } - return true; - })(); - if (_ite_cond) { - Runtime.put_reg(state, 1, Runtime.get_reg(state, 210)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - if (Runtime.op_builtin(program, state, "=/2", 2) !== true) return false; - } else { - Runtime.restore_machine(state, _ite_snap); - while (state.cps.length > _ite_cps) state.cps.pop(); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 206)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 207)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 208)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 209)); - Runtime.put_reg(state, 5, Runtime.get_reg(state, 210)); - if (typeof lowered_dep_breaks_5 === "function") { - if (lowered_dep_breaks_5(program, state) !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["dep_breaks/5"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("dep_breaks", 5)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - } - } - if (Runtime.op_deallocate(state) !== true) return false; - return true; - return true; + return false; + })()) return true; + return false; +} + +lowered_dispatch["direct_on/4"] = function (program, state) { return lowered_direct_on_4(program, state); }; +// Lowered: depends_list/2 (T4 all-clauses inline) +function lowered_depends_list_2(program, state) { + if (Runtime._prof) Runtime.prof_lowered_call("depends_list/2"); + const _t4_trail = state.trail.length; + const _t4_regs = Runtime.copy_table(state.regs); + const _t4_vc = state.var_counter; + const _t4_stack = state.stack.slice(); + const _t4_ysave = (state.y_save || []).slice(); + const _t4_mode = state.mode; + const _t4_build = state.build_stack.slice(); + const _t4_rstack = (state.read_stack || []).slice(); + const _t4_rargs = state.read_args; + const _t4_rcur = state.read_cursor; + if ((function () { + if (Runtime.op_get_structure(program, state, 6, 1, 6) !== true) return false; + if (Runtime.op_unify_variable(state, 101) !== true) return false; + if (Runtime.op_unify_variable(state, 102) !== true) return false; + if (Runtime.op_unify_variable(state, 103) !== true) return false; + if (Runtime.op_unify_variable(state, 104) !== true) return false; + if (Runtime.op_unify_variable(state, 105) !== true) return false; + if (Runtime.op_unify_variable(state, 106) !== true) return false; + if (Runtime.op_get_value(program, state, 102, 2) !== true) return false; + return true; + return false; + })()) return true; + while (state.trail.length > _t4_trail) { const _n = state.trail.pop(); delete state.bindings[_n]; } + state.regs = Runtime.copy_table(_t4_regs); + state.var_counter = _t4_vc; + state.stack = _t4_stack.slice(); + state.y_save = _t4_ysave.slice(); + state.mode = _t4_mode; + state.build_stack = _t4_build.slice(); + state.read_stack = _t4_rstack.slice(); + state.read_args = _t4_rargs; + state.read_cursor = _t4_rcur; + if ((function () { + if (Runtime.op_get_structure(program, state, 6, 1, 9) !== true) return false; + if (Runtime.op_unify_variable(state, 101) !== true) return false; + if (Runtime.op_unify_variable(state, 102) !== true) return false; + if (Runtime.op_unify_variable(state, 103) !== true) return false; + if (Runtime.op_unify_variable(state, 104) !== true) return false; + if (Runtime.op_unify_variable(state, 105) !== true) return false; + if (Runtime.op_unify_variable(state, 106) !== true) return false; + if (Runtime.op_unify_variable(state, 107) !== true) return false; + if (Runtime.op_unify_variable(state, 108) !== true) return false; + if (Runtime.op_unify_variable(state, 109) !== true) return false; + if (Runtime.op_get_value(program, state, 102, 2) !== true) return false; + return true; + return false; + })()) return true; + return false; } -lowered_dispatch["dep_breaks/5"] = function (program, state) { return lowered_dep_breaks_5(program, state); }; +lowered_dispatch["depends_list/2"] = function (program, state) { return lowered_depends_list_2(program, state); }; +// wamjs lower fallback: depends_in/5 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: dependents_installed/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: dependents/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: dep_breaks_moving/5 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: dep_breaks/5 fallback(naked member/2 (or callee) needs interpreter choice points) // Lowered: conflicts_list/2 (T4 all-clauses inline) function lowered_conflicts_list_2(program, state) { if (Runtime._prof) Runtime.prof_lowered_call("conflicts_list/2"); @@ -6128,266 +4597,16 @@ function lowered_conflicts_list_2(program, state) { lowered_dispatch["conflicts_list/2"] = function (program, state) { return lowered_conflicts_list_2(program, state); }; // wamjs lower fallback: conflicts_in/4 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: collect_deps/4 (deterministic) -function lowered_collect_deps_4(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("collect_deps/4"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 105, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 202, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 203, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 204, Runtime.get_reg(state, 4)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 105)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 201, v); Runtime.put_reg(state, 2, v); } - if (typeof lowered_depends_list_2 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_depends_list_2); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["depends_list/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("depends_list", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 203)); - Runtime.put_reg(state, 4, Runtime.get_reg(state, 204)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_matching_deps_4 === "function") return lowered_matching_deps_4(program, state) === true; - { - const target = program.labels["matching_deps/4"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "matching_deps", 4) === true; - } - return true; -} - -lowered_dispatch["collect_deps/4"] = function (program, state) { return lowered_collect_deps_4(program, state); }; +// wamjs lower fallback: collect_deps/4 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: close_moving/3 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: canonicalize_name/3 (deterministic) -function lowered_canonicalize_name_3(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("canonicalize_name/3"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 104, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 202, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 203, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 104)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 201, v); Runtime.put_reg(state, 2, v); } - if (typeof lowered_alias_list_2 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_alias_list_2); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["alias_list/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("alias_list", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 203)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_alias_lookup_3 === "function") return lowered_alias_lookup_3(program, state) === true; - { - const target = program.labels["alias_lookup/3"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "alias_lookup", 3) === true; - } - return true; -} - -lowered_dispatch["canonicalize_name/3"] = function (program, state) { return lowered_canonicalize_name_3(program, state); }; +// wamjs lower fallback: canonicalize_name/3 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: candidates_high_first/4 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: blocked_from/4 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: blocked_acc_list/5 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: blocked_acc/5 fallback(naked member/2 (or callee) needs interpreter choice points) -// Lowered: base_ver/3 (deterministic) -function lowered_base_ver_3(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("base_ver/3"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 201, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 205, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 206, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 202, v); Runtime.put_reg(state, 2, v); } - if (typeof lowered_base_list_2 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_base_list_2); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["base_list/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("base_list", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 203, v); Runtime.put_reg(state, 2, v); } - if (typeof lowered_layers_list_2 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_layers_list_2); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["layers_list/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("layers_list", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 204, v); Runtime.put_reg(state, 3, v); } - if (Runtime.op_builtin(program, state, "append/3", 3) !== true) return false; - Runtime.put_reg(state, 1, Runtime.get_reg(state, 204)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 205)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 206)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_lookup_held_3 === "function") return lowered_lookup_held_3(program, state) === true; - { - const target = program.labels["lookup_held/3"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "lookup_held", 3) === true; - } - return true; -} - -lowered_dispatch["base_ver/3"] = function (program, state) { return lowered_base_ver_3(program, state); }; -// Lowered: base_reason/3 (deterministic) -function lowered_base_reason_3(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("base_reason/3"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 104, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 202, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 203, Runtime.get_reg(state, 3)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 104)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 201, v); Runtime.put_reg(state, 2, v); } - if (typeof lowered_base_holds_2 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_base_holds_2); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["base_holds/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("base_holds", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 3, Runtime.get_reg(state, 203)); - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_hold_reason_3 === "function") return lowered_hold_reason_3(program, state) === true; - { - const target = program.labels["hold_reason/3"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "hold_reason", 3) === true; - } - return true; -} - -lowered_dispatch["base_reason/3"] = function (program, state) { return lowered_base_reason_3(program, state); }; -// Lowered: base_name/2 (deterministic) -function lowered_base_name_2(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("base_name/2"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 101, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 102, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 101)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 102)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 103, v); Runtime.put_reg(state, 3, v); } - if (Runtime.op_deallocate(state) !== true) return false; - if (typeof lowered_base_ver_3 === "function") return lowered_base_ver_3(program, state) === true; - { - const target = program.labels["base_ver/3"]; - if (target !== undefined && target !== null) { - return Runtime.execute_user_isolated(program, state, target) === true; - } - return Runtime.op_builtin(program, state, "base_ver", 3) === true; - } - return true; -} - -lowered_dispatch["base_name/2"] = function (program, state) { return lowered_base_name_2(program, state); }; +// wamjs lower fallback: base_ver/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: base_reason/3 fallback(naked member/2 (or callee) needs interpreter choice points) +// wamjs lower fallback: base_name/2 fallback(naked member/2 (or callee) needs interpreter choice points) // Lowered: base_list/2 (T4 all-clauses inline) function lowered_base_list_2(program, state) { if (Runtime._prof) Runtime.prof_lowered_call("base_list/2"); @@ -6442,76 +4661,7 @@ function lowered_base_list_2(program, state) { } lowered_dispatch["base_list/2"] = function (program, state) { return lowered_base_list_2(program, state); }; -// Lowered: base_holds/2 (deterministic) -function lowered_base_holds_2(program, state) { - if (Runtime._prof) Runtime.prof_lowered_call("base_holds/2"); - if (Runtime.op_allocate(state) !== true) return false; - Runtime.put_reg(state, 104, Runtime.get_reg(state, 1)); - Runtime.put_reg(state, 203, Runtime.get_reg(state, 2)); - Runtime.put_reg(state, 1, Runtime.get_reg(state, 104)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 201, v); Runtime.put_reg(state, 2, v); } - if (typeof lowered_base_list_2 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_base_list_2); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["base_list/2"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("base_list", 2)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 201)); - Runtime.put_reg(state, 2, V.Atom(2)); - { const v = Runtime.new_var(state); Runtime.put_reg(state, 202, v); Runtime.put_reg(state, 3, v); } - if (typeof lowered_scan_base_holds_3 === "function") { - Runtime.push_y_save(state); - const _ok = Runtime.run_lowered_body(program, state, lowered_scan_base_holds_3); - Runtime.pop_y_save(state); - if (_ok !== true) return false; - } else { - const saved_cp = state.cp; - const saved_pc = state.pc; - const target = program.labels["scan_base_holds/3"]; - let _ok = true; - if (target !== undefined && target !== null) { - Runtime.push_y_save(state); - state.cp = 0; - state.pc = target; - state.program = program; - _ok = Runtime.run_isolated(program, state) === true; - state.halt = false; - } else if (Runtime.step(program, state, I.Call("scan_base_holds", 3)) !== true) { - _ok = false; - } - state.cp = saved_cp; - state.pc = saved_pc; - state.halt = false; - if (!_ok) return false; - } - Runtime.put_reg(state, 1, Runtime.get_reg(state, 202)); - Runtime.put_reg(state, 2, Runtime.get_reg(state, 203)); - if (Runtime.op_builtin(program, state, "sort/2", 2) !== true) return false; - if (Runtime.op_deallocate(state) !== true) return false; - return true; - return true; -} - -lowered_dispatch["base_holds/2"] = function (program, state) { return lowered_base_holds_2(program, state); }; +// wamjs lower fallback: base_holds/2 fallback(naked member/2 (or callee) needs interpreter choice points) // wamjs lower fallback: audit_holds/4 fallback(naked member/2 (or callee) needs interpreter choice points) // Lowered: alias_lookup/3 (T4 nil/cons dispatch; no snapshot on bound A1) function lowered_alias_lookup_3(program, state) { @@ -6554,19 +4704,29 @@ function lowered_alias_lookup_3(program, state) { Runtime.put_reg(state, 2, Runtime.get_reg(state, 204)); Runtime.put_reg(state, 3, Runtime.get_reg(state, 205)); if (typeof lowered_alias_lookup_3 === "function") { - if (lowered_alias_lookup_3(program, state) !== true) return false; + const _cpd = state.cps.length; + Runtime.push_cut_barrier(state); + if (lowered_alias_lookup_3(program, state) !== true) { + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); + return false; + } + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); } else { const saved_cp = state.cp; const saved_pc = state.pc; const target = program.labels["alias_lookup/3"]; let _ok = true; if (target !== undefined && target !== null) { - Runtime.push_y_save(state); + const _fm = Runtime.call_frame_mark(state); + Runtime.push_call_frame(state); state.cp = 0; state.pc = target; state.program = program; _ok = Runtime.run_isolated(program, state) === true; state.halt = false; + Runtime.call_frame_release(state, _fm); } else if (Runtime.step(program, state, I.Call("alias_lookup", 3)) !== true) { _ok = false; } @@ -6639,19 +4799,29 @@ function lowered_alias_lookup_3(program, state) { Runtime.put_reg(state, 2, Runtime.get_reg(state, 204)); Runtime.put_reg(state, 3, Runtime.get_reg(state, 205)); if (typeof lowered_alias_lookup_3 === "function") { - if (lowered_alias_lookup_3(program, state) !== true) return false; + const _cpd = state.cps.length; + Runtime.push_cut_barrier(state); + if (lowered_alias_lookup_3(program, state) !== true) { + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); + return false; + } + Runtime.pop_cut_barrier(state); + while (state.cps.length > _cpd) state.cps.pop(); } else { const saved_cp = state.cp; const saved_pc = state.pc; const target = program.labels["alias_lookup/3"]; let _ok = true; if (target !== undefined && target !== null) { - Runtime.push_y_save(state); + const _fm = Runtime.call_frame_mark(state); + Runtime.push_call_frame(state); state.cp = 0; state.pc = target; state.program = program; _ok = Runtime.run_isolated(program, state) === true; state.halt = false; + Runtime.call_frame_release(state, _fm); } else if (Runtime.step(program, state, I.Call("alias_lookup", 3)) !== true) { _ok = false; } @@ -6823,14 +4993,7 @@ function scan_base_holds(a1, a2, a3) { M.scan_base_holds = scan_base_holds; function satisfies(a1, a2) { - const state = Runtime.new_state(); - const args = [a1, a2]; - for (let i = 0; i < 2; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_satisfies_2(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 2115, [a1, a2]); } M.satisfies = satisfies; @@ -6877,38 +5040,17 @@ function requested_list(a1, a2) { M.requested_list = requested_list; function request_to_req(a1, a2, a3) { - const state = Runtime.new_state(); - const args = [a1, a2, a3]; - for (let i = 0; i < 3; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_request_to_req_3(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 1780, [a1, a2, a3]); } M.request_to_req = request_to_req; function reqs_ok_moving(a1, a2) { - const state = Runtime.new_state(); - const args = [a1, a2]; - for (let i = 0; i < 2; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_reqs_ok_moving_2(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 1749, [a1, a2]); } M.reqs_ok_moving = reqs_ok_moving; function repairs_moving(a1, a2, a3, a4) { - const state = Runtime.new_state(); - const args = [a1, a2, a3, a4]; - for (let i = 0; i < 4; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_repairs_moving_4(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 1735, [a1, a2, a3, a4]); } M.repairs_moving = repairs_moving; @@ -6972,14 +5114,7 @@ function member_selected(a1, a2, a3) { M.member_selected = member_selected; function matching_versions(a1, a2, a3, a4) { - const state = Runtime.new_state(); - const args = [a1, a2, a3, a4]; - for (let i = 0; i < 4; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_matching_versions_4(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 1387, [a1, a2, a3, a4]); } M.matching_versions = matching_versions; @@ -6996,26 +5131,12 @@ function matching_deps(a1, a2, a3, a4) { M.matching_deps = matching_deps; function map_requests(a1, a2, a3) { - const state = Runtime.new_state(); - const args = [a1, a2, a3]; - for (let i = 0; i < 3; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_map_requests_3(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 1315, [a1, a2, a3]); } M.map_requests = map_requests; function lookup_held(a1, a2, a3) { - const state = Runtime.new_state(); - const args = [a1, a2, a3]; - for (let i = 0; i < 3; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_lookup_held_3(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 1291, [a1, a2, a3]); } M.lookup_held = lookup_held; @@ -7108,14 +5229,7 @@ function freeze_audit(a1, a2) { M.freeze_audit = freeze_audit; function first_broken(a1, a2, a3, a4) { - const state = Runtime.new_state(); - const args = [a1, a2, a3, a4]; - for (let i = 0; i < 4; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_first_broken_4(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 883, [a1, a2, a3, a4]); } M.first_broken = first_broken; @@ -7147,14 +5261,7 @@ function excluded_list(a1, a2) { M.excluded_list = excluded_list; function exclude_name(a1, a2, a3) { - const state = Runtime.new_state(); - const args = [a1, a2, a3]; - for (let i = 0; i < 3; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_exclude_name_3(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 781, [a1, a2, a3]); } M.exclude_name = exclude_name; @@ -7193,38 +5300,17 @@ function dependents_installed(a1, a2, a3) { M.dependents_installed = dependents_installed; function dependents(a1, a2, a3) { - const state = Runtime.new_state(); - const args = [a1, a2, a3]; - for (let i = 0; i < 3; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_dependents_3(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 653, [a1, a2, a3]); } M.dependents = dependents; function dep_breaks_moving(a1, a2, a3, a4, a5) { - const state = Runtime.new_state(); - const args = [a1, a2, a3, a4, a5]; - for (let i = 0; i < 5; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_dep_breaks_moving_5(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 637, [a1, a2, a3, a4, a5]); } M.dep_breaks_moving = dep_breaks_moving; function dep_breaks(a1, a2, a3, a4, a5) { - const state = Runtime.new_state(); - const args = [a1, a2, a3, a4, a5]; - for (let i = 0; i < 5; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_dep_breaks_5(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 588, [a1, a2, a3, a4, a5]); } M.dep_breaks = dep_breaks; @@ -7246,14 +5332,7 @@ function conflicts_in(a1, a2, a3, a4) { M.conflicts_in = conflicts_in; function collect_deps(a1, a2, a3, a4) { - const state = Runtime.new_state(); - const args = [a1, a2, a3, a4]; - for (let i = 0; i < 4; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_collect_deps_4(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 534, [a1, a2, a3, a4]); } M.collect_deps = collect_deps; @@ -7263,14 +5342,7 @@ function close_moving(a1, a2, a3) { M.close_moving = close_moving; function canonicalize_name(a1, a2, a3) { - const state = Runtime.new_state(); - const args = [a1, a2, a3]; - for (let i = 0; i < 3; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_canonicalize_name_3(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 458, [a1, a2, a3]); } M.canonicalize_name = canonicalize_name; @@ -7295,38 +5367,17 @@ function blocked_acc(a1, a2, a3, a4, a5) { M.blocked_acc = blocked_acc; function base_ver(a1, a2, a3) { - const state = Runtime.new_state(); - const args = [a1, a2, a3]; - for (let i = 0; i < 3; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_base_ver_3(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 203, [a1, a2, a3]); } M.base_ver = base_ver; function base_reason(a1, a2, a3) { - const state = Runtime.new_state(); - const args = [a1, a2, a3]; - for (let i = 0; i < 3; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_base_reason_3(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 191, [a1, a2, a3]); } M.base_reason = base_reason; function base_name(a1, a2) { - const state = Runtime.new_state(); - const args = [a1, a2]; - for (let i = 0; i < 2; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_base_name_2(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 183, [a1, a2]); } M.base_name = base_name; @@ -7343,14 +5394,7 @@ function base_list(a1, a2) { M.base_list = base_list; function base_holds(a1, a2) { - const state = Runtime.new_state(); - const args = [a1, a2]; - for (let i = 0; i < 2; i++) { - Runtime.put_reg(state, i + 1, i < args.length && args[i] !== undefined ? args[i] : Runtime.new_var(state)); - } - state.cp = 0; - state.program = shared_program; - return lowered_base_holds_2(shared_program, state) === true; + return Runtime.run_predicate(shared_program, 144, [a1, a2]); } M.base_holds = base_holds; diff --git a/examples/pkg_resolver/wamjs/js/wam_runtime.js b/examples/pkg_resolver/wamjs/js/wam_runtime.js index 30fdb8d07..ac15baeda 100644 --- a/examples/pkg_resolver/wamjs/js/wam_runtime.js +++ b/examples/pkg_resolver/wamjs/js/wam_runtime.js @@ -508,17 +508,52 @@ function pop_y_save(state) { restore_yregs(state, state.y_save.pop()); } -// Neck-cut barrier: Call (and lowered Call) records cps.length so !/0 -// prunes only this predicate's alternatives, not the caller's member/2 -// (or other) choice points. A previous implementation did state.cps = [] -// which made every lowered helper with a neck cut (satisfies/2) steal -// search from the interpreter. +// --------------------------------------------------------------------- +// Choice-point barrier model (see docs/WAM_JAVASCRIPT_STATUS.md §"Cut and +// choice-point barriers"). +// +// state.cut_barrier -- the WAM B0 of the CURRENT predicate activation: +// the cps.length recorded when this predicate was +// entered. `!` prunes back to it. Saved on +// state.cut_stack by Call, restored by Proceed, +// REPLACED (not stacked) by Execute, because a +// last-call reuses the caller's frame slot but +// still gets its own B0 (WAM: both call and +// execute do B0 <- B). +// state.cp_barrier -- a HARD isolation floor: a nested Runtime.run +// driven from a lowered body (run_isolated) must +// neither backtrack below it nor cut below it. +// +// The effective floor for any cut is max(cut_barrier, cp_barrier): a +// stale (lower) cut_barrier inherited from an outer frame must never let +// an isolated sub-run destroy its caller's choice points. +// +// A previous implementation did state.cps = [] for `!`, which made every +// lowered helper with a neck cut (satisfies/2) steal search from the +// interpreter. +function cut_floor(state) { + let bar = 0; + if (typeof state.cut_barrier === "number") bar = state.cut_barrier; + if (typeof state.cp_barrier === "number" && state.cp_barrier > bar) { + bar = state.cp_barrier; + } + return bar; +} + function push_cut_barrier(state) { state.cut_stack = state.cut_stack || []; state.cut_stack.push(state.cut_barrier); state.cut_barrier = state.cps.length; } +// WAM `execute P`: B0 <- B without pushing a frame. The callee reuses the +// caller's cut_stack slot (its Proceed pops the entry the caller's Call +// pushed) but must get its OWN barrier, or a `!` in the tail-called +// predicate prunes the CALLER's clause alternatives. +function enter_execute(state) { + state.cut_barrier = state.cps.length; +} + function pop_cut_barrier(state) { const stack = state.cut_stack; if (!stack || stack.length === 0) { @@ -541,9 +576,7 @@ function pop_call_frame(state) { } function neck_cut(state) { - let bar = 0; - if (typeof state.cut_barrier === "number") bar = state.cut_barrier; - else if (typeof state.cp_barrier === "number") bar = state.cp_barrier; + const bar = cut_floor(state); while (state.cps.length > bar) state.cps.pop(); return true; } @@ -560,6 +593,35 @@ function proceed_to_cp(state) { Runtime.push_y_save = push_y_save; Runtime.pop_y_save = pop_y_save; +Runtime.push_cut_barrier = push_cut_barrier; +Runtime.pop_cut_barrier = pop_cut_barrier; +Runtime.push_call_frame = push_call_frame; +Runtime.pop_call_frame = pop_call_frame; +Runtime.enter_execute = enter_execute; +Runtime.cut_floor = cut_floor; + +// Frame-stack repair for nested interpreter runs driven from a lowered +// body. The callee's Proceed does pop_call_frame unconditionally; when a +// lowered frame drove that run, the pop can consume an entry the nested +// call never pushed, leaving state.cut_barrier restored to the LOWERED +// CALLER'S caller. A `!` after the call then prunes choice points the +// caller still owns (probe P28: `findall(Y, (e(_), p28_h(Y)), L)` lost +// e/1's second solution). Mark before the nested run, release after. +Runtime.call_frame_mark = function (state) { + return { + y: (state.y_save || []).length, + c: (state.cut_stack || []).length, + b: state.cut_barrier + }; +}; +Runtime.call_frame_release = function (state, m) { + const ys = state.y_save || (state.y_save = []); + while (ys.length > m.y) ys.pop(); + const cs = state.cut_stack || (state.cut_stack = []); + while (cs.length > m.c) cs.pop(); + if (m.b === undefined) delete state.cut_barrier; + else state.cut_barrier = m.b; +}; Runtime.snapshot_machine = snapshot_machine; Runtime.restore_machine = restore_machine; @@ -679,6 +741,13 @@ Runtime.run_lowered_body = run_lowered_body; function invoke_lowered_call(program, state, fn) { const retPc = state.pc + 1; const savedCp = state.cp; + // Interpreter Call sets cp to the return address before entering the + // callee. Do the same for a lowered callee: a choice point the lowered + // body pushes (T5/T6 unbound-A1 dispatch) snapshots state.cp, and on + // backtrack the alt clause's Proceed jumps there. With the caller's + // OLD cp it returned past this call site entirely, printing garbage + // for the second solution of a lowered fact predicate (probe P01). + state.cp = retPc; push_call_frame(state); const ok = run_lowered_body(program, state, fn); pop_call_frame(state); @@ -689,6 +758,7 @@ function invoke_lowered_call(program, state, fn) { } function invoke_lowered_execute(program, state, fn) { + enter_execute(state); const ok = run_lowered_body(program, state, fn); if (ok !== true) return false; state.indexed_entry = false; @@ -3064,7 +3134,68 @@ function run_builtin_or_label(program, parent, goal, shareBindings) { if (pred === "true") return true; if (pred === "fail" || pred === "false") return false; if (pred === "^" && arity >= 2) { - return invoke_goal(program, parent, args[1], share === true); + return invoke_goal(program, parent, args[1], shareBindings === true); + } + // Control constructs reached through call/1 (and through \+ /1, which + // shares this entry point). ISO: a cut inside call/1 is LOCAL to the + // call, so `!` here commits the metacall and does not touch the + // caller's choice points. `call((G, !))` used to fail outright because + // ,/2 has no label and no builtin (probe P10). + if (pred === "," && arity === 2) { + if (invoke_goal(program, parent, args[0], shareBindings) !== true) return false; + return invoke_goal(program, parent, args[1], shareBindings) === true; + } + if (pred === ";" && arity === 2) { + const left = Runtime.deref(parent, args[0]); + const lname = (typeof left === "object" && left !== null && left.tag === "struct") + ? strip_trailing_arity(Runtime.string_of(intern, left.fid)) : null; + const largs = (left && left.args) || []; + if ((lname === "->" || lname === "*->") && largs.length === 2) { + if (goal_succeeds(program, parent, largs[0]) === true) { + // Re-run the condition for its bindings, then the then-branch. + if (invoke_goal(program, parent, largs[0], shareBindings) !== true) return false; + return invoke_goal(program, parent, largs[1], shareBindings) === true; + } + return invoke_goal(program, parent, args[1], shareBindings) === true; + } + if (invoke_goal(program, parent, args[0], shareBindings) === true) return true; + return invoke_goal(program, parent, args[1], shareBindings) === true; + } + if ((pred === "->" || pred === "*->") && arity === 2) { + if (invoke_goal(program, parent, args[0], shareBindings) !== true) return false; + return invoke_goal(program, parent, args[1], shareBindings) === true; + } + if ((pred === "\\+" || pred === "not") && arity === 1) { + return goal_succeeds(program, parent, args[0]) !== true; + } + if (pred === "!" && arity === 0) { + // Opaque: the metacall is already first-solution, so committing is a + // no-op. It must NOT prune the caller's choice points. + return true; + } + if (pred === "once" && arity === 1) { + return invoke_goal(program, parent, args[0], shareBindings) === true; + } + if (pred === "ignore" && arity === 1) { + invoke_goal(program, parent, args[0], shareBindings); + return true; + } + if (pred === "call" && arity >= 1) { + const inner = Runtime.deref(parent, args[0]); + if (arity === 1) return invoke_goal(program, parent, inner, shareBindings) === true; + const extra = args.slice(1); + let fid; + let base = []; + if (typeof inner === "object" && inner !== null && inner.tag === "struct") { + fid = inner.fid; + base = (inner.args || []).slice(); + } else if (typeof inner === "object" && inner !== null && inner.tag === "atom") { + fid = inner.id; + } else { + return false; + } + const built = V.Struct(fid, base.concat(extra)); + return invoke_goal(program, parent, built, shareBindings) === true; } function fresh_sub() { @@ -3987,8 +4118,14 @@ Runtime.step = function (program, state, inst) { return true; } if (op === "Cut") { + // Y-level (soft) cut: prune back to the level get_level recorded. + // Clamp at cp_barrier so an isolated sub-run can never destroy the + // choice points of the lowered frame that drove it. let lvl = Runtime.get_reg(state, inst.yn); if (typeof lvl !== "number") lvl = 0; + if (typeof state.cp_barrier === "number" && state.cp_barrier > lvl) { + lvl = state.cp_barrier; + } while (state.cps.length > lvl) state.cps.pop(); state.pc += 1; return true; @@ -4055,6 +4192,10 @@ Runtime.step = function (program, state, inst) { return false; } state.pc = target; + // WAM: execute does B0 <- B. Without this the tail-called predicate + // inherits the caller's barrier and its `!` prunes the CALLER's + // clause alternatives (probes P01/P04/P22/P33/P34/P35). + enter_execute(state); if (Runtime._prof) { prof_leave(); prof_enter(call_key(inst), true); @@ -4065,6 +4206,7 @@ Runtime.step = function (program, state, inst) { const lowered = lowered_fn_at_pc(program, inst.pc); if (lowered) return invoke_lowered_execute(program, state, lowered); state.pc = inst.pc; + if (state.pc !== undefined && state.pc !== null) enter_execute(state); if (Runtime._prof && state.pc !== undefined && state.pc !== null) { prof_leave(); prof_enter(pred_key_from_pc(program, state.pc), true); @@ -4101,6 +4243,14 @@ Runtime.step = function (program, state, inst) { }); agg.next_pc = endPc + 1; state.cps.push(agg); + // The inner goal of findall/bagof/setof/aggregate_all is an opaque + // cut scope (ISO: like call/1). Raise the barrier above the aggregate + // choice point so a `!` in the inner goal prunes only the inner + // goal's own alternatives and cannot destroy the aggregate CP itself + // (which would strand EndAggregate). The barrier and cut_stack are + // restored by restore_cp_frame when backtrack reaches `agg` -- its + // snapshot was taken before this push. + push_cut_barrier(state); state.pc += 1; return true; } @@ -4163,8 +4313,18 @@ Runtime.run = function (program, state) { // but backtrack must stop at the pre-call length (see cp_barrier). Runtime.run_isolated = function (program, state) { const prev = state.cp_barrier; - state.cp_barrier = state.cps.length; + const floor = state.cps.length; + state.cp_barrier = floor; const ok = Runtime.run(program, state) === true; + // Choice points the isolated run leaves behind are NOT resumable: the + // lowered frame that drove this run returns through JS, so a later + // backtrack into one of them would restart the interpreter at a pc + // inside the callee with a cp captured mid-isolation (it was forced to + // 0), producing an incoherent resume rather than a solution -- probe + // P20 printed `0 a 2 a 3` for `between(1,3,X), X > 1, !`. Dropping them + // makes an isolated call honestly first-solution, which is the lowered + // contract, instead of silently wrong. + while (state.cps.length > floor) state.cps.pop(); if (prev === undefined) delete state.cp_barrier; else state.cp_barrier = prev; return ok === true; @@ -4180,12 +4340,19 @@ Runtime.run_isolated = function (program, state) { // (the lowered wrapper, or invoke_lowered_execute) the actual Proceed. Runtime.execute_user_isolated = function (program, state, target) { const saved_cp = state.cp; + // The isolated callee's Proceed runs pop_call_frame; balance it with a + // frame of our own (and repair on the failure path) so it cannot pop + // the lowered caller's barrier. cut_barrier = cps.length here is also + // exactly the WAM `execute` B0 <- B for the tail-called predicate. + const mark = Runtime.call_frame_mark(state); + push_call_frame(state); state.cp = 0; state.pc = target; state.program = program; state.halt = false; const ok = Runtime.run_isolated(program, state) === true; state.halt = false; + Runtime.call_frame_release(state, mark); state.cp = saved_cp; return ok === true; }; From fe6230743b13e05a832b56804e5f7f6ddf4df728 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 19:38:28 +0000 Subject: [PATCH 3/6] Add store-backed resolver adapter with env/store split. resolver_store.pl keeps the term-catalog API untouched and serves the same 10 queries from P/2 fact predicates (packages/depends/conflicts/ reverse-deps) plus a tiny env term. SWI identity: 39/39 corpus rows match term-catalog results. Co-authored-by: johns243a --- examples/pkg_resolver/resolver_store.pl | 686 +++++++++++++++++++ examples/pkg_resolver/test_resolver_store.pl | 103 +++ 2 files changed, 789 insertions(+) create mode 100644 examples/pkg_resolver/resolver_store.pl create mode 100644 examples/pkg_resolver/test_resolver_store.pl diff --git a/examples/pkg_resolver/resolver_store.pl b/examples/pkg_resolver/resolver_store.pl new file mode 100644 index 000000000..8b1ce1114 --- /dev/null +++ b/examples/pkg_resolver/resolver_store.pl @@ -0,0 +1,686 @@ +:- encoding(utf8). +% SPDX-License-Identifier: MIT OR Apache-2.0 +% Copyright (c) 2026 John William Creighton (@s243a) +% +% resolver_store.pl -- P2 store-backed adapter. The catalog-as-term API in +% resolver.pl is untouched. The big three (packages / depends / conflicts) +% plus a precomputed reverse-deps index are P/2 fact-source predicates +% (D43 indexed(Prefix) on wamjs; the same JSONL P/2 rows on SWI). The +% machine-local environment stays a term: +% +% env(CatId, Base, Installed, Requested, Layers, Excluded, Aliases) +% +% Store keys are `CatId|Name` so many catalogs can share one index (corpus) +% without scanning. Lookups always bind the key (seek, not scan). +% +% Packed a2 cells (atoms; D43 stores are scalars only): +% store_pkg a2 = Major.Minor.Patch +% store_dep a2 = Ver#Dep#Constraint +% store_conflict a2 = Ver#Other +% store_revdep a2 = Name#Ver#Constraint (dependents of the key) + +:- module(resolver_store, [ + resolve_store/3, + resolve_layered_store/3, + explain_blocked_store/3, + explain_blocked_list_store/3, + layer_closure_store/3, + removal_orphans_store/3, + safe_upgrade_store/4, + upgrade_set_store/4, + upgrade_set_result_store/4, + freeze_audit_store/2, + dependents_store/3, + dependents_installed_store/3, + env_from_catalog/3, + store_clear/0, + assert_catalog_store/2, + load_p2_jsonl/1, + pack_ver/2, + unpack_ver/2, + pack_constraint/2, + unpack_constraint/2, + pack_key/3 +]). + +:- use_module(resolver, [satisfies/2, version_lt/2]). +:- use_module(library(http/json)). + +:- dynamic store_pkg/2. +:- dynamic store_dep/2. +:- dynamic store_conflict/2. +:- dynamic store_revdep/2. + +% --------------------------------------------------------------------------- +% Packing (shared with the JS builder — keep in lockstep) +% --------------------------------------------------------------------------- + +pack_key(CatId, Name, Key) :- + atom_concat(CatId, '|', T), + atom_concat(T, Name, Key). + +pack_ver(v(A, B, C), Atom) :- + number_string(A, SA), + number_string(B, SB), + number_string(C, SC), + atom_concat(SA, '.', T1), + atom_concat(T1, SB, T2), + atom_concat(T2, '.', T3), + atom_concat(T3, SC, Atom). + +unpack_ver(Packed0, v(A, B, C)) :- + to_atom(Packed0, Packed), + split_string(Packed, '.', '', [SA, SB, SC]), + number_string(A, SA), + number_string(B, SB), + number_string(C, SC). + +pack_constraint(any, any). +pack_constraint(eq(V), Atom) :- + pack_ver(V, VA), atom_concat('eq:', VA, Atom). +pack_constraint(gte(V), Atom) :- + pack_ver(V, VA), atom_concat('gte:', VA, Atom). +pack_constraint(lt(V), Atom) :- + pack_ver(V, VA), atom_concat('lt:', VA, Atom). +pack_constraint(range(Lo, Hi), Atom) :- + pack_ver(Lo, LA), pack_ver(Hi, HA), + atom_concat('range:', LA, T), + atom_concat(T, ':', T2), + atom_concat(T2, HA, Atom). + +unpack_constraint(Packed0, C) :- + to_atom(Packed0, Packed), + unpack_constraint_atom(Packed, C). + +unpack_constraint_atom(any, any) :- !. +unpack_constraint_atom(Packed, eq(V)) :- + atom_concat('eq:', Rest, Packed), !, unpack_ver(Rest, V). +unpack_constraint_atom(Packed, gte(V)) :- + atom_concat('gte:', Rest, Packed), !, unpack_ver(Rest, V). +unpack_constraint_atom(Packed, lt(V)) :- + atom_concat('lt:', Rest, Packed), !, unpack_ver(Rest, V). +unpack_constraint_atom(Packed, range(Lo, Hi)) :- + atom_concat('range:', Rest, Packed), !, + split_string(Rest, ':', '', [LA, HA]), + unpack_ver(LA, Lo), unpack_ver(HA, Hi). + +pack_dep(Ver, Dep, C, Atom) :- + pack_ver(Ver, VA), + pack_constraint(C, CA), + atom_concat(VA, '#', T1), + atom_concat(T1, Dep, T2), + atom_concat(T2, '#', T3), + atom_concat(T3, CA, Atom). + +unpack_dep(Packed0, Ver, Dep, C) :- + to_atom(Packed0, Packed), + split_string(Packed, '#', '', [VA, DepS, CA]), + unpack_ver(VA, Ver), + to_atom(DepS, Dep), + unpack_constraint(CA, C). + +pack_conflict(Ver, Other, Atom) :- + pack_ver(Ver, VA), + atom_concat(VA, '#', T), + atom_concat(T, Other, Atom). + +unpack_conflict(Packed0, Ver, Other) :- + to_atom(Packed0, Packed), + split_string(Packed, '#', '', [VA, OtherS]), + unpack_ver(VA, Ver), + to_atom(OtherS, Other). + +pack_rev(Name, Ver, C, Atom) :- + pack_ver(Ver, VA), + pack_constraint(C, CA), + atom_concat(Name, '#', T1), + atom_concat(T1, VA, T2), + atom_concat(T2, '#', T3), + atom_concat(T3, CA, Atom). + +unpack_rev(Packed0, Name, Ver, C) :- + to_atom(Packed0, Packed), + split_string(Packed, '#', '', [NameS, VA, CA]), + to_atom(NameS, Name), + unpack_ver(VA, Ver), + unpack_constraint(CA, C). + +to_atom(S, A) :- atom(S), !, A = S. +to_atom(S, A) :- atom_string(A, S). + +tight_constraint(C) :- + C \== any. + +% --------------------------------------------------------------------------- +% Env accessors (tiny, term-side) +% --------------------------------------------------------------------------- + +env_id(env(Id, _, _, _, _, _, _), Id). +env_base(env(_, B, _, _, _, _, _), B). +env_installed(env(_, _, I, _, _, _, _), I). +env_requested(env(_, _, _, R, _, _, _), R). +env_layers(env(_, _, _, _, L, _, _), L). +env_excluded(env(_, _, _, _, _, E, _), E). +env_aliases(env(_, _, _, _, _, _, A), A). + +env_from_catalog(Id, catalog(_Ps, _Ds, _Cs, B, I, R), + env(Id, B, I, R, [], [], [])). +env_from_catalog(Id, catalog(_Ps, _Ds, _Cs, B, I, R, L, E, A), + env(Id, B, I, R, L, E, A)). + +base_ver_env(Env, Name, Ver) :- + env_base(Env, Bs), + env_layers(Env, Ls), + append(Bs, Ls, All), + lookup_held(All, Name, Ver). + +base_name_env(Env, Name) :- + base_ver_env(Env, Name, _). + +installed_ver_env(Env, Name, Ver) :- + env_installed(Env, Is), + member(Name-Ver, Is). + +excluded_name_env(Env, Name) :- + env_excluded(Env, Es), + member(Name, Es). + +lookup_held([H|T], Name, Ver) :- + ( item_ver(H, Name, V0) + -> Ver = V0 + ; lookup_held(T, Name, Ver) + ). + +item_ver(N-V, Name, V) :- + N == Name. +item_ver(base(N-V, _R), Name, V) :- + N == Name. +item_ver(layer(_L, Pkgs), Name, V) :- + lookup_held(Pkgs, Name, V). + +canonicalize_name_env(Env, In, Out) :- + env_aliases(Env, As), + alias_lookup(As, In, Out). + +alias_lookup([], N, N). +alias_lookup([alias(A, Canon)|Rest], In, Out) :- + ( In == A + -> Out = Canon + ; alias_lookup(Rest, In, Out) + ). + +request_to_req_env(Env, R, req(Name, C)) :- + ( R = req(Raw, C) + -> canonicalize_name_env(Env, Raw, Name) + ; canonicalize_name_env(Env, R, Name), + C = any + ). + +map_requests_env(_Env, [], []). +map_requests_env(Env, [R|Rs], [Q|Qs]) :- + request_to_req_env(Env, R, Q), + map_requests_env(Env, Rs, Qs). + +selected_ver([H|Rest], Name, Ver) :- + ( H = Name-Ver + -> true + ; selected_ver(Rest, Name, Ver) + ). + +% --------------------------------------------------------------------------- +% Bound store lookups (seek by CatId|Name) +% --------------------------------------------------------------------------- + +store_key(Env, Name, Key) :- + env_id(Env, Id), + pack_key(Id, Name, Key). + +package_in_store(Env, Name, Ver) :- + store_key(Env, Name, Key), + store_pkg(Key, Packed), + unpack_ver(Packed, Ver). + +candidates_high_first_store(Env, Name, C, Ver) :- + \+ excluded_name_env(Env, Name), + store_key(Env, Name, Key), + findall(V, ( + store_pkg(Key, Packed), + unpack_ver(Packed, V), + satisfies(V, C) + ), Vs), + sort(Vs, Asc), + reverse(Asc, Desc), + member(Ver, Desc). + +collect_deps_store(Env, Name, Ver, Reqs) :- + store_key(Env, Name, Key), + findall(req(Dep, C), ( + store_dep(Key, Packed), + unpack_dep(Packed, Ver0, Dep, C), + Ver0 == Ver + ), Reqs). + +conflicts_in_store(Env, Name, Ver, Other) :- + store_key(Env, Name, Key), + store_conflict(Key, Packed), + unpack_conflict(Packed, Ver0, Other), + Ver0 == Ver. + +no_acc_conflicts_store(_Env, _Name, _Ver, []). +no_acc_conflicts_store(Env, Name, Ver, [Other-OtherVer|Rest]) :- + \+ (conflicts_in_store(Env, Name, Ver, Other)), + \+ (conflicts_in_store(Env, Other, OtherVer, Name)), + no_acc_conflicts_store(Env, Name, Ver, Rest). + +% --------------------------------------------------------------------------- +% Load a term catalog into the P/2 store (SWI oracle / tests) +% --------------------------------------------------------------------------- + +store_clear :- + retractall(store_pkg(_, _)), + retractall(store_dep(_, _)), + retractall(store_conflict(_, _)), + retractall(store_revdep(_, _)). + +assert_catalog_store(Id, catalog(Ps, Ds, Cs, B, I, R)) :- + assert_catalog_store(Id, catalog(Ps, Ds, Cs, B, I, R, [], [], [])). +assert_catalog_store(Id, catalog(Ps, Ds, Cs, _B, _I, _R, _L, _E, _A)) :- + forall(member(package(N, V), Ps), + ( pack_key(Id, N, K), pack_ver(V, VA), + assertz(store_pkg(K, VA)) + )), + forall(member(depends(N, V, D, C), Ds), + ( pack_key(Id, N, KN), pack_dep(V, D, C, DA), + assertz(store_dep(KN, DA)), + pack_key(Id, D, KD), pack_rev(N, V, C, RA), + assertz(store_revdep(KD, RA)) + )), + forall(member(conflicts(N, V, O), Cs), + ( pack_key(Id, N, K), pack_conflict(V, O, CA), + assertz(store_conflict(K, CA)) + )). + +load_p2_jsonl(Dir) :- + store_clear, + atom_concat(Dir, '/pkg.jsonl', PkgF), + atom_concat(Dir, '/dep.jsonl', DepF), + atom_concat(Dir, '/conflict.jsonl', ConfF), + atom_concat(Dir, '/revdep.jsonl', RevF), + load_pairs(PkgF, store_pkg), + load_pairs(DepF, store_dep), + load_pairs(ConfF, store_conflict), + load_pairs(RevF, store_revdep). + +load_pairs(Path, Pred) :- + exists_file(Path), + !, + setup_call_cleanup(open(Path, read, S), load_pair_lines(S, Pred), close(S)). +load_pairs(_Path, _Pred). + +load_pair_lines(S, Pred) :- + read_line_to_string(S, Line), + ( Line == end_of_file + -> true + ; ( Line == "" + -> true + ; atom_string(Atom, Line), + atom_json_term(Atom, Term, [value_string_as(atom)]), + Term = [K, V], + Fact =.. [Pred, K, V], + assertz(Fact) + ), + load_pair_lines(S, Pred) + ). + +% --------------------------------------------------------------------------- +% Queries (same 10 as resolver.pl; store lookups instead of list members) +% --------------------------------------------------------------------------- + +resolve_store(Env, Requests, Selection) :- + map_requests_env(Env, Requests, Pending), + resolve_pending_store(classic, Env, Pending, [], Acc), + !, + sort(Acc, Selection). + +resolve_layered_store(Env, Requests, Selection) :- + map_requests_env(Env, Requests, Pending), + resolve_pending_store(layered, Env, Pending, [], Acc), + !, + sort(Acc, Selection). + +resolve_pending_store(_Mode, _Env, [], Acc, Acc). +resolve_pending_store(Mode, Env, [req(Name, C)|Rest], Acc, Sel) :- + ( selected_ver(Acc, Name, Ver) + -> satisfies(Ver, C), + resolve_pending_store(Mode, Env, Rest, Acc, Sel) + ; pick_store(Mode, Env, Name, C, Ver, Origin), + collect_deps_store(Env, Name, Ver, DepReqs), + append(DepReqs, Rest, More), + ( Origin = from_base + -> resolve_pending_store(Mode, Env, More, Acc, Sel) + ; no_acc_conflicts_store(Env, Name, Ver, Acc), + resolve_pending_store(Mode, Env, More, [Name-Ver|Acc], Sel) + ) + ). + +pick_store(classic, Env, Name, C, Ver, from_catalog) :- + candidates_high_first_store(Env, Name, C, Ver). +pick_store(layered, Env, Name, C, Ver, Origin) :- + ( base_ver_env(Env, Name, BV) + -> satisfies(BV, C), + Ver = BV, + Origin = from_base + ; candidates_high_first_store(Env, Name, C, Ver), + Origin = from_catalog + ). + +explain_blocked_store(Env, Request, Blocked) :- + request_to_req_env(Env, Request, Req), + blocked_from_store(Env, Req, [], Blocked). + +explain_blocked_list_store(Env, Request, List) :- + request_to_req_env(Env, Request, Req), + blocked_acc_store(Env, Req, [], [], Acc), + sort(Acc, List), + !. + +blocked_from_store(Env, req(Name, C), Seen, Blocked) :- + \+ seen_name(Seen, Name), + base_ver_env(Env, Name, BV), + \+ satisfies(BV, C), + Blocked = blocked(Name, needs(C), base_has(BV)). +blocked_from_store(Env, req(Name, C), Seen, Blocked) :- + \+ seen_name(Seen, Name), + layered_walk_ver_store(Env, Name, C, Ver), + collect_deps_store(Env, Name, Ver, DepReqs), + member(Dep, DepReqs), + blocked_from_store(Env, Dep, [Name|Seen], Blocked). + +blocked_acc_store(_Env, req(Name, _C), Seen, Acc, Acc) :- + seen_name(Seen, Name), !. +blocked_acc_store(Env, req(Name, C), Seen, Acc0, Acc) :- + ( base_ver_env(Env, Name, BV), + \+ satisfies(BV, C) + -> Acc1 = [blocked(Name, needs(C), base_has(BV))|Acc0] + ; Acc1 = Acc0 + ), + ( layered_walk_ver_store(Env, Name, C, Ver) + -> collect_deps_store(Env, Name, Ver, DepReqs), + blocked_acc_list_store(Env, DepReqs, [Name|Seen], Acc1, Acc) + ; Acc = Acc1 + ). + +blocked_acc_list_store(_Env, [], _Seen, Acc, Acc). +blocked_acc_list_store(Env, [Dep|Rest], Seen, Acc0, Acc) :- + blocked_acc_store(Env, Dep, Seen, Acc0, Acc1), + blocked_acc_list_store(Env, Rest, Seen, Acc1, Acc). + +seen_name([H|Rest], Name) :- + ( H == Name + -> true + ; seen_name(Rest, Name) + ). + +layered_walk_ver_store(Env, Name, C, Ver) :- + ( base_ver_env(Env, Name, BV) + -> satisfies(BV, C), + Ver = BV + ; candidates_high_first_store(Env, Name, C, Ver) + ), + !. + +layer_closure_store(Env, Request, Layer) :- + resolve_layered_store(Env, [Request], Sel), + topo_sort_sel_store(Env, Sel, Layer), + !. + +topo_sort_sel_store(_Env, [], []) :- !. +topo_sort_sel_store(Env, Sel, Layer) :- + sort(Sel, Sorted), + names_of(Sorted, Names), + topo_all_store(Env, Names, Sel, [], _Seen, [], Acc), + reverse(Acc, Layer). + +names_of([], []). +names_of([N-_|Rest], [N|Ns]) :- + names_of(Rest, Ns). + +topo_all_store(_Env, [], _Sel, Seen, Seen, Acc, Acc). +topo_all_store(Env, [N|Ns], Sel, Seen0, Seen, Acc0, Acc) :- + topo_one_store(Env, N, Sel, Seen0, Seen1, Acc0, Acc1), + topo_all_store(Env, Ns, Sel, Seen1, Seen, Acc1, Acc). + +topo_one_store(_Env, Name, _Sel, Seen, Seen, Acc, Acc) :- + member(Name, Seen), + !. +topo_one_store(Env, Name, Sel, Seen0, Seen, Acc0, Acc) :- + ( member(Name-Ver, Sel) + -> findall(D, ( + collect_deps_store(Env, Name, Ver, Reqs), + member(req(D, _), Reqs) + ), Ds0), + sort(Ds0, Ds), + topo_all_store(Env, Ds, Sel, [Name|Seen0], Seen1, Acc0, Acc1), + Acc = [Name-Ver|Acc1], + Seen = Seen1 + ; Seen = [Name|Seen0], + Acc = Acc0 + ). + +removal_orphans_store(Env, Pkg0, Orphans) :- + canonicalize_name_env(Env, Pkg0, Pkg), + env_installed(Env, Inst), + ( installed_ver_env(Env, Pkg, Ver) + -> true + ; Ver = none + ), + ( Ver == none + -> Orphans = [] + ; inst_closure_names_store(Env, Inst, Pkg, Ver, Closure), + env_requested(Env, Reqs0), + exclude_name(Pkg, Reqs0, Reqs1), + needed_names_store(Env, Inst, Reqs1, Needed), + findall(N-V, ( + member(N-V, Inst), + N \== Pkg, + member(N, Closure), + \+ member(N, Needed), + \+ base_name_env(Env, N) + ), Or0), + sort(Or0, Orphans) + ), + !. + +exclude_name(_N, [], []). +exclude_name(N, [N|Rs], Out) :- !, + exclude_name(N, Rs, Out). +exclude_name(N, [R|Rs], [R|Out]) :- + exclude_name(N, Rs, Out). + +inst_closure_names_store(Env, Inst, Name, Ver, Names) :- + inst_walk_store([Name-Ver], Env, Inst, [], [], Names). + +inst_walk_store([], _Env, _Inst, _Seen, Acc, Acc). +inst_walk_store([Name-Ver|Rest], Env, Inst, Seen, Acc0, Acc) :- + ( member(Name, Seen) + -> inst_walk_store(Rest, Env, Inst, Seen, Acc0, Acc) + ; collect_deps_store(Env, Name, Ver, Reqs), + findall(D-DV, ( + member(req(D, _), Reqs), + member(D-DV, Inst) + ), Kids), + append(Kids, Rest, More), + inst_walk_store(More, Env, Inst, [Name|Seen], [Name|Acc0], Acc) + ). + +needed_names_store(_Env, _Inst, [], []). +needed_names_store(Env, Inst, Roots, Needed) :- + roots_to_pairs(Roots, Inst, Pairs), + inst_walk_store(Pairs, Env, Inst, [], [], Needed). + +roots_to_pairs([], _Inst, []). +roots_to_pairs([N|Ns], Inst, [N-V|Ps]) :- + member(N-V, Inst), + !, + roots_to_pairs(Ns, Inst, Ps). +roots_to_pairs([_|Ns], Inst, Ps) :- + roots_to_pairs(Ns, Inst, Ps). + +base_holds_env(Env, Holds) :- + env_base(Env, Bs), + scan_base_holds(Bs, [], Acc), + sort(Acc, Holds). + +scan_base_holds([], Acc, Acc). +scan_base_holds([H|T], Acc0, Acc) :- + ( H = layer(base, Pkgs) + -> scan_base_holds(Pkgs, Acc0, Acc1) + ; H = layer(_, _) + -> Acc1 = Acc0 + ; H = base(N-V, R) + -> Acc1 = [hold(N, V, R)|Acc0] + ; H = N-V + -> Acc1 = [hold(N, V, blanket)|Acc0] + ; Acc1 = Acc0 + ), + scan_base_holds(T, Acc1, Acc). + +base_reason_env(Env, Name, Reason) :- + base_holds_env(Env, Holds), + hold_reason(Holds, Name, Reason). + +hold_reason([hold(N, _V, R)|T], Name, Reason) :- + ( N == Name + -> Reason = R + ; hold_reason(T, Name, Reason) + ). + +safe_upgrade_store(Env, Pkg0, NewVer, Verdict) :- + canonicalize_name_env(Env, Pkg0, Pkg), + ( \+ package_in_store(Env, Pkg, NewVer) + -> Verdict = no_candidate + ; \+ base_reason_env(Env, Pkg, _) + -> Verdict = no_candidate + ; base_reason_env(Env, Pkg, Reason), + safe_upgrade_reason_store(Env, Pkg, NewVer, Reason, Verdict) + ), + !. + +safe_upgrade_reason_store(_Env, _Pkg, _NewVer, modified, unsafe(modified)). +safe_upgrade_reason_store(_Env, _Pkg, _NewVer, footprint, safe(cost(footprint))). +safe_upgrade_reason_store(_Env, _Pkg, _NewVer, blanket, safe(cost(blanket))). +safe_upgrade_reason_store(_Env, _Pkg, _NewVer, layer_shadow, safe(cost(layer_shadow))). +safe_upgrade_reason_store(Env, Pkg, NewVer, abi_anchor, coordinated(Set)) :- + upgrade_set_result_store(Env, Pkg, NewVer, ok(Set)). + +upgrade_set_store(Env, Pkg, NewVer, Set) :- + upgrade_set_result_store(Env, Pkg, NewVer, ok(Set)), + !. + +upgrade_set_result_store(Env, Pkg0, NewVer, Result) :- + canonicalize_name_env(Env, Pkg0, Pkg), + ( package_in_store(Env, Pkg, NewVer) + -> close_moving_store(Env, [Pkg-NewVer], Result) + ; Result = no_candidate + ), + !. + +close_moving_store(Env, Acc, Result) :- + base_holds_env(Env, Holds), + first_broken_store(Holds, Env, Acc, Broken), + ( Broken = none + -> sort(Acc, Sorted), + Result = ok(Sorted) + ; Broken = broken(N, V, C), + ( pick_repair_store(Env, N, Acc, NewV) + -> close_moving_store(Env, [N-NewV|Acc], Result) + ; Result = blocked(N, needs(C), base_has(V)) + ) + ). + +first_broken_store([], _Env, _Acc, none). +first_broken_store([hold(N, V, _R)|Rest], Env, Acc, Broken) :- + ( selected_ver(Acc, N, _) + -> first_broken_store(Rest, Env, Acc, Broken) + ; dep_breaks_moving_store(Env, N, V, Acc, C) + -> Broken = broken(N, V, C) + ; first_broken_store(Rest, Env, Acc, Broken) + ). + +dep_breaks_moving_store(Env, N, V, Acc, C) :- + collect_deps_store(Env, N, V, Reqs), + member(req(D, C), Reqs), + selected_ver(Acc, D, MV), + \+ satisfies(MV, C). + +pick_repair_store(Env, Name, Acc, NewV) :- + candidates_high_first_store(Env, Name, any, NewV), + repairs_moving_store(Env, Name, NewV, Acc). + +repairs_moving_store(Env, Name, NewV, Acc) :- + collect_deps_store(Env, Name, NewV, Reqs), + reqs_ok_moving(Reqs, Acc). + +reqs_ok_moving([], _). +reqs_ok_moving([req(D, C)|Rest], Acc) :- + ( selected_ver(Acc, D, MV) + -> satisfies(MV, C) + ; true + ), + reqs_ok_moving(Rest, Acc). + +freeze_audit_store(Env, Audit) :- + base_holds_env(Env, Holds), + audit_holds_store(Holds, Env, [], Acc), + sort(Acc, Audit), + !. + +audit_holds_store([], _Env, Acc, Acc). +audit_holds_store([hold(N, _V, R)|Rest], Env, Acc0, Acc) :- + ( R == blanket + -> ( tight_base_revdep_store(Env, N) + -> Item = audit(N, suggest(abi_anchor)) + ; Item = audit(N, over_frozen) + ) + ; Item = audit(N, held(R)) + ), + audit_holds_store(Rest, Env, [Item|Acc0], Acc). + +tight_base_revdep_store(Env, Pkg) :- + store_key(Env, Pkg, Key), + store_revdep(Key, Packed), + unpack_rev(Packed, N, V, C), + N \== Pkg, + tight_constraint(C), + base_ver_env(Env, N, BV), + V == BV. + +dependents_store(Env, Pkg0, Deps) :- + canonicalize_name_env(Env, Pkg0, Pkg), + store_key(Env, Pkg, Key), + findall(N-V, ( + store_revdep(Key, Packed), + unpack_rev(Packed, N, V, _C) + ), Acc), + sort(Acc, Deps), + !. + +dependents_installed_store(Env, Pkg0, Deps) :- + dependents_store(Env, Pkg0, All), + keep_installed_or_base_env(All, Env, [], Acc), + sort(Acc, Deps), + !. + +keep_installed_or_base_env([], _Env, Acc, Acc). +keep_installed_or_base_env([N-V|Rest], Env, Acc0, Acc) :- + ( installed_or_base_env(Env, N, V) + -> Acc1 = [N-V|Acc0] + ; Acc1 = Acc0 + ), + keep_installed_or_base_env(Rest, Env, Acc1, Acc). + +installed_or_base_env(Env, N, V) :- + installed_ver_env(Env, N, V). +installed_or_base_env(Env, N, V) :- + base_ver_env(Env, N, BV), + V == BV. diff --git a/examples/pkg_resolver/test_resolver_store.pl b/examples/pkg_resolver/test_resolver_store.pl new file mode 100644 index 000000000..dde47df12 --- /dev/null +++ b/examples/pkg_resolver/test_resolver_store.pl @@ -0,0 +1,103 @@ +:- encoding(utf8). +% SPDX-License-Identifier: MIT OR Apache-2.0 +% Copyright (c) 2026 John William Creighton (@s243a) +% +% test_resolver_store.pl -- store adapter vs term-catalog identity. +% Existing test_resolver.pl scenarios are not modified. +% +% swipl -q -g test_resolver_store -t halt examples/pkg_resolver/test_resolver_store.pl + +:- module(test_resolver_store, [test_resolver_store/0]). + +:- use_module(library(plunit)). +:- use_module(resolver). +:- use_module(resolver_store). +:- use_module(test_resolver, [scenario_catalog/2, corpus_case/4]). + +test_resolver_store :- + run_tests(pkg_resolver_store), + format("pkg_resolver store-backed corpus: tests finished~n", []). + +term_result(resolve, Cat, Args, ok(Sel)) :- + resolve(Cat, Args, Sel), !. +term_result(resolve, _, _, fail). +term_result(resolve_layered, Cat, Args, ok(Sel)) :- + resolve_layered(Cat, Args, Sel), !. +term_result(resolve_layered, _, _, fail). +term_result(explain_blocked, Cat, Args, ok(List)) :- + explain_blocked_list(Cat, Args, List). +term_result(layer_closure, Cat, Args, ok(Layer)) :- + layer_closure(Cat, Args, Layer), !. +term_result(layer_closure, _, _, fail). +term_result(removal_orphans, Cat, Args, ok(Orphans)) :- + removal_orphans(Cat, Args, Orphans). +term_result(safe_upgrade, Cat, [Pkg, Ver], ok(V)) :- + safe_upgrade(Cat, Pkg, Ver, V). +term_result(upgrade_set, Cat, [Pkg, Ver], R) :- + upgrade_set_result(Cat, Pkg, Ver, R). +term_result(freeze_audit, Cat, _, ok(A)) :- + freeze_audit(Cat, A). +term_result(dependents, Cat, Args, ok(Ds)) :- + dependents(Cat, Args, Ds). +term_result(dependents_installed, Cat, Args, ok(Ds)) :- + dependents_installed(Cat, Args, Ds). + +store_result(resolve, Env, Args, ok(Sel)) :- + resolve_store(Env, Args, Sel), !. +store_result(resolve, _, _, fail). +store_result(resolve_layered, Env, Args, ok(Sel)) :- + resolve_layered_store(Env, Args, Sel), !. +store_result(resolve_layered, _, _, fail). +store_result(explain_blocked, Env, Args, ok(List)) :- + explain_blocked_list_store(Env, Args, List). +store_result(layer_closure, Env, Args, ok(Layer)) :- + layer_closure_store(Env, Args, Layer), !. +store_result(layer_closure, _, _, fail). +store_result(removal_orphans, Env, Args, ok(Orphans)) :- + removal_orphans_store(Env, Args, Orphans). +store_result(safe_upgrade, Env, [Pkg, Ver], ok(V)) :- + safe_upgrade_store(Env, Pkg, Ver, V). +store_result(upgrade_set, Env, [Pkg, Ver], R) :- + upgrade_set_result_store(Env, Pkg, Ver, R). +store_result(freeze_audit, Env, _, ok(A)) :- + freeze_audit_store(Env, A). +store_result(dependents, Env, Args, ok(Ds)) :- + dependents_store(Env, Args, Ds). +store_result(dependents_installed, Env, Args, ok(Ds)) :- + dependents_installed_store(Env, Args, Ds). + +prepare_store(CatName, Env) :- + store_clear, + scenario_catalog(CatName, Cat), + assert_catalog_store(CatName, Cat), + env_from_catalog(CatName, Cat, Env). + +:- begin_tests(pkg_resolver_store). + +test(store_matches_term_corpus) :- + findall(Id-Term-Store, ( + corpus_case(Id, CatName, Query, Args), + scenario_catalog(CatName, Cat), + prepare_store(CatName, Env), + term_result(Query, Cat, Args, Term), + store_result(Query, Env, Args, Store), + Term \== Store + ), Bad), + ( Bad == [] + -> findall(Id, corpus_case(Id, _, _, _), All), + length(All, N), + format("store-backed corpus: ~w/~w identical to term-catalog~n", [N, N]) + ; format("store mismatches: ~q~n", [Bad]), + assertion(Bad == []) + ). + +test(store_env_split_ignores_big_lists) :- + scenario_catalog(linear, Cat), + prepare_store(linear, Env), + % Env has no packages/depends; resolution still closes a→b→c via the store. + resolve_store(Env, [a], Sel), + resolve(Cat, [a], Term), + assertion(Sel == Term), + !. + +:- end_tests(pkg_resolver_store). From 8e37162958ddb419e2058637870bb0712d95f6b0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 19:38:28 +0000 Subject: [PATCH 4/6] Dump catalogs to D43 indexed stores and compile the store WAM project. Rich JSONL plus four P/2 indexes (pkg/dep/conflict/revdep, reverse-deps precomputed). wamjs_store binds those stores as javascript_wam_fact_sources. lmdb stays opt-in; the missing-package error path is ungated. Co-authored-by: johns243a --- examples/pkg_resolver/.gitignore | 3 + examples/pkg_resolver/dump_store_data.pl | 267 ++++++++++++ examples/pkg_resolver/store/build_stores.sh | 25 ++ examples/pkg_resolver/store/lmdb_smoke.sh | 39 ++ examples/pkg_resolver/store/pack.mjs | 34 ++ examples/pkg_resolver/store/rich_to_p2.mjs | 56 +++ examples/pkg_resolver/wamjs_store/build.pl | 64 +++ examples/pkg_resolver/wamjs_store/build.sh | 28 ++ .../wamjs_store/diff_runner_wamjs.mjs | 26 ++ .../wamjs_store/resolver_store.mjs | 392 ++++++++++++++++++ .../pkg_resolver/wamjs_store/run_corpus.mjs | 51 +++ .../wamjs_store/run_corpus_wamjs.sh | 23 + 12 files changed, 1008 insertions(+) create mode 100644 examples/pkg_resolver/dump_store_data.pl create mode 100755 examples/pkg_resolver/store/build_stores.sh create mode 100755 examples/pkg_resolver/store/lmdb_smoke.sh create mode 100644 examples/pkg_resolver/store/pack.mjs create mode 100644 examples/pkg_resolver/store/rich_to_p2.mjs create mode 100644 examples/pkg_resolver/wamjs_store/build.pl create mode 100755 examples/pkg_resolver/wamjs_store/build.sh create mode 100644 examples/pkg_resolver/wamjs_store/diff_runner_wamjs.mjs create mode 100644 examples/pkg_resolver/wamjs_store/resolver_store.mjs create mode 100644 examples/pkg_resolver/wamjs_store/run_corpus.mjs create mode 100755 examples/pkg_resolver/wamjs_store/run_corpus_wamjs.sh diff --git a/examples/pkg_resolver/.gitignore b/examples/pkg_resolver/.gitignore index e421dde23..0c9684d1c 100644 --- a/examples/pkg_resolver/.gitignore +++ b/examples/pkg_resolver/.gitignore @@ -1,2 +1,5 @@ # Regenerated harness artifacts. .diff_out/ +store/.out/ +wamjs_store/js/ +wamjs_store/.corpus_out/ diff --git a/examples/pkg_resolver/dump_store_data.pl b/examples/pkg_resolver/dump_store_data.pl new file mode 100644 index 000000000..c89c18e32 --- /dev/null +++ b/examples/pkg_resolver/dump_store_data.pl @@ -0,0 +1,267 @@ +:- encoding(utf8). +% SPDX-License-Identifier: MIT OR Apache-2.0 +% Copyright (c) 2026 John William Creighton (@s243a) +% +% dump_store_data.pl -- write corpus P/2 JSONL + query rows for the store adapter. +% +% swipl -q -g dump_store_data -t halt examples/pkg_resolver/dump_store_data.pl -- DIR +% +% Writes DIR/pkg.jsonl DIR/dep.jsonl DIR/conflict.jsonl DIR/revdep.jsonl +% DIR/rich.jsonl DIR/cases.jsonl DIR/envs.jsonl + +:- module(dump_store_data, [dump_store_data/0]). + +:- use_module(library(http/json)). +:- use_module(library(filesex), [make_directory_path/1]). +:- use_module(resolver). +:- use_module(resolver_store). +:- use_module(test_resolver, [scenario_catalog/2, corpus_case/4]). + +dump_store_data :- + current_prolog_flag(argv, Argv), + ( Argv = [Dir|_] -> true ; Dir = 'examples/pkg_resolver/store/.out/corpus' ), + make_directory_path(Dir), + atom_concat(Dir, '/pkg.jsonl', PkgF), + atom_concat(Dir, '/dep.jsonl', DepF), + atom_concat(Dir, '/conflict.jsonl', ConfF), + atom_concat(Dir, '/revdep.jsonl', RevF), + atom_concat(Dir, '/rich.jsonl', RichF), + atom_concat(Dir, '/cases.jsonl', CaseF), + atom_concat(Dir, '/envs.jsonl', EnvF), + setup_call_cleanup(open(PkgF, write, PS), + setup_call_cleanup(open(DepF, write, DS), + setup_call_cleanup(open(ConfF, write, CS), + setup_call_cleanup(open(RevF, write, RS), + setup_call_cleanup(open(RichF, write, RichS), + dump_all_catalogs(PS, DS, CS, RS, RichS), + close(RichS)), close(RS)), close(CS)), close(DS)), close(PS)), + setup_call_cleanup(open(EnvF, write, ES), dump_envs(ES), close(ES)), + setup_call_cleanup(open(CaseF, write, KS), dump_cases(KS), close(KS)), + format("dump_store_data: wrote P/2 + cases under ~w~n", [Dir]). + +dump_all_catalogs(PS, DS, CS, RS, RichS) :- + findall(Name, scenario_catalog(Name, _), Names0), + sort(Names0, Names), + forall(member(Name, Names), + ( scenario_catalog(Name, Cat), + dump_one_catalog(Name, Cat, PS, DS, CS, RS, RichS) + )). + +dump_one_catalog(Id, catalog(Ps, Ds, Cs, B, I, R), PS, DS, CS, RS, RichS) :- + dump_one_catalog(Id, catalog(Ps, Ds, Cs, B, I, R, [], [], []), PS, DS, CS, RS, RichS). +dump_one_catalog(Id, catalog(Ps, Ds, Cs, B, I, R, Ls, Es, As), PS, DS, CS, RS, RichS) :- + atom_string(Id, IdS), + forall(member(package(N, V), Ps), + ( pack_key(Id, N, K), pack_ver(V, VA), + write_pair(PS, K, VA), + ver_json(V, VJ), atom_string(N, NS), + json_write_dict(RichS, _{kind: "package", catalog: IdS, name: NS, ver: VJ}, + [width(0)]), nl(RichS) + )), + forall(member(depends(N, V, D, C), Ds), + ( pack_key(Id, N, KN), pack_dep_local(V, D, C, DA), + write_pair(DS, KN, DA), + pack_key(Id, D, KD), pack_rev_local(N, V, C, RA), + write_pair(RS, KD, RA), + ver_json(V, VJ), atom_string(N, NS), atom_string(D, DepS), + constraint_json(C, CJ), + json_write_dict(RichS, + _{kind: "depends", catalog: IdS, name: NS, ver: VJ, dep: DepS, constraint: CJ}, + [width(0)]), nl(RichS) + )), + forall(member(conflicts(N, V, O), Cs), + ( pack_key(Id, N, K), pack_conflict_local(V, O, CA), + write_pair(CS, K, CA), + ver_json(V, VJ), atom_string(N, NS), atom_string(O, OS), + json_write_dict(RichS, + _{kind: "conflicts", catalog: IdS, name: NS, ver: VJ, other: OS}, + [width(0)]), nl(RichS) + )), + dump_env_rich(IdS, B, I, R, Ls, Es, As, RichS). + +pack_dep_local(V, D, C, DA) :- + resolver_store:pack_ver(V, VA), + resolver_store:pack_constraint(C, CA), + atom_concat(VA, '#', T1), atom_concat(T1, D, T2), + atom_concat(T2, '#', T3), atom_concat(T3, CA, DA). + +pack_rev_local(N, V, C, RA) :- + resolver_store:pack_ver(V, VA), + resolver_store:pack_constraint(C, CA), + atom_concat(N, '#', T1), atom_concat(T1, VA, T2), + atom_concat(T2, '#', T3), atom_concat(T3, CA, RA). + +pack_conflict_local(V, O, CA) :- + resolver_store:pack_ver(V, VA), + atom_concat(VA, '#', T), atom_concat(T, O, CA). + +write_pair(S, K, V) :- + atom_string(K, KS), atom_string(V, VS), + json_write(S, [KS, VS], [width(0)]), nl(S). + +dump_env_rich(IdS, B, I, R, Ls, Es, As, RichS) :- + forall(member(H, B), dump_hold_rich(IdS, "base", H, RichS)), + forall(member(layer(LN, Pkgs), Ls), + forall(member(H, Pkgs), dump_layer_hold_rich(IdS, LN, H, RichS))), + forall(member(N-V, I), + ( ver_json(V, VJ), atom_string(N, NS), + json_write_dict(RichS, _{kind: "installed", catalog: IdS, name: NS, ver: VJ}, [width(0)]), + nl(RichS) + )), + forall(member(N, R), + ( atom_string(N, NS), + json_write_dict(RichS, _{kind: "requested", catalog: IdS, name: NS}, [width(0)]), + nl(RichS) + )), + forall(member(N, Es), + ( atom_string(N, NS), + json_write_dict(RichS, _{kind: "excluded", catalog: IdS, name: NS}, [width(0)]), + nl(RichS) + )), + forall(member(alias(A, C), As), + ( atom_string(A, AS), atom_string(C, CS), + json_write_dict(RichS, _{kind: "alias", catalog: IdS, alias: AS, canonical: CS}, [width(0)]), + nl(RichS) + )). + +dump_hold_rich(IdS, Kind, N-V, RichS) :- + ver_json(V, VJ), atom_string(N, NS), + json_write_dict(RichS, _{kind: Kind, catalog: IdS, name: NS, ver: VJ, reason: "blanket"}, [width(0)]), + nl(RichS). +dump_hold_rich(IdS, Kind, base(N-V, Reason), RichS) :- + ver_json(V, VJ), atom_string(N, NS), atom_string(Reason, RS), + json_write_dict(RichS, _{kind: Kind, catalog: IdS, name: NS, ver: VJ, reason: RS}, [width(0)]), + nl(RichS). +dump_hold_rich(IdS, _Kind, layer(LN, Pkgs), RichS) :- + forall(member(H, Pkgs), dump_layer_hold_rich(IdS, LN, H, RichS)). + +dump_layer_hold_rich(IdS, LN, N-V, RichS) :- + ver_json(V, VJ), atom_string(N, NS), atom_string(LN, LS), + json_write_dict(RichS, _{kind: "layer", catalog: IdS, layer: LS, name: NS, ver: VJ}, [width(0)]), + nl(RichS). +dump_layer_hold_rich(IdS, LN, base(N-V, Reason), RichS) :- + ver_json(V, VJ), atom_string(N, NS), atom_string(LN, LS), atom_string(Reason, RS), + json_write_dict(RichS, _{kind: "layer", catalog: IdS, layer: LS, name: NS, ver: VJ, reason: RS}, [width(0)]), + nl(RichS). + +dump_envs(ES) :- + findall(Name, scenario_catalog(Name, _), Names0), + sort(Names0, Names), + forall(member(Name, Names), + ( scenario_catalog(Name, Cat), + env_from_catalog(Name, Cat, Env), + env_json(Env, J), + json_write_dict(ES, J, [width(0), true(true), false(false)]), + nl(ES) + )). + +env_json(env(Id, B, I, R, L, E, A), + _{catalog_id: IdS, base: BJ, installed: IJ, requested: RJ, + layers: LJ, excluded: EJ, aliases: AJ}) :- + atom_string(Id, IdS), + maplist(hold_json, B, BJ), + maplist(pair_json, I, IJ), + maplist(atom_string, R, RJ), + maplist(layer_json, L, LJ), + maplist(atom_string, E, EJ), + maplist(alias_json, A, AJ). + +dump_cases(KS) :- + forall(corpus_case(IdAtom, CatName, Query, Args), + dump_one_case(KS, IdAtom, CatName, Query, Args)). + +dump_one_case(KS, IdAtom, CatName, Query, Args) :- + scenario_catalog(CatName, Cat), + env_from_catalog(CatName, Cat, Env), + env_json(Env, EJ), + atom_string(IdAtom, Id), + atom_string(Query, QAtom), + atom_string(CatName, CatS), + args_json_local(Query, Args, ArgsJ), + run_term_local(Query, Cat, Args, Exp), + json_write_dict(KS, + _{id: Id, catalog_id: CatS, env: EJ, query: QAtom, args: ArgsJ, expected: Exp}, + [width(0), true(true), false(false)]), + nl(KS). + +args_json_local(resolve, Reqs, Js) :- reqs_json(Reqs, Js). +args_json_local(resolve_layered, Reqs, Js) :- reqs_json(Reqs, Js). +args_json_local(explain_blocked, Req, Js) :- req_json(Req, Js). +args_json_local(layer_closure, Req, Js) :- req_json(Req, Js). +args_json_local(removal_orphans, Pkg, Js) :- atom_string(Pkg, Js). +args_json_local(safe_upgrade, [Pkg, Ver], [NS, VJ]) :- + atom_string(Pkg, NS), ver_json(Ver, VJ). +args_json_local(upgrade_set, [Pkg, Ver], [NS, VJ]) :- + atom_string(Pkg, NS), ver_json(Ver, VJ). +args_json_local(freeze_audit, _, []). +args_json_local(dependents, Pkg, Js) :- atom_string(Pkg, Js). +args_json_local(dependents_installed, Pkg, Js) :- atom_string(Pkg, Js). + +reqs_json([], []). +reqs_json([R|Rs], [J|Js]) :- req_json(R, J), reqs_json(Rs, Js). + +req_json(req(Name, C), _{req: N, constraint: CJ}) :- + atom_string(Name, N), constraint_json(C, CJ). +req_json(Name, N) :- atom(Name), atom_string(Name, N). + +run_term_local(resolve, Cat, Args, Exp) :- + ( resolve(Cat, Args, Sel) -> sel_json(Sel, Js), Exp = _{ok: Js} ; Exp = _{fail: true} ). +run_term_local(resolve_layered, Cat, Args, Exp) :- + ( resolve_layered(Cat, Args, Sel) -> sel_json(Sel, Js), Exp = _{ok: Js} ; Exp = _{fail: true} ). +run_term_local(explain_blocked, Cat, Args, Exp) :- + explain_blocked_list(Cat, Args, List), blocked_list_json(List, Js), Exp = _{ok: Js}. +run_term_local(layer_closure, Cat, Args, Exp) :- + ( layer_closure(Cat, Args, Layer) -> sel_json(Layer, Js), Exp = _{ok: Js} ; Exp = _{fail: true} ). +run_term_local(removal_orphans, Cat, Args, Exp) :- + removal_orphans(Cat, Args, Orphans), sel_json(Orphans, Js), Exp = _{ok: Js}. +run_term_local(safe_upgrade, Cat, [Pkg, Ver], Exp) :- + safe_upgrade(Cat, Pkg, Ver, Verdict), verdict_json(Verdict, Js), Exp = _{ok: Js}. +run_term_local(upgrade_set, Cat, [Pkg, Ver], Exp) :- + upgrade_set_result(Cat, Pkg, Ver, R), upgrade_json(R, Exp). +run_term_local(freeze_audit, Cat, _, Exp) :- + freeze_audit(Cat, Audit), maplist(audit_json, Audit, Js), Exp = _{ok: Js}. +run_term_local(dependents, Cat, Args, Exp) :- + dependents(Cat, Args, Deps), sel_json(Deps, Js), Exp = _{ok: Js}. +run_term_local(dependents_installed, Cat, Args, Exp) :- + dependents_installed(Cat, Args, Deps), sel_json(Deps, Js), Exp = _{ok: Js}. + +ver_json(v(A, B, C), [A, B, C]). +pair_json(N-V, [NS, VJ]) :- atom_string(N, NS), ver_json(V, VJ). +hold_json(N-V, [NS, VJ]) :- atom_string(N, NS), ver_json(V, VJ). +hold_json(base(N-V, R), [NS, VJ, RS]) :- atom_string(N, NS), ver_json(V, VJ), atom_string(R, RS). +layer_json(layer(N, Pkgs), _{name: NS, packages: PJ}) :- + atom_string(N, NS), maplist(hold_json, Pkgs, PJ). +alias_json(alias(A, C), [AS, CS]) :- atom_string(A, AS), atom_string(C, CS). + +constraint_json(any, any). +constraint_json(eq(V), _{op: "eq", v: J}) :- ver_json(V, J). +constraint_json(gte(V), _{op: "gte", v: J}) :- ver_json(V, J). +constraint_json(lt(V), _{op: "lt", v: J}) :- ver_json(V, J). +constraint_json(range(Lo, Hi), _{op: "range", lo: LJ, hi: HJ}) :- + ver_json(Lo, LJ), ver_json(Hi, HJ). + +sel_json([], []). +sel_json([N-V|Rest], [[NS, VJ]|Js]) :- + atom_string(N, NS), ver_json(V, VJ), sel_json(Rest, Js). + +blocked_list_json([], []). +blocked_list_json([blocked(N, needs(C), base_has(V))|Rest], + [_{name: NS, needs: CJ, base_has: VJ}|Js]) :- + atom_string(N, NS), constraint_json(C, CJ), ver_json(V, VJ), + blocked_list_json(Rest, Js). + +verdict_json(safe(cost(R)), _{cost: RS, verdict: "safe"}) :- atom_string(R, RS). +verdict_json(coordinated(Set), _{set: SJ, verdict: "coordinated"}) :- sel_json(Set, SJ). +verdict_json(unsafe(modified), _{reason: "modified", verdict: "unsafe"}). +verdict_json(no_candidate, _{verdict: "no_candidate"}). + +upgrade_json(ok(Set), _{ok: Js}) :- sel_json(Set, Js). +upgrade_json(no_candidate, _{fail: true}). +upgrade_json(blocked(N, needs(C), base_has(V)), _{ok: _{blocked: BJ}}) :- + blocked_list_json([blocked(N, needs(C), base_has(V))], [BJ]). + +audit_json(audit(N, over_frozen), _{kind: "over_frozen", name: NS}) :- atom_string(N, NS). +audit_json(audit(N, suggest(R)), _{kind: "suggest", name: NS, reason: RS}) :- + atom_string(N, NS), atom_string(R, RS). +audit_json(audit(N, held(R)), _{kind: "held", name: NS, reason: RS}) :- + atom_string(N, NS), atom_string(R, RS). diff --git a/examples/pkg_resolver/store/build_stores.sh b/examples/pkg_resolver/store/build_stores.sh new file mode 100755 index 000000000..c3152e663 --- /dev/null +++ b/examples/pkg_resolver/store/build_stores.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT OR Apache-2.0 +# Copyright (c) 2026 John William Creighton (@s243a) +# +# Index P/2 JSONL dumps with D43 uw_fact_index.js. +# +# bash examples/pkg_resolver/store/build_stores.sh DIR +# +# Expects DIR/{pkg,dep,conflict,revdep}.jsonl +# Writes DIR/{pkg,dep,conflict,revdep}.data + .idx + +set -euo pipefail + +DIR="${1:?usage: build_stores.sh DIR}" +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +INDEX="$ROOT/scripts/js_wam/uw_fact_index.js" + +for name in pkg dep conflict revdep; do + src="$DIR/${name}.jsonl" + prefix="$DIR/${name}" + if [[ ! -f "$src" ]]; then + : > "$src" + fi + node "$INDEX" build "$src" "$prefix" +done diff --git a/examples/pkg_resolver/store/lmdb_smoke.sh b/examples/pkg_resolver/store/lmdb_smoke.sh new file mode 100755 index 000000000..b648d61d7 --- /dev/null +++ b/examples/pkg_resolver/store/lmdb_smoke.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT OR Apache-2.0 +# Copyright (c) 2026 John William Creighton (@s243a) +# +# lmdb_smoke.sh — D43 policy: missing-package path is ungated; the real +# lmdb(Dir) arm is gated on `require('lmdb')`. +# +# bash examples/pkg_resolver/store/lmdb_smoke.sh + +set -euo pipefail +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../../.." && pwd)" +cd "$ROOT" + +echo "== lmdb missing-package (ungated) ==" +# Reuse the D43 fixture path: compiling lmdb(...) without the npm package +# must refuse loudly. The fact-sources suite already covers this; we echo +# the same error string from the codec so the policy sentence is pinned. +node -e ' +const path = require("path"); +const c = require("./scripts/js_wam/uw_fact_codec.js"); +const msg = c.lmdbMissingError("store_pkg/2", "/tmp/uw-no-such-lmdb"); +if (!/npm install lmdb/.test(msg) || !/not used as a fallback/.test(msg)) { + process.stderr.write("lmdb missing error text drifted\n"); + process.exit(1); +} +process.stdout.write("lmdb_missing_error_ok\n"); +' + +echo "== lmdb store arm (gated) ==" +if node -e "require('lmdb')" 2>/dev/null; then + CORPUS="$HERE/.out/corpus" + LMDIR="$HERE/.out/lmdb_pkg" + mkdir -p "$LMDIR" + node scripts/js_wam/uw_fact_lmdb.js build "$CORPUS/pkg.jsonl" "$LMDIR" + echo "lmdb_arm_ran ok -> $LMDIR" +else + echo "lmdb_arm_skipped (lmdb npm package not loadable)" +fi diff --git a/examples/pkg_resolver/store/pack.mjs b/examples/pkg_resolver/store/pack.mjs new file mode 100644 index 000000000..caae5cdd0 --- /dev/null +++ b/examples/pkg_resolver/store/pack.mjs @@ -0,0 +1,34 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 John William Creighton (@s243a) +// +// pack.mjs -- P/2 packing matching resolver_store.pl (keep in lockstep). + +export function packVer(v) { + return `${v[0]}.${v[1]}.${v[2]}`; +} + +export function packConstraint(c) { + if (c === "any" || c == null) return "any"; + if (c.op === "eq") return "eq:" + packVer(c.v); + if (c.op === "gte") return "gte:" + packVer(c.v); + if (c.op === "lt") return "lt:" + packVer(c.v); + if (c.op === "range") return "range:" + packVer(c.lo) + ":" + packVer(c.hi); + throw new Error("unknown constraint " + JSON.stringify(c)); +} + +export function packKey(catId, name) { + return String(catId) + "|" + String(name); +} + +export function packDep(ver, dep, c) { + return packVer(ver) + "#" + dep + "#" + packConstraint(c); +} + +export function packConflict(ver, other) { + return packVer(ver) + "#" + other; +} + +export function packRev(name, ver, c) { + return name + "#" + packVer(ver) + "#" + packConstraint(c); +} diff --git a/examples/pkg_resolver/store/rich_to_p2.mjs b/examples/pkg_resolver/store/rich_to_p2.mjs new file mode 100644 index 000000000..35ec6ea2e --- /dev/null +++ b/examples/pkg_resolver/store/rich_to_p2.mjs @@ -0,0 +1,56 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 John William Creighton (@s243a) +// +// rich_to_p2.mjs -- compile a rich JSONL catalog dump into four P/2 JSONL +// files (the D43 indexer input). Reverse-deps are precomputed here so +// dependents/upgrade_set is a seek, not a scan. +// +// node examples/pkg_resolver/store/rich_to_p2.mjs + +import { createReadStream } from "node:fs"; +import { mkdirSync, writeFileSync } from "node:fs"; +import { createInterface } from "node:readline"; +import { packKey, packVer, packDep, packConflict, packRev } from "./pack.mjs"; + +const src = process.argv[2]; +const outDir = process.argv[3]; +if (!src || !outDir) { + console.error("usage: rich_to_p2.mjs "); + process.exit(2); +} + +mkdirSync(outDir, { recursive: true }); +const pkg = []; +const dep = []; +const conflict = []; +const rev = []; + +function pair(k, v) { + return JSON.stringify([k, v]); +} + +const rl = createInterface({ input: createReadStream(src), crlfDelay: Infinity }); +for await (const line of rl) { + if (!line) continue; + const row = JSON.parse(line); + const cat = row.catalog || "default"; + const kind = row.kind; + if (kind === "package") { + pkg.push(pair(packKey(cat, row.name), packVer(row.ver))); + } else if (kind === "depends") { + dep.push(pair(packKey(cat, row.name), packDep(row.ver, row.dep, row.constraint))); + rev.push(pair(packKey(cat, row.dep), packRev(row.name, row.ver, row.constraint))); + } else if (kind === "conflicts") { + conflict.push(pair(packKey(cat, row.name), packConflict(row.ver, row.other))); + } +} + +writeFileSync(outDir + "/pkg.jsonl", pkg.join("\n") + (pkg.length ? "\n" : "")); +writeFileSync(outDir + "/dep.jsonl", dep.join("\n") + (dep.length ? "\n" : "")); +writeFileSync(outDir + "/conflict.jsonl", conflict.join("\n") + (conflict.length ? "\n" : "")); +writeFileSync(outDir + "/revdep.jsonl", rev.join("\n") + (rev.length ? "\n" : "")); +process.stdout.write( + "rich_to_p2: pkg=" + pkg.length + " dep=" + dep.length + + " conflict=" + conflict.length + " revdep=" + rev.length + " -> " + outDir + "\n" +); diff --git a/examples/pkg_resolver/wamjs_store/build.pl b/examples/pkg_resolver/wamjs_store/build.pl new file mode 100644 index 000000000..55f60bcb5 --- /dev/null +++ b/examples/pkg_resolver/wamjs_store/build.pl @@ -0,0 +1,64 @@ +:- encoding(utf8). +% SPDX-License-Identifier: MIT OR Apache-2.0 +% Copyright (c) 2026 John William Creighton (@s243a) +% +% build.pl -- compile resolver.pl + resolver_store.pl with D43 indexed +% fact sources for store_pkg/2, store_dep/2, store_conflict/2, store_revdep/2. + +:- use_module('../../../src/unifyweaver/targets/wam_javascript_target', + [write_wam_javascript_project/3]). + +load_into_user(File, Preds) :- + setup_call_cleanup(open(File, read, S), load_terms(S, [], Acc), close(S)), + sort(Acc, Preds). + +load_terms(S, Acc, Preds) :- + read_term(S, T, []), + ( T == end_of_file + -> Preds = Acc + ; T = (:- _) + -> load_terms(S, Acc, Preds) + ; pred_of_term(T, PA), + assertz(user:T), + load_terms(S, [PA|Acc], Preds) + ). + +pred_of_term((Head :- _), P/A) :- !, functor(Head, P, A). +pred_of_term(Head, P/A) :- functor(Head, P, A). + +store_prefix(StoreDir, Name, Prefix) :- + atom_concat(StoreDir, '/', T), + atom_concat(T, Name, Prefix). + +main :- + current_prolog_flag(argv, Argv), + ( Argv = [SrcStore, OutDir, StoreDir|_] + -> true + ; SrcStore = '../resolver_store.pl', + OutDir = '.', + StoreDir = '../store/.out/corpus' + ), + file_directory_name(SrcStore, SrcDir), + atom_concat(SrcDir, '/resolver.pl', SrcRes), + load_into_user(SrcRes, PredsR), + load_into_user(SrcStore, PredsS), + append(PredsR, PredsS, Preds0), + StorePreds = [store_pkg/2, store_dep/2, store_conflict/2, store_revdep/2], + append(Preds0, StorePreds, Preds1), + sort(Preds1, Preds), + store_prefix(StoreDir, pkg, PkgP), + store_prefix(StoreDir, dep, DepP), + store_prefix(StoreDir, conflict, ConfP), + store_prefix(StoreDir, revdep, RevP), + Sources = [ + source(store_pkg/2, indexed(PkgP)), + source(store_dep/2, indexed(DepP)), + source(store_conflict/2, indexed(ConfP)), + source(store_revdep/2, indexed(RevP)) + ], + length(Preds, N), + format("wamjs_store build.pl: compiling ~w predicates; stores under ~w~n", [N, StoreDir]), + write_wam_javascript_project(Preds, + [emit_mode(mixed), javascript_wam_fact_sources(Sources)], + OutDir), + format("wamjs_store build.pl: wrote JS WAM project under ~w/js/~n", [OutDir]). diff --git a/examples/pkg_resolver/wamjs_store/build.sh b/examples/pkg_resolver/wamjs_store/build.sh new file mode 100755 index 000000000..7cddf2a1a --- /dev/null +++ b/examples/pkg_resolver/wamjs_store/build.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT OR Apache-2.0 +# Copyright (c) 2026 John William Creighton (@s243a) +# +# build.sh -- dump corpus catalogs to P/2 JSONL, index with D43, compile +# resolver_store.pl against those stores. +# +# bash examples/pkg_resolver/wamjs_store/build.sh +# STORE_DIR=path bash examples/pkg_resolver/wamjs_store/build.sh # override + +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../../.." && pwd)" +SRC="$HERE/../resolver_store.pl" +OUT="$HERE" +STORE="${STORE_DIR:-$HERE/../store/.out/corpus}" + +mkdir -p "$STORE" +cd "$ROOT" +swipl -q -g dump_store_data -t halt examples/pkg_resolver/dump_store_data.pl -- "$STORE" +bash examples/pkg_resolver/store/build_stores.sh "$STORE" + +swipl -q -g main -t halt "$HERE/build.pl" -- "$SRC" "$OUT" "$STORE" + +node --check "$OUT/js/generated_program.js" +node --check "$OUT/js/wam_runtime.js" +echo "wamjs_store/build.sh: node --check clean -> $OUT/js/generated_program.js" diff --git a/examples/pkg_resolver/wamjs_store/diff_runner_wamjs.mjs b/examples/pkg_resolver/wamjs_store/diff_runner_wamjs.mjs new file mode 100644 index 000000000..ee7336fe0 --- /dev/null +++ b/examples/pkg_resolver/wamjs_store/diff_runner_wamjs.mjs @@ -0,0 +1,26 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 John William Creighton (@s243a) +// +// diff_runner_wamjs.mjs -- JS-WAM side of the pkg_resolver differential. +// Reads the same JSONL as diff_runner.pl; writes one result object per line. +// +// node examples/pkg_resolver/wamjs/diff_runner_wamjs.mjs < cases.jsonl + +import { runCase } from "./resolver_store.mjs"; + +const chunks = []; +for await (const chunk of process.stdin) chunks.push(chunk); +const input = Buffer.concat(chunks).toString("utf8"); + +for (const line of input.split("\n")) { + if (line === "") continue; + const row = JSON.parse(line); + let got; + try { + got = runCase(row); + } catch (err) { + got = { crash: String((err && err.stack) || err) }; + } + process.stdout.write(JSON.stringify({ id: row.id, ...got }) + "\n"); +} diff --git a/examples/pkg_resolver/wamjs_store/resolver_store.mjs b/examples/pkg_resolver/wamjs_store/resolver_store.mjs new file mode 100644 index 000000000..94607d0b3 --- /dev/null +++ b/examples/pkg_resolver/wamjs_store/resolver_store.mjs @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 John William Creighton (@s243a) +// +// resolver_store.mjs -- EDGE of the JS-WAM compiled uw-resolve P2 store adapter. +// +// WHAT IS IN HERE, exhaustively: conversion between JSON env/requests +// and WAM terms, plus driving Runtime.run / lowered_dispatch. There is NO +// resolver logic. Catalog facts come from D43 indexed stores compiled in. +// +// WHAT IS IN HERE, exhaustively: conversion between JSON catalogs/requests +// and WAM terms, plus driving Runtime.run / lowered_dispatch. There is NO +// resolver logic — no candidate order, no constraint arithmetic, no +// layer walk. Those live in js/generated_program.js (compiler output from +// examples/pkg_resolver/resolver.pl). + +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const require = createRequire(import.meta.url); +const generated = require(join(dirname(fileURLToPath(import.meta.url)), "js", "generated_program.js")); +const { Runtime, V } = generated; +const program = generated.program || generated.M && generated.M.program; + +function internAtom(name) { + return V.Atom(Runtime.intern(program.intern_table, String(name))); +} + +function functorId(name) { + return Runtime.intern(program.intern_table, String(name)); +} + +function functorName(fid) { + return Runtime.functor_name(program.intern_table, fid); +} + +function isCons(term) { + if (!term || term.tag !== "struct" || (term.args || []).length !== 2) return false; + const n = functorName(term.fid); + return n === "[|]" || n === "." || n === "./2" || n === "[|]/2"; +} + +function jsListToTerm(arr, mapItem) { + let cur = internAtom("[]"); + const fid = functorId("[|]"); + for (let i = arr.length - 1; i >= 0; i--) { + cur = V.Struct(fid, [mapItem(arr[i]), cur]); + } + return cur; +} + +function vTerm(triple) { + return V.Struct(functorId("v"), [ + V.Int(triple[0] | 0), + V.Int(triple[1] | 0), + V.Int(triple[2] | 0) + ]); +} + +function constraintTerm(c) { + if (c === "any" || c == null) return internAtom("any"); + if (typeof c === "object" && c.op === "eq") return V.Struct(functorId("eq"), [vTerm(c.v)]); + if (typeof c === "object" && c.op === "gte") return V.Struct(functorId("gte"), [vTerm(c.v)]); + if (typeof c === "object" && c.op === "lt") return V.Struct(functorId("lt"), [vTerm(c.v)]); + if (typeof c === "object" && c.op === "range") { + return V.Struct(functorId("range"), [vTerm(c.lo), vTerm(c.hi)]); + } + throw new Error("resolver shim: unknown constraint " + JSON.stringify(c)); +} + +function pairTerm(name, ver) { + return V.Struct(functorId("-"), [internAtom(name), vTerm(ver)]); +} + +function holdTerm(row) { + if (!row) return internAtom("[]"); + if (row.length >= 3) { + return V.Struct(functorId("base"), [pairTerm(row[0], row[1]), internAtom(row[2])]); + } + return pairTerm(row[0], row[1]); +} + +function layerTerm(row) { + const name = (row && row.name) || row[0]; + const pkgs = (row && row.packages) || row[1] || []; + return V.Struct(functorId("layer"), [internAtom(name), jsListToTerm(pkgs, holdTerm)]); +} + +function aliasTerm(row) { + return V.Struct(functorId("alias"), [internAtom(row[0]), internAtom(row[1])]); +} + +function pkgTerm(row) { + return V.Struct(functorId("package"), [internAtom(row[0]), vTerm(row[1])]); +} + +function depTerm(row) { + return V.Struct(functorId("depends"), [ + internAtom(row[0]), vTerm(row[1]), internAtom(row[2]), constraintTerm(row[3]) + ]); +} + +function confTerm(row) { + return V.Struct(functorId("conflicts"), [ + internAtom(row[0]), vTerm(row[1]), internAtom(row[2]) + ]); +} + +function requestTerm(req) { + if (req && typeof req === "object" && req.req) { + return V.Struct(functorId("req"), [internAtom(req.req), constraintTerm(req.constraint)]); + } + return internAtom(req); +} + +export function envToTerm(env0) { + const e = env0 || {}; + const catId = e.catalog_id || e.catalogId || "default"; + return V.Struct(functorId("env"), [ + internAtom(catId), + jsListToTerm(e.base || [], holdTerm), + jsListToTerm(e.installed || [], (p) => pairTerm(p[0], p[1])), + jsListToTerm(e.requested || [], internAtom), + jsListToTerm(e.layers || [], layerTerm), + jsListToTerm(e.excluded || [], internAtom), + jsListToTerm(e.aliases || [], aliasTerm) + ]); +} + +function envOf(row) { + if (row.env) { + const e = { ...row.env }; + if (row.catalog_id && !e.catalog_id) e.catalog_id = row.catalog_id; + return e; + } + const c = row.catalog || {}; + return { + catalog_id: row.catalog_id || "default", + base: c.base || [], + installed: c.installed || [], + requested: c.requested || [], + layers: c.layers || [], + excluded: c.excluded || [], + aliases: c.aliases || [] + }; +} + +function termToJs(state, term0) { + const term = Runtime.deref(state, term0); + if (!term || typeof term !== "object") return term; + if (term.tag === "int" || term.tag === "float") return term.val; + if (term.tag === "string") return String(term.val); + if (term.tag === "atom") { + const n = Runtime.string_of(program.intern_table, term.id); + if (n === "[]") return []; + if (n === "any") return "any"; + if (n === "true") return true; + if (n === "false") return false; + return n; + } + if (term.tag === "unbound") return null; + if (isCons(term)) { + const out = []; + let cur = term; + const nilId = Runtime.intern(program.intern_table, "[]"); + while (true) { + cur = Runtime.deref(state, cur); + if (cur.tag === "atom" && cur.id === nilId) return out; + if (!isCons(cur)) throw new Error("resolver shim: expected a list"); + out.push(termToJs(state, cur.args[0])); + cur = cur.args[1]; + } + } + if (term.tag === "struct") { + const n = functorName(term.fid).replace(/\/\d+$/, ""); + const args = (term.args || []).map((a) => termToJs(state, a)); + if (n === "v" && args.length === 3) return args; + if (n === "-" && args.length === 2) return [args[0], args[1]]; + if (n === "blocked" && args.length === 3) { + const needs = args[1] && args[1][0] === "needs" ? args[1][1] : args[1]; + const bh = args[2] && args[2][0] === "base_has" ? args[2][1] : args[2]; + return { name: args[0], needs: needs, base_has: bh }; + } + if (n === "safe" && args.length === 1) { + const cost = Array.isArray(args[0]) && args[0][0] === "cost" ? args[0][1] : args[0]; + return { cost: cost, verdict: "safe" }; + } + if (n === "coordinated" && args.length === 1) { + return { set: args[0], verdict: "coordinated" }; + } + if (n === "unsafe" && args.length === 1) { + return { reason: args[0], verdict: "unsafe" }; + } + if (n === "audit" && args.length === 2) { + return normalizeAuditTerm(args[0], args[1]); + } + if (n === "ok" && args.length === 1) return { __ok_set: args[0] }; + if (n === "needs" || n === "base_has" || n === "eq" || n === "gte" || n === "lt" + || n === "cost" || n === "held" || n === "suggest") { + return [n, args[0]]; + } + if (n === "range") return { op: "range", lo: args[0], hi: args[1] }; + return [n, ...args]; + } + throw new Error("resolver shim: unhandled term " + JSON.stringify(term)); +} + +function normalizeConstraint(c) { + if (c === "any") return "any"; + if (Array.isArray(c) && (c[0] === "gte" || c[0] === "eq" || c[0] === "lt")) { + return { op: c[0], v: c[1] }; + } + if (c && typeof c === "object" && c.op) return c; + if (Array.isArray(c) && c[0] === "range") return { op: "range", lo: c[1], hi: c[2] }; + return c; +} + +function normalizeAuditTerm(name, payload) { + if (payload === "over_frozen") return { kind: "over_frozen", name: name }; + if (Array.isArray(payload) && payload[0] === "suggest") { + return { kind: "suggest", name: name, reason: payload[1] }; + } + if (Array.isArray(payload) && payload[0] === "held") { + return { kind: "held", name: name, reason: payload[1] }; + } + if (payload && typeof payload === "object" && payload.kind) return payload; + return { kind: "held", name: name, reason: payload }; +} + +function normalizeVerdict(v) { + if (v === "no_candidate") return { verdict: "no_candidate" }; + if (v && typeof v === "object" && v.verdict) return v; + return v; +} + +function normalizeUpgrade(r) { + if (r === "no_candidate") return { fail: true }; + if (r && typeof r === "object" && r.__ok_set) return { ok: r.__ok_set }; + if (Array.isArray(r)) return { ok: r }; + if (r && typeof r === "object" && r.name && r.base_has !== undefined) { + return { ok: { blocked: normalizeBlocked(r) } }; + } + return r; +} + +function normalizeBlocked(b) { + if (b && typeof b === "object" && b.name) { + // Key order matches SWI json_write_dict (alpha) so corpus stringify compares. + return { + base_has: b.base_has, + name: b.name, + needs: normalizeConstraint(b.needs) + }; + } + return b; +} + +function runPred(predArity, argTerms) { + const state = Runtime.new_state(); + state.program = program; + const slash = String(predArity).lastIndexOf("/"); + const arity = slash >= 0 ? Number(predArity.slice(slash + 1)) : argTerms.length; + const saved = []; + // Hold the result cells themselves: lowered T4 list-walks (map_requests/2) + // overwrite A-registers with the recursive tail, but unification still + // binds this unbound. Deref after run — same trick as cliArgs.mjs. + for (let i = 0; i < arity; i++) { + const t = i < argTerms.length && argTerms[i] !== undefined + ? argTerms[i] + : Runtime.new_var(state); + Runtime.put_reg(state, i + 1, t); + saved.push(t); + } + const lowered = program.lowered_dispatch && program.lowered_dispatch[predArity]; + const startPc = program.labels && program.labels[predArity]; + let ok; + if (typeof lowered === "function") { + ok = lowered(program, state) === true; + } else if (startPc !== undefined) { + state.pc = startPc; + ok = Runtime.run(program, state) === true; + } else { + throw new Error("unknown predicate: " + predArity); + } + return { ok: ok === true, state, saved }; +} + +function readSaved(state, saved, n) { + return termToJs(state, saved[n - 1]); +} + +export function resolve(env, requests) { + const e = envToTerm(env); + const reqs = jsListToTerm(requests || [], requestTerm); + const { ok, state, saved } = runPred("resolve_store/3", [e, reqs, undefined]); + if (!ok) return { fail: true }; + return { ok: readSaved(state, saved, 3) }; +} + +export function resolveLayered(env, requests) { + const e = envToTerm(env); + const reqs = jsListToTerm(requests || [], requestTerm); + const { ok, state, saved } = runPred("resolve_layered_store/3", [e, reqs, undefined]); + if (!ok) return { fail: true }; + return { ok: readSaved(state, saved, 3) }; +} + +export function explainBlocked(env, request) { + const e = envToTerm(env); + const req = requestTerm(request); + const { ok, state, saved } = runPred("explain_blocked_list_store/3", [e, req, undefined]); + if (!ok) return { fail: true }; + const list = readSaved(state, saved, 3) || []; + return { ok: list.map(normalizeBlocked) }; +} + +export function layerClosure(env, request) { + const e = envToTerm(env); + const req = requestTerm(request); + const { ok, state, saved } = runPred("layer_closure_store/3", [e, req, undefined]); + if (!ok) return { fail: true }; + return { ok: readSaved(state, saved, 3) }; +} + +export function removalOrphans(env, pkg) { + const e = envToTerm(env); + const { ok, state, saved } = runPred("removal_orphans_store/3", [e, internAtom(pkg), undefined]); + if (!ok) return { fail: true }; + return { ok: readSaved(state, saved, 3) }; +} + +export function safeUpgrade(env, pkg, ver) { + const e = envToTerm(env); + const { ok, state, saved } = runPred("safe_upgrade_store/4", [e, internAtom(pkg), vTerm(ver), undefined]); + if (!ok) return { fail: true }; + return { ok: normalizeVerdict(readSaved(state, saved, 4)) }; +} + +export function upgradeSet(env, pkg, ver) { + const e = envToTerm(env); + const { ok, state, saved } = runPred("upgrade_set_result_store/4", [e, internAtom(pkg), vTerm(ver), undefined]); + if (!ok) return { fail: true }; + return normalizeUpgrade(readSaved(state, saved, 4)); +} + +export function freezeAudit(env) { + const e = envToTerm(env); + const { ok, state, saved } = runPred("freeze_audit_store/2", [e, undefined]); + if (!ok) return { fail: true }; + return { ok: readSaved(state, saved, 2) || [] }; +} + +export function dependents(env, pkg) { + const e = envToTerm(env); + const { ok, state, saved } = runPred("dependents_store/3", [e, internAtom(pkg), undefined]); + if (!ok) return { fail: true }; + return { ok: readSaved(state, saved, 3) }; +} + +export function dependentsInstalled(env, pkg) { + const e = envToTerm(env); + const { ok, state, saved } = runPred("dependents_installed_store/3", [e, internAtom(pkg), undefined]); + if (!ok) return { fail: true }; + return { ok: readSaved(state, saved, 3) }; +} + +function pkgVerArgs(args) { + return { pkg: args[0], ver: args[1] }; +} + +export function runCase(row) { + const env = envOf(row); + const q = row.query; + const args = row.args; + if (q === "resolve") return resolve(env, args); + if (q === "resolve_layered") return resolveLayered(env, args); + if (q === "explain_blocked") return explainBlocked(env, args); + if (q === "layer_closure") return layerClosure(env, args); + if (q === "removal_orphans") return removalOrphans(env, args); + if (q === "safe_upgrade") { + const a = pkgVerArgs(args); + return safeUpgrade(env, a.pkg, a.ver); + } + if (q === "upgrade_set") { + const a = pkgVerArgs(args); + return upgradeSet(env, a.pkg, a.ver); + } + if (q === "freeze_audit") return freezeAudit(env); + if (q === "dependents") return dependents(env, args); + if (q === "dependents_installed") return dependentsInstalled(env, args); + throw new Error("unknown query " + q); +} diff --git a/examples/pkg_resolver/wamjs_store/run_corpus.mjs b/examples/pkg_resolver/wamjs_store/run_corpus.mjs new file mode 100644 index 000000000..9b2a6325f --- /dev/null +++ b/examples/pkg_resolver/wamjs_store/run_corpus.mjs @@ -0,0 +1,51 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 John William Creighton (@s243a) +// +// run_corpus.mjs -- compare dump_corpus JSONL (SWI expected) to the JS-WAM shim. + +import { readFileSync, writeFileSync } from "node:fs"; +import { runCase } from "./resolver_store.mjs"; + +const src = process.argv[2]; +const dest = process.argv[3]; +if (!src || !dest) { + console.error("usage: node run_corpus.mjs "); + process.exit(2); +} + +function stableStringify(x) { + if (x === null || typeof x !== "object") return JSON.stringify(x); + if (Array.isArray(x)) return "[" + x.map(stableStringify).join(",") + "]"; + const keys = Object.keys(x).sort(); + return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableStringify(x[k])).join(",") + "}"; +} + +const lines = readFileSync(src, "utf8").split("\n").filter((l) => l !== ""); +const out = []; +let divergences = 0; +for (const line of lines) { + const row = JSON.parse(line); + let got; + try { + got = runCase(row); + } catch (err) { + got = { crash: String((err && err.stack) || err) }; + } + out.push(JSON.stringify({ id: row.id, got })); + const exp = row.expected; + if (stableStringify(got) !== stableStringify(exp)) { + divergences += 1; + console.error("DIVERGE", row.id); + console.error(" expected", JSON.stringify(exp)); + console.error(" got ", JSON.stringify(got)); + } else { + console.log("ok", row.id); + } +} +writeFileSync(dest, out.join("\n") + "\n"); +if (divergences !== 0) { + console.error("corpus-under-node: " + divergences + " divergences / " + lines.length); + process.exit(1); +} +console.log("corpus-under-node: " + lines.length + "/" + lines.length + " matched SWI"); diff --git a/examples/pkg_resolver/wamjs_store/run_corpus_wamjs.sh b/examples/pkg_resolver/wamjs_store/run_corpus_wamjs.sh new file mode 100755 index 000000000..f64ea8b5f --- /dev/null +++ b/examples/pkg_resolver/wamjs_store/run_corpus_wamjs.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT OR Apache-2.0 +# Copyright (c) 2026 John William Creighton (@s243a) +# +# bash examples/pkg_resolver/wamjs_store/run_corpus_wamjs.sh + +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../../.." && pwd)" +STORE="${STORE_DIR:-$HERE/../store/.out/corpus}" +OUT="$HERE/.corpus_out" +mkdir -p "$OUT" + +cd "$ROOT" +if [[ ! -f "$HERE/js/generated_program.js" ]]; then + bash "$HERE/build.sh" +fi +# dump_store_data writes cases.jsonl with term-catalog expected results +if [[ ! -f "$STORE/cases.jsonl" ]]; then + swipl -q -g dump_store_data -t halt examples/pkg_resolver/dump_store_data.pl -- "$STORE" +fi +node "$HERE/run_corpus.mjs" "$STORE/cases.jsonl" "$OUT/wamjs.jsonl" From 3d9ef0d3c1da5dc3abd22ea897f1ab80944132a8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 19:38:28 +0000 Subject: [PATCH 5/6] Add a 5k-package scale demo and store-backed differential. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seeded catalog (≥5000 packages, ≥15000 deps) with a bound resolve_layered bytes-read proof, term-vs-store timings, and 500 SWI-vs-wamjs cases at 0 divergences. Co-authored-by: johns243a --- examples/pkg_resolver/run_scale_demo.sh | 53 ++++++ .../pkg_resolver/run_store_differential.sh | 54 ++++++ .../pkg_resolver/store/gen_scale_catalog.mjs | 153 +++++++++++++++++ examples/pkg_resolver/store/run_probe.mjs | 22 +++ examples/pkg_resolver/store/scale_demo.pl | 103 ++++++++++++ examples/pkg_resolver/store_diff_runner.pl | 159 ++++++++++++++++++ 6 files changed, 544 insertions(+) create mode 100755 examples/pkg_resolver/run_scale_demo.sh create mode 100755 examples/pkg_resolver/run_store_differential.sh create mode 100644 examples/pkg_resolver/store/gen_scale_catalog.mjs create mode 100644 examples/pkg_resolver/store/run_probe.mjs create mode 100644 examples/pkg_resolver/store/scale_demo.pl create mode 100644 examples/pkg_resolver/store_diff_runner.pl diff --git a/examples/pkg_resolver/run_scale_demo.sh b/examples/pkg_resolver/run_scale_demo.sh new file mode 100755 index 000000000..d62e10cee --- /dev/null +++ b/examples/pkg_resolver/run_scale_demo.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT OR Apache-2.0 +# Copyright (c) 2026 John William Creighton (@s243a) +# +# run_scale_demo.sh -- 5k catalog: bytes-read proof + term-vs-store timings. +# +# bash examples/pkg_resolver/run_scale_demo.sh + +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" +SCALE="$HERE/store/.out/scale" +WAM="$SCALE/wamjs" +mkdir -p "$SCALE" "$WAM" +cd "$ROOT" + +if [[ ! -f "$SCALE/pkg.data" ]]; then + node "$HERE/store/gen_scale_catalog.mjs" "$SCALE" + node "$HERE/store/rich_to_p2.mjs" "$SCALE/rich.jsonl" "$SCALE" + bash "$HERE/store/build_stores.sh" "$SCALE" +fi +if [[ ! -f "$WAM/js/generated_program.js" ]]; then + swipl -q -g main -t halt "$HERE/wamjs_store/build.pl" -- \ + "$HERE/resolver_store.pl" "$WAM" "$SCALE" + cp "$HERE/wamjs_store/resolver_store.mjs" "$WAM/resolver_store.mjs" +fi + +echo "== store sizes ==" +python3 - < "$SCALE/probe_wamjs.err" | tee "$SCALE/probe_wamjs.log" +echo "--- fact_io stderr ---" +tee -a "$SCALE/probe_wamjs.log" < "$SCALE/probe_wamjs.err" + +echo "== SWI term vs store timings (load included) ==" +STORE_DIR="$SCALE" swipl -q -g scale_demo -t halt "$HERE/store/scale_demo.pl" -- "$SCALE" \ + | tee "$SCALE/timing_swi.log" diff --git a/examples/pkg_resolver/run_store_differential.sh b/examples/pkg_resolver/run_store_differential.sh new file mode 100755 index 000000000..dd1814ce5 --- /dev/null +++ b/examples/pkg_resolver/run_store_differential.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT OR Apache-2.0 +# Copyright (c) 2026 John William Creighton (@s243a) +# +# run_store_differential.sh -- ≥500 seeded envs against the 5k catalog; +# SWI store adapter vs wamjs_store, 0 divergences. +# +# bash examples/pkg_resolver/run_store_differential.sh + +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" +SCALE="$HERE/store/.out/scale" +WAM="$SCALE/wamjs" +OUT="$HERE/store/.out/scale_diff" +mkdir -p "$SCALE" "$WAM" "$OUT" + +cd "$ROOT" + +echo "== generating 5k catalog + 500 cases ==" +node "$HERE/store/gen_scale_catalog.mjs" "$SCALE" +if [[ ! -f "$SCALE/pkg.data" ]]; then + node "$HERE/store/rich_to_p2.mjs" "$SCALE/rich.jsonl" "$SCALE" + bash "$HERE/store/build_stores.sh" "$SCALE" +fi + +echo "== compiling wamjs_store against scale indexes ==" +if [[ ! -f "$WAM/js/generated_program.js" ]]; then + swipl -q -g main -t halt "$HERE/wamjs_store/build.pl" -- \ + "$HERE/resolver_store.pl" "$WAM" "$SCALE" + node --check "$WAM/js/generated_program.js" +fi +cp "$HERE/wamjs_store/resolver_store.mjs" "$WAM/resolver_store.mjs" +cp "$HERE/wamjs_store/diff_runner_wamjs.mjs" "$WAM/diff_runner_wamjs.mjs" + +echo "== SWI store oracle ==" +START_SWI=$(date +%s%N) +STORE_DIR="$SCALE" swipl -q -g main -t halt "$HERE/store_diff_runner.pl" \ + < "$SCALE/cases.jsonl" > "$OUT/swi.jsonl" +END_SWI=$(date +%s%N) + +echo "== wamjs_store ==" +START_WAM=$(date +%s%N) +node "$WAM/diff_runner_wamjs.mjs" < "$SCALE/cases.jsonl" > "$OUT/wamjs.jsonl" +END_WAM=$(date +%s%N) + +python3 - < +// Writes rich.jsonl, then caller runs rich_to_p2 + build_stores. +// Writes cases.jsonl (500 store-backed differential cases) and probe.json +// (the bound resolve_layered request used for the bytes-read proof). + +import { mkdirSync, writeFileSync } from "node:fs"; +import { packKey } from "./pack.mjs"; + +const SEED = 0xc0ffee01; +const N_PKGS = 5000; +const N_CASES = 500; +const CAT = "s5k"; + +function mulberry32(a) { + return function () { + a |= 0; + a = (a + 0x6d2b79f5) | 0; + let t = Math.imul(a ^ (a >>> 15), 1 | a); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +} + +function pick(rng, n) { + return Math.floor(rng() * n); +} + +const rng = mulberry32(SEED); +const outDir = process.argv[2]; +if (!outDir) { + console.error("usage: gen_scale_catalog.mjs "); + process.exit(2); +} +mkdirSync(outDir, { recursive: true }); + +const names = Array.from({ length: N_PKGS }, (_, i) => "p" + i); +const rich = []; +let nDeps = 0; + +for (let i = 0; i < N_PKGS; i++) { + const name = names[i]; + const nVer = 1 + pick(rng, 2); // 1–2 versions + const vers = []; + for (let k = 0; k < nVer; k++) vers.push([k, 0, 0]); + for (const v of vers) { + rich.push({ kind: "package", catalog: CAT, name, ver: v }); + } + const nDep = i === 0 ? 0 : 2 + pick(rng, 3); // 2–4, DAG onto earlier names + const used = new Set(); + for (let d = 0; d < nDep && i > 0; d++) { + const j = pick(rng, i); + if (used.has(j)) continue; + used.add(j); + const ver = vers[0]; + const dep = names[j]; + const kind = rng(); + let constraint = "any"; + if (kind < 0.25) constraint = { op: "gte", v: [0, 0, 0] }; + rich.push({ + kind: "depends", + catalog: CAT, + name, + ver, + dep, + constraint + }); + nDeps += 1; + } +} + +if (nDeps < 15000) { + // pad with extra edges on later packages + for (let i = 3; i < N_PKGS && nDeps < 15000; i++) { + const j = (i * 7 + 3) % i; + rich.push({ + kind: "depends", + catalog: CAT, + name: names[i], + ver: [0, 0, 0], + dep: names[j], + constraint: "any" + }); + nDeps += 1; + } +} + +const richPath = outDir + "/rich.jsonl"; +writeFileSync(richPath, rich.map((r) => JSON.stringify(r)).join("\n") + "\n"); + +// Probe: a mid-graph package whose closure is small-ish (depends on earlier). +const probeName = "p30"; +const probe = { + catalog_id: CAT, + query: "resolve_layered", + args: [probeName], + env: { + catalog_id: CAT, + base: [["p0", [0, 0, 0]]], + installed: [], + requested: [], + layers: [], + excluded: [], + aliases: [] + } +}; +writeFileSync(outDir + "/probe.json", JSON.stringify(probe, null, 2)); + +const cases = []; +for (let i = 0; i < N_CASES; i++) { + const qn = ["resolve", "resolve_layered", "explain_blocked", "dependents", + "freeze_audit", "dependents_installed"][i % 6]; + // Closure queries stay in the first 25 packages so a request touches a + // slice of the 5k store. dependents* may target any package (pure seek). + const small = names[pick(rng, 25)]; + const any = names[pick(rng, N_PKGS)]; + const nBase = pick(rng, 3); + const base = []; + for (let b = 0; b < nBase; b++) { + const bn = names[pick(rng, 25)]; + base.push([bn, [0, 0, 0]]); + } + let args; + if (qn === "resolve" || qn === "resolve_layered") args = [small]; + else if (qn === "explain_blocked") args = small; + else if (qn === "freeze_audit") args = []; + else args = any; + cases.push({ + id: "s" + i, + catalog_id: CAT, + query: qn, + args, + env: { + catalog_id: CAT, + base, + installed: base.slice(0, 2), + requested: base.length ? [base[0][0]] : [], + layers: [], + excluded: [], + aliases: [] + } + }); +} +writeFileSync(outDir + "/cases.jsonl", cases.map((c) => JSON.stringify(c)).join("\n") + "\n"); + +console.log("gen_scale_catalog: packages=" + N_PKGS + " dep_edges=" + nDeps + + " rich_rows=" + rich.length + " cases=" + N_CASES + " cat=" + CAT); +console.log(" key example " + packKey(CAT, "p0")); diff --git a/examples/pkg_resolver/store/run_probe.mjs b/examples/pkg_resolver/store/run_probe.mjs new file mode 100644 index 000000000..abe44fe14 --- /dev/null +++ b/examples/pkg_resolver/store/run_probe.mjs @@ -0,0 +1,22 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 John William Creighton (@s243a) +// +// node run_probe.mjs +// Must be launched with cwd or imports resolving generated_program.js +// next to resolver_store.mjs inside the wam project dir. + +import { readFileSync } from "node:fs"; +import { pathToFileURL } from "node:url"; +import { join, resolve } from "node:path"; + +const wamDir = resolve(process.argv[2]); +const probePath = resolve(process.argv[3]); +const shimUrl = pathToFileURL(join(wamDir, "resolver_store.mjs")).href; +const { resolveLayered } = await import(shimUrl); +const probe = JSON.parse(readFileSync(probePath, "utf8")); +const t0 = process.hrtime.bigint(); +const got = resolveLayered(probe.env, probe.args); +const t1 = process.hrtime.bigint(); +process.stdout.write("wamjs_result " + JSON.stringify(got) + "\n"); +process.stdout.write("wamjs_wall_ms " + (Number(t1 - t0) / 1e6).toFixed(3) + "\n"); diff --git a/examples/pkg_resolver/store/scale_demo.pl b/examples/pkg_resolver/store/scale_demo.pl new file mode 100644 index 000000000..b6c2c3e4a --- /dev/null +++ b/examples/pkg_resolver/store/scale_demo.pl @@ -0,0 +1,103 @@ +:- encoding(utf8). +% SPDX-License-Identifier: MIT OR Apache-2.0 +% Copyright (c) 2026 John William Creighton (@s243a) +% +% scale_demo.pl -- load the 5k catalog as a term AND as P/2 facts; time +% the same resolve_layered query both ways (load cost included). +% +% STORE_DIR=... swipl -q -g scale_demo -t halt examples/pkg_resolver/store/scale_demo.pl -- DIR + +:- module(scale_demo, [scale_demo/0]). + +:- use_module(library(http/json)). +:- use_module('../resolver'). +:- use_module('../resolver_store'). + +scale_demo :- + current_prolog_flag(argv, Argv), + ( Argv = [Dir|_] -> true ; getenv('STORE_DIR', Dir) ), + atom_concat(Dir, '/rich.jsonl', Rich), + atom_concat(Dir, '/probe.json', ProbeF), + setup_call_cleanup(open(ProbeF, read, S), json_read_dict(S, Probe, [value_string_as(atom)]), close(S)), + probe_request(Probe, Req), + statistics(cputime, T0), + load_rich_catalog(Rich, Cat), + statistics(cputime, T1), + ( resolve_layered(Cat, [Req], TermSel) -> true ; TermSel = fail ), + statistics(cputime, T2), + format("swi_term_load_s ~3f~n", [T1 - T0]), + format("swi_term_resolve_s ~3f~n", [T2 - T1]), + format("swi_term_total_s ~3f~n", [T2 - T0]), + format("swi_term_result ~q~n", [TermSel]), + statistics(cputime, U0), + load_p2_jsonl(Dir), + statistics(cputime, U1), + env_from_probe(Probe, Env), + ( resolve_layered_store(Env, [Req], StoreSel) -> true ; StoreSel = fail ), + statistics(cputime, U2), + format("swi_store_load_s ~3f~n", [U1 - U0]), + format("swi_store_resolve_s ~3f~n", [U2 - U1]), + format("swi_store_total_s ~3f~n", [U2 - U0]), + format("swi_store_result ~q~n", [StoreSel]), + ( TermSel == StoreSel + -> format("swi_term_store_match true~n", []) + ; format("swi_term_store_match false~n", []), + halt(1) + ). + +probe_request(Probe, Req) :- + get_dict(args, Probe, Args), + ( is_list(Args) -> Args = [Req0] ; Req0 = Args ), + json_atom(Req0, Req). + +env_from_probe(Probe, env(Id, B, I, R, L, E, A)) :- + get_dict(env, Probe, D), + ( get_dict(catalog_id, D, Id0) -> json_atom(Id0, Id) ; Id = s5k ), + ( get_dict(base, D, B0) -> maplist(json_pair, B0, B) ; B = [] ), + ( get_dict(installed, D, I0) -> maplist(json_pair, I0, I) ; I = [] ), + ( get_dict(requested, D, R0) -> maplist(json_atom, R0, R) ; R = [] ), + L = [], E = [], A = []. + +json_pair([N, V], Name-Ver) :- json_atom(N, Name), json_ver(V, Ver). +json_ver([X, Y, Z], v(X, Y, Z)). +json_atom(A, A) :- atom(A), !. +json_atom(S, A) :- string(S), atom_string(A, S). + +load_rich_catalog(Path, catalog(Ps, Ds, Cs, B, I, R, [], [], [])) :- + setup_call_cleanup(open(Path, read, S), read_rich(S, [], Ps, [], Ds, [], Cs), close(S)), + B = [p0-v(0, 0, 0)], + I = [], + R = []. + +read_rich(S, Ps0, Ps, Ds0, Ds, Cs0, Cs) :- + read_line_to_string(S, Line), + ( Line == end_of_file + -> reverse(Ps0, Ps), reverse(Ds0, Ds), reverse(Cs0, Cs) + ; Line == "" + -> read_rich(S, Ps0, Ps, Ds0, Ds, Cs0, Cs) + ; atom_json_dict(Line, Row, [value_string_as(atom)]), + acc_rich(Row, Ps0, Ps1, Ds0, Ds1, Cs0, Cs1), + read_rich(S, Ps1, Ps, Ds1, Ds, Cs1, Cs) + ). + +acc_rich(Row, Ps, [package(N, V)|Ps], Ds, Ds, Cs, Cs) :- + get_dict(kind, Row, package), !, + json_atom(Row.name, N), json_ver(Row.ver, V). +acc_rich(Row, Ps, Ps, Ds, [depends(N, V, D, C)|Ds], Cs, Cs) :- + get_dict(kind, Row, depends), !, + json_atom(Row.name, N), json_ver(Row.ver, V), + json_atom(Row.dep, D), json_constraint(Row.constraint, C). +acc_rich(Row, Ps, Ps, Ds, Ds, Cs, [conflicts(N, V, O)|Cs]) :- + get_dict(kind, Row, conflicts), !, + json_atom(Row.name, N), json_ver(Row.ver, V), json_atom(Row.other, O). +acc_rich(_, Ps, Ps, Ds, Ds, Cs, Cs). + +json_constraint(any, any) :- !. +json_constraint("any", any) :- !. +json_constraint(D, C) :- + is_dict(D), json_atom(D.op, Op), + ( Op == gte -> json_ver(D.v, V), C = gte(V) + ; Op == eq -> json_ver(D.v, V), C = eq(V) + ; Op == lt -> json_ver(D.v, V), C = lt(V) + ; Op == range -> json_ver(D.lo, Lo), json_ver(D.hi, Hi), C = range(Lo, Hi) + ). diff --git a/examples/pkg_resolver/store_diff_runner.pl b/examples/pkg_resolver/store_diff_runner.pl new file mode 100644 index 000000000..b31426fd9 --- /dev/null +++ b/examples/pkg_resolver/store_diff_runner.pl @@ -0,0 +1,159 @@ +:- encoding(utf8). +% SPDX-License-Identifier: MIT OR Apache-2.0 +% Copyright (c) 2026 John William Creighton (@s243a) +% +% store_diff_runner.pl -- SWI oracle for store-backed resolution. +% Reads JSONL {env, query, args} ; writes one result object per line. +% The P/2 JSONL under STORE_DIR must already be loaded... we load it here. +% +% STORE_DIR=... swipl -q -g main -t halt examples/pkg_resolver/store_diff_runner.pl \ +% < cases.jsonl > swi.jsonl + +:- module(store_diff_runner, [main/0]). + +:- use_module(library(http/json)). +:- use_module(resolver_store). + +main :- + prompt(_, ''), + getenv('STORE_DIR', Dir), + load_p2_jsonl(Dir), + read_line_to_string(user_input, Line), + process_lines(Line). + +process_lines(end_of_file) :- !. +process_lines("") :- + read_line_to_string(user_input, Next), + process_lines(Next). +process_lines(Line) :- + atom_json_dict(Line, Row, [value_string_as(atom)]), + json_to_env(Row, Env), + get_dict(query, Row, Q0), + json_atom(Q0, Q), + run_query(Q, Env, Row.args, Exp), + json_write_dict(current_output, Exp, [width(0), true(true), false(false)]), + nl, + read_line_to_string(user_input, Next), + process_lines(Next). + +json_to_env(Row, env(Id, B, I, R, L, E, A)) :- + ( get_dict(catalog_id, Row, Id0) -> json_atom(Id0, Id) + ; get_dict(env, Row, Env0), get_dict(catalog_id, Env0, Id0) -> json_atom(Id0, Id) + ; Id = default + ), + ( get_dict(env, Row, D) -> true ; D = Row ), + ( get_dict(base, D, B0) -> maplist(json_hold, B0, B) ; B = [] ), + ( get_dict(installed, D, I0) -> maplist(json_pair, I0, I) ; I = [] ), + ( get_dict(requested, D, R0) -> maplist(json_atom, R0, R) ; R = [] ), + ( get_dict(layers, D, L0) -> maplist(json_layer, L0, L) ; L = [] ), + ( get_dict(excluded, D, E0) -> maplist(json_atom, E0, E) ; E = [] ), + ( get_dict(aliases, D, A0) -> maplist(json_alias, A0, A) ; A = [] ). + +json_hold([N, V], Name-Ver) :- + json_atom(N, Name), json_ver(V, Ver). +json_hold([N, V, R], base(Name-Ver, Reason)) :- + json_atom(N, Name), json_ver(V, Ver), json_atom(R, Reason). +json_pair([N, V], Name-Ver) :- + json_atom(N, Name), json_ver(V, Ver). +json_layer(D, layer(Name, Pkgs)) :- + is_dict(D), + json_atom(D.name, Name), + maplist(json_hold, D.packages, Pkgs). +json_alias([A, C], alias(Alias, Canon)) :- + json_atom(A, Alias), json_atom(C, Canon). +json_ver([X, Y, Z], v(X, Y, Z)). +json_atom(A, A) :- atom(A), !. +json_atom(S, A) :- string(S), atom_string(A, S). + +run_query(resolve, Env, Args, Exp) :- + json_to_reqs(Args, Reqs), + ( resolve_store(Env, Reqs, Sel) -> sel_json(Sel, Js), Exp = _{ok: Js} ; Exp = _{fail: true} ). +run_query(resolve_layered, Env, Args, Exp) :- + json_to_reqs(Args, Reqs), + ( resolve_layered_store(Env, Reqs, Sel) -> sel_json(Sel, Js), Exp = _{ok: Js} ; Exp = _{fail: true} ). +run_query(explain_blocked, Env, Args, Exp) :- + json_to_req(Args, Req), + explain_blocked_list_store(Env, Req, List), + blocked_list_json(List, Js), + Exp = _{ok: Js}. +run_query(layer_closure, Env, Args, Exp) :- + json_to_req(Args, Req), + ( layer_closure_store(Env, Req, Layer) -> sel_json(Layer, Js), Exp = _{ok: Js} ; Exp = _{fail: true} ). +run_query(removal_orphans, Env, Args, Exp) :- + json_to_pkg(Args, Pkg), + removal_orphans_store(Env, Pkg, Orphans), + sel_json(Orphans, Js), + Exp = _{ok: Js}. +run_query(safe_upgrade, Env, Args, Exp) :- + json_to_pkg_ver(Args, Pkg, Ver), + safe_upgrade_store(Env, Pkg, Ver, Verdict), + verdict_json(Verdict, Js), + Exp = _{ok: Js}. +run_query(upgrade_set, Env, Args, Exp) :- + json_to_pkg_ver(Args, Pkg, Ver), + upgrade_set_result_store(Env, Pkg, Ver, R), + upgrade_json(R, Exp). +run_query(freeze_audit, Env, _, Exp) :- + freeze_audit_store(Env, Audit), + maplist(audit_json, Audit, Js), + Exp = _{ok: Js}. +run_query(dependents, Env, Args, Exp) :- + json_to_pkg(Args, Pkg), + dependents_store(Env, Pkg, Deps), + sel_json(Deps, Js), + Exp = _{ok: Js}. +run_query(dependents_installed, Env, Args, Exp) :- + json_to_pkg(Args, Pkg), + dependents_installed_store(Env, Pkg, Deps), + sel_json(Deps, Js), + Exp = _{ok: Js}. + +json_to_reqs(List, Reqs) :- maplist(json_to_req, List, Reqs). +json_to_req(D, req(Name, C)) :- + is_dict(D), !, json_atom(D.req, Name), json_constraint(D.constraint, C). +json_to_req(A, Name) :- json_atom(A, Name). +json_to_pkg(A, Name) :- json_atom(A, Name). +json_to_pkg_ver([P, V], Pkg, Ver) :- json_atom(P, Pkg), json_ver(V, Ver). + +json_constraint(any, any) :- !. +json_constraint("any", any) :- !. +json_constraint(D, C) :- + is_dict(D), json_atom(D.op, Op), json_constraint_op(Op, D, C). +json_constraint_op(eq, D, eq(V)) :- json_ver(D.v, V). +json_constraint_op(gte, D, gte(V)) :- json_ver(D.v, V). +json_constraint_op(lt, D, lt(V)) :- json_ver(D.v, V). +json_constraint_op(range, D, range(Lo, Hi)) :- json_ver(D.lo, Lo), json_ver(D.hi, Hi). + +sel_json([], []). +sel_json([N-V|Rest], [[NS, VJ]|Js]) :- + atom_string(N, NS), ver_json(V, VJ), sel_json(Rest, Js). +ver_json(v(A, B, C), [A, B, C]). + +blocked_list_json([], []). +blocked_list_json([blocked(N, needs(C), base_has(V))|Rest], + [_{name: NS, needs: CJ, base_has: VJ}|Js]) :- + atom_string(N, NS), constraint_json(C, CJ), ver_json(V, VJ), + blocked_list_json(Rest, Js). + +constraint_json(any, any). +constraint_json(eq(V), _{op: "eq", v: J}) :- ver_json(V, J). +constraint_json(gte(V), _{op: "gte", v: J}) :- ver_json(V, J). +constraint_json(lt(V), _{op: "lt", v: J}) :- ver_json(V, J). +constraint_json(range(Lo, Hi), _{op: "range", lo: LJ, hi: HJ}) :- + ver_json(Lo, LJ), ver_json(Hi, HJ). + +verdict_json(safe(cost(R)), _{cost: RS, verdict: "safe"}) :- atom_string(R, RS). +verdict_json(coordinated(Set), _{set: SJ, verdict: "coordinated"}) :- sel_json(Set, SJ). +verdict_json(unsafe(modified), _{reason: "modified", verdict: "unsafe"}). +verdict_json(no_candidate, _{verdict: "no_candidate"}). + +upgrade_json(ok(Set), _{ok: Js}) :- sel_json(Set, Js). +upgrade_json(no_candidate, _{fail: true}). +upgrade_json(blocked(N, needs(C), base_has(V)), _{ok: _{blocked: BJ}}) :- + blocked_list_json([blocked(N, needs(C), base_has(V))], [BJ]). + +audit_json(audit(N, over_frozen), _{kind: "over_frozen", name: NS}) :- atom_string(N, NS). +audit_json(audit(N, suggest(R)), _{kind: "suggest", name: NS, reason: RS}) :- + atom_string(N, NS), atom_string(R, RS). +audit_json(audit(N, held(R)), _{kind: "held", name: NS, reason: RS}) :- + atom_string(N, NS), atom_string(R, RS). From 5f4c4f590887b20b0e82e2211b245719edc7b75f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 2 Sep 2026 19:38:28 +0000 Subject: [PATCH 6/6] Document the P2 dump schema, store layout, and environment split. Co-authored-by: johns243a --- examples/pkg_resolver/README.md | 122 ++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 21 deletions(-) diff --git a/examples/pkg_resolver/README.md b/examples/pkg_resolver/README.md index 670eb2f08..b3d95f16b 100644 --- a/examples/pkg_resolver/README.md +++ b/examples/pkg_resolver/README.md @@ -1,7 +1,16 @@ -# uw-resolve P0.5 — holds get reasons; mining adoptions land +# uw-resolve P2 — store-backed resolution + +P0.5 (term catalog) is unchanged and still the API. P2 adds **store-backed** +resolution: packages / depends / conflicts / reverse-deps live in D43 +indexed fact stores; the machine-local environment stays a term. Bytes +read on a bound query are proportional to the query, not the catalog. + +Debian epoch/tilde, `Provides:`/virtual packages, write paths, incremental +store updates, and a `pkg`-style CLI remain deferred. + Layered / frugal distros (Puppy, Woof-CE) run an **immutable curated base** under a writable layer. Stock apt cannot reason about that boundary: it will @@ -11,9 +20,9 @@ is the oracle; the same spec is compiled through `wam_javascript` (`emit_mode(mixed)`) and gated by the contract corpus plus a seeded differential. -Debian epoch/tilde version semantics, `Provides:`/virtual packages, a GP-LMDB -catalog backend, and a `pkg`-style CLI (via `examples/cli_args`) remain -deferred. +Debian epoch/tilde version semantics, `Provides:`/virtual packages, write +paths, incremental store updates, and a `pkg`-style CLI remain deferred. +The GP-LMDB / D43 indexed catalog path is P2 (this document). ## The model @@ -190,41 +199,112 @@ that version or held in a loaded layer at that version. `dependents/3`, `dependents_installed/3` each return one sorted / ground answer. +## P2: store-backed resolution + +Real catalogs cannot be an in-memory term per query. The **big three** +(packages, depends, conflicts) plus a **precomputed reverse-deps** index +are D43 P/2 fact stores. The **environment** (base / layers / installed / +requested / excluded / aliases) is machine-local and tiny — it stays a +term, passed per query: + +``` +env(CatId, Base, Installed, Requested, Layers, Excluded, Aliases) +``` + +`resolver.pl`'s catalog-as-term API is untouched. `resolver_store.pl` +exposes the same 10 queries as `*_store` predicates. Lookups always bind +`CatId|Name` (seek, not scan). + +### JSONL dump schema + +One JSON object per fact (`kind` discriminates). `catalog` is the store +key prefix so many catalogs can share one index. + +```jsonl +{"kind":"package","catalog":"linear","name":"a","ver":[1,0,0]} +{"kind":"depends","catalog":"linear","name":"a","ver":[1,0,0],"dep":"b","constraint":"any"} +{"kind":"depends","catalog":"blocked_base","name":"app","ver":[1,0,0],"dep":"lib","constraint":{"op":"gte","v":[2,0,0]}} +{"kind":"conflicts","catalog":"conflict_pair","name":"foo","ver":[1,0,0],"other":"bar"} +{"kind":"base","catalog":"upgradeable_base","name":"lib","ver":[1,0,0],"reason":"blanket"} +{"kind":"layer","catalog":"named_devx","layer":"devx","name":"gcc","ver":[1,0,0]} +{"kind":"installed","catalog":"removal_basic","name":"app","ver":[1,0,0]} +{"kind":"requested","catalog":"removal_basic","name":"app"} +{"kind":"excluded","catalog":"excluded_select","name":"bad"} +{"kind":"alias","catalog":"alias_rxvt","alias":"urxvt","canonical":"rxvt"} +``` + +Only `package` / `depends` / `conflicts` are indexed. Env rows stay +term-side. `depends` also emits a reverse-dep posting at build time. + +### Store key layout (D43 P/2) + +D43 stores are scalar P/2 (atom/int/float/string). Compounds are packed: + +| Store prefix | a1 (index key) | a2 (packed atom) | +| --- | --- | --- | +| `pkg` | `CatId\|Name` | `Major.Minor.Patch` | +| `dep` | `CatId\|Name` | `Ver#Dep#Constraint` | +| `conflict` | `CatId\|Name` | `Ver#Other` | +| `revdep` | `CatId\|DepName` | `Name#Ver#Constraint` | + +Constraint packing: `any` · `eq:1.0.0` · `gte:1.0.0` · `lt:2.0.0` · +`range:1.0.0:2.0.0`. Builder: `store/rich_to_p2.mjs` then +`store/build_stores.sh` (`scripts/js_wam/uw_fact_index.js`). `lmdb(Dir)` +uses the same P/2 JSONL via `uw_fact_lmdb.js` (opt-in; no repo +`package.json` dependency). + +### SWI-side data + +SWI (the oracle) reads the **same P/2 JSONL files** the indexer consumes +(`load_p2_jsonl/1`), not the binary `.data`/`.idx`. SWI has no UWFI +reader; the JSONL is the canonical row encoding, so both engines see +identical keys and packed cells. WAM seeks `indexed(Prefix)` built from +those rows. + ## Files | path | what | | --- | --- | -| `resolver.pl` | the spec (pure relations) | -| `test_resolver.pl` | contract corpus (plunit + `corpus_case/4`) | -| `dump_corpus.pl` | SWI → JSONL for the wamjs runner | -| `wamjs/` | P1 build, argparser-playbook style | -| `wamjs/build.sh` | `swipl` → `wam_javascript`, `emit_mode(mixed)` | -| `wamjs/resolver.mjs` | term ↔ JSON **only** — no resolver logic | -| `wamjs/run_corpus_wamjs.sh` | the same corpus through node vs SWI | -| `gen_catalogs.mjs` | mulberry32, 2400 catalogs (seed `0xa5b6c7d8`) | -| `run_differential.sh` | SWI vs wamjs, 0-divergence gate | +| `resolver.pl` | the spec (pure relations); API unchanged in P2 | +| `resolver_store.pl` | store adapter (same 10 queries; env term + P/2 facts) | +| `test_resolver.pl` | P0.5 contract corpus (append-only; unchanged) | +| `test_resolver_store.pl` | store vs term identity | +| `dump_corpus.pl` | SWI → JSONL for the term-catalog wamjs runner | +| `dump_store_data.pl` | rich JSONL + P/2 JSONL + store corpus cases | +| `wamjs/` | term-catalog JS WAM build | +| `wamjs_store/` | store-backed JS WAM build (D43 fact sources) | +| `store/` | dump schema helpers, indexer wrapper, 5k generator | +| `gen_catalogs.mjs` | mulberry32, 2400 term catalogs (seed `0xa5b6c7d8`) | +| `run_differential.sh` | term-catalog SWI vs wamjs, 0-divergence gate | +| `run_store_differential.sh` | store-backed 5k catalog, ≥500 cases, 0 divergences | +| `run_scale_demo.sh` | bytes-read proof + term-vs-store timings | ## Build / run From the repo root (SWI-Prolog 9.x, Node v18+, `mkdir -p output/advanced`): ```bash -# contract corpus (SWI oracle) +# P0.5 term-catalog contract swipl -q -g test_resolver -t halt examples/pkg_resolver/test_resolver.pl - -# regenerate the JS WAM project, then drive the same corpus through node bash examples/pkg_resolver/wamjs/build.sh bash examples/pkg_resolver/wamjs/run_corpus_wamjs.sh - -# ≥2200 seeded catalogs, all queries, 0 divergences bash examples/pkg_resolver/run_differential.sh + +# P2 store adapter (identity + indexed corpus) +swipl -q -g test_resolver_store -t halt examples/pkg_resolver/test_resolver_store.pl +bash examples/pkg_resolver/wamjs_store/build.sh +bash examples/pkg_resolver/wamjs_store/run_corpus_wamjs.sh + +# 5k catalog: bytes-read + timings + ≥500-case store differential +bash examples/pkg_resolver/run_scale_demo.sh +bash examples/pkg_resolver/run_store_differential.sh ``` -## Deferred (not P0.5) +## Deferred (not P2) - Debian epoch / tilde / letter version semantics - `Provides:` / virtual packages / alternatives -- GP-LMDB catalog backend (P2 — the `indexed` / `lmdb` fact-source path) -- `pkg`-style CLI on top of `examples/cli_args` +- `pkg`-style CLI on top of `examples/cli_args` (concurrent round owns `cli/`) - write paths (install / remove / commit a layer) - per-file / per-SFS modeling inside a named layer +- incremental store updates (rebuild the four indexes from a full dump)