Conversation
Workspace versioning (Cargo.toml): - [workspace.package] with version = "0.1.0", edition, authors, license - Both crates use version.workspace = true - patch-prolog-core dependency pinned with version = "=0.1.0" (exact match) Release workflow (.github/workflows/release.yml): 1. Triggered on GitHub release with v* tag 2. Extracts version from tag, updates [workspace.package] version and dependency pins via awk 3. Commits + pushes the version bump 4. Runs tests 5. Verifies all crates use workspace version and pins match 6. Publishes patch-prolog-core first, waits 60s for indexing, then publishes patch-prolog 7. Builds static Linux (musl), macOS ARM64, and macOS x86_64 binaries 8. Attaches all binaries to the GitHub release To use: Create a release tagged v0.2.0 on GitHub. The workflow handles everything else. You'll need CRATES_IO_TOKEN and PAT secrets configured in the repo.
Prolog Engine ReviewThis PR is a crate rename plus a release workflow; engine source files are unchanged but appear in the diff. 1. Parser:
2. Tokenizer: overflowing float literals silently become
3.
Consequence: 4.
Both predicates handle only 5. Rust call-stack overflow risk in
Both functions recurse via Rust call frames for every resolution step of user-defined predicates, with no Rust stack depth tracking — only a step counter. With 6. First-argument index returns all clauses on a ground-arg miss
The 7.
Minor
|
3 bugs fixed: ┌─────┬─────────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────┐ │ # │ Issue │ Fix │ ├─────┼─────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤ │ 1 │ \+ parsed argument at primary precedence — \+ X = Y became (\+ X) = │ Changed parse_primary() to parse_term() (precedence 700, matching ISO │ │ │ Y │ 900fy) │ ├─────┼─────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤ │ 2 │ Float literals overflowing f64 silently became infinity │ Added .is_infinite() check after parse::<f64>() in tokenizer │ ├─────┼─────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────┤ │ 6 │ First-arg index returned all clauses on ground-arg miss instead of │ Changed fallback from all_clause_indices to vec![] │ │ │ empty │ │ └─────┴─────────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────┘ 4 issues not fixed (by design): ┌─────┬───────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────┐ │ # │ Issue │ Reason │ ├─────┼───────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 3 │ =../2 and functor/3 error vs silent failure in │ Known Option<bool> limitation, documented in SOLVER.md and SAFETY.md │ │ │ try_exec_misc │ │ ├─────┼───────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 4 │ atom_chars/2 and atom_length/2 should accept numbers │ Incorrect — ISO 8.16.1/8.16.4 require atom arguments. SWI accepts numbers as a │ │ │ │ non-standard extension │ ├─────┼───────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 5 │ Rust stack overflow in try_solve_once/collecting │ Known limitation, documented in SAFETY.md │ ├─────┼───────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤ │ 7 │ between/3 goal-list cloning per iteration │ Valid optimization but requires API restructuring; clone is already hoisted outside │ │ │ │ the loop │ └─────┴───────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────┘ 4 regression tests added (281 total now).
|
Code Review - see details below |
|
REVIEW PART 1 of 3 -- index.rs line 101 None => vec![]: Correct and an improvement. The old fallback to all_clause_indices was unnecessary when var_clause_indices is empty and a ground query key misses arg_index, no clause can possibly unify. Float and List first-arg clause heads return None from first_arg_key() and are absent from both arg_index and var_clause_indices. Their corresponding query paths (float/list/variable first arg in goal) also produce None in lookup_clauses lines 91-94, falling through to the None => all_clause_indices branch at line 104, never reaching line 101. Ground atom/integer/compound queries against float-headed clauses would fail unification regardless, so vec![] is correct. |
|
REVIEW PART 2 of 3 -- parser.rs line 308 negation-as-failure fix: Correct fix for a real bug. With parse_primary(), NAF X = Y at goal level parsed as NAF(X) and left = Y stranded causing a parse error. With parse_term() (= parse_expr_700()), it correctly parses as NAF(X = Y). The comment ISO precedence 900fy, parses argument at 700 is accurate -- ISO says fy 900 but the parser only goes to 700. Since no operators are defined between 701 and 899, this matches ISO for all current operators. Any future operator in that range would need a parse_expr_900() level. One subtle interaction: parse_primary() now calling parse_term() means NAF inside an arithmetic sub-expression greedily consumes a following = ..., e.g. a + NAF b = c parses as plus(a, NAF(equals(b, c))) rather than a syntax error. Pre-existing structural issue, not introduced by this PR. |
|
REVIEW PART 3 of 3 -- tokenizer.rs line 560: The val.is_infinite() check is correct. The tokenizer only lexes digit strings so parse f64 can produce Inf from overflow but not NaN. The guard correctly rejects overflowing literals at lex time. Four new integration tests all verify the fixed behaviors correctly. release.yml: publish job uses actions/checkout@v5 while build jobs use @v4 (worth aligning); the 60-second sleep between publishing the two crates is sometimes insufficient for crates.io indexing (consider 90-120 seconds or a retry loop). SUMMARY: No correctness bugs found in the Prolog engine changes. The index optimization is sound, the NAF parse fix is correct, and the float overflow guard is a good safety addition. |
Workspace versioning (Cargo.toml):
Release workflow (.github/workflows/release.yml):
To use: Create a release tagged v0.2.0 on GitHub. The workflow handles everything else. You'll need CRATES_IO_TOKEN and PAT secrets configured in the
repo.