● All three issues are fixed, tested, and verified end-to-end. Full workspace test #34

Merged
navicore merged 2 commits from issue-31-32-33 into main 2026-06-23 20:26:52 +00:00
Owner

suite passes (24 test binaries, 0 failures) and clippy is clean.

#31
#32
#33

Summary

#31 — functor/3 construct aliased all arg slots (termops.rs)
The construct loop called m.new_var() (which pushes its own self-ref cell) and then
pushed the returned ref again, laying down two cells per slot so every later arg
aliased the first variable. Now it writes a self-referencing REF directly into each
contiguous arg cell.

  • functor(T, point, 2) → T = point(_6, _7) (was point(_6, _6))
  • functor(T, point, 2), T = point(3, 4) → succeeds (was false.)

#32 — whole floats lost .0 in write/1 (render.rs)
term_to_string (used by write/1 and the binding display) formatted floats with raw
{}; render.rs already had the correct fmt_float helper and term_to_json used it.
Routed term_to_string through fmt_float too.

  • write(2.0) → 2.0, 2 ** 10 → 1024.0, 3.5 unchanged.

#33 — missing /1 evaluable and writeq/1

  • Added unary bitwise-complement arm ("\", 1) + bit_not to the arith evaluator
    (arith.rs). X is \ 0 → -1, \ 5 → -6. The operator already parsed; only evaluation
    was missing.
  • Added writeq/1: a quoting variant of the term writer (render.rs
    term_to_string_quoted + atom_is_unquoted/quote_atom), wired through the runtime
    impl (miscops.rs), the shared vocabulary (builtins.rs), the compiler symbol map
    (lower.rs), the runtime metacall dispatch (control.rs), and the reference doc.
    writeq(foo) → foo, writeq('hello world') → 'hello world'.

On ordering: #31 was fully independent; #32 landed before #33 so writeq inherited
correct float rendering through the shared writer. Each came with a regression test
pinning the issue's exact reproduction.

suite passes (24 test binaries, 0 failures) and clippy is clean. https://git.navicore.tech/navicore/patch-prolog/issues/31 https://git.navicore.tech/navicore/patch-prolog/issues/32 https://git.navicore.tech/navicore/patch-prolog/issues/33 Summary #31 — functor/3 construct aliased all arg slots (termops.rs) The construct loop called m.new_var() (which pushes its own self-ref cell) and then pushed the returned ref again, laying down two cells per slot so every later arg aliased the first variable. Now it writes a self-referencing REF directly into each contiguous arg cell. - functor(T, point, 2) → T = point(_6, _7) (was point(_6, _6)) - functor(T, point, 2), T = point(3, 4) → succeeds (was false.) #32 — whole floats lost .0 in write/1 (render.rs) term_to_string (used by write/1 and the binding display) formatted floats with raw {}; render.rs already had the correct fmt_float helper and term_to_json used it. Routed term_to_string through fmt_float too. - write(2.0) → 2.0, 2 ** 10 → 1024.0, 3.5 unchanged. #33 — missing \/1 evaluable and writeq/1 - Added unary bitwise-complement arm ("\\", 1) + bit_not to the arith evaluator (arith.rs). X is \ 0 → -1, \ 5 → -6. The operator already parsed; only evaluation was missing. - Added writeq/1: a quoting variant of the term writer (render.rs term_to_string_quoted + atom_is_unquoted/quote_atom), wired through the runtime impl (miscops.rs), the shared vocabulary (builtins.rs), the compiler symbol map (lower.rs), the runtime metacall dispatch (control.rs), and the reference doc. writeq(foo) → foo, writeq('hello world') → 'hello world'. On ordering: #31 was fully independent; #32 landed before #33 so writeq inherited correct float rendering through the shared writer. Each came with a regression test pinning the issue's exact reproduction.
● All three issues are fixed, tested, and verified end-to-end. Full workspace test
All checks were successful
WASM Gates / WASM gates (Tier 1 wasi + Tier 2 reactor) (pull_request) Successful in 17s
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 1m2s
fd4b8640a3
suite passes (24 test binaries, 0 failures) and clippy is clean.

  Summary

  #31 — functor/3 construct aliased all arg slots (termops.rs)
  The construct loop called m.new_var() (which pushes its own self-ref cell) and then
  pushed the returned ref again, laying down two cells per slot so every later arg
  aliased the first variable. Now it writes a self-referencing REF directly into each
  contiguous arg cell.
  - functor(T, point, 2) → T = point(_6, _7) (was point(_6, _6))
  - functor(T, point, 2), T = point(3, 4) → succeeds (was false.)

  #32 — whole floats lost .0 in write/1 (render.rs)
  term_to_string (used by write/1 and the binding display) formatted floats with raw
  {}; render.rs already had the correct fmt_float helper and term_to_json used it.
  Routed term_to_string through fmt_float too.
  - write(2.0) → 2.0, 2 ** 10 → 1024.0, 3.5 unchanged.

  #33 — missing \/1 evaluable and writeq/1
  - Added unary bitwise-complement arm ("\\", 1) + bit_not to the arith evaluator
  (arith.rs). X is \ 0 → -1, \ 5 → -6. The operator already parsed; only evaluation
  was missing.
  - Added writeq/1: a quoting variant of the term writer (render.rs
  term_to_string_quoted + atom_is_unquoted/quote_atom), wired through the runtime
  impl (miscops.rs), the shared vocabulary (builtins.rs), the compiler symbol map
  (lower.rs), the runtime metacall dispatch (control.rs), and the reference doc.
  writeq(foo) → foo, writeq('hello world') → 'hello world'.

  On ordering: #31 was fully independent; #32 landed before #33 so writeq inherited
  correct float rendering through the shared writer. Each came with a regression test
  pinning the issue's exact reproduction.
Author
Owner

Review: all three issues fixed and verified end-to-end (one minor follow-up)

Reviewed the full diff and verified every fix against the original issue
reproductions on a build of this branch (fd4b864).

#31functor/3 construct

Correct root-cause fix: replaces new_var() (which double-pushed a cell and
aliased every argument slot to one variable) with a self-referencing REF
written directly into each contiguous arg cell. The regression test asserts both
distinct vars and the behavioral T = point(3, 4) binding.

?- functor(T, point, 2).            T = point(_6, _7)   % distinct
?- functor(T, point, 2), T = point(3, 4).   % succeeds

#33\/1 + writeq/1

\/1 is !n, integer-only (errors on float via int_args_required — correct,
ISO \/1 is integer-only). writeq/1 is wired through every registration point
consistently (codegen DET_BUILTINS, det_builtin dispatch, vocab_invariant,
shared roster 55→56 / Det 25→26), with thorough quoting logic and tests.

?- X is \ 0.    X = -1
?- X is \ 5.    X = -6
?- writeq('hello world'), nl.    'hello world'

#32write/1 whole-valued floats (with a gap, below)

Routes the term_to_string float branch through the existing fmt_float (which
already forced .0, and which term_to_json already used). Minimal and correct.

?- write(2.0).        2.0
?- X is 2 ** 10, write(X).    1024.0
?- X is 7 / 2, write(X).      3.5

Minor follow-up (non-blocking): #32 misses the error-message path

fmt_float now covers term_to_string and term_to_json, but format_term_v
(render.rs, the byte contract for error messages) still uses raw
f64::to_string(), which drops the .0. So a whole float embedded in an error
term reintroduces exactly the "indistinguishable from an integer" problem #32
fixes:

?- atom_length(2.0, X).
Error: ... error(type_error(atom, 2), ...)      % culprit is 2.0, prints as "2"
?- atom_concat(2.0, b, X).
Error: ... error(type_error(atom, 2), ...)

Cosmetic (error messages only), so not a merge blocker — but routing that last
TAG_FLT branch through fmt_float would make all float rendering consistent.

Everything else — code quality, regression test coverage, doc updates
(builtin-reference.md, roster counts) — is clean. Nice work.

## Review: all three issues fixed and verified end-to-end ✅ (one minor follow-up) Reviewed the full diff and verified every fix against the original issue reproductions on a build of this branch (`fd4b864`). ### #31 — `functor/3` construct ✅ Correct root-cause fix: replaces `new_var()` (which double-pushed a cell and aliased every argument slot to one variable) with a self-referencing `REF` written directly into each contiguous arg cell. The regression test asserts both distinct vars **and** the behavioral `T = point(3, 4)` binding. ``` ?- functor(T, point, 2). T = point(_6, _7) % distinct ?- functor(T, point, 2), T = point(3, 4). % succeeds ``` ### #33 — `\/1` + `writeq/1` ✅ `\/1` is `!n`, integer-only (errors on float via `int_args_required` — correct, ISO `\/1` is integer-only). `writeq/1` is wired through every registration point consistently (codegen `DET_BUILTINS`, `det_builtin` dispatch, `vocab_invariant`, shared roster 55→56 / Det 25→26), with thorough quoting logic and tests. ``` ?- X is \ 0. X = -1 ?- X is \ 5. X = -6 ?- writeq('hello world'), nl. 'hello world' ``` ### #32 — `write/1` whole-valued floats ✅ (with a gap, below) Routes the `term_to_string` float branch through the existing `fmt_float` (which already forced `.0`, and which `term_to_json` already used). Minimal and correct. ``` ?- write(2.0). 2.0 ?- X is 2 ** 10, write(X). 1024.0 ?- X is 7 / 2, write(X). 3.5 ``` ### Minor follow-up (non-blocking): #32 misses the error-message path `fmt_float` now covers `term_to_string` and `term_to_json`, but **`format_term_v`** (`render.rs`, the byte contract for error messages) still uses raw `f64::to_string()`, which drops the `.0`. So a whole float embedded in an error term reintroduces exactly the "indistinguishable from an integer" problem #32 fixes: ``` ?- atom_length(2.0, X). Error: ... error(type_error(atom, 2), ...) % culprit is 2.0, prints as "2" ?- atom_concat(2.0, b, X). Error: ... error(type_error(atom, 2), ...) ``` Cosmetic (error messages only), so not a merge blocker — but routing that last `TAG_FLT` branch through `fmt_float` would make all float rendering consistent. Everything else — code quality, regression test coverage, doc updates (`builtin-reference.md`, roster counts) — is clean. Nice work.
Done. Addressed the one non-blocking follow-up from PR 34.
All checks were successful
WASM Gates / WASM gates (Tier 1 wasi + Tier 2 reactor) (pull_request) Successful in 17s
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 1m1s
8236ed83d6
format_term_v now routes through fmt_float (render.rs:272) — the last of
the three TAG_FLT rendering paths. All float rendering (JSON, text
bindings/write, and error-message terms) is now consistent. A
whole-valued float embedded in an error culprit keeps its .0:

?- atom_length(2.0, X).
Error: ... error(type_error(atom, 2.0), ...)      % was "2"
?- atom_concat(2.0, b, X).
Error: ... error(type_error(atom, 2.0), ...)

I extended the existing #32 regression test to also assert format_term
keeps the .0, locking the error-path behavior in. Full workspace suite
(24 binaries) passes and clippy is clean — no oracle/error-message test
had pinned the old 2 rendering, confirming the change is safe.
navicore deleted branch issue-31-32-33 2026-06-23 20:26:53 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-prolog!34
No description provided.