eyeprolog 1.3.67 → 1.3.69
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 +10 -6
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +19 -2
- package/src/CLPZ-MIGRATION.md +49 -11
- package/src/expansion-builtins.js +99 -0
- package/src/lib/arithmetic.pl +36 -0
- package/src/lib/assoc.pl +42 -0
- package/src/lib/between.pl +54 -0
- package/src/lib/dcgs.pl +23 -0
- package/src/lib/debug.pl +17 -0
- package/src/lib/error.pl +123 -0
- package/src/lib/format.pl +35 -0
- package/src/lib/freeze.pl +28 -0
- package/src/lib/pairs.pl +36 -0
- package/src/lib/si.pl +62 -0
- package/src/lib/terms.pl +18 -0
- package/src/parser.js +26 -0
- package/src/program.js +73 -7
- package/src/scryer-compat.js +39 -0
- package/src/solver.js +1 -1
- package/src/source-expansion.js +173 -0
- package/src/standard-library.js +15 -0
- package/src/term.js +13 -0
- package/test/run-regression.mjs +118 -1
- package/the-art-of-eyeprolog.md +12 -5
package/README.md
CHANGED
|
@@ -159,13 +159,17 @@ variable representatives and ordinary Prolog backtracking, and the REPL projects
|
|
|
159
159
|
module `attribute_goals//1` hooks as residual goals. See
|
|
160
160
|
[`examples/attributed-variables.pl`](examples/attributed-variables.pl).
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
The next two migration stages toward reusing Markus Triska's MIT-licensed
|
|
163
163
|
[Scryer `library(clpz)`](https://github.com/mthom/scryer-prolog/blob/master/src/lib/clpz.pl)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
are now in place as well. Normal-profile loading supports user-defined
|
|
165
|
+
`term_expansion/2` and `goal_expansion/2`, clause-list generation, and
|
|
166
|
+
`expand_term/2` for processor DCG lowering. EyeProlog also ships the support
|
|
167
|
+
modules used by Scryer's CLP(Z) source (`assoc`, `pairs`, `between`, `dcgs`,
|
|
168
|
+
`terms`, `error`, `si`, `freeze`, `arithmetic`, `debug`, and `format`) together
|
|
169
|
+
with generic backtrackable blackboard primitives. The current CLP(Z) engine
|
|
170
|
+
remains active while the upstream solver itself is integrated and checked for
|
|
171
|
+
behavioral and performance parity. The staged plan and exact compatibility
|
|
172
|
+
boundary are documented in [`src/CLPZ-MIGRATION.md`](src/CLPZ-MIGRATION.md).
|
|
169
173
|
|
|
170
174
|
Recursion through negation is explicit. EyeProlog provides `tnot/1` for
|
|
171
175
|
well-founded negation over finite, range-restricted, function-free Datalog
|
package/package.json
CHANGED
package/src/ARCHITECTURE.md
CHANGED
|
@@ -7,8 +7,8 @@ higher-level frontends.
|
|
|
7
7
|
|
|
8
8
|
1. **Kernel representation and syntax** — `term.js`, `number-value.js`,
|
|
9
9
|
`syntax-scan.js`, `parser.js`, `write.js`, `errors.js`.
|
|
10
|
-
2. **Program preparation** — `program.js
|
|
11
|
-
`program-indexing.js`. Static recursion/Datalog/WFS classification lives in
|
|
10
|
+
2. **Program preparation** — `program.js`, `source-expansion.js`, plus
|
|
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
14
|
3. **Execution** — `solver.js`, `cleanup.js`, `io.js`, `datalog.js`, `wfs.js`,
|
|
@@ -48,6 +48,23 @@ needed by Scryer's `library(atts)` and `library(clpz)`. The current CLP(Z)-speci
|
|
|
48
48
|
`Env._clpz` store remains transitional until the Scryer Prolog implementation can
|
|
49
49
|
replace it without losing test or performance parity; see `src/CLPZ-MIGRATION.md`.
|
|
50
50
|
|
|
51
|
+
`Env` also owns the small generic backtrackable blackboard used by Scryer-style
|
|
52
|
+
libraries. Blackboard maps are shared by clones and copied only on write, so
|
|
53
|
+
`bb_b_put/2` follows ordinary Prolog backtracking without any CLP(Z)-specific
|
|
54
|
+
state in the VM. `scryer-compat.js` exposes the private host bridge used by
|
|
55
|
+
`library(debug)`; the rest of the Stage 3 compatibility surface lives as Prolog
|
|
56
|
+
modules under `src/lib/`.
|
|
57
|
+
|
|
58
|
+
`source-expansion.js` is the explicit compile-time execution boundary. When an
|
|
59
|
+
already-loaded `term_expansion/2` or `goal_expansion/2` hook exists, program
|
|
60
|
+
preparation invokes it with a fresh bounded `Solver` against the partially built
|
|
61
|
+
program. `solver.js` therefore imports clause-selection primitives directly from
|
|
62
|
+
`program-indexing.js` rather than importing the `program.js` facade. This is the
|
|
63
|
+
one deliberate preparation-to-execution service edge and keeps the JavaScript
|
|
64
|
+
import graph acyclic while avoiding a second meta-interpreter solely for source
|
|
65
|
+
expansion. `expansion-builtins.js` provides the lower-level `expand_term/2` DCG
|
|
66
|
+
service without depending on `Solver`.
|
|
67
|
+
|
|
51
68
|
The JavaScript runtime stays flat directly under `src/`; the existing `src/lib/`
|
|
52
69
|
contains Prolog library sources rather than JavaScript runtime modules. The architecture
|
|
53
70
|
test rejects JavaScript import cycles under `src/`. Cleanup lifecycle hooks are
|
package/src/CLPZ-MIGRATION.md
CHANGED
|
@@ -32,20 +32,58 @@ attributes, post-binding goals, backtracking, `term_attributed_variables/2`, and
|
|
|
32
32
|
REPL projection through the conventional module `attribute_goals//1` hook.
|
|
33
33
|
`examples/attributed-variables.pl` is the runnable source-level example.
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## Stage 2: source expansion substrate
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
This stage is implemented. Normal-profile source loading now invokes already
|
|
38
|
+
loaded module-local and `user` `term_expansion/2` and `goal_expansion/2` hooks
|
|
39
|
+
left-to-right. A term expansion may yield one clause or a list of clauses, and
|
|
40
|
+
`expand_term/2` exposes the processor's DCG lowering so a hook can generate the
|
|
41
|
+
ordinary clauses used by Scryer's CLP(Z) parser generators. Goal expansion is
|
|
42
|
+
recursive across control constructs and preserves sharing with variables in the
|
|
43
|
+
surrounding clause.
|
|
39
44
|
|
|
40
|
-
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
Module-qualified hook clauses such as `user:goal_expansion/2` are also compiled
|
|
46
|
+
with the correct split between procedure ownership and lexical body context. A
|
|
47
|
+
hook defined from a library module therefore lives in `user` while its
|
|
48
|
+
unqualified helper calls still resolve in the defining library. The compiler
|
|
49
|
+
executes hooks with a fresh bounded solver against the partially built program;
|
|
50
|
+
`solver.js` imports clause-selection helpers directly from `program-indexing.js`,
|
|
51
|
+
so this compile-time service remains acyclic. Strict ISO Part 1 mode does not run
|
|
52
|
+
these extension hooks.
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
The regression corpus covers clause-list generation, variable-sharing goal
|
|
55
|
+
expansion, qualified hook lexical context, and the Scryer pattern where a
|
|
56
|
+
`term_expansion/2` hook calls `expand_term/2` to generate a DCG predicate.
|
|
57
|
+
|
|
58
|
+
## Stage 3: Scryer support-library surface
|
|
59
|
+
|
|
60
|
+
This stage is implemented. EyeProlog now registers the library names imported by
|
|
61
|
+
Scryer's current `clpz.pl`: `assoc`, `pairs`, `between`, `dcgs`, `terms`,
|
|
62
|
+
`error`, `si`, `freeze`, `arithmetic`, `debug`, and `format`. The implementations
|
|
63
|
+
are deliberately small compatibility layers where CLP(Z) needs only a subset of
|
|
64
|
+
the upstream module, while observable predicate contracts stay at the Prolog
|
|
65
|
+
library boundary. In particular, `library(freeze)` is implemented with the same
|
|
66
|
+
attributed-variable `verify_attributes/3` protocol introduced in Stage 1.
|
|
67
|
+
|
|
68
|
+
Two remaining Scryer runtime facilities are generic rather than constraint
|
|
69
|
+
specific. `Env` now carries a copy-on-write backtrackable blackboard, exposed to
|
|
70
|
+
Prolog through `bb_get/2` and `bb_b_put/2` compatibility wrappers, and
|
|
71
|
+
`library(terms)` provides `copy_term_nat/2` using EyeProlog's attribute-free
|
|
72
|
+
logical copy semantics. Regression coverage exercises the support modules,
|
|
73
|
+
blackboard rollback, attribute-free copying, and the exact import/attribute
|
|
74
|
+
prelude used by upstream CLP(Z).
|
|
75
|
+
|
|
76
|
+
## What still prevents replacing `src/clpz.js`
|
|
77
|
+
|
|
78
|
+
The VM/compiler and named support-library blockers are now closed. The remaining
|
|
79
|
+
work is direct upstream-source integration: vendor a pinned Scryer `clpz.pl`,
|
|
80
|
+
apply only the small compatibility patches exposed by loading the complete file,
|
|
81
|
+
and run it side-by-side against EyeProlog's existing CLP(Z) corpus plus selected
|
|
82
|
+
Scryer regressions. Only after behavioral and performance parity should the
|
|
83
|
+
`eyeprolog__clpz_*` adapters and `Env._clpz` store be removed.
|
|
84
|
+
|
|
85
|
+
This boundary is intentional: Stage 3 does not claim that the complete upstream
|
|
86
|
+
solver is active merely because its dependency prelude loads.
|
|
49
87
|
|
|
50
88
|
## Migration rule
|
|
51
89
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Generic source-expansion helpers exposed to Prolog code.
|
|
2
|
+
//
|
|
3
|
+
// expand_term/2 provides the system part of source expansion that libraries
|
|
4
|
+
// such as Scryer's CLP(Z) use while generating predicates. User-defined
|
|
5
|
+
// term_expansion/2 and goal_expansion/2 are invoked by program preparation in
|
|
6
|
+
// source-expansion.js; keeping this builtin independent of Solver avoids an
|
|
7
|
+
// import cycle through the standard-library registry.
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
ATOM, COMPOUND, Env, atom, compound, deref, flattenConjunction, unify,
|
|
11
|
+
} from './term.js';
|
|
12
|
+
import { expandDcgRuleClause } from './dcg.js';
|
|
13
|
+
import { PrologError } from './errors.js';
|
|
14
|
+
|
|
15
|
+
function conjunctionFromGoals(goals) {
|
|
16
|
+
if (goals.length === 0) return atom('true');
|
|
17
|
+
let body = goals[goals.length - 1];
|
|
18
|
+
for (let index = goals.length - 2; index >= 0; index--) {
|
|
19
|
+
body = compound(',', [goals[index], body]);
|
|
20
|
+
}
|
|
21
|
+
return body;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function clauseToSourceTerm(clause) {
|
|
25
|
+
if (clause == null || clause.kind === 'quad') return null;
|
|
26
|
+
if (!Array.isArray(clause.body) || clause.body.length === 0) return clause.head;
|
|
27
|
+
return compound(':-', [clause.head, conjunctionFromGoals(clause.body)]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function sourceTermToClauses(term, env = new Env()) {
|
|
31
|
+
term = deref(term, env);
|
|
32
|
+
const list = sourceTermList(term, env);
|
|
33
|
+
if (list != null) {
|
|
34
|
+
const clauses = [];
|
|
35
|
+
for (const item of list) clauses.push(...sourceTermToClauses(item, env));
|
|
36
|
+
return clauses;
|
|
37
|
+
}
|
|
38
|
+
if (term.type === COMPOUND && term.name === ':-' && term.arity === 2) {
|
|
39
|
+
const head = deref(term.args[0], env);
|
|
40
|
+
const body = deref(term.args[1], env);
|
|
41
|
+
return [{ head, body: body.type === ATOM && body.name === 'true' ? [] : flattenConjunction(body) }];
|
|
42
|
+
}
|
|
43
|
+
if (term.type === COMPOUND && term.name === ':-' && term.arity === 1) {
|
|
44
|
+
return [{ head: term, body: [] }];
|
|
45
|
+
}
|
|
46
|
+
if (term.type === COMPOUND && term.name === '?-' && term.arity >= 1) {
|
|
47
|
+
return [{ head: term, body: [] }];
|
|
48
|
+
}
|
|
49
|
+
if (term.type !== ATOM && term.type !== COMPOUND) {
|
|
50
|
+
throw new PrologError('type_error(callable)', term);
|
|
51
|
+
}
|
|
52
|
+
return [{ head: term, body: [] }];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function sourceTermList(term, env) {
|
|
56
|
+
const items = [];
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
let cursor = deref(term, env);
|
|
59
|
+
while (cursor.type === COMPOUND && cursor.name === '.' && cursor.arity === 2) {
|
|
60
|
+
if (seen.has(cursor)) throw new PrologError('type_error(list)', term);
|
|
61
|
+
seen.add(cursor);
|
|
62
|
+
items.push(deref(cursor.args[0], env));
|
|
63
|
+
cursor = deref(cursor.args[1], env);
|
|
64
|
+
}
|
|
65
|
+
if (cursor.type === ATOM && cursor.name === '[]') return items;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function systemExpandTerm(term, module = 'user') {
|
|
70
|
+
const resolved = deref(term, new Env());
|
|
71
|
+
if (resolved.type === COMPOUND && resolved.name === '-->' && resolved.arity === 2) {
|
|
72
|
+
const expanded = expandDcgRuleClause({ head: resolved, body: [] }, module);
|
|
73
|
+
if (expanded == null) return resolved;
|
|
74
|
+
return clauseToSourceTerm(expanded);
|
|
75
|
+
}
|
|
76
|
+
return resolved;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function* expandTermBuiltin({ goal, env }) {
|
|
80
|
+
const input = deref(goal.args[0], env);
|
|
81
|
+
if (input.type === 'var') throw new PrologError('instantiation_error');
|
|
82
|
+
const expanded = systemExpandTerm(input, goal.module ?? 'user');
|
|
83
|
+
const next = env.clone();
|
|
84
|
+
if (unify(goal.args[1], expanded, next)) yield next;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function* expandGoalBuiltin({ goal, env }) {
|
|
88
|
+
const input = deref(goal.args[0], env);
|
|
89
|
+
if (input.type === 'var') throw new PrologError('instantiation_error');
|
|
90
|
+
const next = env.clone();
|
|
91
|
+
if (unify(goal.args[1], input, next)) yield next;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const expansionBuiltins = {
|
|
95
|
+
register(registry) {
|
|
96
|
+
registry.add('expand_term', 2, expandTermBuiltin, { deterministic: true, eyePrologLibrary: true });
|
|
97
|
+
registry.add('expand_goal', 2, expandGoalBuiltin, { deterministic: true, eyePrologLibrary: true });
|
|
98
|
+
},
|
|
99
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Integer arithmetic helpers used by Scryer's CLP(Z) source. */
|
|
2
|
+
|
|
3
|
+
:- module(arithmetic, [lsb/2, msb/2, popcount/2]).
|
|
4
|
+
|
|
5
|
+
:- use_module(library(error), [must_be/2, domain_error/3]).
|
|
6
|
+
|
|
7
|
+
lsb(X, N) :-
|
|
8
|
+
must_be(integer, X),
|
|
9
|
+
( X < 1 -> domain_error(not_less_than_one, X, lsb/2)
|
|
10
|
+
; Low is X /\ (-X), arithmetic__msb(Low, -1, N)
|
|
11
|
+
).
|
|
12
|
+
|
|
13
|
+
msb(X, N) :-
|
|
14
|
+
must_be(integer, X),
|
|
15
|
+
( X < 1 -> domain_error(not_less_than_one, X, msb/2)
|
|
16
|
+
; Shifted is X >> 1, arithmetic__msb(Shifted, 0, N)
|
|
17
|
+
).
|
|
18
|
+
|
|
19
|
+
arithmetic__msb(0, N, N) :- !.
|
|
20
|
+
arithmetic__msb(X, N0, N) :-
|
|
21
|
+
X1 is X >> 1,
|
|
22
|
+
N1 is N0 + 1,
|
|
23
|
+
arithmetic__msb(X1, N1, N).
|
|
24
|
+
|
|
25
|
+
popcount(X, Count) :-
|
|
26
|
+
must_be(integer, X),
|
|
27
|
+
( X < 0 -> domain_error(not_less_than_zero, X, popcount/2)
|
|
28
|
+
; arithmetic__popcount(X, 0, Count)
|
|
29
|
+
).
|
|
30
|
+
|
|
31
|
+
arithmetic__popcount(0, Count, Count) :- !.
|
|
32
|
+
arithmetic__popcount(X, Count0, Count) :-
|
|
33
|
+
Bit is X /\ 1,
|
|
34
|
+
Count1 is Count0 + Bit,
|
|
35
|
+
X1 is X >> 1,
|
|
36
|
+
arithmetic__popcount(X1, Count1, Count).
|
package/src/lib/assoc.pl
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Minimal Scryer-compatible association maps.
|
|
2
|
+
|
|
3
|
+
The public operations used by library(clpz) are represented as a sorted
|
|
4
|
+
list wrapped in assoc/1. The representation is intentionally private: the
|
|
5
|
+
predicates preserve the observable assoc contracts while keeping the
|
|
6
|
+
compatibility layer small and declarative.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
:- module(assoc, [
|
|
10
|
+
empty_assoc/1,
|
|
11
|
+
assoc_to_list/2,
|
|
12
|
+
get_assoc/3,
|
|
13
|
+
put_assoc/4
|
|
14
|
+
]).
|
|
15
|
+
|
|
16
|
+
empty_assoc(assoc([])).
|
|
17
|
+
|
|
18
|
+
assoc_to_list(assoc(Pairs), Pairs).
|
|
19
|
+
|
|
20
|
+
get_assoc(Key, assoc(Pairs), Value) :-
|
|
21
|
+
assoc__get(Pairs, Key, Value).
|
|
22
|
+
|
|
23
|
+
assoc__get([K-V|Pairs], Key, Value) :-
|
|
24
|
+
compare(Order, Key, K),
|
|
25
|
+
assoc__get(Order, Key, Value, V, Pairs).
|
|
26
|
+
|
|
27
|
+
assoc__get(=, _, Value, Value, _).
|
|
28
|
+
assoc__get(>, Key, Value, _, Pairs) :-
|
|
29
|
+
assoc__get(Pairs, Key, Value).
|
|
30
|
+
|
|
31
|
+
put_assoc(Key, assoc(Pairs0), Value, assoc(Pairs)) :-
|
|
32
|
+
assoc__put(Pairs0, Key, Value, Pairs).
|
|
33
|
+
|
|
34
|
+
assoc__put([], Key, Value, [Key-Value]).
|
|
35
|
+
assoc__put([K-V|Pairs0], Key, Value, Pairs) :-
|
|
36
|
+
compare(Order, Key, K),
|
|
37
|
+
assoc__put(Order, Key, Value, K, V, Pairs0, Pairs).
|
|
38
|
+
|
|
39
|
+
assoc__put(=, Key, Value, _, _, Pairs, [Key-Value|Pairs]).
|
|
40
|
+
assoc__put(<, Key, Value, K, V, Pairs, [Key-Value,K-V|Pairs]).
|
|
41
|
+
assoc__put(>, Key, Value, K, V, Pairs0, [K-V|Pairs]) :-
|
|
42
|
+
assoc__put(Pairs0, Key, Value, Pairs).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Scryer-compatible integer generators needed by CLP(Z). */
|
|
2
|
+
|
|
3
|
+
:- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]).
|
|
4
|
+
|
|
5
|
+
:- use_module(library(error), [must_be/2, can_be/2]).
|
|
6
|
+
|
|
7
|
+
between(Lower, Upper, X) :-
|
|
8
|
+
must_be(integer, Lower),
|
|
9
|
+
must_be(integer, Upper),
|
|
10
|
+
can_be(integer, X),
|
|
11
|
+
( nonvar(X) -> Lower =< X, X =< Upper
|
|
12
|
+
; Lower =< Upper, between__from(Lower, Upper, X)
|
|
13
|
+
).
|
|
14
|
+
|
|
15
|
+
between__from(Lower, Upper, Lower).
|
|
16
|
+
between__from(Lower, Upper, X) :-
|
|
17
|
+
Lower < Upper,
|
|
18
|
+
Next is Lower + 1,
|
|
19
|
+
between__from(Next, Upper, X).
|
|
20
|
+
|
|
21
|
+
gen_nat(N) :-
|
|
22
|
+
can_be(integer, N),
|
|
23
|
+
( var(N) -> between__nats(0, N) ; N >= 0 ).
|
|
24
|
+
|
|
25
|
+
between__nats(N, N).
|
|
26
|
+
between__nats(N0, N) :- N1 is N0 + 1, between__nats(N1, N).
|
|
27
|
+
|
|
28
|
+
gen_int(N) :-
|
|
29
|
+
can_be(integer, N),
|
|
30
|
+
( var(N) -> between__ints(0, N) ; true ).
|
|
31
|
+
|
|
32
|
+
between__ints(0, 0).
|
|
33
|
+
between__ints(I, I) :- I > 0.
|
|
34
|
+
between__ints(I, N) :- I > 0, N is -I.
|
|
35
|
+
between__ints(I0, N) :- I1 is I0 + 1, between__ints(I1, N).
|
|
36
|
+
|
|
37
|
+
repeat(N) :-
|
|
38
|
+
must_be(integer, N), N > 0,
|
|
39
|
+
between__repeat(N).
|
|
40
|
+
between__repeat(_).
|
|
41
|
+
between__repeat(N0) :- N0 > 1, N is N0 - 1, between__repeat(N).
|
|
42
|
+
|
|
43
|
+
numlist(Upper, List) :- numlist(1, Upper, List).
|
|
44
|
+
|
|
45
|
+
numlist(Lower, Upper, List) :-
|
|
46
|
+
must_be(integer, Lower),
|
|
47
|
+
must_be(integer, Upper),
|
|
48
|
+
between__numlist(Lower, Upper, List).
|
|
49
|
+
|
|
50
|
+
between__numlist(Lower, Upper, []) :- Lower > Upper, !.
|
|
51
|
+
between__numlist(Lower, Upper, [Lower|Rest]) :-
|
|
52
|
+
Lower =< Upper,
|
|
53
|
+
Next is Lower + 1,
|
|
54
|
+
between__numlist(Next, Upper, Rest).
|
package/src/lib/dcgs.pl
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Scryer-compatible DCG helpers.
|
|
2
|
+
|
|
3
|
+
EyeProlog already provides phrase/2-3 and ISO-style grammar expansion in the
|
|
4
|
+
runtime. This module supplies the sequence combinators used by Scryer's
|
|
5
|
+
declarative libraries and exports the built-in phrase predicates under the
|
|
6
|
+
expected module name.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
:- module(dcgs, [
|
|
10
|
+
phrase/2,
|
|
11
|
+
phrase/3,
|
|
12
|
+
seq//1,
|
|
13
|
+
seqq//1
|
|
14
|
+
]).
|
|
15
|
+
|
|
16
|
+
seq([]) --> [].
|
|
17
|
+
seq([Element|Elements]) --> [Element], seq(Elements).
|
|
18
|
+
|
|
19
|
+
seqq([]) --> [].
|
|
20
|
+
seqq([Elements|Sequences]) --> seq(Elements), seqq(Sequences).
|
|
21
|
+
|
|
22
|
+
'...' --> [].
|
|
23
|
+
'...' --> [_], '...'.
|
package/src/lib/debug.pl
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Compatibility hooks for Scryer libraries.
|
|
2
|
+
|
|
3
|
+
CLP(Z) only needs the backtrackable blackboard operations; the debugging
|
|
4
|
+
predicates are deliberately harmless no-ops unless EyeProlog grows a debug
|
|
5
|
+
topic UI later.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
:- module(debug, [debug/1, debug/3, nodebug/1, bb_get/2, bb_b_put/2]).
|
|
9
|
+
|
|
10
|
+
:- meta_predicate(debug(+, +, +)).
|
|
11
|
+
|
|
12
|
+
debug(_).
|
|
13
|
+
debug(_, _, _).
|
|
14
|
+
nodebug(_).
|
|
15
|
+
|
|
16
|
+
bb_get(Key, Value) :- eyeprolog__bb_get(Key, Value).
|
|
17
|
+
bb_b_put(Key, Value) :- eyeprolog__bb_b_put(Key, Value).
|
package/src/lib/error.pl
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/** Small Scryer-compatible error/type layer used by reusable libraries. */
|
|
2
|
+
|
|
3
|
+
:- module(error, [
|
|
4
|
+
must_be/2,
|
|
5
|
+
can_be/2,
|
|
6
|
+
instantiation_error/0,
|
|
7
|
+
instantiation_error/1,
|
|
8
|
+
domain_error/2,
|
|
9
|
+
domain_error/3,
|
|
10
|
+
type_error/2,
|
|
11
|
+
type_error/3,
|
|
12
|
+
representation_error/1,
|
|
13
|
+
resource_error/1,
|
|
14
|
+
call_with_error_context/2
|
|
15
|
+
]).
|
|
16
|
+
|
|
17
|
+
:- meta_predicate(call_with_error_context(0, +)).
|
|
18
|
+
|
|
19
|
+
must_be(Type, Term) :-
|
|
20
|
+
( var(Type) -> instantiation_error(must_be/2)
|
|
21
|
+
; error__must_be(Type, Term)
|
|
22
|
+
).
|
|
23
|
+
|
|
24
|
+
error__must_be(integer, Term) :- !,
|
|
25
|
+
( var(Term) -> instantiation_error(must_be/2)
|
|
26
|
+
; integer(Term) -> true
|
|
27
|
+
; type_error(integer, Term, must_be/2)
|
|
28
|
+
).
|
|
29
|
+
error__must_be(atom, Term) :- !,
|
|
30
|
+
( var(Term) -> instantiation_error(must_be/2)
|
|
31
|
+
; atom(Term) -> true
|
|
32
|
+
; type_error(atom, Term, must_be/2)
|
|
33
|
+
).
|
|
34
|
+
error__must_be(number, Term) :- !,
|
|
35
|
+
( var(Term) -> instantiation_error(must_be/2)
|
|
36
|
+
; number(Term) -> true
|
|
37
|
+
; type_error(number, Term, must_be/2)
|
|
38
|
+
).
|
|
39
|
+
error__must_be(var, Term) :- !,
|
|
40
|
+
( var(Term) -> true
|
|
41
|
+
; throw(error(uninstantiation_error(Term), must_be/2))
|
|
42
|
+
).
|
|
43
|
+
error__must_be(ground, Term) :- !,
|
|
44
|
+
( ground(Term) -> true ; instantiation_error(must_be/2) ).
|
|
45
|
+
error__must_be(acyclic, Term) :- !,
|
|
46
|
+
( acyclic_term(Term) -> true ; type_error(acyclic_term, Term, must_be/2) ).
|
|
47
|
+
error__must_be(list, Term) :- !,
|
|
48
|
+
error__proper_list(Term).
|
|
49
|
+
error__must_be(list(Type), Term) :- !,
|
|
50
|
+
error__proper_list_of(Term, Type).
|
|
51
|
+
error__must_be(pair, Term) :- !,
|
|
52
|
+
( var(Term) -> instantiation_error(must_be/2)
|
|
53
|
+
; Term = _-_ -> true
|
|
54
|
+
; type_error(pair, Term, must_be/2)
|
|
55
|
+
).
|
|
56
|
+
error__must_be(not_less_than_zero, Term) :- !,
|
|
57
|
+
must_be(integer, Term),
|
|
58
|
+
( Term >= 0 -> true ; domain_error(not_less_than_zero, Term, must_be/2) ).
|
|
59
|
+
error__must_be(Type, Term) :-
|
|
60
|
+
( var(Term) -> instantiation_error(must_be/2)
|
|
61
|
+
; type_error(Type, Term, must_be/2)
|
|
62
|
+
).
|
|
63
|
+
|
|
64
|
+
error__proper_list([]).
|
|
65
|
+
error__proper_list([_|Tail]) :- !, error__proper_list(Tail).
|
|
66
|
+
error__proper_list(Term) :-
|
|
67
|
+
( var(Term) -> instantiation_error(must_be/2)
|
|
68
|
+
; type_error(list, Term, must_be/2)
|
|
69
|
+
).
|
|
70
|
+
|
|
71
|
+
error__proper_list_of([], _).
|
|
72
|
+
error__proper_list_of([Head|Tail], Type) :- !,
|
|
73
|
+
must_be(Type, Head),
|
|
74
|
+
error__proper_list_of(Tail, Type).
|
|
75
|
+
error__proper_list_of(Term, _) :-
|
|
76
|
+
( var(Term) -> instantiation_error(must_be/2)
|
|
77
|
+
; type_error(list, Term, must_be/2)
|
|
78
|
+
).
|
|
79
|
+
|
|
80
|
+
can_be(Type, Term) :-
|
|
81
|
+
( var(Type) -> instantiation_error(can_be/2)
|
|
82
|
+
; var(Term) -> true
|
|
83
|
+
; error__can_be(Type, Term)
|
|
84
|
+
).
|
|
85
|
+
|
|
86
|
+
error__can_be(integer, Term) :- !,
|
|
87
|
+
( integer(Term) -> true ; type_error(integer, Term, can_be/2) ).
|
|
88
|
+
error__can_be(atom, Term) :- !,
|
|
89
|
+
( atom(Term) -> true ; type_error(atom, Term, can_be/2) ).
|
|
90
|
+
error__can_be(number, Term) :- !,
|
|
91
|
+
( number(Term) -> true ; type_error(number, Term, can_be/2) ).
|
|
92
|
+
error__can_be(list, Term) :- !, error__partial_list(Term).
|
|
93
|
+
error__can_be(list(Type), Term) :- !, error__partial_list_of(Term, Type).
|
|
94
|
+
error__can_be(not_less_than_zero, Term) :- !,
|
|
95
|
+
( integer(Term) ->
|
|
96
|
+
( Term >= 0 -> true ; domain_error(not_less_than_zero, Term, can_be/2) )
|
|
97
|
+
; type_error(integer, Term, can_be/2)
|
|
98
|
+
).
|
|
99
|
+
error__can_be(Type, Term) :- error__must_be(Type, Term).
|
|
100
|
+
|
|
101
|
+
error__partial_list(Term) :- var(Term), !.
|
|
102
|
+
error__partial_list([]) :- !.
|
|
103
|
+
error__partial_list([_|Tail]) :- !, error__partial_list(Tail).
|
|
104
|
+
error__partial_list(Term) :- type_error(list, Term, can_be/2).
|
|
105
|
+
|
|
106
|
+
error__partial_list_of(Term, _) :- var(Term), !.
|
|
107
|
+
error__partial_list_of([], _) :- !.
|
|
108
|
+
error__partial_list_of([Head|Tail], Type) :- !,
|
|
109
|
+
can_be(Type, Head),
|
|
110
|
+
error__partial_list_of(Tail, Type).
|
|
111
|
+
error__partial_list_of(Term, _) :- type_error(list, Term, can_be/2).
|
|
112
|
+
|
|
113
|
+
instantiation_error :- throw(error(instantiation_error, [])).
|
|
114
|
+
instantiation_error(Context) :- throw(error(instantiation_error, Context)).
|
|
115
|
+
domain_error(Type, Term) :- throw(error(domain_error(Type, Term), [])).
|
|
116
|
+
domain_error(Type, Term, Context) :- throw(error(domain_error(Type, Term), Context)).
|
|
117
|
+
type_error(Type, Term) :- throw(error(type_error(Type, Term), [])).
|
|
118
|
+
type_error(Type, Term, Context) :- throw(error(type_error(Type, Term), Context)).
|
|
119
|
+
representation_error(Flag) :- throw(error(representation_error(Flag), [])).
|
|
120
|
+
resource_error(Resource) :- throw(error(resource_error(Resource), [])).
|
|
121
|
+
|
|
122
|
+
call_with_error_context(Goal, Pair) :-
|
|
123
|
+
catch(Goal, error(Error, Context), throw(error(Error, [Pair|Context]))).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Small format/2 compatibility layer covering the controls used by CLP(Z). */
|
|
2
|
+
|
|
3
|
+
:- module(format, [format/2]).
|
|
4
|
+
|
|
5
|
+
format(Template, Arguments) :-
|
|
6
|
+
format__chars(Template, Chars),
|
|
7
|
+
format__emit(Chars, Arguments).
|
|
8
|
+
|
|
9
|
+
format__chars(Template, Chars) :-
|
|
10
|
+
atom(Template), !,
|
|
11
|
+
atom_chars(Template, Chars).
|
|
12
|
+
format__chars(Chars, Chars).
|
|
13
|
+
|
|
14
|
+
format__emit([], []).
|
|
15
|
+
format__emit(['~','~'|Chars], Args) :- !,
|
|
16
|
+
put_char('~'),
|
|
17
|
+
format__emit(Chars, Args).
|
|
18
|
+
format__emit(['~',n|Chars], Args) :- !,
|
|
19
|
+
nl,
|
|
20
|
+
format__emit(Chars, Args).
|
|
21
|
+
format__emit(['~',w|Chars], [Arg|Args]) :- !,
|
|
22
|
+
write(Arg),
|
|
23
|
+
format__emit(Chars, Args).
|
|
24
|
+
format__emit(['~',q|Chars], [Arg|Args]) :- !,
|
|
25
|
+
writeq(Arg),
|
|
26
|
+
format__emit(Chars, Args).
|
|
27
|
+
format__emit(['~',a|Chars], [Arg|Args]) :- !,
|
|
28
|
+
write(Arg),
|
|
29
|
+
format__emit(Chars, Args).
|
|
30
|
+
format__emit(['~',d|Chars], [Arg|Args]) :- !,
|
|
31
|
+
write(Arg),
|
|
32
|
+
format__emit(Chars, Args).
|
|
33
|
+
format__emit([Char|Chars], Args) :-
|
|
34
|
+
put_char(Char),
|
|
35
|
+
format__emit(Chars, Args).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Attributed-variable implementation of Scryer-compatible freeze/2. */
|
|
2
|
+
|
|
3
|
+
:- module(freeze, [freeze/2]).
|
|
4
|
+
|
|
5
|
+
:- use_module(library(atts)).
|
|
6
|
+
:- meta_predicate(freeze(-, 0)).
|
|
7
|
+
:- attribute frozen/1.
|
|
8
|
+
|
|
9
|
+
verify_attributes(Var, Other, Goals) :-
|
|
10
|
+
get_atts(Var, frozen(FrozenA)), !,
|
|
11
|
+
( var(Other) ->
|
|
12
|
+
( get_atts(Other, frozen(FrozenB)) ->
|
|
13
|
+
put_atts(Other, frozen((FrozenB,FrozenA)))
|
|
14
|
+
; put_atts(Other, frozen(FrozenA))
|
|
15
|
+
),
|
|
16
|
+
Goals = []
|
|
17
|
+
; Goals = [FrozenA]
|
|
18
|
+
).
|
|
19
|
+
verify_attributes(_, _, []).
|
|
20
|
+
|
|
21
|
+
freeze(X, Goal) :-
|
|
22
|
+
put_atts(Fresh, frozen(Goal)),
|
|
23
|
+
Fresh = X.
|
|
24
|
+
|
|
25
|
+
attribute_goals(Var) -->
|
|
26
|
+
{ get_atts(Var, frozen(Goals)),
|
|
27
|
+
put_atts(Var, -frozen(_)) },
|
|
28
|
+
[freeze:freeze(Var, Goals)].
|
package/src/lib/pairs.pl
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Scryer-compatible pair helpers used by CLP(Z). */
|
|
2
|
+
|
|
3
|
+
:- module(pairs, [
|
|
4
|
+
pairs_keys_values/3,
|
|
5
|
+
pairs_keys/2,
|
|
6
|
+
pairs_values/2,
|
|
7
|
+
group_pairs_by_key/2,
|
|
8
|
+
map_list_to_pairs/3
|
|
9
|
+
]).
|
|
10
|
+
|
|
11
|
+
:- meta_predicate(map_list_to_pairs(2, ?, ?)).
|
|
12
|
+
|
|
13
|
+
pairs_keys_values([], [], []).
|
|
14
|
+
pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :-
|
|
15
|
+
pairs_keys_values(ABs, As, Bs).
|
|
16
|
+
|
|
17
|
+
pairs_keys(Pairs, Keys) :- pairs_keys_values(Pairs, Keys, _).
|
|
18
|
+
pairs_values(Pairs, Values) :- pairs_keys_values(Pairs, _, Values).
|
|
19
|
+
|
|
20
|
+
map_list_to_pairs(Pred, List, Pairs) :-
|
|
21
|
+
pairs__map_list_to_pairs(List, Pred, Pairs).
|
|
22
|
+
|
|
23
|
+
pairs__map_list_to_pairs([], _, []).
|
|
24
|
+
pairs__map_list_to_pairs([Head|Tail0], Pred, [Key-Head|Tail]) :-
|
|
25
|
+
call(Pred, Head, Key),
|
|
26
|
+
pairs__map_list_to_pairs(Tail0, Pred, Tail).
|
|
27
|
+
|
|
28
|
+
group_pairs_by_key([], []).
|
|
29
|
+
group_pairs_by_key([K-V|Pairs0], [K-[V|Values]|Pairs]) :-
|
|
30
|
+
pairs__same_key(K, Pairs0, Values, Pairs1),
|
|
31
|
+
group_pairs_by_key(Pairs1, Pairs).
|
|
32
|
+
|
|
33
|
+
pairs__same_key(K0, [K1-V|Pairs0], [V|Values], Pairs) :-
|
|
34
|
+
K0 == K1, !,
|
|
35
|
+
pairs__same_key(K0, Pairs0, Values, Pairs).
|
|
36
|
+
pairs__same_key(_, Pairs, [], Pairs).
|