eyeprolog 1.5.12 → 1.5.14
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 +1 -1
- package/package.json +4 -3
- package/src/lib/aggregate.pl +78 -1
- package/src/lib/error.pl +2 -0
- package/src/lib/freeze.pl +20 -2
- package/src/lib/lists.pl +64 -0
- package/src/lib/random.pl +11 -2
- package/src/lib/tabling.pl +23 -2
- package/src/library-autoload-index.js +21 -0
- package/src/standard-library.js +9 -8
- package/src/tabling-host.js +38 -1
- package/test/run-architecture.mjs +17 -0
- package/test/run-regression.mjs +60 -3
- package/test/trealla-library-exports.json +28 -0
- package/the-art-of-eyeprolog.md +678 -15
- package/tools/generate-predicate-reference.mjs +215 -0
- package/tools/predicate-reference.json +4672 -0
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ Normal mode executes top-level `Conclusion :+ Premise` rules through the bundled
|
|
|
61
61
|
|
|
62
62
|
Bundled `src/lib/` predicates autoload in files, CLI/API goals, and the REPL; use `--no-autoload` to require explicit imports, and explicitly import libraries that introduce operators before using their syntax. See [*The Art of EyeProlog*](the-art-of-eyeprolog.md) for the full semantics.
|
|
63
63
|
|
|
64
|
-
All 32 bundled modules that overlap Scryer's current `src/lib/` surface
|
|
64
|
+
All 32 bundled modules that overlap Scryer's current `src/lib/` surface cover Scryer's exported predicates, and all 26 bundled modules with public-module counterparts in Trealla's current `library/` surface cover those Trealla exports as well. The stricter Trealla/Scryer intersection remains a separate portability profile. Runtime-dependent library primitives follow one ownership rule: `src/lib/<module>.pl` calls private adapters from `src/<module>-host.js`; pure Prolog libraries need no host file. This includes the full Scryer `library(files)` surface, Scryer character-list paths in `library(pio)` with atom-path compatibility, chars/UTF-8/term/Base64 helpers, the completed arithmetic/time/`iso_ext` surfaces, and the full Scryer `library(crypto)`. Filesystem and OS side effects require Node; crypto uses Node's backend where needed, with Web Crypto also used for secure random bytes when available. Non-integral rational conversions remain structural `rdiv(Numerator,Denominator)` terms until rational numbers become processor numeric values.
|
|
65
65
|
|
|
66
66
|
## Links
|
|
67
67
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.5.
|
|
6
|
+
"version": "1.5.14",
|
|
7
7
|
"description": "EyeProlog turns facts and rules into answers and proofs.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./index.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"test": "node test/run-all.mjs",
|
|
49
|
-
"generate": "node tools/generate-library-autoload-index.mjs && node tools/extract-book-examples.mjs",
|
|
49
|
+
"generate": "node tools/generate-library-autoload-index.mjs && node tools/generate-predicate-reference.mjs && node tools/extract-book-examples.mjs",
|
|
50
50
|
"generate:autoload": "node tools/generate-library-autoload-index.mjs",
|
|
51
51
|
"test:eyeprolog": "node test/run-all.mjs",
|
|
52
52
|
"test:conformance": "node test/run-conformance.mjs",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"test:architecture": "node test/run-architecture.mjs",
|
|
70
70
|
"benchmark": "node test/benchmark.mjs",
|
|
71
71
|
"benchmark:baseline": "node test/benchmark.mjs --save .benchmarks/baseline.json",
|
|
72
|
-
"test:benchmark": "node test/run-benchmark-tests.mjs"
|
|
72
|
+
"test:benchmark": "node test/run-benchmark-tests.mjs",
|
|
73
|
+
"generate:reference": "node tools/generate-predicate-reference.mjs"
|
|
73
74
|
}
|
|
74
75
|
}
|
package/src/lib/aggregate.pl
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
/** Portable solution aggregation relations. */
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/* Trealla-compatible aggregate_all/3 and aggregate/3 behavior is adapted
|
|
4
|
+
from Trealla Prolog library/aggregate.pl (MIT),
|
|
5
|
+
Copyright (c) 2020 Andrew George Davison. */
|
|
6
|
+
|
|
7
|
+
:- module(aggregate, [
|
|
8
|
+
sumall/3, aggregate_min/5, aggregate_max/5,
|
|
9
|
+
aggregate_all/3, aggregate/3
|
|
10
|
+
]).
|
|
4
11
|
|
|
5
12
|
:- meta_predicate(sumall('?', 0, '?')).
|
|
13
|
+
:- meta_predicate(aggregate_all('?', 0, '?')).
|
|
14
|
+
:- meta_predicate(aggregate('?', 0, '?')).
|
|
6
15
|
:- meta_predicate(aggregate_min('?', '?', 0, '?', '?')).
|
|
7
16
|
:- meta_predicate(aggregate_max('?', '?', 0, '?', '?')).
|
|
8
17
|
|
|
@@ -31,3 +40,71 @@ aggregate__reverse([X|Xs], Acc, Out) :- aggregate__reverse(Xs, [X|Acc], Out).
|
|
|
31
40
|
aggregate__last_pair([Pair], Pair).
|
|
32
41
|
aggregate__last_pair([_|Pairs], Pair) :- aggregate__last_pair(Pairs, Pair).
|
|
33
42
|
|
|
43
|
+
|
|
44
|
+
% Trealla-compatible aggregate templates. These are source-level relations;
|
|
45
|
+
% EyeProlog keeps its older sumall/3 and aggregate_min/max helpers alongside
|
|
46
|
+
% them for backward compatibility.
|
|
47
|
+
aggregate_all(Aggregate, Goal, Result) :-
|
|
48
|
+
aggregate__init(Aggregate, Template, Initial),
|
|
49
|
+
findall(Template, Goal, Values),
|
|
50
|
+
aggregate__compute(Values, Aggregate, Initial, Result).
|
|
51
|
+
|
|
52
|
+
aggregate(Aggregate, Goal, Result) :-
|
|
53
|
+
aggregate__init(Aggregate, Template, Initial),
|
|
54
|
+
bagof(Template, Goal, Values),
|
|
55
|
+
aggregate__compute(Values, Aggregate, Initial, Result).
|
|
56
|
+
|
|
57
|
+
aggregate__compute([], Aggregate, Initial, Result) :-
|
|
58
|
+
aggregate__finish(Aggregate, Initial, Result).
|
|
59
|
+
aggregate__compute([Value|Values], Aggregate, Acc0, Result) :-
|
|
60
|
+
aggregate__next(Aggregate, Acc0, Value, Acc),
|
|
61
|
+
aggregate__compute(Values, Aggregate, Acc, Result).
|
|
62
|
+
|
|
63
|
+
aggregate__init(Aggregate, _, _) :- var(Aggregate), !,
|
|
64
|
+
throw(error(instantiation_error, aggregate/3)).
|
|
65
|
+
aggregate__init(count, '', 0) :- !.
|
|
66
|
+
aggregate__init(sum(X), X, 0) :- !.
|
|
67
|
+
aggregate__init(mul(X), X, 1) :- !.
|
|
68
|
+
aggregate__init(min(X), X, sup) :- !.
|
|
69
|
+
aggregate__init(max(X), X, inf) :- !.
|
|
70
|
+
aggregate__init(bag(X), X, []) :- !.
|
|
71
|
+
aggregate__init(set(X), X, []) :- !.
|
|
72
|
+
aggregate__init((F,G), (X,Y), (A,B)) :- !,
|
|
73
|
+
aggregate__init(F, X, A),
|
|
74
|
+
aggregate__init(G, Y, B).
|
|
75
|
+
aggregate__init(Aggregate, _, _) :-
|
|
76
|
+
throw(error(type_error(aggregate, Aggregate), aggregate/3)).
|
|
77
|
+
|
|
78
|
+
aggregate__next(count, A, '', C) :- C is A + 1.
|
|
79
|
+
aggregate__next(sum(_), A, B, C) :- C is A + B.
|
|
80
|
+
aggregate__next(mul(_), A, B, C) :- C is A * B.
|
|
81
|
+
aggregate__next(min(_), A, B, C) :- aggregate__min(A, B, C).
|
|
82
|
+
aggregate__next(max(_), A, B, C) :- aggregate__max(A, B, C).
|
|
83
|
+
aggregate__next(bag(_), A, B, [B|A]).
|
|
84
|
+
aggregate__next(set(_), A, B, [B|A]).
|
|
85
|
+
aggregate__next((F,G), (A,B), (X,Y), (C,D)) :-
|
|
86
|
+
aggregate__next(F, A, X, C),
|
|
87
|
+
aggregate__next(G, B, Y, D).
|
|
88
|
+
|
|
89
|
+
aggregate__finish(count, A, A).
|
|
90
|
+
aggregate__finish(sum(_), A, A).
|
|
91
|
+
aggregate__finish(mul(_), A, A).
|
|
92
|
+
aggregate__finish(min(_), A, A).
|
|
93
|
+
aggregate__finish(max(_), A, A).
|
|
94
|
+
aggregate__finish(bag(_), A, B) :- aggregate__reverse(A, [], B).
|
|
95
|
+
aggregate__finish(set(_), A, B) :- aggregate__reverse(A, [], R), sort(R, B).
|
|
96
|
+
aggregate__finish((F,G), (X,Y), (A,B)) :-
|
|
97
|
+
aggregate__finish(F, X, A),
|
|
98
|
+
aggregate__finish(G, Y, B).
|
|
99
|
+
|
|
100
|
+
aggregate__min(sup, X, X) :- !.
|
|
101
|
+
aggregate__min(X, sup, X) :- !.
|
|
102
|
+
aggregate__min(inf, _, inf) :- !.
|
|
103
|
+
aggregate__min(_, inf, inf) :- !.
|
|
104
|
+
aggregate__min(X, Y, R) :- R is min(X, Y).
|
|
105
|
+
|
|
106
|
+
aggregate__max(inf, X, X) :- !.
|
|
107
|
+
aggregate__max(X, inf, X) :- !.
|
|
108
|
+
aggregate__max(sup, _, sup) :- !.
|
|
109
|
+
aggregate__max(_, sup, sup) :- !.
|
|
110
|
+
aggregate__max(X, Y, R) :- R is max(X, Y).
|
package/src/lib/error.pl
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
type_error/3,
|
|
12
12
|
representation_error/1,
|
|
13
13
|
resource_error/1,
|
|
14
|
+
resource_error/2,
|
|
14
15
|
call_with_error_context/2
|
|
15
16
|
]).
|
|
16
17
|
|
|
@@ -118,6 +119,7 @@ type_error(Type, Term) :- throw(error(type_error(Type, Term), [])).
|
|
|
118
119
|
type_error(Type, Term, Context) :- throw(error(type_error(Type, Term), Context)).
|
|
119
120
|
representation_error(Flag) :- throw(error(representation_error(Flag), [])).
|
|
120
121
|
resource_error(Resource) :- throw(error(resource_error(Resource), [])).
|
|
122
|
+
resource_error(Resource, Context) :- throw(error(resource_error(Resource), Context)).
|
|
121
123
|
|
|
122
124
|
call_with_error_context(Goal, Pair) :-
|
|
123
125
|
catch(Goal, error(Error, Context), throw(error(Error, [Pair|Context]))).
|
package/src/lib/freeze.pl
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
/** Attributed-variable
|
|
1
|
+
/** Attributed-variable freeze/2 plus Trealla-compatible frozen/2 inspection. */
|
|
2
2
|
|
|
3
|
-
:- module(freeze, [freeze/2]).
|
|
3
|
+
:- module(freeze, [freeze/2, frozen/2]).
|
|
4
4
|
|
|
5
5
|
:- use_module(library(atts)).
|
|
6
|
+
:- use_module(library(iso_ext), [copy_term/3]).
|
|
6
7
|
:- meta_predicate(freeze(-, 0)).
|
|
7
8
|
:- attribute frozen/1.
|
|
8
9
|
|
|
@@ -27,6 +28,23 @@ freeze(X, Goal) :-
|
|
|
27
28
|
put_atts(Fresh, frozen(frozen_goal(Goal))),
|
|
28
29
|
Fresh = X.
|
|
29
30
|
|
|
31
|
+
% Trealla-compatible frozen/2. copy_term/3 already projects both native and
|
|
32
|
+
% Prolog-defined attributed-variable residual goals. Re-unifying the fresh
|
|
33
|
+
% structural copy aliases those projected variables back to Term without
|
|
34
|
+
% instantiating them, so Goal describes the suspensions on the original term.
|
|
35
|
+
frozen(Term, Goal) :-
|
|
36
|
+
copy_term(Term, Copy, Goals),
|
|
37
|
+
Term = Copy,
|
|
38
|
+
freeze__goals_conjunction(Goals, Goal).
|
|
39
|
+
|
|
40
|
+
freeze__goals_conjunction([], true).
|
|
41
|
+
freeze__goals_conjunction([Goal|Goals], Conjunction) :-
|
|
42
|
+
freeze__goals_conjunction_(Goals, Goal, Conjunction).
|
|
43
|
+
|
|
44
|
+
freeze__goals_conjunction_([], Goal, Goal).
|
|
45
|
+
freeze__goals_conjunction_([Goal|Goals], Acc, Conjunction) :-
|
|
46
|
+
freeze__goals_conjunction_(Goals, (Acc, Goal), Conjunction).
|
|
47
|
+
|
|
30
48
|
frozen_goals(Frozen, Goals) :-
|
|
31
49
|
frozen_goals(Frozen, Goals, []).
|
|
32
50
|
|
package/src/lib/lists.pl
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
member/2,
|
|
5
5
|
memberchk/2,
|
|
6
6
|
select/3,
|
|
7
|
+
selectchk/3,
|
|
8
|
+
subtract/3,
|
|
9
|
+
union/3,
|
|
10
|
+
intersection/3,
|
|
11
|
+
is_set/1,
|
|
7
12
|
append/2,
|
|
8
13
|
append/3,
|
|
9
14
|
last/2,
|
|
@@ -14,6 +19,8 @@
|
|
|
14
19
|
nth1/4,
|
|
15
20
|
reverse/2,
|
|
16
21
|
length/2,
|
|
22
|
+
exclude/3,
|
|
23
|
+
include/3,
|
|
17
24
|
maplist/2,
|
|
18
25
|
maplist/3,
|
|
19
26
|
maplist/4,
|
|
@@ -22,6 +29,13 @@
|
|
|
22
29
|
maplist/7,
|
|
23
30
|
maplist/8,
|
|
24
31
|
maplist/9,
|
|
32
|
+
tasklist/2,
|
|
33
|
+
tasklist/3,
|
|
34
|
+
tasklist/4,
|
|
35
|
+
tasklist/5,
|
|
36
|
+
tasklist/6,
|
|
37
|
+
tasklist/7,
|
|
38
|
+
tasklist/8,
|
|
25
39
|
foldl/4,
|
|
26
40
|
foldl/5,
|
|
27
41
|
foldl/6,
|
|
@@ -50,6 +64,16 @@
|
|
|
50
64
|
:- meta_predicate(foldl(3, '?', '?', '?')).
|
|
51
65
|
:- meta_predicate(foldl(4, '?', '?', '?', '?')).
|
|
52
66
|
:- meta_predicate(foldl(5, '?', '?', '?', '?', '?')).
|
|
67
|
+
:- meta_predicate(include(1, '?', '?')).
|
|
68
|
+
:- meta_predicate(exclude(1, '?', '?')).
|
|
69
|
+
:- meta_predicate(tasklist(1, '?')).
|
|
70
|
+
:- meta_predicate(tasklist(2, '?', '?')).
|
|
71
|
+
:- meta_predicate(tasklist(3, '?', '?', '?')).
|
|
72
|
+
:- meta_predicate(tasklist(4, '?', '?', '?', '?')).
|
|
73
|
+
:- meta_predicate(tasklist(5, '?', '?', '?', '?', '?')).
|
|
74
|
+
:- meta_predicate(tasklist(6, '?', '?', '?', '?', '?', '?')).
|
|
75
|
+
:- meta_predicate(tasklist(7, '?', '?', '?', '?', '?', '?', '?')).
|
|
76
|
+
|
|
53
77
|
|
|
54
78
|
% Common pure-Prolog library predicates for EyeProlog.
|
|
55
79
|
%
|
|
@@ -99,6 +123,27 @@ maplist(Closure, [A|As], [B|Bs], [C|Cs], [D|Ds], [E|Es], [F|Fs], [G|Gs], [H|Hs])
|
|
|
99
123
|
eyeprolog__call8(Closure, A, B, C, D, E, F, G, H),
|
|
100
124
|
maplist(Closure, As, Bs, Cs, Ds, Es, Fs, Gs, Hs).
|
|
101
125
|
|
|
126
|
+
include(_, [], []).
|
|
127
|
+
include(Predicate, [X|Xs], Included) :-
|
|
128
|
+
( call(Predicate, X) -> Included = [X|Rest] ; Included = Rest ),
|
|
129
|
+
include(Predicate, Xs, Rest).
|
|
130
|
+
|
|
131
|
+
exclude(_, [], []).
|
|
132
|
+
exclude(Predicate, [X|Xs], Excluded) :-
|
|
133
|
+
( call(Predicate, X) -> Excluded = Rest ; Excluded = [X|Rest] ),
|
|
134
|
+
exclude(Predicate, Xs, Rest).
|
|
135
|
+
|
|
136
|
+
% Trealla runs tasklist/* items through its task scheduler. EyeProlog has no
|
|
137
|
+
% task scheduler, so the compatibility surface deliberately uses deterministic
|
|
138
|
+
% sequential maplist semantics while preserving solutions and failures.
|
|
139
|
+
tasklist(Goal, A) :- maplist(Goal, A).
|
|
140
|
+
tasklist(Goal, A, B) :- maplist(Goal, A, B).
|
|
141
|
+
tasklist(Goal, A, B, C) :- maplist(Goal, A, B, C).
|
|
142
|
+
tasklist(Goal, A, B, C, D) :- maplist(Goal, A, B, C, D).
|
|
143
|
+
tasklist(Goal, A, B, C, D, E) :- maplist(Goal, A, B, C, D, E).
|
|
144
|
+
tasklist(Goal, A, B, C, D, E, F) :- maplist(Goal, A, B, C, D, E, F).
|
|
145
|
+
tasklist(Goal, A, B, C, D, E, F, G) :- maplist(Goal, A, B, C, D, E, F, G).
|
|
146
|
+
|
|
102
147
|
append([], []).
|
|
103
148
|
append([Xs|Xss], Ys) :-
|
|
104
149
|
append(Xs, Rest, Ys),
|
|
@@ -115,6 +160,25 @@ memberchk(X, Xs) :- member(X, Xs), !.
|
|
|
115
160
|
select(X, [X|Xs], Xs).
|
|
116
161
|
select(X, [Y|Ys], [Y|Zs]) :- select(X, Ys, Zs).
|
|
117
162
|
|
|
163
|
+
selectchk(X, Xs, Rest) :- select(X, Xs, Rest), !.
|
|
164
|
+
|
|
165
|
+
subtract([], _, []) :- !.
|
|
166
|
+
subtract([X|Xs], Ys, Rest) :- memberchk(X, Ys), !, subtract(Xs, Ys, Rest).
|
|
167
|
+
subtract([X|Xs], Ys, [X|Rest]) :- subtract(Xs, Ys, Rest).
|
|
168
|
+
|
|
169
|
+
union([], Ys, Ys).
|
|
170
|
+
union([X|Xs], Ys, Union) :- member(X, Ys), !, union(Xs, Ys, Union).
|
|
171
|
+
union([X|Xs], Ys, [X|Union]) :- union(Xs, Ys, Union).
|
|
172
|
+
|
|
173
|
+
intersection([], _, []).
|
|
174
|
+
intersection([X|Xs], Ys, [X|Intersection]) :- member(X, Ys), !, intersection(Xs, Ys, Intersection).
|
|
175
|
+
intersection([_|Xs], Ys, Intersection) :- intersection(Xs, Ys, Intersection).
|
|
176
|
+
|
|
177
|
+
is_set(Set) :-
|
|
178
|
+
sort(Set, Sorted),
|
|
179
|
+
length(Set, Length),
|
|
180
|
+
length(Sorted, Length).
|
|
181
|
+
|
|
118
182
|
last([X], X).
|
|
119
183
|
last([_|Xs], X) :- last(Xs, X).
|
|
120
184
|
|
package/src/lib/random.pl
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/** Reproducible pseudo-random values.
|
|
2
2
|
|
|
3
|
-
random/3 is EyeProlog's explicit-state interface. maybe/0,
|
|
3
|
+
random/3 is EyeProlog's explicit-state interface. maybe/0, maybe/1, maybe/2,
|
|
4
4
|
random/1, random_integer/3, and set_random/1 are the mutable-seed library
|
|
5
5
|
interface used for compatibility. random/1 uses EyeProlog's private native
|
|
6
6
|
state step for hot-loop performance; random/3 remains the portable explicit-
|
|
7
7
|
state Park-Miller generator, and both retain the same sequence.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
:- module(random, [maybe/0, random/1, random/3, random_integer/3, set_random/1]).
|
|
10
|
+
:- module(random, [maybe/0, maybe/1, maybe/2, random/1, random/3, random_integer/3, set_random/1]).
|
|
11
11
|
|
|
12
12
|
:- use_module(library(iso_ext), [bb_get/2, bb_put/2]).
|
|
13
13
|
:- use_module(library(error), [instantiation_error/1, type_error/3]).
|
|
@@ -15,6 +15,15 @@
|
|
|
15
15
|
maybe :-
|
|
16
16
|
random_integer(0, 2, 0).
|
|
17
17
|
|
|
18
|
+
maybe(Probability) :-
|
|
19
|
+
random(Value),
|
|
20
|
+
Value < Probability.
|
|
21
|
+
|
|
22
|
+
maybe(K, N) :-
|
|
23
|
+
Probability is K / N,
|
|
24
|
+
random(Value),
|
|
25
|
+
Value < Probability.
|
|
26
|
+
|
|
18
27
|
|
|
19
28
|
random(Value) :-
|
|
20
29
|
eyeprolog__random_value(Value).
|
package/src/lib/tabling.pl
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
EyeProlog tables predicates only when requested explicitly with
|
|
4
4
|
`:- table ...`. The library keeps the portable start_tabling/2 and
|
|
5
|
-
abolish_all_tables/0 surface used by Scryer/Trealla-compatible source.
|
|
5
|
+
abolish_all_tables/0 and abolish_table/1 surface used by Scryer/Trealla-compatible source.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
:- module(tabling, [start_tabling/2, abolish_all_tables/0, op(1150, fx, table)]).
|
|
8
|
+
:- module(tabling, [start_tabling/2, abolish_all_tables/0, abolish_table/1, op(1150, fx, table)]).
|
|
9
9
|
|
|
10
10
|
:- meta_predicate(start_tabling(?, 0)).
|
|
11
11
|
|
|
@@ -14,3 +14,24 @@ start_tabling(_, Worker) :-
|
|
|
14
14
|
|
|
15
15
|
abolish_all_tables :-
|
|
16
16
|
eyeprolog__abolish_all_tables.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
abolish_table(Spec) :-
|
|
20
|
+
( var(Spec) -> throw(error(instantiation_error, abolish_table/1))
|
|
21
|
+
; tabling__abolish_table(Spec)
|
|
22
|
+
).
|
|
23
|
+
|
|
24
|
+
tabling__abolish_table((A,B)) :- !,
|
|
25
|
+
tabling__abolish_table(A),
|
|
26
|
+
tabling__abolish_table(B).
|
|
27
|
+
tabling__abolish_table(Name//Arity) :-
|
|
28
|
+
atom(Name), integer(Arity), Arity >= 0, !,
|
|
29
|
+
ExpandedArity is Arity + 2,
|
|
30
|
+
tabling__abolish_table(Name/ExpandedArity).
|
|
31
|
+
tabling__abolish_table(Name/Arity) :-
|
|
32
|
+
atom(Name), integer(Arity), Arity >= 0, !,
|
|
33
|
+
( eyeprolog__abolish_table(Name, Arity) -> true
|
|
34
|
+
; throw(error(existence_error(table, Name/Arity), abolish_table/1))
|
|
35
|
+
).
|
|
36
|
+
tabling__abolish_table(Spec) :-
|
|
37
|
+
throw(error(type_error(predicate_indicator, Spec), abolish_table/1)).
|
|
@@ -50,10 +50,13 @@ export const eyePrologLibraryAutoload = Object.freeze({
|
|
|
50
50
|
"$-/1": "debug",
|
|
51
51
|
"$/1": "debug",
|
|
52
52
|
"abolish_all_tables/0": "tabling",
|
|
53
|
+
"abolish_table/1": "tabling",
|
|
53
54
|
"add_edges/3": "ugraphs",
|
|
54
55
|
"add_vertices/3": "ugraphs",
|
|
56
|
+
"aggregate_all/3": "aggregate",
|
|
55
57
|
"aggregate_max/5": "aggregate",
|
|
56
58
|
"aggregate_min/5": "aggregate",
|
|
59
|
+
"aggregate/3": "aggregate",
|
|
57
60
|
"all_different/1": "clpz",
|
|
58
61
|
"all_distinct/1": "clpz",
|
|
59
62
|
"append/2": "lists",
|
|
@@ -139,6 +142,7 @@ export const eyePrologLibraryAutoload = Object.freeze({
|
|
|
139
142
|
"edges/2": "ugraphs",
|
|
140
143
|
"element/3": "clpz",
|
|
141
144
|
"empty_assoc/1": "assoc",
|
|
145
|
+
"exclude/3": "lists",
|
|
142
146
|
"expmod/4": "arithmetic",
|
|
143
147
|
"fd_dom/2": "clpz",
|
|
144
148
|
"fd_inf/2": "clpz",
|
|
@@ -161,6 +165,7 @@ export const eyePrologLibraryAutoload = Object.freeze({
|
|
|
161
165
|
"format/2": "format",
|
|
162
166
|
"format/3": "format",
|
|
163
167
|
"freeze/2": "freeze",
|
|
168
|
+
"frozen/2": "freeze",
|
|
164
169
|
"ge/2": "comparison",
|
|
165
170
|
"gen_assoc/3": "assoc",
|
|
166
171
|
"gen_int/1": "between",
|
|
@@ -181,13 +186,16 @@ export const eyePrologLibraryAutoload = Object.freeze({
|
|
|
181
186
|
"hex_bytes/2": "crypto",
|
|
182
187
|
"if_/3": "reif",
|
|
183
188
|
"in/2": "clpz",
|
|
189
|
+
"include/3": "lists",
|
|
184
190
|
"indomain/1": "clpz",
|
|
185
191
|
"ins/2": "clpz",
|
|
186
192
|
"instantiation_error/0": "error",
|
|
187
193
|
"instantiation_error/1": "error",
|
|
188
194
|
"integer_si/1": "si",
|
|
195
|
+
"intersection/3": "lists",
|
|
189
196
|
"is_assoc/1": "assoc",
|
|
190
197
|
"is_ordset/1": "ordsets",
|
|
198
|
+
"is_set/1": "lists",
|
|
191
199
|
"join/3": "strings",
|
|
192
200
|
"label/1": "clpz",
|
|
193
201
|
"labeling/1": "clpb",
|
|
@@ -226,6 +234,8 @@ export const eyePrologLibraryAutoload = Object.freeze({
|
|
|
226
234
|
"max_list/2": "lists",
|
|
227
235
|
"max_sleep_time/1": "time",
|
|
228
236
|
"maybe/0": "random",
|
|
237
|
+
"maybe/1": "random",
|
|
238
|
+
"maybe/2": "random",
|
|
229
239
|
"member/2": "lists",
|
|
230
240
|
"memberchk/2": "lists",
|
|
231
241
|
"memberd_t/3": "reif",
|
|
@@ -309,12 +319,14 @@ export const eyePrologLibraryAutoload = Object.freeze({
|
|
|
309
319
|
"representation_error/1": "error",
|
|
310
320
|
"reset_gensym/1": "gensym",
|
|
311
321
|
"resource_error/1": "error",
|
|
322
|
+
"resource_error/2": "error",
|
|
312
323
|
"reverse/2": "lists",
|
|
313
324
|
"same_length/2": "lists",
|
|
314
325
|
"sat_count/2": "clpb",
|
|
315
326
|
"sat/1": "clpb",
|
|
316
327
|
"scalar_product/4": "clpz",
|
|
317
328
|
"select/3": "lists",
|
|
329
|
+
"selectchk/3": "lists",
|
|
318
330
|
"seq/3": "dcgs",
|
|
319
331
|
"seqq/3": "dcgs",
|
|
320
332
|
"serialized/2": "clpz",
|
|
@@ -333,11 +345,19 @@ export const eyePrologLibraryAutoload = Object.freeze({
|
|
|
333
345
|
"statistics/2": "time",
|
|
334
346
|
"string_concat/3": "strings",
|
|
335
347
|
"substring/4": "strings",
|
|
348
|
+
"subtract/3": "lists",
|
|
336
349
|
"succ/2": "iso_ext",
|
|
337
350
|
"sum_list/2": "lists",
|
|
338
351
|
"sum/3": "clpz",
|
|
339
352
|
"sumall/3": "aggregate",
|
|
340
353
|
"take/3": "lists",
|
|
354
|
+
"tasklist/2": "lists",
|
|
355
|
+
"tasklist/3": "lists",
|
|
356
|
+
"tasklist/4": "lists",
|
|
357
|
+
"tasklist/5": "lists",
|
|
358
|
+
"tasklist/6": "lists",
|
|
359
|
+
"tasklist/7": "lists",
|
|
360
|
+
"tasklist/8": "lists",
|
|
341
361
|
"taut/2": "clpb",
|
|
342
362
|
"term_attributed_variables/2": "atts",
|
|
343
363
|
"term_si/1": "si",
|
|
@@ -357,6 +377,7 @@ export const eyePrologLibraryAutoload = Object.freeze({
|
|
|
357
377
|
"type_error/2": "error",
|
|
358
378
|
"type_error/3": "error",
|
|
359
379
|
"ugraph_union/3": "ugraphs",
|
|
380
|
+
"union/3": "lists",
|
|
360
381
|
"unsetenv/1": "os",
|
|
361
382
|
"uppercase/2": "strings",
|
|
362
383
|
"uuid_string/2": "uuid",
|
package/src/standard-library.js
CHANGED
|
@@ -121,8 +121,8 @@ export const eyePrologNativeLibraryIndicators = Object.freeze([
|
|
|
121
121
|
'random/1',
|
|
122
122
|
]);
|
|
123
123
|
export const eyePrologPortableLibraryIndicators = Object.freeze([
|
|
124
|
-
'sumall/3', 'aggregate_min/5', 'aggregate_max/5',
|
|
125
|
-
'freeze/2',
|
|
124
|
+
'sumall/3', 'aggregate_min/5', 'aggregate_max/5', 'aggregate_all/3', 'aggregate/3',
|
|
125
|
+
'freeze/2', 'frozen/2',
|
|
126
126
|
'expmod/4', 'lcm/3', 'lsb/2', 'msb/2', 'popcount/2',
|
|
127
127
|
'empty_assoc/1', 'assoc_to_list/2', 'get_assoc/3', 'put_assoc/4',
|
|
128
128
|
'assoc_to_keys/2', 'assoc_to_values/2', 'del_assoc/4', 'del_max_assoc/4',
|
|
@@ -141,7 +141,7 @@ export const eyePrologPortableLibraryIndicators = Object.freeze([
|
|
|
141
141
|
'*/1', '$/1', '$-/1',
|
|
142
142
|
'must_be/2', 'can_be/2', 'instantiation_error/0', 'instantiation_error/1',
|
|
143
143
|
'domain_error/2', 'domain_error/3', 'type_error/2', 'type_error/3',
|
|
144
|
-
'representation_error/1', 'resource_error/1', 'call_with_error_context/2',
|
|
144
|
+
'representation_error/1', 'resource_error/1', 'resource_error/2', 'call_with_error_context/2',
|
|
145
145
|
'format_/4', 'format/2', 'format/3', 'portray_clause_/3',
|
|
146
146
|
'portray_clause/1', 'portray_clause/2', 'listing/1',
|
|
147
147
|
'gensym/2', 'reset_gensym/1',
|
|
@@ -150,10 +150,11 @@ export const eyePrologPortableLibraryIndicators = Object.freeze([
|
|
|
150
150
|
'^/3', '^/4', '^/5', '^/6', '^/7', '^/8', '^/9', '^/10',
|
|
151
151
|
'\\/1', '\\/2', '\\/3', '\\/4', '\\/5', '\\/6', '\\/7', '\\/8',
|
|
152
152
|
'+\\/2', '+\\/3', '+\\/4', '+\\/5', '+\\/6', '+\\/7', '+\\/8', '+\\/9',
|
|
153
|
-
'member/2', 'memberchk/2', 'select/3', 'append/2', 'append/3', 'last/2', 'same_length/2',
|
|
153
|
+
'member/2', 'memberchk/2', 'select/3', 'selectchk/3', 'subtract/3', 'union/3', 'intersection/3', 'is_set/1', 'append/2', 'append/3', 'last/2', 'same_length/2',
|
|
154
154
|
'nth0/3', 'nth0/4', 'nth1/3', 'nth1/4', 'set_nth0/4', 'take/3', 'drop/3', 'slice/4', 'reverse/2',
|
|
155
|
-
'length/2', 'maplist/2', 'maplist/3', 'maplist/4', 'maplist/5',
|
|
156
|
-
'maplist/6', 'maplist/7', 'maplist/8', 'maplist/9',
|
|
155
|
+
'length/2', 'exclude/3', 'include/3', 'maplist/2', 'maplist/3', 'maplist/4', 'maplist/5',
|
|
156
|
+
'maplist/6', 'maplist/7', 'maplist/8', 'maplist/9',
|
|
157
|
+
'tasklist/2', 'tasklist/3', 'tasklist/4', 'tasklist/5', 'tasklist/6', 'tasklist/7', 'tasklist/8', 'foldl/4', 'foldl/5', 'foldl/6',
|
|
157
158
|
'sum_list/2', 'min_list/2', 'max_list/2', 'list_to_set/2',
|
|
158
159
|
'transpose/2', 'list_max/2', 'list_min/2', 'permutation/2',
|
|
159
160
|
'is_ordset/1', 'list_to_ord_set/2', 'ord_add_element/3', 'ord_del_element/3',
|
|
@@ -166,7 +167,7 @@ export const eyePrologPortableLibraryIndicators = Object.freeze([
|
|
|
166
167
|
'phrase_from_file/2', 'phrase_from_file/3', 'phrase_from_stream/2', 'phrase_to_file/2',
|
|
167
168
|
'phrase_to_file/3', 'phrase_to_stream/2',
|
|
168
169
|
'smallest_divisor_from/3',
|
|
169
|
-
'maybe/0', 'random/3', 'random_integer/3', 'set_random/1',
|
|
170
|
+
'maybe/0', 'maybe/1', 'maybe/2', 'random/3', 'random_integer/3', 'set_random/1',
|
|
170
171
|
',/3', ';/3', '=/3', 'cond_t/3', 'dif/3', 'if_/3', 'memberd_t/3',
|
|
171
172
|
'tfilter/3', 'tmember/2', 'tmember_t/3', 'tpartition/4',
|
|
172
173
|
'atom_si/1', 'integer_si/1', 'atomic_si/1', 'list_si/1', 'character_si/1',
|
|
@@ -175,7 +176,7 @@ export const eyePrologPortableLibraryIndicators = Object.freeze([
|
|
|
175
176
|
'number_string/2', 'atom_string/2', 'term_string/2', 'string_concat/3',
|
|
176
177
|
'contains/2', 'matches/2', 'join/3', 'substring/4',
|
|
177
178
|
'numbervars/3', 'copy_term_nat/2',
|
|
178
|
-
'start_tabling/2', 'abolish_all_tables/0',
|
|
179
|
+
'start_tabling/2', 'abolish_all_tables/0', 'abolish_table/1',
|
|
179
180
|
'max_sleep_time/1', 'current_time/1', 'format_time/4',
|
|
180
181
|
'add_edges/3', 'add_vertices/3', 'complement/2', 'compose/3', 'connect_ugraph/3',
|
|
181
182
|
'del_edges/3', 'del_vertices/3', 'edges/2', 'neighbors/3', 'neighbours/3',
|
package/src/tabling-host.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// Runtime table-cache lifecycle
|
|
1
|
+
// Runtime table-cache lifecycle adapters for library(tabling).
|
|
2
|
+
|
|
3
|
+
import { ATOM, NUMBER, deref } from './term.js';
|
|
2
4
|
|
|
3
5
|
function* abolishAllTablesBuiltin({ solver, env }) {
|
|
4
6
|
solver.memo.clear();
|
|
@@ -15,8 +17,43 @@ function* abolishAllTablesBuiltin({ solver, env }) {
|
|
|
15
17
|
yield env;
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
function clearPredicateFromMemo(memo, prefix) {
|
|
21
|
+
for (const key of [...memo.keys()]) {
|
|
22
|
+
if (key.startsWith(prefix)) memo.delete(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function clearPredicateTables(solver, module, name, arity) {
|
|
27
|
+
const broadKey = `${module}:${name}/${arity}`;
|
|
28
|
+
clearPredicateFromMemo(solver.memo, `${broadKey}:`);
|
|
29
|
+
solver.subsumptiveMemo.delete(broadKey);
|
|
30
|
+
for (const scope of solver.innerTableScopes.values()) {
|
|
31
|
+
clearPredicateFromMemo(scope.memo, `${broadKey}:`);
|
|
32
|
+
scope.subsumptiveMemo.delete(broadKey);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function* abolishTableBuiltin({ solver, goal, env }) {
|
|
37
|
+
const name = deref(goal.args[0], env);
|
|
38
|
+
const arity = deref(goal.args[1], env);
|
|
39
|
+
if (name.type !== ATOM || arity.type !== NUMBER || !/^\d+$/.test(arity.name)) return;
|
|
40
|
+
const arityValue = Number(arity.name);
|
|
41
|
+
const matches = [...solver.program.groups.values()].filter((group) =>
|
|
42
|
+
group.name === name.name && group.arity === arityValue && group.tabled === true);
|
|
43
|
+
if (matches.length === 0) return;
|
|
44
|
+
for (const group of matches) clearPredicateTables(solver, group.module, group.name, group.arity);
|
|
45
|
+
// A completed WFS/Datalog model can depend on a tabled predicate; these
|
|
46
|
+
// caches do not expose per-predicate dependency keys, so invalidate them
|
|
47
|
+
// conservatively while preserving unrelated explicit memo tables.
|
|
48
|
+
solver.wfsModels.clear();
|
|
49
|
+
solver.datalogModels.clear();
|
|
50
|
+
solver.tableCoordinator = null;
|
|
51
|
+
yield env;
|
|
52
|
+
}
|
|
53
|
+
|
|
18
54
|
export const tablingHostBuiltins = {
|
|
19
55
|
register(registry) {
|
|
20
56
|
registry.add('eyeprolog__abolish_all_tables', 0, abolishAllTablesBuiltin, { deterministic: true, eyePrologLibrary: true });
|
|
57
|
+
registry.add('eyeprolog__abolish_table', 2, abolishTableBuiltin, { deterministic: true, eyePrologLibrary: true });
|
|
21
58
|
},
|
|
22
59
|
};
|
|
@@ -57,6 +57,23 @@ export function runArchitecture(reporter = new TestReporter()) {
|
|
|
57
57
|
}
|
|
58
58
|
if (issues.length > 0) throw new Error(issues.join('\n'));
|
|
59
59
|
});
|
|
60
|
+
reporter.test('overlapping bundled libraries cover the pinned Trealla export surface', () => {
|
|
61
|
+
const snapshotPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'trealla-library-exports.json');
|
|
62
|
+
const expected = JSON.parse(fs.readFileSync(snapshotPath, 'utf8'));
|
|
63
|
+
const issues = [];
|
|
64
|
+
for (const [module, indicators] of Object.entries(expected)) {
|
|
65
|
+
const file = path.join(ROOT, 'lib', `${module}.pl`);
|
|
66
|
+
if (!fs.existsSync(file)) {
|
|
67
|
+
issues.push(`missing src/lib/${module}.pl`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const actual = new Set(moduleExportIndicators(fs.readFileSync(file, 'utf8'), `src/lib/${module}.pl`));
|
|
71
|
+
for (const indicator of indicators) {
|
|
72
|
+
if (!actual.has(indicator)) issues.push(`${module}: missing Trealla export ${indicator}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (issues.length > 0) throw new Error(issues.join('\n'));
|
|
76
|
+
});
|
|
60
77
|
reporter.test('legacy grab-bag library host files are absent', () => {
|
|
61
78
|
for (const name of ['library-host.js', 'scryer-compat.js']) {
|
|
62
79
|
if (fs.existsSync(path.join(ROOT, name))) throw new Error(`${name} must stay split into module-owned hosts`);
|