eyeprolog 1.5.11 → 1.5.13
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 +1 -1
- package/src/execute.js +1 -0
- package/src/iso.js +2 -0
- 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/parser.js +31 -3
- package/src/repl.js +2 -0
- package/src/standard-library.js +9 -8
- package/src/tabling-host.js +38 -1
- package/src/term.js +17 -5
- package/src/write.js +19 -5
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +1 -1
- package/test/conformance/expected/iso/scryer_lexical_terms.pl +1 -1
- package/test/conformance/expected/lists/extra_append_to_improper_tail.pl +1 -1
- package/test/conformance/expected/lists/improper_list_readback.pl +1 -1
- package/test/conformance/expected/terms/003_terms_and_readback.pl +1 -1
- package/test/run-architecture.mjs +17 -0
- package/test/run-regression.mjs +87 -4
- package/test/trealla-library-exports.json +28 -0
- package/the-art-of-eyeprolog.md +37 -14
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
package/src/execute.js
CHANGED
|
@@ -51,6 +51,7 @@ export function executeGoals(program, solver, goals, { onAnswer = () => {} } = {
|
|
|
51
51
|
function currentWriteOptions(program, solver) {
|
|
52
52
|
return {
|
|
53
53
|
doubleQuotes: solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
54
|
+
doubleBar: !solver.isoStrict,
|
|
54
55
|
operators: [...program.operators.values()],
|
|
55
56
|
quoted: true,
|
|
56
57
|
};
|
package/src/iso.js
CHANGED
|
@@ -1988,6 +1988,7 @@ function writeBuiltin(mode) {
|
|
|
1988
1988
|
generateVariableNames: true,
|
|
1989
1989
|
variableNameState: solver.writeVariableState,
|
|
1990
1990
|
operators: solver.program.operators.values(),
|
|
1991
|
+
doubleBar: !solver.isoStrict,
|
|
1991
1992
|
}));
|
|
1992
1993
|
yield env;
|
|
1993
1994
|
};
|
|
@@ -2019,6 +2020,7 @@ function* writeTermBuiltin({ solver, goal, env }) {
|
|
|
2019
2020
|
generateVariableNames: true,
|
|
2020
2021
|
variableNameState: solver.writeVariableState,
|
|
2021
2022
|
operators: solver.program.operators.values(),
|
|
2023
|
+
doubleBar: !solver.isoStrict,
|
|
2022
2024
|
}));
|
|
2023
2025
|
yield env;
|
|
2024
2026
|
}
|
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/parser.js
CHANGED
|
@@ -937,9 +937,37 @@ class Parser {
|
|
|
937
937
|
this.parserFlagState.doubleQuotes === 'chars'
|
|
938
938
|
? atom(character)
|
|
939
939
|
: numberTerm(character.codePointAt(0)));
|
|
940
|
-
let
|
|
941
|
-
|
|
942
|
-
|
|
940
|
+
let tail = emptyList();
|
|
941
|
+
|
|
942
|
+
// Trealla-compatible double-bar right splice (issue #88): in normal
|
|
943
|
+
// mode, a double-quoted chars/codes prefix may be followed by `||Tail`.
|
|
944
|
+
// It is equivalent to the corresponding list prefix with Tail as its
|
|
945
|
+
// final list tail, e.g. "ab"||T is [a,b|T] with double_quotes(chars).
|
|
946
|
+
// The extension binds at priority 1 (tighter than ordinary operators)
|
|
947
|
+
// and is deliberately absent from the strict ISO profile.
|
|
948
|
+
if (!this.strictIso && this.token.type === TOK.BAR) {
|
|
949
|
+
const state = {
|
|
950
|
+
pos: this.pos,
|
|
951
|
+
line: this.line,
|
|
952
|
+
previousToken: this.previousToken,
|
|
953
|
+
token: this.token,
|
|
954
|
+
};
|
|
955
|
+
this.advance();
|
|
956
|
+
if (this.token.type === TOK.BAR) {
|
|
957
|
+
this.advance();
|
|
958
|
+
tail = this.parseTerm(operatorStrength(1), false, allowBar, false);
|
|
959
|
+
} else {
|
|
960
|
+
// A single bar remains ordinary bar syntax. We had to advance once
|
|
961
|
+
// to distinguish it from `||`, so restore the tokenizer state.
|
|
962
|
+
this.pos = state.pos;
|
|
963
|
+
this.line = state.line;
|
|
964
|
+
this.previousToken = state.previousToken;
|
|
965
|
+
this.token = state.token;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
for (let i = items.length - 1; i >= 0; i--) tail = cons(items[i], tail);
|
|
970
|
+
return tail;
|
|
943
971
|
}
|
|
944
972
|
if (this.token.type === TOK.NUMBER) {
|
|
945
973
|
const value = this.token.text;
|
package/src/repl.js
CHANGED
|
@@ -746,6 +746,7 @@ function formatAnswer(engine, state, variables, env) {
|
|
|
746
746
|
operatorAtomsAsArgs: true,
|
|
747
747
|
dottedGraphicAtoms: true,
|
|
748
748
|
doubleQuotes: state.solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
749
|
+
doubleBar: !state.strictIso,
|
|
749
750
|
};
|
|
750
751
|
let generated = 0;
|
|
751
752
|
|
|
@@ -844,6 +845,7 @@ function formatError(engine, state, error) {
|
|
|
844
845
|
quoted: true,
|
|
845
846
|
operators: [...state.program.operators.values()],
|
|
846
847
|
variableNames,
|
|
848
|
+
doubleBar: !state.strictIso,
|
|
847
849
|
})}.`;
|
|
848
850
|
}
|
|
849
851
|
const message = error?.message ?? String(error);
|
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
|
};
|
package/src/term.js
CHANGED
|
@@ -1067,14 +1067,18 @@ function writeString(value, quoteStrings) {
|
|
|
1067
1067
|
return out + '"';
|
|
1068
1068
|
}
|
|
1069
1069
|
|
|
1070
|
-
function
|
|
1070
|
+
function quotedListSplice(term, env, doubleQuotes) {
|
|
1071
1071
|
if (doubleQuotes !== 'chars' && doubleQuotes !== 'codes') return null;
|
|
1072
1072
|
const characters = [];
|
|
1073
1073
|
let cursor = term;
|
|
1074
1074
|
while (true) {
|
|
1075
1075
|
cursor = deref(cursor, env);
|
|
1076
|
-
if (isEmptyList(cursor))
|
|
1077
|
-
|
|
1076
|
+
if (isEmptyList(cursor)) {
|
|
1077
|
+
return characters.length === 0 ? null : { text: characters.join(''), tail: null };
|
|
1078
|
+
}
|
|
1079
|
+
if (!isCons(cursor)) {
|
|
1080
|
+
return characters.length === 0 ? null : { text: characters.join(''), tail: cursor };
|
|
1081
|
+
}
|
|
1078
1082
|
const item = deref(cursor.args[0], env);
|
|
1079
1083
|
if (doubleQuotes === 'chars') {
|
|
1080
1084
|
if (item.type !== ATOM || Array.from(item.name).length !== 1) return null;
|
|
@@ -1090,8 +1094,12 @@ function quotedListText(term, env, doubleQuotes) {
|
|
|
1090
1094
|
}
|
|
1091
1095
|
|
|
1092
1096
|
function writeList(term, env, options) {
|
|
1093
|
-
const
|
|
1094
|
-
if (
|
|
1097
|
+
const quotedSplice = quotedListSplice(term, env, options.doubleQuotes);
|
|
1098
|
+
if (quotedSplice != null && (quotedSplice.tail == null || options.doubleBar === true)) {
|
|
1099
|
+
const prefix = writeString(quotedSplice.text, true);
|
|
1100
|
+
if (quotedSplice.tail == null) return prefix;
|
|
1101
|
+
return `${prefix}||${termToString(quotedSplice.tail, env, true, options)}`;
|
|
1102
|
+
}
|
|
1095
1103
|
const parts = [];
|
|
1096
1104
|
let cursor = term;
|
|
1097
1105
|
while (true) {
|
|
@@ -1110,6 +1118,10 @@ export function termToString(term, env = new Env(), quoteStrings = true, options
|
|
|
1110
1118
|
options = {
|
|
1111
1119
|
...options,
|
|
1112
1120
|
doubleQuotes: options.doubleQuotes ?? 'chars',
|
|
1121
|
+
// termToString is also used for context-free processor-error messages.
|
|
1122
|
+
// Keep the normal-profile `||` extension opt-in here so strict errors do
|
|
1123
|
+
// not accidentally emit syntax that the strict parser rejects.
|
|
1124
|
+
doubleBar: options.doubleBar === true,
|
|
1113
1125
|
readVariableNames: options.readVariableNames instanceof Map ? options.readVariableNames : new Map(),
|
|
1114
1126
|
usedReadVariableNames: options.usedReadVariableNames instanceof Set ? options.usedReadVariableNames : new Set(),
|
|
1115
1127
|
};
|
package/src/write.js
CHANGED
|
@@ -152,14 +152,18 @@ function writeString(value) {
|
|
|
152
152
|
return out + '"';
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
function
|
|
155
|
+
function quotedListSplice(term, env, doubleQuotes) {
|
|
156
156
|
if (doubleQuotes !== 'chars' && doubleQuotes !== 'codes') return null;
|
|
157
157
|
const characters = [];
|
|
158
158
|
let cursor = term;
|
|
159
159
|
while (true) {
|
|
160
160
|
cursor = deref(cursor, env);
|
|
161
|
-
if (isEmptyList(cursor))
|
|
162
|
-
|
|
161
|
+
if (isEmptyList(cursor)) {
|
|
162
|
+
return characters.length === 0 ? null : { text: characters.join(''), tail: null };
|
|
163
|
+
}
|
|
164
|
+
if (!isCons(cursor)) {
|
|
165
|
+
return characters.length === 0 ? null : { text: characters.join(''), tail: cursor };
|
|
166
|
+
}
|
|
163
167
|
const item = deref(cursor.args[0], env);
|
|
164
168
|
if (doubleQuotes === 'chars') {
|
|
165
169
|
if (item.type !== ATOM || Array.from(item.name).length !== 1) return null;
|
|
@@ -331,8 +335,15 @@ function format(term, env, options, table, maxPriority = 1200, context = 'term')
|
|
|
331
335
|
}
|
|
332
336
|
|
|
333
337
|
if (!options.ignoreOps && isCons(resolved)) {
|
|
334
|
-
const
|
|
335
|
-
if (
|
|
338
|
+
const quotedSplice = quotedListSplice(resolved, env, options.doubleQuotes);
|
|
339
|
+
if (quotedSplice != null && (quotedSplice.tail == null || options.doubleBar)) {
|
|
340
|
+
const prefix = writeString(quotedSplice.text);
|
|
341
|
+
if (quotedSplice.tail == null) return prefix;
|
|
342
|
+
// `||` is a normal-profile syntax extension with priority 1, so a
|
|
343
|
+
// looser operator in the right operand must be parenthesized to keep the
|
|
344
|
+
// emitted text readable as the same term.
|
|
345
|
+
return `${prefix}||${format(quotedSplice.tail, env, options, table, 1, 'term')}`;
|
|
346
|
+
}
|
|
336
347
|
const parts = [];
|
|
337
348
|
let cursor = resolved;
|
|
338
349
|
while (true) {
|
|
@@ -442,6 +453,9 @@ export function formatTermForWrite(term, env = new Env(), options = {}) {
|
|
|
442
453
|
ignoreOps: options.ignoreOps === true,
|
|
443
454
|
numbervars: options.numbervars !== false,
|
|
444
455
|
doubleQuotes: options.doubleQuotes,
|
|
456
|
+
// `||` is a normal-profile syntax extension. Public normal-mode writers
|
|
457
|
+
// enable it by default; strict callers explicitly turn it off.
|
|
458
|
+
doubleBar: options.doubleBar !== false,
|
|
445
459
|
variableNames: options.generateVariableNames === true
|
|
446
460
|
? printableGeneratedVariableNames(term, env, explicitVariableNames, options.variableNameState)
|
|
447
461
|
: printableReadVariableNames(term, env, explicitVariableNames),
|
|
@@ -113,7 +113,7 @@ families; `--iso-strict` is intended to remove their Part 1 interpretation.
|
|
|
113
113
|
|
|
114
114
|
| Part 1 extension hook | EyeProlog normal-profile feature | Strict-core disposition |
|
|
115
115
|
| --- | --- | --- |
|
|
116
|
-
| 5.5.1 Syntax | Part 2 modules, Part 3 grammar-rule expansion,
|
|
116
|
+
| 5.5.1 Syntax | Part 2 modules, Part 3 grammar-rule expansion, embedded quad syntax, and Trealla-compatible `"text"||Tail` right-splicing for double-quoted `chars`/`codes` lists | Module directives are rejected; grammar rules remain ordinary `-->/2` terms rather than being expanded; quad syntax and double-bar right-splicing are rejected. Unicode PCS membership/classification is implementation defined and therefore shared with normal mode rather than treated as an extension. |
|
|
117
117
|
| 5.5.2 Predefined operators | Part 3 `|` and EyeProlog's labelable infix `(?-)/2`; CLP(Z) operators when that library is imported | Only the Part 1 operator table is predefined; a conforming `op/3` may still add permitted operators. |
|
|
118
118
|
| 5.5.3 Character-conversion mapping | No non-identity initial `Convc` extension | Identity initial mapping. |
|
|
119
119
|
| 5.5.4 Types | The normal JavaScript API exposes an implementation-specific string term `stringTerm(Text)`. It is disjoint from the five Part 1 term types; normal term order places it after atoms and before compound terms, and normal `atomic/1` treats it as atomic. It has no Prolog source token syntax (double-quoted source still follows `double_quotes`), is non-callable for term-to-clause conversion, is not evaluable as an arithmetic expression, and writes as a double-quoted host string. | Strict mode rejects a programmatic string term at program/goal entry with `representation_error(term)`, so the additional type cannot enter the Part 1 execution domain. **defined** — `src/term.js`, `src/program.js`, `src/solver.js`; strict API-boundary regression. |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
answer(append_to_improper_tail,
|
|
1
|
+
answer(append_to_improper_tail, "ab"||tail).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
answer(
|
|
1
|
+
answer("ab"||tail).
|
|
@@ -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`);
|
package/test/run-regression.mjs
CHANGED
|
@@ -2441,7 +2441,7 @@ c4 ?- call((!;1)).
|
|
|
2441
2441
|
'?- Xs = [].\n' +
|
|
2442
2442
|
'?- true.\n' +
|
|
2443
2443
|
'?- true.\n' +
|
|
2444
|
-
'?- Xs0 =
|
|
2444
|
+
'?- Xs0 = "a"||Xs.\n' +
|
|
2445
2445
|
'?- ',
|
|
2446
2446
|
'stdout');
|
|
2447
2447
|
assertEqual(result.stderr, '', 'stderr');
|
|
@@ -6493,7 +6493,7 @@ answer(ok) :-
|
|
|
6493
6493
|
assertEqual(Boolean(registry.get('is', 2)), true, 'ISO is/2 exists');
|
|
6494
6494
|
assertEqual(Boolean(registry.get('append', 3)), false, 'append/3 is not ISO core');
|
|
6495
6495
|
assertEqual(library.eyePrologLibrary, true, 'complete registry marker');
|
|
6496
|
-
assertEqual(library.defs.size,
|
|
6496
|
+
assertEqual(library.defs.size, 216, 'EyeProlog registry contains ISO definitions, cleanup controls, observability extensions, WFS tnot/1, and generic library adapters');
|
|
6497
6497
|
assertEqual(registry.get('eyeprolog__dynify', 1), null, 'Eyelet dynify adapter is absent from the ISO registry');
|
|
6498
6498
|
assertEqual(Boolean(library.get('eyeprolog__dynify', 1)), true, 'Eyelet dynify adapter is an internal EyeProlog library primitive');
|
|
6499
6499
|
assertEqual(registry.get('eyeprolog__eyelet_emit', 2), null, 'Eyelet event adapter is absent from the ISO registry');
|
|
@@ -6517,7 +6517,7 @@ answer(ok) :-
|
|
|
6517
6517
|
assertEqual(registry.get('setup_call_cleanup', 3), null, 'setup_call_cleanup/3 is absent from the ISO registry');
|
|
6518
6518
|
assertEqual(Boolean(library.get('setup_call_cleanup', 3)), true, 'setup_call_cleanup/3 is an EyeProlog cleanup control');
|
|
6519
6519
|
assertEqual(registeredNativeEyePrologLibraryNames().length, 109, 'public host-supported EyeProlog library count');
|
|
6520
|
-
assertEqual(eyePrologPortableLibraryIndicators.length,
|
|
6520
|
+
assertEqual(eyePrologPortableLibraryIndicators.length, 280, 'portable Prolog library count');
|
|
6521
6521
|
assertEqual(eyePrologInteropLibraryIndicators.length, 218, 'cross-implementation interop profile count');
|
|
6522
6522
|
assertEqual(eyePrologInteropLibraryModules.length, 26, 'common explicit library module profile count');
|
|
6523
6523
|
assertEqual(eyePrologInteropAutoload['member/2'], 'lists', 'member/2 canonical autoload');
|
|
@@ -6535,7 +6535,7 @@ answer(ok) :-
|
|
|
6535
6535
|
assertEqual(eyePrologNativeLibraryIndicators.length, 109, 'host-supported library count');
|
|
6536
6536
|
assertEqual(eyePrologNativeLibraryIndicators.includes('call_nth/2'), true, 'call_nth/2 remains classified as host-supported');
|
|
6537
6537
|
assertEqual(eyePrologNativeLibraryIndicators.includes('random/1'), true, 'stateful random/1 is classified as host-supported');
|
|
6538
|
-
assertEqual(eyePrologLibraryIndicators.length,
|
|
6538
|
+
assertEqual(eyePrologLibraryIndicators.length, 389, 'complete non-ISO EyeProlog library surface');
|
|
6539
6539
|
assertEqual(registry.get('eyeprolog__call_nth', 2), null, 'private call_nth adapter is absent from ISO registry');
|
|
6540
6540
|
assertEqual(Boolean(library.get('eyeprolog__call_nth', 2)), true, 'private call_nth adapter is registered for EyeProlog');
|
|
6541
6541
|
assertEqual(Boolean(library.get('eyeprolog__call_residue_vars', 2)), true, 'private call_residue_vars adapter is registered for EyeProlog');
|
|
@@ -6588,6 +6588,51 @@ answer(ok) :-
|
|
|
6588
6588
|
assertEqual(program.findGroup('random', 3)?.module, 'random', 'random/3 is imported from library(random)');
|
|
6589
6589
|
},
|
|
6590
6590
|
},
|
|
6591
|
+
{
|
|
6592
|
+
name: 'Trealla library overlap predicates execute with EyeProlog semantics',
|
|
6593
|
+
run: () => {
|
|
6594
|
+
const program = Program.parse(`
|
|
6595
|
+
:- use_module(library(aggregate)).
|
|
6596
|
+
:- use_module(library(error)).
|
|
6597
|
+
:- use_module(library(freeze)).
|
|
6598
|
+
:- use_module(library(lists)).
|
|
6599
|
+
:- use_module(library(random)).
|
|
6600
|
+
:- use_module(library(tabling)).
|
|
6601
|
+
|
|
6602
|
+
positive(X) :- X > 0.
|
|
6603
|
+
inc(X, Y) :- Y is X + 1.
|
|
6604
|
+
|
|
6605
|
+
:- dynamic(edge/2).
|
|
6606
|
+
:- table(path/2).
|
|
6607
|
+
path(X, Y) :- edge(X, Y).
|
|
6608
|
+
path(X, Z) :- edge(X, Y), path(Y, Z).
|
|
6609
|
+
|
|
6610
|
+
trealla_overlap_probe :-
|
|
6611
|
+
aggregate_all(count, member(_, [a,b,c]), 3),
|
|
6612
|
+
aggregate_all(sum(X), member(X, [1,2,3]), 6),
|
|
6613
|
+
aggregate_all(bag(X), member(X, [a,b]), [a,b]),
|
|
6614
|
+
aggregate_all(set(X), member(X, [b,a,b]), [a,b]),
|
|
6615
|
+
aggregate(sum(X), member(K-X, [a-1,a-2,b-3]), 3), K = a,
|
|
6616
|
+
aggregate(sum(Y), J^member(J-Y, [a-1,a-2,b-3]), 6),
|
|
6617
|
+
selectchk(b, [a,b,c], [a,c]),
|
|
6618
|
+
subtract([a,b,a,c], [a], [b,c]),
|
|
6619
|
+
union([a,b], [b,c], [a,b,c]),
|
|
6620
|
+
intersection([a,b,c], [b,d,c], [b,c]),
|
|
6621
|
+
is_set([a,b,c]), \\+ is_set([a,a]),
|
|
6622
|
+
include(positive, [-1,2,0,3], [2,3]),
|
|
6623
|
+
exclude(positive, [-1,2,0,3], [-1,0]),
|
|
6624
|
+
tasklist(inc, [1,2,3], [2,3,4]),
|
|
6625
|
+
maybe(1.0), maybe(1, 1),
|
|
6626
|
+
freeze(F, true), frozen(F, Frozen), Frozen \\== true, F = ready,
|
|
6627
|
+
catch(resource_error(memory, context), error(resource_error(memory), context), true),
|
|
6628
|
+
assertz(edge(a,b)), path(a,b),
|
|
6629
|
+
retract(edge(a,b)), assertz(edge(a,c)),
|
|
6630
|
+
abolish_table(path/2), \\+ path(a,b), path(a,c),
|
|
6631
|
+
catch(abolish_table(missing/1), error(existence_error(table, missing/1), abolish_table/1), true).
|
|
6632
|
+
`);
|
|
6633
|
+
assertEqual(run(program, { goal: 'trealla_overlap_probe' }).stdout, 'trealla_overlap_probe.\n', 'Trealla overlap execution');
|
|
6634
|
+
},
|
|
6635
|
+
},
|
|
6591
6636
|
{
|
|
6592
6637
|
name: 'Prologue facade re-exports canonical library predicates without conflicts',
|
|
6593
6638
|
run: () => {
|
|
@@ -6947,6 +6992,44 @@ function whiteBoxCases() {
|
|
|
6947
6992
|
assertEqual(`${quotedAtom.type}:${quotedAtom.name}`, 'atom:aλ', 'atom');
|
|
6948
6993
|
},
|
|
6949
6994
|
},
|
|
6995
|
+
{
|
|
6996
|
+
name: 'double-bar syntax splices double-quoted list prefixes (issue #88)',
|
|
6997
|
+
run: () => {
|
|
6998
|
+
const chars = parseGoalText('p("ab"||Tail)').args[0];
|
|
6999
|
+
assertEqual(chars.args[0].name, 'a', 'chars first element');
|
|
7000
|
+
assertEqual(chars.args[1].args[0].name, 'b', 'chars second element');
|
|
7001
|
+
assertEqual(chars.args[1].args[1].type, 'var', 'chars partial tail type');
|
|
7002
|
+
assertEqual(chars.args[1].args[1].name, 'Tail', 'chars partial tail name');
|
|
7003
|
+
assertEqual(termToString(chars, new Env(), true, { doubleBar: true }), '"ab"||Tail', 'chars readback');
|
|
7004
|
+
|
|
7005
|
+
const codes = parseGoalText('p("aλ" || Tail)', { doubleQuotes: 'codes' }).args[0];
|
|
7006
|
+
assertEqual(codes.args[0].name, '97', 'codes first element');
|
|
7007
|
+
assertEqual(codes.args[1].args[0].name, '955', 'codes second element');
|
|
7008
|
+
assertEqual(termToString(codes, new Env(), true, { doubleQuotes: 'codes', doubleBar: true }), '"aλ"||Tail', 'codes readback');
|
|
7009
|
+
|
|
7010
|
+
const nested = parseGoalText('p(["ab"|Tail])');
|
|
7011
|
+
assertEqual(termToString(nested), 'p(["ab" | Tail])', 'single bar remains list syntax');
|
|
7012
|
+
|
|
7013
|
+
for (const options of [{ isoStrict: true }, { doubleQuotes: 'atom' }]) {
|
|
7014
|
+
let threw = false;
|
|
7015
|
+
try { parseGoalText('p("ab"||Tail)', options); } catch (_) { threw = true; }
|
|
7016
|
+
assertEqual(threw, true, `double-bar rejected for ${JSON.stringify(options)}`);
|
|
7017
|
+
}
|
|
7018
|
+
},
|
|
7019
|
+
},
|
|
7020
|
+
{
|
|
7021
|
+
name: 'REPL prints partial character lists with double-bar syntax (issue #88)',
|
|
7022
|
+
run: () => {
|
|
7023
|
+
const result = runCli([], {
|
|
7024
|
+
input: 'phrase("Think of this text, and much more.", S0,S).\nhalt.\n',
|
|
7025
|
+
});
|
|
7026
|
+
assertEqual(result.status, 0, 'REPL exit status');
|
|
7027
|
+
assertIncludes(result.stdout,
|
|
7028
|
+
'S0 = "Think of this text, and much more."||S.',
|
|
7029
|
+
'Trealla-compatible partial string answer');
|
|
7030
|
+
assertNotIncludes(result.stdout, "S0 = ['T'", 'legacy expanded list answer');
|
|
7031
|
+
},
|
|
7032
|
+
},
|
|
6950
7033
|
{
|
|
6951
7034
|
name: 'double_quotes directives affect subsequent source text',
|
|
6952
7035
|
run: () => {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"aggregate": ["aggregate_all/3", "aggregate/3"],
|
|
3
|
+
"arithmetic": ["lsb/2", "msb/2", "popcount/2", "number_to_rational/2", "rational_numerator_denominator/3"],
|
|
4
|
+
"assoc": ["empty_assoc/1", "is_assoc/1", "assoc_to_list/2", "assoc_to_keys/2", "assoc_to_values/2", "gen_assoc/3", "get_assoc/3", "get_assoc/5", "list_to_assoc/2", "map_assoc/2", "map_assoc/3", "max_assoc/3", "min_assoc/3", "ord_list_to_assoc/2", "put_assoc/4", "del_assoc/4", "del_min_assoc/4", "del_max_assoc/4"],
|
|
5
|
+
"atts": ["get_attr/3", "put_attr/3", "del_attr/2"],
|
|
6
|
+
"charsio": ["char_type/2", "get_line_to_chars/3", "get_single_char/1", "get_n_chars/3"],
|
|
7
|
+
"clpb": ["sat/1", "taut/2", "labeling/1", "sat_count/2", "weighted_maximum/3", "random_labeling/2"],
|
|
8
|
+
"clpz": ["#>/2", "#</2", "#>=/2", "#=</2", "#=/2", "#\\=/2", "#\\/1", "#<==>/2", "#==>/2", "#<==/2", "#\\//2", "#\\/2", "#/\\/2", "in/2", "ins/2", "all_different/1", "all_distinct/1", "nvalue/2", "sum/3", "scalar_product/4", "tuples_in/2", "labeling/2", "label/1", "indomain/1", "lex_chain/1", "serialized/2", "global_cardinality/2", "global_cardinality/3", "circuit/1", "cumulative/1", "cumulative/2", "disjoint2/1", "element/3", "automaton/3", "automaton/8", "zcompare/3", "chain/2", "fd_var/1", "fd_inf/2", "fd_sup/2", "fd_size/2", "fd_dom/2", "clpz_t/2", "#=/3", "#</3"],
|
|
9
|
+
"debug": ["*/1", "$/1", "$-/1"],
|
|
10
|
+
"dif": ["dif/2"],
|
|
11
|
+
"error": ["must_be/2", "can_be/2", "instantiation_error/0", "domain_error/2", "type_error/2", "representation_error/1", "resource_error/1", "resource_error/2", "instantiation_error/1", "domain_error/3", "type_error/3", "call_with_error_context/2"],
|
|
12
|
+
"format": ["format_/4", "format/2", "format/3", "portray_clause_/3", "portray_clause/1", "portray_clause/2", "listing/1"],
|
|
13
|
+
"freeze": ["freeze/2", "frozen/2"],
|
|
14
|
+
"gensym": ["gensym/2", "reset_gensym/1"],
|
|
15
|
+
"lambda": ["^/3", "^/4", "^/5", "^/6", "^/7", "^/8", "^/9", "^/10", "\\/1", "\\/2", "\\/3", "\\/4", "\\/5", "\\/6", "\\/7", "\\/8", "+\\/2", "+\\/3", "+\\/4", "+\\/5", "+\\/6", "+\\/7", "+\\/8", "+\\/9"],
|
|
16
|
+
"lists": ["member/2", "memberchk/2", "select/3", "selectchk/3", "append/2", "append/3", "subtract/3", "union/3", "intersection/3", "is_set/1", "nth1/3", "nth0/3", "nth1/4", "nth0/4", "last/2", "same_length/2", "transpose/2", "sum_list/2", "max_list/2", "min_list/2", "list_to_set/2", "reverse/2", "exclude/3", "include/3", "permutation/2", "foldl/4", "foldl/5", "foldl/6", "maplist/2", "maplist/3", "maplist/4", "maplist/5", "maplist/6", "maplist/7", "maplist/8", "tasklist/2", "tasklist/3", "tasklist/4", "tasklist/5", "tasklist/6", "tasklist/7", "tasklist/8"],
|
|
17
|
+
"ordsets": ["is_ordset/1", "list_to_ord_set/2", "ord_add_element/3", "ord_del_element/3", "ord_selectchk/3", "ord_intersect/2", "ord_intersect/3", "ord_intersection/3", "ord_intersection/4", "ord_disjoint/2", "ord_subtract/3", "ord_union/2", "ord_union/3", "ord_union/4", "ord_subset/2", "ord_empty/1", "ord_memberchk/2", "ord_symdiff/3", "ord_seteq/2", "ord_intersection/2"],
|
|
18
|
+
"pairs": ["pairs_keys_values/3", "pairs_keys/2", "pairs_values/2", "group_pairs_by_key/2", "map_list_to_pairs/3"],
|
|
19
|
+
"pio": ["phrase_from_file/2", "phrase_from_file/3", "phrase_to_file/2", "phrase_to_file/3", "phrase_to_stream/2"],
|
|
20
|
+
"random": ["maybe/0", "maybe/1", "maybe/2", "random_integer/3", "set_random/1"],
|
|
21
|
+
"reif": ["if_/3", "=/3", ",/3", ";/3", "cond_t/3", "dif/3", "memberd_t/3", "tfilter/3", "tmember/2", "tmember_t/3", "tpartition/4"],
|
|
22
|
+
"si": ["atom_si/1", "integer_si/1", "atomic_si/1", "list_si/1", "character_si/1", "term_si/1", "chars_si/1", "dif_si/2", "not_si/1", "when_si/2"],
|
|
23
|
+
"tabling": ["start_tabling/2", "abolish_all_tables/0", "abolish_table/1"],
|
|
24
|
+
"time": ["current_time/1", "format_time/4"],
|
|
25
|
+
"ugraphs": ["add_edges/3", "add_vertices/3", "complement/2", "compose/3", "del_edges/3", "del_vertices/3", "edges/2", "neighbors/3", "neighbours/3", "reachable/3", "top_sort/2", "top_sort/3", "transitive_closure/2", "transpose_ugraph/2", "vertices/2", "vertices_edges_to_ugraph/3", "ugraph_union/3", "connect_ugraph/3"],
|
|
26
|
+
"uuid": ["uuidv4/1", "uuidv4_string/1", "uuid_string/2"],
|
|
27
|
+
"when": ["when/2"]
|
|
28
|
+
}
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -474,6 +474,12 @@ Prolog programs accepted by EyeProlog are built from terms:
|
|
|
474
474
|
- compound terms: `point(3, 4)`, `reading(temp, 91)`;
|
|
475
475
|
- lists: `[]`, `[red, green, blue]`, `[Head | Tail]`.
|
|
476
476
|
|
|
477
|
+
In normal mode, double-quoted character/code lists also support Trealla's
|
|
478
|
+
right-splice notation. With `double_quotes(chars)`, `"ab"||Tail` is shorthand
|
|
479
|
+
for `[a,b|Tail]`; with `double_quotes(codes)`, it denotes `[97,98|Tail]`. The
|
|
480
|
+
splice is not available when `double_quotes(atom)` is active, and
|
|
481
|
+
`--iso-strict` rejects it as an implementation-specific syntax extension.
|
|
482
|
+
|
|
477
483
|
Plain atom constants begin with a lowercase ASCII letter. Variables begin with
|
|
478
484
|
an uppercase letter or underscore. The bare `_` is anonymous and every
|
|
479
485
|
occurrence is fresh. `_Name` is a named variable; repeated occurrences refer to
|
|
@@ -5492,8 +5498,10 @@ still retained where adjacent graphic tokens would otherwise merge, as in
|
|
|
5492
5498
|
`write_term/2-3` supports `quoted/1`, `ignore_ops/1`, `numbervars/1`, and
|
|
5493
5499
|
`variable_names/1`. Normal mode additionally accepts the EyeProlog extension
|
|
5494
5500
|
`double_quotes(true|false)`: `true` lets eligible character/code lists use the
|
|
5495
|
-
current `double_quotes` representation
|
|
5496
|
-
|
|
5501
|
+
current `double_quotes` representation. Proper lists can therefore be written
|
|
5502
|
+
as `"text"`, while a partial list such as `[a,b|Tail]` is written as
|
|
5503
|
+
`"ab"||Tail`. Strict ISO mode rejects this implementation-specific write
|
|
5504
|
+
option. Normal mode also accepts the
|
|
5497
5505
|
implementation-specific boolean `spacing(true|false)` option: `false` emits
|
|
5498
5506
|
only separators required to avoid lexical ambiguity, while `true` adds
|
|
5499
5507
|
conventional layout around operators. For example,
|
|
@@ -5588,6 +5596,8 @@ lowercase ASCII letter. Variables begin with uppercase or underscore. The bare
|
|
|
5588
5596
|
`_` is fresh each time. Single quotes delimit quoted atoms; double quotes use
|
|
5589
5597
|
ISO double-quoted-list notation. Integers, decimals, scientific notation,
|
|
5590
5598
|
binary/octal/hexadecimal integers, and character-code constants are accepted.
|
|
5599
|
+
Normal mode additionally accepts the Trealla-compatible `"text"||Tail`
|
|
5600
|
+
right-splice for double-quoted `chars`/`codes` lists; strict ISO mode does not.
|
|
5591
5601
|
|
|
5592
5602
|
The processor character set is shared by normal and `--iso-strict` modes because
|
|
5593
5603
|
Part 1 makes it implementation defined rather than an extension boundary.
|
|
@@ -6298,12 +6308,12 @@ so side effects occur in Prolog execution order.
|
|
|
6298
6308
|
|
|
6299
6309
|
### The EyeProlog library
|
|
6300
6310
|
|
|
6301
|
-
EyeProlog exposes **
|
|
6311
|
+
EyeProlog exposes **389 distinct non-ISO library and normal-extension predicate
|
|
6302
6312
|
indicators** in addition to the 129 indicators in its isolated ISO profile.
|
|
6303
|
-
**
|
|
6313
|
+
**280 are defined entirely as ordinary Prolog clauses** in focused modules under
|
|
6304
6314
|
`src/lib/`; **109 use host support** for control, attributed variables,
|
|
6305
6315
|
constraints, character conversion, filesystem/OS access, timing, cryptography,
|
|
6306
|
-
or observability. The ISO and library catalogs therefore cover **
|
|
6316
|
+
or observability. The ISO and library catalogs therefore cover **518 distinct predicate indicators**. A normal-runtime predicate that is intentionally
|
|
6307
6317
|
re-exported by a compatibility module is counted once in this library surface:
|
|
6308
6318
|
for example `call_cleanup/2` and `setup_call_cleanup/3` are exported by
|
|
6309
6319
|
`library(iso_ext)`, while `time/1` and `statistics/2` are available from
|
|
@@ -6382,7 +6392,7 @@ bindings make one aligned subterm pair sufficient. For example:
|
|
|
6382
6392
|
|
|
6383
6393
|
| Module | Exported predicate indicators | Primary role |
|
|
6384
6394
|
| --- | --- | --- |
|
|
6385
|
-
| `library(aggregate)` | `sumall/3`, `aggregate_min/5`, `aggregate_max/5` | Aggregation |
|
|
6395
|
+
| `library(aggregate)` | `sumall/3`, `aggregate_min/5`, `aggregate_max/5`, `aggregate_all/3`, `aggregate/3` | Aggregation, including Trealla-compatible aggregate templates |
|
|
6386
6396
|
| `library(arithmetic)` | `expmod/4`, `lcm/3`, `lsb/2`, `msb/2`, `number_to_rational/2`, `number_to_rational/3`, `popcount/2`, `rational_numerator_denominator/3` | Scryer-compatible arithmetic helpers and rational-form conversion |
|
|
6387
6397
|
| `library(assoc)` | `empty_assoc/1`, `assoc_to_keys/2`, `assoc_to_list/2`, `assoc_to_values/2`, `del_assoc/4`, `del_max_assoc/4`, `del_min_assoc/4`, `gen_assoc/3`, `get_assoc/3`, `get_assoc/5`, `is_assoc/1`, `list_to_assoc/2`, `map_assoc/2`, `map_assoc/3`, `max_assoc/3`, `min_assoc/3`, `ord_list_to_assoc/2`, `put_assoc/4` | AVL association trees; reused from the shared portable source |
|
|
6388
6398
|
| `library(atts)` | `put_atts/2`, `get_atts/2`, `put_attr/3`, `get_attr/3`, `del_attr/2`, `term_attributed_variables/2`, `call_residue_vars/2` | Attributed variables |
|
|
@@ -6396,26 +6406,26 @@ bindings make one aligned subterm pair sufficient. For example:
|
|
|
6396
6406
|
| `library(dcgs)` | `-->/2`, `.../2`, `phrase/2`, `phrase/3`, `phrase/4`, `phrase/5`, `seq/3`, `seqq/3` | Full Scryer DCG export surface; nonterminals are shown at their expanded arities |
|
|
6397
6407
|
| `library(debug)` | `*/1`, `$/1`, `$-/1`, `debug/1`, `debug/3`, `nodebug/1`, `bb_get/2`, `bb_put/2`, `bb_b_put/2` | Declarative debug operators plus compatibility re-exports of the canonical `iso_ext` blackboard |
|
|
6398
6408
|
| `library(dif)` | `dif/2` | Common module facade over native delayed disequality |
|
|
6399
|
-
| `library(error)` | `must_be/2`, `can_be/2`, `instantiation_error/0`, `instantiation_error/1`, `domain_error/2`, `domain_error/3`, `type_error/2`, `type_error/3`, `representation_error/1`, `resource_error/1`, `call_with_error_context/2` | Error checking and construction |
|
|
6409
|
+
| `library(error)` | `must_be/2`, `can_be/2`, `instantiation_error/0`, `instantiation_error/1`, `domain_error/2`, `domain_error/3`, `type_error/2`, `type_error/3`, `representation_error/1`, `resource_error/1`, `resource_error/2`, `call_with_error_context/2` | Error checking and construction |
|
|
6400
6410
|
| `library(eyelet)` | `stable/1`, `becomes/2` | Eyelet forward-reasoning driver and state helpers; the `:+` operator is exported by the module and its fixed point is implemented in Prolog |
|
|
6401
6411
|
| `library(files)` | `delete_directory/1`, `delete_file/1`, `directory_exists/1`, `directory_files/2`, `file_access_time/2`, `file_copy/2`, `file_creation_time/2`, `file_exists/1`, `file_modification_time/2`, `file_size/2`, `make_directory/1`, `make_directory_path/1`, `path_canonical/2`, `path_segments/2`, `rename_file/2`, `working_directory/2` | Full Scryer filesystem surface backed by the Node host where filesystem access is required |
|
|
6402
6412
|
| `library(format)` | `format_/4`, `format/2`, `format/3`, `listing/1`, `portray_clause_/3`, `portray_clause/1`, `portray_clause/2` | Formatted DCG text and output; `format_/4` and `portray_clause_/3` are the expanded nonterminals |
|
|
6403
|
-
| `library(freeze)` | `freeze/2` | Delayed goals |
|
|
6413
|
+
| `library(freeze)` | `freeze/2`, `frozen/2` | Delayed goals and residual suspension inspection |
|
|
6404
6414
|
| `library(gensym)` | `gensym/2`, `reset_gensym/1` | Process-local generated atoms |
|
|
6405
6415
|
| `library(iso_ext)` | `bb_b_put/2`, `bb_get/2`, `bb_put/2`, `call_cleanup/2`, `call_nth/2`, `call_residue_vars/2`, `call_with_inference_limit/3`, `cfor/3`, `copy_term/3`, `copy_term_nat/2`, `countall/2`, `findall/4`, `forall/2`, `partial_string/1`, `partial_string/3`, `partial_string_tail/2`, `setup_call_cleanup/3`, `succ/2`, `time/1`, `variant/2` | Scryer ISO extensions plus EyeProlog compatibility helpers |
|
|
6406
6416
|
| `library(lambda)` | `^/3`, `^/4`, `^/5`, `^/6`, `^/7`, `^/8`, `^/9`, `^/10`, `\/1`, `\/2`, `\/3`, `\/4`, `\/5`, `\/6`, `\/7`, `\/8`, `+\/2`, `+\/3`, `+\/4`, `+\/5`, `+\/6`, `+\/7`, `+\/8`, `+\/9` | Higher-order lambda notation |
|
|
6407
|
-
| `library(lists)` | `member/2`, `memberchk/2`, `select/3`, `append/2`, `append/3`, `last/2`, `same_length/2`, `nth0/3`, `nth0/4`, `nth1/3`, `nth1/4`, `reverse/2`, `length/2`, `maplist/2`, `maplist/3`, `maplist/4`, `maplist/5`, `maplist/6`, `maplist/7`, `maplist/8`, `maplist/9`, `foldl/4`, `foldl/5`, `foldl/6`, `sum_list/2`, `min_list/2`, `max_list/2`, `list_to_set/2`, `list_max/2`, `list_min/2`, `permutation/2`, `transpose/2`, `set_nth0/4`, `take/3`, `drop/3`, `slice/4` | List relations and shared matrix/permutation helpers |
|
|
6417
|
+
| `library(lists)` | `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`, `nth0/3`, `nth0/4`, `nth1/3`, `nth1/4`, `reverse/2`, `length/2`, `include/3`, `exclude/3`, `maplist/2`, `maplist/3`, `maplist/4`, `maplist/5`, `maplist/6`, `maplist/7`, `maplist/8`, `maplist/9`, `tasklist/2`, `tasklist/3`, `tasklist/4`, `tasklist/5`, `tasklist/6`, `tasklist/7`, `tasklist/8`, `foldl/4`, `foldl/5`, `foldl/6`, `sum_list/2`, `min_list/2`, `max_list/2`, `list_to_set/2`, `list_max/2`, `list_min/2`, `permutation/2`, `transpose/2`, `set_nth0/4`, `take/3`, `drop/3`, `slice/4` | List relations and shared matrix/permutation helpers; `tasklist/*` is a sequential compatibility fallback |
|
|
6408
6418
|
| `library(ordsets)` | `is_ordset/1`, `list_to_ord_set/2`, `ord_add_element/3`, `ord_del_element/3`, `ord_disjoint/2`, `ord_empty/1`, `ord_intersect/2`, `ord_intersect/3`, `ord_intersection/2`, `ord_intersection/3`, `ord_intersection/4`, `ord_memberchk/2`, `ord_selectchk/3`, `ord_seteq/2`, `ord_subset/2`, `ord_subtract/3`, `ord_symdiff/3`, `ord_union/2`, `ord_union/3`, `ord_union/4` | Ordered-set relations; reused upstream source |
|
|
6409
6419
|
| `library(os)` | `argv/1`, `getenv/2`, `pid/1`, `raw_argv/1`, `setenv/2`, `shell/1`, `shell/2`, `unsetenv/1` | Environment, shell, PID, and command-line access backed by the Node host |
|
|
6410
6420
|
| `library(pairs)` | `pairs_keys_values/3`, `pairs_keys/2`, `pairs_values/2`, `group_pairs_by_key/2`, `map_list_to_pairs/3` | Key-value pair support |
|
|
6411
6421
|
| `library(pio)` | `phrase_from_file/2`, `phrase_from_file/3`, `phrase_from_stream/2`, `phrase_to_file/2`, `phrase_to_file/3`, `phrase_to_stream/2` | Scryer-compatible eager DCG file/stream I/O with character-list and atom paths |
|
|
6412
6422
|
| `library(primes)` | `smallest_divisor_from/3` | Prime factor support |
|
|
6413
6423
|
| `library(prologue)` | `member/2`, `append/3`, `length/2`, `between/3`, `select/3`, `succ/2`, `maplist/2`, `maplist/3`, `maplist/4`, `maplist/5`, `maplist/6`, `maplist/7`, `maplist/8`, `nth0/3`, `nth0/4`, `nth1/3`, `nth1/4`, `call_nth/2`, `freeze/2`, `foldl/4`, `foldl/5`, `foldl/6`, `countall/2` | Legacy facade over canonical focused modules |
|
|
6414
|
-
| `library(random)` | `maybe/0`, `random/1`, `random/3`, `random_integer/3`, `set_random/1` |
|
|
6424
|
+
| `library(random)` | `maybe/0`, `maybe/1`, `maybe/2`, `random/1`, `random/3`, `random_integer/3`, `set_random/1` | Trealla-compatible probability helpers plus the common mutable-seed interface and EyeProlog's pure state-threaded generator |
|
|
6415
6425
|
| `library(reif)` | `,/3`, `;/3`, `=/3`, `cond_t/3`, `dif/3`, `if_/3`, `memberd_t/3`, `tfilter/3`, `tmember/2`, `tmember_t/3`, `tpartition/4` | Reified conditions and list filtering; reused upstream source |
|
|
6416
6426
|
| `library(si)` | `atom_si/1`, `integer_si/1`, `atomic_si/1`, `list_si/1`, `character_si/1`, `term_si/1`, `chars_si/1`, `dif_si/2`, `not_si/1`, `when_si/2` | Sufficient-instantiation checks used by CLP(Z) |
|
|
6417
6427
|
| `library(strings)` | `matches/3`, `split/3`, `replace/4`, `lowercase/2`, `uppercase/2`, `trim/2`, `number_string/2`, `atom_string/2`, `term_string/2`, `string_concat/3`, `contains/2`, `matches/2`, `join/3`, `substring/4` | Text relations |
|
|
6418
|
-
| `library(tabling)` | `abolish_all_tables/0`, `start_tabling/2` | Common helpers for explicitly declared tabling |
|
|
6428
|
+
| `library(tabling)` | `abolish_all_tables/0`, `abolish_table/1`, `start_tabling/2` | Common helpers for explicitly declared tabling, including targeted invalidation |
|
|
6419
6429
|
| `library(terms)` | `numbervars/3`, `copy_term_nat/2` | Term operations used by bundled libraries |
|
|
6420
6430
|
| `library(time)` | `current_time/1`, `format_time/4`, `max_sleep_time/1`, `sleep/1`, `statistics/2`, `time/1` | Clock timestamps, sleep limits, timing/statistics, and the expanded `format_time//2` nonterminal |
|
|
6421
6431
|
| `library(ugraphs)` | `add_edges/3`, `add_vertices/3`, `complement/2`, `compose/3`, `connect_ugraph/3`, `del_edges/3`, `del_vertices/3`, `edges/2`, `neighbors/3`, `neighbours/3`, `reachable/3`, `top_sort/2`, `top_sort/3`, `transitive_closure/2`, `transpose_ugraph/2`, `ugraph_union/3`, `vertices/2`, `vertices_edges_to_ugraph/3` | Directed graph relations; reused upstream source |
|
|
@@ -6452,7 +6462,13 @@ narrower than either implementation's union of exports; EyeProlog's explicit-sta
|
|
|
6452
6462
|
interfaces. Separately, all 32 bundled EyeProlog modules whose basenames overlap
|
|
6453
6463
|
Scryer's current `src/lib/` tree are checked against the frozen export snapshot in
|
|
6454
6464
|
`test/scryer-library-exports.json` and cover the corresponding Scryer public
|
|
6455
|
-
predicate surface.
|
|
6465
|
+
predicate surface. The same structural check is now applied to Trealla: the 26
|
|
6466
|
+
bundled modules that have public-module counterparts in Trealla `library/` cover
|
|
6467
|
+
Trealla's exported predicates at pinned upstream commit
|
|
6468
|
+
`f7a93bd521c07a4841f5123348111dd005918c89`, recorded in
|
|
6469
|
+
`test/trealla-library-exports.json`. This is module-overlap coverage, not a claim
|
|
6470
|
+
that EyeProlog bundles Trealla's native host libraries such as `curl`, `gsl`,
|
|
6471
|
+
`janus`, `raylib`, `socket`, or `sqlite3`.
|
|
6456
6472
|
|
|
6457
6473
|
Source reuse is preferred over translation. `clpb.pl`, `ordsets.pl`,
|
|
6458
6474
|
`reif.pl`, and `ugraphs.pl` retain the upstream Prolog algorithms and license
|
|
@@ -6466,7 +6482,12 @@ compatibility. `files.pl` covers Scryer's complete filesystem export surface,
|
|
|
6466
6482
|
while `os.pl` covers its environment/shell process-context surface. Their actual
|
|
6467
6483
|
filesystem and OS side effects are isolated in module-owned Node adapters.
|
|
6468
6484
|
`crypto.pl` exposes Scryer's complete public crypto surface; its strict Trealla/Scryer overlap is `hex_bytes/2`,
|
|
6469
|
-
`crypto_n_random_bytes/2`, and `crypto_data_hash/3`.
|
|
6485
|
+
`crypto_n_random_bytes/2`, and `crypto_data_hash/3`. Trealla-specific overlap additions include `aggregate_all/3` and
|
|
6486
|
+
`aggregate/3`, `frozen/2`, the set/filter/list helpers and `tasklist/2-8`,
|
|
6487
|
+
`maybe/1-2`, `resource_error/2`, and `abolish_table/1`. Because EyeProlog has no
|
|
6488
|
+
Trealla task scheduler, `tasklist/2-8` deliberately executes sequentially with
|
|
6489
|
+
`maplist`-equivalent success/failure semantics; it does not promise Trealla's
|
|
6490
|
+
parallel scheduling behavior. The
|
|
6470
6491
|
[portable library overlap example](https://github.com/eyereasoner/eyeprolog/blob/main/examples/portable-library-overlap.pl)
|
|
6471
6492
|
composes Boolean constraints, ordered sets, graphs, reification, delayed goals,
|
|
6472
6493
|
generated names, character conversion, transposition, and explicit table
|
|
@@ -8017,7 +8038,9 @@ Notable implementation boundaries are:
|
|
|
8017
8038
|
- module and ISO/IEC TS 13211-3-oriented DCG compatibility profiles are supported in normal mode, without a complete Part 2/Part 3 certification claim;
|
|
8018
8039
|
- variables cannot occupy functor or predicate position;
|
|
8019
8040
|
- double-quoted text follows `double_quotes` exactly; the default `chars` value
|
|
8020
|
-
matches Trealla and Scryer and may be changed to `codes` or `atom`;
|
|
8041
|
+
matches Trealla and Scryer and may be changed to `codes` or `atom`; normal
|
|
8042
|
+
mode additionally accepts Trealla-compatible `"text"||Tail` right-splicing
|
|
8043
|
+
for the `chars` and `codes` values, while strict mode rejects that syntax;
|
|
8021
8044
|
- `write_term/2-3` implements the Part 1 plus Corrigendum 3 `quoted/1`,
|
|
8022
8045
|
`ignore_ops/1`, `numbervars/1`, and `variable_names/1` option surface,
|
|
8023
8046
|
including option validation and traversal rules; normal mode also offers
|