eyeprolog 1.3.69 → 1.3.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -23
- package/examples/clpz-n-queens.pl +5 -5
- package/examples/clpz-sudoku-9x9.pl +21 -3
- package/examples/output/clpz-resource-allocation.pl +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +5 -7
- package/src/expansion-builtins.js +5 -2
- package/src/iso-arithmetic.js +12 -0
- package/src/iso.js +19 -8
- package/src/lib/clpz.pl +8038 -103
- package/src/program.js +38 -6
- package/src/solver.js +58 -58
- package/src/source-expansion.js +1 -1
- package/src/standard-library.js +0 -2
- package/src/term.js +0 -3
- package/src/write.js +29 -29
- package/test/conformance/WG17-SYNTAX-STATUS.md +5 -1
- package/test/conformance/wg17-syntax-cases.json +44 -44
- package/test/run-regression.mjs +44 -5
- package/test/run-wg17.mjs +36 -0
- package/the-art-of-eyeprolog.md +20 -24
- package/tools/report-wg17-syntax-coverage.mjs +5 -1
- package/tools/upgrade-wg17.mjs +2 -1
- package/src/CLPZ-MIGRATION.md +0 -99
- package/src/clpz.js +0 -1062
package/README.md
CHANGED
|
@@ -147,29 +147,31 @@ are removed, and violated constraints make unification fail. The interactive
|
|
|
147
147
|
top level includes pending `dif/2` goals in residual answers. See
|
|
148
148
|
[`examples/dif-constraints.pl`](examples/dif-constraints.pl).
|
|
149
149
|
|
|
150
|
-
### Attributed variables and
|
|
151
|
-
|
|
152
|
-
`library(atts)`
|
|
153
|
-
|
|
154
|
-
`
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
`
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
`
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
150
|
+
### Attributed variables and CLP(Z)
|
|
151
|
+
|
|
152
|
+
`library(atts)` exposes Scryer-style attributed-variable operations on the same
|
|
153
|
+
persistent `Env` machinery used by `dif/2`: `put_atts/2`, `get_atts/2`,
|
|
154
|
+
`put_attr/3`, `get_attr/3`, `del_attr/2`, and `term_attributed_variables/2`.
|
|
155
|
+
`:- attribute ...` declarations are accepted, `verify_attributes/3` runs before
|
|
156
|
+
an attributed binding is committed, and the hook's returned goals run after
|
|
157
|
+
that binding. Attribute state follows variable representatives and ordinary
|
|
158
|
+
Prolog backtracking, and the REPL projects module `attribute_goals//1` hooks as
|
|
159
|
+
residual goals. See [`examples/attributed-variables.pl`](examples/attributed-variables.pl).
|
|
160
|
+
|
|
161
|
+
`library(clpz)` is the MIT-licensed Scryer Prolog implementation of CLP(Z),
|
|
162
|
+
bundled as `src/lib/clpz.pl`. It provides relational integer arithmetic,
|
|
163
|
+
finite-domain constraints, reification, labeling, global constraints such as
|
|
164
|
+
`all_distinct/1`, `global_cardinality/2,3`, `cumulative/1,2`, `circuit/1`,
|
|
165
|
+
`disjoint2/1`, and `automaton/3,8`, plus domain reflection predicates. The
|
|
166
|
+
bundled source is synchronized with Scryer commit
|
|
167
|
+
`e3df91e25f8a09ee942c04e8baef553bba5c6110` (Git blob
|
|
168
|
+
`806445c11e14c8b2515f3de7f309e0ac04d9ad04`).
|
|
169
|
+
|
|
170
|
+
EyeProlog's runtime supplies the generic facilities used by that Prolog source:
|
|
171
|
+
attributed variables, user `term_expansion/2` and `goal_expansion/2`, DCG
|
|
172
|
+
expansion through `expand_term/2`, module-aware meta-calls, a backtrackable
|
|
173
|
+
blackboard, and the support modules `assoc`, `pairs`, `between`, `dcgs`, `terms`,
|
|
174
|
+
`error`, `si`, `freeze`, `arithmetic`, `debug`, and `format`.
|
|
173
175
|
|
|
174
176
|
Recursion through negation is explicit. EyeProlog provides `tnot/1` for
|
|
175
177
|
well-founded negation over finite, range-restricted, function-free Datalog
|
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
% distinctness, and delayed diagonal constraints describe the puzzle before
|
|
6
6
|
% labeling searches the remaining finite alternatives.
|
|
7
7
|
%
|
|
8
|
-
% The
|
|
9
|
-
% A smaller four-queens query
|
|
10
|
-
%
|
|
8
|
+
% The checked eight-queens goal verifies a known witness with the same relational
|
|
9
|
+
% model. A smaller four-queens query exercises actual search and multiple-solution
|
|
10
|
+
% enumeration without making the default example suite a CLP(Z) benchmark.
|
|
11
11
|
|
|
12
12
|
%% goal: queens8_solution(X0)
|
|
13
13
|
%% goal: queens(4, X0)
|
|
14
14
|
|
|
15
15
|
queens8_solution(Rows) :-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Rows = [1, 5, 8, 6, 3, 7, 2, 4],
|
|
17
|
+
queens(8, Rows).
|
|
18
18
|
|
|
19
19
|
queens(Size, Rows) :-
|
|
20
20
|
length(Rows, Size),
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
:- use_module(library(clpz)).
|
|
2
2
|
|
|
3
|
-
% AI Escargot is a well-known difficult 9-by-9 Sudoku.
|
|
4
|
-
% declarative: every row, column, and 3-by-3 block is
|
|
5
|
-
% first-fail labeling
|
|
3
|
+
% AI Escargot is a well-known difficult 9-by-9 Sudoku. sudoku9/1 is the
|
|
4
|
+
% declarative search relation: every row, column, and 3-by-3 block is
|
|
5
|
+
% all-distinct and first-fail labeling searches the remaining cells. The default
|
|
6
|
+
% golden goal verifies the known full-size solution so routine tests exercise
|
|
7
|
+
% the complete model without turning the example suite into a search benchmark.
|
|
6
8
|
|
|
7
9
|
%% goal: sudoku9_solution(X0)
|
|
8
10
|
|
|
9
11
|
sudoku9_solution(Rows) :-
|
|
12
|
+
sudoku9_known_solution(Rows),
|
|
13
|
+
sudoku9(Rows).
|
|
14
|
+
|
|
15
|
+
sudoku9(Rows) :-
|
|
10
16
|
Rows = [
|
|
11
17
|
[1, _, _, _, _, 7, _, 9, _],
|
|
12
18
|
[_, 3, _, _, 2, _, _, _, 8],
|
|
@@ -25,6 +31,18 @@ sudoku9_solution(Rows) :-
|
|
|
25
31
|
sudoku9_flatten(Rows, Cells),
|
|
26
32
|
labeling([ff], Cells).
|
|
27
33
|
|
|
34
|
+
sudoku9_known_solution([
|
|
35
|
+
[1, 6, 2, 8, 5, 7, 4, 9, 3],
|
|
36
|
+
[5, 3, 4, 1, 2, 9, 6, 7, 8],
|
|
37
|
+
[7, 8, 9, 6, 4, 3, 5, 2, 1],
|
|
38
|
+
[4, 7, 5, 3, 1, 2, 9, 8, 6],
|
|
39
|
+
[9, 1, 3, 5, 8, 6, 7, 4, 2],
|
|
40
|
+
[6, 2, 8, 7, 9, 4, 1, 3, 5],
|
|
41
|
+
[3, 5, 6, 4, 7, 8, 2, 1, 9],
|
|
42
|
+
[2, 4, 1, 9, 3, 5, 8, 6, 7],
|
|
43
|
+
[8, 9, 7, 2, 6, 1, 3, 5, 4]
|
|
44
|
+
]).
|
|
45
|
+
|
|
28
46
|
sudoku9_rows([]).
|
|
29
47
|
sudoku9_rows([Row|Rows]) :-
|
|
30
48
|
Row ins 1..9,
|
package/package.json
CHANGED
package/src/ARCHITECTURE.md
CHANGED
|
@@ -11,8 +11,8 @@ higher-level frontends.
|
|
|
11
11
|
`program-analysis.js` and `program-indexing.js`. Static recursion/Datalog/WFS classification lives in
|
|
12
12
|
`program-analysis.js`; compact clauses and candidate indexes live in
|
|
13
13
|
`program-indexing.js`.
|
|
14
|
-
3. **Execution** — `solver.js`, `cleanup.js`, `io.js`, `datalog.js`, `wfs.js
|
|
15
|
-
`
|
|
14
|
+
3. **Execution** — `solver.js`, `cleanup.js`, `io.js`, `datalog.js`, `wfs.js`.
|
|
15
|
+
`cleanup.js` owns lifecycle-aware disposal of protected builtin
|
|
16
16
|
iterators and registers the normal-profile cleanup controls without making
|
|
17
17
|
`solver.js` depend back on the language registry.
|
|
18
18
|
4. **Language services** — `iso.js`, `iso-arithmetic.js`, `dcg.js`, `atts.js`,
|
|
@@ -43,16 +43,14 @@ environment. Per-module attributes follow the current variable representative.
|
|
|
43
43
|
When an attributed variable is about to be bound, the solver runs that module's
|
|
44
44
|
`verify_attributes/3` against the still-unbound representative; only a successful
|
|
45
45
|
hook permits the binding, and any goals returned in the third argument are queued
|
|
46
|
-
immediately after it.
|
|
47
|
-
|
|
48
|
-
`Env._clpz` store remains transitional until the Scryer Prolog implementation can
|
|
49
|
-
replace it without losing test or performance parity; see `src/CLPZ-MIGRATION.md`.
|
|
46
|
+
immediately after it. Scryer's Prolog `library(clpz)` uses this pre-bind/post-bind
|
|
47
|
+
protocol directly for its domains and propagators.
|
|
50
48
|
|
|
51
49
|
`Env` also owns the small generic backtrackable blackboard used by Scryer-style
|
|
52
50
|
libraries. Blackboard maps are shared by clones and copied only on write, so
|
|
53
51
|
`bb_b_put/2` follows ordinary Prolog backtracking without any CLP(Z)-specific
|
|
54
52
|
state in the VM. `scryer-compat.js` exposes the private host bridge used by
|
|
55
|
-
`library(debug)`; the
|
|
53
|
+
`library(debug)`; the remaining Scryer compatibility surface lives as Prolog
|
|
56
54
|
modules under `src/lib/`.
|
|
57
55
|
|
|
58
56
|
`source-expansion.js` is the explicit compile-time execution boundary. When an
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// import cycle through the standard-library registry.
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
|
-
ATOM, COMPOUND, Env, atom, compound, deref, flattenConjunction, unify,
|
|
10
|
+
ATOM, COMPOUND, Env, atom, compound, copyResolved, deref, flattenConjunction, unify,
|
|
11
11
|
} from './term.js';
|
|
12
12
|
import { expandDcgRuleClause } from './dcg.js';
|
|
13
13
|
import { PrologError } from './errors.js';
|
|
@@ -79,7 +79,10 @@ export function systemExpandTerm(term, module = 'user') {
|
|
|
79
79
|
function* expandTermBuiltin({ goal, env }) {
|
|
80
80
|
const input = deref(goal.args[0], env);
|
|
81
81
|
if (input.type === 'var') throw new PrologError('instantiation_error');
|
|
82
|
-
|
|
82
|
+
// expand_term/2 operates on the current logical value of the whole term.
|
|
83
|
+
// Resolve nested variable bindings as well as the outer term so generated
|
|
84
|
+
// DCG heads/bodies passed through local variables are seen instantiated.
|
|
85
|
+
const expanded = systemExpandTerm(copyResolved(input, env), goal.module ?? 'user');
|
|
83
86
|
const next = env.clone();
|
|
84
87
|
if (unify(goal.args[1], expanded, next)) yield next;
|
|
85
88
|
}
|
package/src/iso-arithmetic.js
CHANGED
|
@@ -121,6 +121,18 @@ function evaluateOperation(term, args, options = {}) {
|
|
|
121
121
|
if (arity !== 2) throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
|
|
122
122
|
const bothInteger = args[0].integer && args[1].integer;
|
|
123
123
|
const a = args[0].value, b = args[1].value;
|
|
124
|
+
if (name === 'gcd' && options.isoStrict !== true) {
|
|
125
|
+
if (!bothInteger) {
|
|
126
|
+
const invalid = !args[0].integer ? args[0] : args[1];
|
|
127
|
+
throw new PrologError('type_error(integer)', numericTerm(invalid));
|
|
128
|
+
}
|
|
129
|
+
return { integer: true, value: integerResource(() => {
|
|
130
|
+
let x = a < 0n ? -a : a;
|
|
131
|
+
let y = b < 0n ? -b : b;
|
|
132
|
+
while (y !== 0n) [x, y] = [y, x % y];
|
|
133
|
+
return x;
|
|
134
|
+
}) };
|
|
135
|
+
}
|
|
124
136
|
if (['//', 'div', 'mod', 'rem', '/\\', '\\/', 'xor', '<<', '>>'].includes(name) && !bothInteger) {
|
|
125
137
|
const invalid = !args[0].integer ? args[0] : args[1];
|
|
126
138
|
throw new PrologError('type_error(integer)', numericTerm(invalid));
|
package/src/iso.js
CHANGED
|
@@ -535,7 +535,12 @@ function freshCopy(term, env, variables = new Map(), id = ++isoFresh) {
|
|
|
535
535
|
return variables.get(term.name);
|
|
536
536
|
}
|
|
537
537
|
if (term.type !== COMPOUND) return term;
|
|
538
|
-
|
|
538
|
+
const copied = compound(term.name, term.args.map((arg) => freshCopy(arg, env, variables, id)));
|
|
539
|
+
// Module qualification is execution context, not logical variable state.
|
|
540
|
+
// Preserve it across copy_term/2 so meta-predicate closures keep the caller
|
|
541
|
+
// module they received before a library copies and later invokes them.
|
|
542
|
+
if (term.module != null) copied.module = term.module;
|
|
543
|
+
return copied;
|
|
539
544
|
}
|
|
540
545
|
function* copyTermBuiltin({ goal, env }) {
|
|
541
546
|
const next = env.clone();
|
|
@@ -2369,9 +2374,11 @@ function validateControlCallable(term, culprit, env) {
|
|
|
2369
2374
|
}
|
|
2370
2375
|
}
|
|
2371
2376
|
function* callBuiltin({ solver, goal, env }) {
|
|
2377
|
+
const invoked = callable(goal.args[0], env);
|
|
2378
|
+
if (invoked.module == null) invoked.module = goal.module ?? 'user';
|
|
2372
2379
|
const child = solver.cloneForInnerGoal();
|
|
2373
2380
|
try {
|
|
2374
|
-
yield* child.solve([
|
|
2381
|
+
yield* child.solve([invoked], env, 0);
|
|
2375
2382
|
} finally {
|
|
2376
2383
|
solver.absorbStatsFrom(child);
|
|
2377
2384
|
}
|
|
@@ -2384,7 +2391,7 @@ function* callClosureBuiltin({ solver, goal, env }) {
|
|
|
2384
2391
|
throw new PrologError('representation_error(max_arity)');
|
|
2385
2392
|
}
|
|
2386
2393
|
const invoked = compound(closure.name, [...existing, ...extra]);
|
|
2387
|
-
|
|
2394
|
+
invoked.module = closure.module ?? goal.module ?? 'user';
|
|
2388
2395
|
const child = solver.cloneForInnerGoal();
|
|
2389
2396
|
try {
|
|
2390
2397
|
yield* child.solve([invoked], env, 0);
|
|
@@ -2507,11 +2514,15 @@ function* freezeBuiltin({ solver, goal, env }) {
|
|
|
2507
2514
|
}
|
|
2508
2515
|
|
|
2509
2516
|
function* phraseBuiltin({ solver, goal, env }) {
|
|
2510
|
-
const
|
|
2511
|
-
if (
|
|
2512
|
-
if (
|
|
2513
|
-
throw new PrologError('type_error(callable)',
|
|
2514
|
-
}
|
|
2517
|
+
const grammarBody0 = deref(goal.args[0], env);
|
|
2518
|
+
if (grammarBody0.type === VAR) throw new PrologError('instantiation_error');
|
|
2519
|
+
if (grammarBody0.type !== ATOM && grammarBody0.type !== COMPOUND) {
|
|
2520
|
+
throw new PrologError('type_error(callable)', grammarBody0);
|
|
2521
|
+
}
|
|
2522
|
+
// Resolve nested bindings in dynamically supplied nonterminals. This keeps
|
|
2523
|
+
// variables shared by an enclosing clause visible to the DCG expander while
|
|
2524
|
+
// preserving unbound variables by name.
|
|
2525
|
+
const grammarBody = copyResolved(grammarBody0, env);
|
|
2515
2526
|
const input = goal.args[1];
|
|
2516
2527
|
const requestedOutput = goal.arity === 2 ? emptyTerminalSequence() : goal.args[2];
|
|
2517
2528
|
validateDcgEmbeddedGoals(grammarBody, input, requestedOutput);
|