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 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
- This is the first migration stage toward reusing Markus Triska's MIT-licensed
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
- instead of maintaining a separate constraint solver in `src/clpz.js`. The
165
- current CLP(Z) implementation remains active while compatibility gaps such as
166
- Scryer's compile-time term/goal expansion and supporting libraries are closed.
167
- The staged plan and exact compatibility boundary are documented in
168
- [`src/CLPZ-MIGRATION.md`](src/CLPZ-MIGRATION.md).
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
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.67",
6
+ "version": "1.3.69",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
@@ -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` plus `program-analysis.js` and
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
@@ -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
- ## What still prevents a drop-in Scryer `clpz.pl`
35
+ ## Stage 2: source expansion substrate
36
36
 
37
- The upstream solver is not only an attributed-variable client. It also relies on
38
- compile-time facilities that EyeProlog does not yet provide generically:
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
- - user-defined `term_expansion/2` and `goal_expansion/2` during module loading;
41
- - the generated predicates at the end of Scryer's `clpz.pl` and its custom Duo
42
- DCG `++>` expansion;
43
- - the supporting Scryer library surface (`assoc`, `pairs`, `between`, `dcgs`,
44
- `terms`, `error`, `si`, `freeze`, `arithmetic`, `debug`, and `format`) at the
45
- interfaces expected by that source;
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
- These are compatibility gaps, not reasons to add more CLP(Z)-specific code to
48
- `term.js` or `solver.js`.
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).
@@ -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).
@@ -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
+ '...' --> [_], '...'.
@@ -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).
@@ -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)].
@@ -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).