eyeprolog 0.5.12 → 0.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 +5 -3
- package/examples/age.pl +1 -1
- package/examples/blocks-world-planning.pl +3 -3
- package/examples/combinatorics-findall-sort.pl +4 -4
- package/examples/d3-group.pl +3 -3
- package/examples/dijkstra-findall-sort.pl +3 -3
- package/examples/enigma1225.pl +1 -2
- package/examples/eulerian-path.pl +4 -4
- package/examples/four-color-map.pl +2 -2
- package/examples/input/odrl-dpv-healthcare-risk-ranked-rules.pl +1 -1
- package/examples/input/odrl-dpv-risk-ranked-rules.pl +1 -1
- package/examples/input/rdf12-annotated-claims-rules.pl +1 -1
- package/examples/odrl-dpv-healthcare-risk-ranked.pl +1 -1
- package/examples/odrl-dpv-risk-ranked.pl +1 -1
- package/examples/proof/age.pl +1 -1
- package/examples/proof/d3-group.pl +4 -4
- package/examples/proof/iso-dynamic-database.pl +1 -1
- package/examples/quine-mccluskey.pl +7 -7
- package/examples/rdf12-annotated-claims.pl +1 -1
- package/examples/sudoku.pl +1 -1
- package/package.json +1 -1
- package/playground.html +27 -7
- package/src/cli.js +1 -1
- package/src/explain.js +1 -1
- package/src/{eyeprolog-library.js → eyeprolog-autoload.js} +23 -8
- package/src/eyeprolog-common-library.pl +44 -0
- package/src/eyeprolog-library.pl +49 -76
- package/src/index.js +2 -2
- package/src/playground-worker.js +2 -2
- package/src/solver.js +1 -1
- package/test/conformance/cases/lists/031_lists_aggregation_ordering.pl +1 -2
- package/test/conformance/cases/lists/045_sort_deduplicates_atoms.pl +2 -3
- package/test/conformance/cases/lists/087_sort_reverse_length.pl +2 -2
- package/test/conformance/cases/lists/extra_sort_iri_atoms.pl +1 -1
- package/test/conformance/cases/lists/sort_structured_terms.pl +1 -1
- package/test/run-playground.mjs +5 -1
- package/test/run-regression.mjs +6 -3
- package/the-art-of-eyeprolog.md +22 -17
package/README.md
CHANGED
|
@@ -32,9 +32,11 @@ Programs may declare their default queries with `%% goal:` comments.
|
|
|
32
32
|
## Portable library
|
|
33
33
|
|
|
34
34
|
EyeProlog adds 50 public library predicates to its 115-predicate ISO profile.
|
|
35
|
-
**All 50 are ordinary Prolog clauses**
|
|
36
|
-
autoloaded in Node and the
|
|
37
|
-
|
|
35
|
+
**All 50 are ordinary Prolog clauses** across `src/eyeprolog-library.pl` and
|
|
36
|
+
`src/eyeprolog-common-library.pl`; both are autoloaded in Node and the
|
|
37
|
+
browser. None requires host support. Other Prolog systems should load only
|
|
38
|
+
`eyeprolog-library.pl`, which avoids redefining their common list predicates.
|
|
39
|
+
Portable text predicates use ISO atoms or character lists; the
|
|
38
40
|
RDF tools emit lexical values as ISO atoms as well.
|
|
39
41
|
|
|
40
42
|
## RDF 1.2
|
package/examples/age.pl
CHANGED
|
@@ -48,7 +48,7 @@ move(State, move(Block, From, To), Newstate) :-
|
|
|
48
48
|
(Block \= To),
|
|
49
49
|
(From \= To),
|
|
50
50
|
select(on(Block, From), State, Rest),
|
|
51
|
-
|
|
51
|
+
sort_unique([on(Block, To)|Rest], Newstate).
|
|
52
52
|
|
|
53
53
|
plan(State, Goal, 0, _visited, [], State) :-
|
|
54
54
|
(State = Goal).
|
|
@@ -63,8 +63,8 @@ plan(State, Goal, Depth, Visited, [Move|Moves], Final) :-
|
|
|
63
63
|
five_move_plan(Moves, Final) :-
|
|
64
64
|
initial(Start),
|
|
65
65
|
goal(Goal),
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
sort_unique(Start, Sortedstart),
|
|
67
|
+
sort_unique(Goal, Sortedgoal),
|
|
68
68
|
plan(Sortedstart, Sortedgoal, 5, [Sortedstart], Moves, Final).
|
|
69
69
|
|
|
70
70
|
status(blocks_world, planned) :-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
% Eyelet-inspired combinations example using findall/3 and
|
|
1
|
+
% Eyelet-inspired combinations example using findall/3 and sort_unique/2.
|
|
2
2
|
%
|
|
3
3
|
% combination/3 generates the same subset in several selection orders. findall/3
|
|
4
|
-
% collects those candidates, and
|
|
4
|
+
% collects those candidates, and sort_unique/2 canonicalizes the list so each unordered
|
|
5
5
|
% 3-combination of five items is reported once.
|
|
6
6
|
%% goal: combinations(X0, X1)
|
|
7
7
|
|
|
@@ -24,12 +24,12 @@ combination(I, Items, Combination) :-
|
|
|
24
24
|
select(Item, Items, Remaining),
|
|
25
25
|
(J is I - 1),
|
|
26
26
|
combination(J, Remaining, Partial),
|
|
27
|
-
|
|
27
|
+
sort_unique([Item | Partial], Combination).
|
|
28
28
|
|
|
29
29
|
% findall collects all generation orders; sort canonicalizes and deduplicates.
|
|
30
30
|
unique_combinations(K, Items, Unique) :-
|
|
31
31
|
findall(C, combination(K, Items, C), All),
|
|
32
|
-
|
|
32
|
+
sort_unique(All, Unique).
|
|
33
33
|
|
|
34
34
|
combinations(combinations_5_choose_3, Unique) :-
|
|
35
35
|
unique_combinations(3, [1, 2, 3, 4, 5], Unique).
|
package/examples/d3-group.pl
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
% Eyelet-inspired D3 group example using findall/3 and
|
|
1
|
+
% Eyelet-inspired D3 group example using findall/3 and sort_unique/2.
|
|
2
2
|
% The six facts are the symmetries of an equilateral triangle, with compose/3 as
|
|
3
3
|
% the Cayley table and inverse/2 as the inverse relation. Candidate subsets are
|
|
4
4
|
% generated as subsequences, then filtered for subgroup closure.
|
|
@@ -72,7 +72,7 @@ subsequence([_head | Tail], Rest) :-
|
|
|
72
72
|
|
|
73
73
|
all_symmetries(Symmetries) :-
|
|
74
74
|
findall(X, symmetry(X), Raw),
|
|
75
|
-
|
|
75
|
+
sort_unique(Raw, Symmetries).
|
|
76
76
|
|
|
77
77
|
% A valid subgroup is closed under both composition and inverse.
|
|
78
78
|
closed_under_composition(Group) :-
|
|
@@ -90,7 +90,7 @@ valid_group(Group) :-
|
|
|
90
90
|
|
|
91
91
|
all_subgroups(Groups) :-
|
|
92
92
|
findall(G, valid_group(G), Raw),
|
|
93
|
-
|
|
93
|
+
sort_unique(Raw, Groups).
|
|
94
94
|
|
|
95
95
|
subgroups(d3_group, Groups) :-
|
|
96
96
|
all_subgroups(Groups).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
% Eyelet-inspired Dijkstra example using findall/3 and
|
|
1
|
+
% Eyelet-inspired Dijkstra example using findall/3 and sort_unique/2.
|
|
2
2
|
% The priority queue is represented as sorted list entries [Cost, Node | Path].
|
|
3
3
|
% Each expansion collects unvisited neighbors with findall/3, appends them to
|
|
4
|
-
% the frontier, and uses
|
|
4
|
+
% the frontier, and uses sort_unique/2 so the cheapest frontier entry is processed next.
|
|
5
5
|
|
|
6
6
|
%% goal: shortestPath(X0, X1)
|
|
7
7
|
|
|
@@ -34,7 +34,7 @@ dijkstra_queue([[Cost, Node | Path] | Queue], Goal, Visited, Resultpath, Resultc
|
|
|
34
34
|
(edge(Node, Neighbor, Weight), \+ member(Neighbor, Visited), (Newcost is Cost + Weight)),
|
|
35
35
|
Neighbors),
|
|
36
36
|
append(Queue, Neighbors, Newqueue),
|
|
37
|
-
|
|
37
|
+
sort_unique(Newqueue, Sortedqueue),
|
|
38
38
|
dijkstra_queue(Sortedqueue, Goal, [Node | Visited], Resultpath, Resultcost).
|
|
39
39
|
|
|
40
40
|
shortestPath(dijkstra_findall_sort, Path) :-
|
package/examples/enigma1225.pl
CHANGED
|
@@ -89,7 +89,7 @@ eval_matrix(Matrix, FreqSorted) :-
|
|
|
89
89
|
setof(E, member(E, Entries), Set),
|
|
90
90
|
maplist(count_var(Entries), Set, Multiplicities),
|
|
91
91
|
zip(Multiplicities, Set, Frequencies),
|
|
92
|
-
|
|
92
|
+
sort_unique(Frequencies, FreqSorted),
|
|
93
93
|
maplist(snd, FreqSorted, VarsSorted),
|
|
94
94
|
length(VarsSorted, NVars),
|
|
95
95
|
from_to(1, NVars, VarsSorted).
|
|
@@ -237,4 +237,3 @@ lists_reform([[A|B]|C], [A|D], [B|E]) :-
|
|
|
237
237
|
|
|
238
238
|
% query
|
|
239
239
|
%% goal: enigma1225(8, _)
|
|
240
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
% Eyelet-inspired Eulerian path example using findall/3 and
|
|
1
|
+
% Eyelet-inspired Eulerian path example using findall/3 and sort_unique/2.
|
|
2
2
|
%
|
|
3
3
|
% The graph is undirected; edges have identifiers so the trail consumes each
|
|
4
4
|
% physical edge exactly once even when vertices are revisited. The remaining
|
|
@@ -49,15 +49,15 @@ odd_degree(V) :-
|
|
|
49
49
|
|
|
50
50
|
odd_vertices(Odds) :-
|
|
51
51
|
findall(V, odd_degree(V), Raw),
|
|
52
|
-
|
|
52
|
+
sort_unique(Raw, Odds).
|
|
53
53
|
|
|
54
54
|
all_edges(Edges) :-
|
|
55
55
|
findall(E, edge(E, _a, _b), Raw),
|
|
56
|
-
|
|
56
|
+
sort_unique(Raw, Edges).
|
|
57
57
|
|
|
58
58
|
vertices(Vertices) :-
|
|
59
59
|
findall(V, vertex(V), Raw),
|
|
60
|
-
|
|
60
|
+
sort_unique(Raw, Vertices).
|
|
61
61
|
|
|
62
62
|
eulerian_start(Start) :-
|
|
63
63
|
odd_vertices([Start, _end]).
|
|
@@ -102,13 +102,13 @@ border_count(36).
|
|
|
102
102
|
|
|
103
103
|
all_countries_coloured(map_eu) :-
|
|
104
104
|
findall(Country, valid_assignment(Country), Countries),
|
|
105
|
-
|
|
105
|
+
sort_unique(Countries, Uniquecountries),
|
|
106
106
|
length(Uniquecountries, Count),
|
|
107
107
|
country_count(Count).
|
|
108
108
|
|
|
109
109
|
all_borders_checked(map_eu) :-
|
|
110
110
|
findall([A, B], border_colours_differ(A, B), Borders),
|
|
111
|
-
|
|
111
|
+
sort_unique(Borders, Uniqueborders),
|
|
112
112
|
length(Uniqueborders, Count),
|
|
113
113
|
border_count(Count).
|
|
114
114
|
|
package/examples/proof/age.pl
CHANGED
|
@@ -16,8 +16,8 @@ why(
|
|
|
16
16
|
by(builtin(findall, 3))
|
|
17
17
|
),
|
|
18
18
|
proof(
|
|
19
|
-
goal(
|
|
20
|
-
by(library(
|
|
19
|
+
goal(sort_unique([], [])),
|
|
20
|
+
by(library(sort_unique, 2))
|
|
21
21
|
)
|
|
22
22
|
])
|
|
23
23
|
)
|
|
@@ -43,8 +43,8 @@ why(
|
|
|
43
43
|
by(builtin(findall, 3))
|
|
44
44
|
),
|
|
45
45
|
proof(
|
|
46
|
-
goal(
|
|
47
|
-
by(library(
|
|
46
|
+
goal(sort_unique([], [])),
|
|
47
|
+
by(library(sort_unique, 2))
|
|
48
48
|
)
|
|
49
49
|
])
|
|
50
50
|
),
|
|
@@ -75,7 +75,7 @@ check(4, 'Canonical Tie-Breaking (Lexicographical First)', Status) :-
|
|
|
75
75
|
find_combination(Size, Primes, C),
|
|
76
76
|
covers_all(C, Minterms)
|
|
77
77
|
), Alternatives),
|
|
78
|
-
|
|
78
|
+
sort_unique(Alternatives, [Best|_]),
|
|
79
79
|
equality_status(Cover, Best, Status).
|
|
80
80
|
|
|
81
81
|
check(5, 'Consistency (Solution is subset of Primes)', Status) :-
|
|
@@ -166,9 +166,9 @@ compute_primes(Primes) :-
|
|
|
166
166
|
findall(B, (minterm(M), int_to_bits(M, B)), Mts),
|
|
167
167
|
findall(B, (dont_care(D), int_to_bits(D, B)), Dcs),
|
|
168
168
|
append(Mts, Dcs, All),
|
|
169
|
-
|
|
169
|
+
sort_unique(All, Init),
|
|
170
170
|
generate_loop(Init, [], RawPrimes),
|
|
171
|
-
|
|
171
|
+
sort_unique(RawPrimes, Primes).
|
|
172
172
|
|
|
173
173
|
generate_loop(Group, Acc, Final) :-
|
|
174
174
|
findall(Nx-[P1,P2], (
|
|
@@ -176,8 +176,8 @@ generate_loop(Group, Acc, Final) :-
|
|
|
176
176
|
), Pairs),
|
|
177
177
|
findall(N, member(N-_, Pairs), NextRaw),
|
|
178
178
|
findall(P, (member(_-Pars, Pairs), member(P, Pars)), UsedRaw),
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
sort_unique(NextRaw, NextGen),
|
|
180
|
+
sort_unique(UsedRaw, Used),
|
|
181
181
|
findall(P, (member(P, Group), \+ member(P, Used)), New),
|
|
182
182
|
append(Acc, New, AccUpd),
|
|
183
183
|
(NextGen = [] -> Final = AccUpd ; generate_loop(NextGen, AccUpd, Final)).
|
|
@@ -187,13 +187,13 @@ solve_minimal_cover(Primes, MinimalCover) :-
|
|
|
187
187
|
% 1. Essentials
|
|
188
188
|
findall(M-Ps, (member(M, Minterms), findall(P, (member(P, Primes), covers_int(P, M)), Ps)), Chart),
|
|
189
189
|
findall(P, member(_-[P], Chart), EssRaw),
|
|
190
|
-
|
|
190
|
+
sort_unique(EssRaw, Essentials),
|
|
191
191
|
% 2. Remaining
|
|
192
192
|
remaining_minterms(Minterms, Essentials, RemMts),
|
|
193
193
|
subtract_list(Primes, Essentials, NonEss),
|
|
194
194
|
(RemMts = [] -> BestRest = [] ; find_smallest_subset(NonEss, RemMts, BestRest)),
|
|
195
195
|
append(Essentials, BestRest, Total),
|
|
196
|
-
|
|
196
|
+
sort_unique(Total, MinimalCover).
|
|
197
197
|
|
|
198
198
|
remaining_minterms([], _, []).
|
|
199
199
|
remaining_minterms([M|Ms], Essentials, Rest) :-
|
package/examples/sudoku.pl
CHANGED
|
@@ -46,7 +46,7 @@ best_choice(Grid, Row, Column, Candidates) :-
|
|
|
46
46
|
),
|
|
47
47
|
Choices
|
|
48
48
|
),
|
|
49
|
-
|
|
49
|
+
sort_unique(Choices, [choice(_Count, Row, Column, Candidates)|_]).
|
|
50
50
|
|
|
51
51
|
empty_cell(Grid, Row, Column) :-
|
|
52
52
|
between(0, 8, Row),
|
package/package.json
CHANGED
package/playground.html
CHANGED
|
@@ -655,6 +655,7 @@
|
|
|
655
655
|
"aggregate_max",
|
|
656
656
|
"aggregate_min",
|
|
657
657
|
"append",
|
|
658
|
+
"apply",
|
|
658
659
|
"arg",
|
|
659
660
|
"asin",
|
|
660
661
|
"atan2",
|
|
@@ -707,7 +708,7 @@
|
|
|
707
708
|
"sin",
|
|
708
709
|
"slice",
|
|
709
710
|
"smallest_divisor_from",
|
|
710
|
-
"
|
|
711
|
+
"sort_unique",
|
|
711
712
|
"split",
|
|
712
713
|
"sqrt",
|
|
713
714
|
"string_concat",
|
|
@@ -744,6 +745,7 @@
|
|
|
744
745
|
let backgroundSource = '';
|
|
745
746
|
let backgroundName = '';
|
|
746
747
|
let activeWorker = null;
|
|
748
|
+
let activeWorkerStartupTimer = null;
|
|
747
749
|
let renderToken = 0;
|
|
748
750
|
let syntaxErrorLine = null;
|
|
749
751
|
let sourceReference = { kind: 'custom' };
|
|
@@ -957,9 +959,29 @@
|
|
|
957
959
|
runButton.disabled = true;
|
|
958
960
|
stopButton.disabled = false;
|
|
959
961
|
|
|
960
|
-
const workerUrl = new URL('./src/playground-worker.js?playground=
|
|
962
|
+
const workerUrl = new URL('./src/playground-worker.js?playground=20260807b', location.href);
|
|
961
963
|
activeWorker = new Worker(workerUrl, { type: 'module' });
|
|
962
|
-
|
|
964
|
+
const worker = activeWorker;
|
|
965
|
+
const request = {
|
|
966
|
+
source: combinedSource(),
|
|
967
|
+
options: { goals: selectedGoals(), proof: proof.checked, stats: stats.checked },
|
|
968
|
+
};
|
|
969
|
+
activeWorkerStartupTimer = setTimeout(() => {
|
|
970
|
+
if (activeWorker !== worker) return;
|
|
971
|
+
finishRun({
|
|
972
|
+
ok: false,
|
|
973
|
+
error: 'The reasoning worker did not finish loading. Reload the page to refresh cached EyeProlog assets.',
|
|
974
|
+
});
|
|
975
|
+
}, 10000);
|
|
976
|
+
activeWorker.onmessage = (event) => {
|
|
977
|
+
if (event.data?.type === 'ready') {
|
|
978
|
+
clearTimeout(activeWorkerStartupTimer);
|
|
979
|
+
activeWorkerStartupTimer = null;
|
|
980
|
+
worker.postMessage(request);
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
finishRun(event.data);
|
|
984
|
+
};
|
|
963
985
|
activeWorker.onerror = (event) => {
|
|
964
986
|
const location = event.filename
|
|
965
987
|
? ` (${event.filename}${event.lineno ? `:${event.lineno}${event.colno ? `:${event.colno}` : ''}` : ''})`
|
|
@@ -974,10 +996,6 @@
|
|
|
974
996
|
ok: false,
|
|
975
997
|
error: 'The reasoning worker returned an unreadable message.',
|
|
976
998
|
});
|
|
977
|
-
activeWorker.postMessage({
|
|
978
|
-
source: combinedSource(),
|
|
979
|
-
options: { goals: selectedGoals(), proof: proof.checked, stats: stats.checked },
|
|
980
|
-
});
|
|
981
999
|
}
|
|
982
1000
|
|
|
983
1001
|
function combinedSource() {
|
|
@@ -1072,6 +1090,8 @@
|
|
|
1072
1090
|
}
|
|
1073
1091
|
|
|
1074
1092
|
function cleanupWorker() {
|
|
1093
|
+
if (activeWorkerStartupTimer != null) clearTimeout(activeWorkerStartupTimer);
|
|
1094
|
+
activeWorkerStartupTimer = null;
|
|
1075
1095
|
if (activeWorker) activeWorker.terminate();
|
|
1076
1096
|
activeWorker = null;
|
|
1077
1097
|
}
|
package/src/cli.js
CHANGED
|
@@ -115,7 +115,7 @@ async function loadEngine() {
|
|
|
115
115
|
import('./program.js'),
|
|
116
116
|
import('./solver.js'),
|
|
117
117
|
import('./iso.js'),
|
|
118
|
-
import('./eyeprolog-
|
|
118
|
+
import('./eyeprolog-autoload.js'),
|
|
119
119
|
]);
|
|
120
120
|
engineModule = { ...term, ...parser, ...program, ...solver, ...iso, ...library };
|
|
121
121
|
}
|
package/src/explain.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// human-readable and machine-readable.
|
|
5
5
|
import { ATOM, COMPOUND, Env, Term, VAR, deref, flattenConjunction, freshTerm, termToString, unify, variantTerms } from './term.js';
|
|
6
6
|
import { selectClauseCandidates } from './program.js';
|
|
7
|
-
import { getEyePrologRegistry } from './eyeprolog-
|
|
7
|
+
import { getEyePrologRegistry } from './eyeprolog-autoload.js';
|
|
8
8
|
import { Solver, nextFreshId } from './solver.js';
|
|
9
9
|
|
|
10
10
|
export function whyProof(program, goal, options = {}) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Autoload the pure-Prolog EyeProlog library in Node and the browser.
|
|
2
2
|
import { createDefaultRegistry } from './iso.js';
|
|
3
3
|
import { parseClauses } from './parser.js';
|
|
4
4
|
import { fs, isNode } from './platform.js';
|
|
@@ -8,7 +8,7 @@ export const eyePrologNativeLibraryIndicators = Object.freeze([]);
|
|
|
8
8
|
export const eyePrologPortableLibraryIndicators = Object.freeze([
|
|
9
9
|
'uuid/3',
|
|
10
10
|
'difference/3',
|
|
11
|
-
'
|
|
11
|
+
'apply/3',
|
|
12
12
|
'maplist/3',
|
|
13
13
|
'tan/2',
|
|
14
14
|
'asin/2',
|
|
@@ -51,7 +51,7 @@ export const eyePrologPortableLibraryIndicators = Object.freeze([
|
|
|
51
51
|
'min_list/2',
|
|
52
52
|
'max_list/2',
|
|
53
53
|
'list_to_set/2',
|
|
54
|
-
'
|
|
54
|
+
'sort_unique/2',
|
|
55
55
|
'countall/2',
|
|
56
56
|
'sumall/3',
|
|
57
57
|
'aggregate_min/5',
|
|
@@ -64,12 +64,21 @@ export const eyePrologLibraryIndicators = Object.freeze([
|
|
|
64
64
|
]);
|
|
65
65
|
|
|
66
66
|
const autoloadedPrograms = new WeakSet();
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
const libraryFiles = [
|
|
68
|
+
'eyeprolog-library.pl',
|
|
69
|
+
'eyeprolog-common-library.pl',
|
|
70
|
+
];
|
|
71
|
+
const libraryCacheKey = isNode
|
|
72
|
+
? null
|
|
73
|
+
: (new URL(import.meta.url).searchParams.get('playground') ?? '20260807b');
|
|
74
|
+
const librarySources = await Promise.all(libraryFiles.map(async (filename) => ({
|
|
75
|
+
filename,
|
|
76
|
+
source: await loadLibrarySource(libraryFileUrl(filename)),
|
|
77
|
+
})));
|
|
78
|
+
const portableClauseTemplates = librarySources.flatMap(({ filename, source }) => parseClauses(source, {
|
|
79
|
+
filename: `src/${filename}`,
|
|
71
80
|
sourceMetadata: true,
|
|
72
|
-
});
|
|
81
|
+
}));
|
|
73
82
|
|
|
74
83
|
async function loadLibrarySource(url) {
|
|
75
84
|
if (isNode) return fs.readFileSync(url, 'utf8');
|
|
@@ -78,6 +87,12 @@ async function loadLibrarySource(url) {
|
|
|
78
87
|
return response.text();
|
|
79
88
|
}
|
|
80
89
|
|
|
90
|
+
function libraryFileUrl(filename) {
|
|
91
|
+
const url = new URL(`./${filename}`, import.meta.url);
|
|
92
|
+
if (!isNode && libraryCacheKey) url.searchParams.set('playground', libraryCacheKey);
|
|
93
|
+
return url;
|
|
94
|
+
}
|
|
95
|
+
|
|
81
96
|
export function ensureEyePrologLibrary(program) {
|
|
82
97
|
if (autoloadedPrograms.has(program)) return program;
|
|
83
98
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
% Common pure-Prolog library predicates for EyeProlog.
|
|
2
|
+
%
|
|
3
|
+
% EyeProlog autoloads this file together with eyeprolog-library.pl. Other
|
|
4
|
+
% Prolog systems should load eyeprolog-library.pl only and use their native or
|
|
5
|
+
% library versions of these widespread predicates, avoiding redefinition of
|
|
6
|
+
% protected procedures.
|
|
7
|
+
|
|
8
|
+
maplist(_, [], []).
|
|
9
|
+
maplist(Closure, [A|As], [B|Bs]) :-
|
|
10
|
+
apply(Closure, A, B),
|
|
11
|
+
maplist(Closure, As, Bs).
|
|
12
|
+
|
|
13
|
+
append([], Ys, Ys).
|
|
14
|
+
append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).
|
|
15
|
+
|
|
16
|
+
member(X, [X|_]).
|
|
17
|
+
member(X, [_|Xs]) :- member(X, Xs).
|
|
18
|
+
|
|
19
|
+
select(X, [X|Xs], Xs).
|
|
20
|
+
select(X, [Y|Ys], [Y|Zs]) :- select(X, Ys, Zs).
|
|
21
|
+
|
|
22
|
+
last([X], X).
|
|
23
|
+
last([_|Xs], X) :- last(Xs, X).
|
|
24
|
+
|
|
25
|
+
nth0(0, [X|_], X).
|
|
26
|
+
nth0(N, [_|Xs], X) :- var(N), nth0(N0, Xs, X), N is N0 + 1.
|
|
27
|
+
nth0(N, [_|Xs], X) :- nonvar(N), N > 0, N1 is N - 1, nth0(N1, Xs, X).
|
|
28
|
+
|
|
29
|
+
nth1(N, List, X) :- nth0(N0, List, X), N is N0 + 1.
|
|
30
|
+
|
|
31
|
+
reverse(List, Reversed) :- eyeprolog__reverse(List, [], Reversed).
|
|
32
|
+
|
|
33
|
+
length(List, Length) :- nonvar(List), eyeprolog__length_count(List, 0, Length).
|
|
34
|
+
length(List, Length) :- var(List), integer(Length), Length >= 0, eyeprolog__length_make(Length, List).
|
|
35
|
+
|
|
36
|
+
sum_list(List, Sum) :- eyeprolog__sum_list(List, 0, Sum).
|
|
37
|
+
|
|
38
|
+
min_list([X|Xs], Min) :- eyeprolog__min_list(Xs, X, Min).
|
|
39
|
+
|
|
40
|
+
max_list([X|Xs], Max) :- eyeprolog__max_list(Xs, X, Max).
|
|
41
|
+
|
|
42
|
+
list_to_set(List, Set) :- eyeprolog__list_to_set(List, [], Set).
|
|
43
|
+
|
|
44
|
+
countall(Goal, Count) :- findall(1, Goal, Ones), eyeprolog__length_count(Ones, 0, Count).
|
package/src/eyeprolog-library.pl
CHANGED
|
@@ -37,17 +37,18 @@ eyeprolog__atomic_chars(Value, Chars) :-
|
|
|
37
37
|
|
|
38
38
|
% ---------- core/meta ----------
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
eyeprolog__append([], Ys, Ys).
|
|
41
|
+
eyeprolog__append([X|Xs], Ys, [X|Zs]) :- eyeprolog__append(Xs, Ys, Zs).
|
|
42
|
+
|
|
43
|
+
eyeprolog__member(X, [X|_]).
|
|
44
|
+
eyeprolog__member(X, [_|Xs]) :- eyeprolog__member(X, Xs).
|
|
45
|
+
|
|
46
|
+
apply(Closure, A, B) :-
|
|
41
47
|
Closure =.. Parts,
|
|
42
|
-
|
|
48
|
+
eyeprolog__append(Parts, [A, B], CallParts),
|
|
43
49
|
Goal =.. CallParts,
|
|
44
50
|
call(Goal).
|
|
45
51
|
|
|
46
|
-
maplist(_, [], []).
|
|
47
|
-
maplist(Closure, [A|As], [B|Bs]) :-
|
|
48
|
-
call(Closure, A, B),
|
|
49
|
-
maplist(Closure, As, Bs).
|
|
50
|
-
|
|
51
52
|
% ---------- arithmetic helpers ----------
|
|
52
53
|
|
|
53
54
|
tan(X, Y) :- Y is sin(X) / cos(X).
|
|
@@ -86,7 +87,7 @@ eyeprolog__duration_field([C|Cs], [C|Ds], Unit, Rest) :-
|
|
|
86
87
|
char_code(C, Code), Code >= 48, Code =< 57,
|
|
87
88
|
eyeprolog__duration_field(Cs, Ds, Unit, Rest).
|
|
88
89
|
eyeprolog__duration_field([Unit|Rest], [], Unit, Rest) :-
|
|
89
|
-
|
|
90
|
+
eyeprolog__member(Unit, ['Y', 'M', 'D']).
|
|
90
91
|
|
|
91
92
|
eyeprolog__duration_assign('Y', N, 0, M, D, N, M, D).
|
|
92
93
|
eyeprolog__duration_assign('M', N, Y, 0, D, Y, N, D).
|
|
@@ -174,10 +175,10 @@ uuid(Seed0, UUID, Seed) :-
|
|
|
174
175
|
eyeprolog__hex_digit(VariantValue, Variant),
|
|
175
176
|
eyeprolog__uuid_hex(3, Seed4, Group4, Seed5),
|
|
176
177
|
eyeprolog__uuid_hex(12, Seed5, Group5, Seed),
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
eyeprolog__append(Group1, ['-'|Tail1], Chars),
|
|
179
|
+
eyeprolog__append(Group2, ['-','4'|Tail2], Tail1),
|
|
180
|
+
eyeprolog__append(Group3, ['-',Variant|Tail3], Tail2),
|
|
181
|
+
eyeprolog__append(Group4, ['-'|Group5], Tail3),
|
|
181
182
|
atom_chars(UUID, Chars).
|
|
182
183
|
|
|
183
184
|
eyeprolog__uuid_hex(0, Seed, [], Seed).
|
|
@@ -215,7 +216,7 @@ smallest_divisor_from(N, Start, Divisor) :-
|
|
|
215
216
|
% in the exact range covered by bases 2,3,5,7,11,13,17. Above that range the
|
|
216
217
|
% implementation falls back to exact trial division, preserving semantics for
|
|
217
218
|
% arbitrary-size integers.
|
|
218
|
-
eyeprolog__smallest_divisor_fast(N,
|
|
219
|
+
eyeprolog__smallest_divisor_fast(N, _, N) :-
|
|
219
220
|
N >= 2,
|
|
220
221
|
N < 341550071728321,
|
|
221
222
|
eyeprolog__mr_prime(N),
|
|
@@ -327,8 +328,8 @@ eyeprolog__previous_month(Y, 1, PY, 12) :- PY is Y - 1.
|
|
|
327
328
|
|
|
328
329
|
eyeprolog__days_in_month(Y, 2, 29) :- eyeprolog__leap_year(Y).
|
|
329
330
|
eyeprolog__days_in_month(Y, 2, 28) :- \+ eyeprolog__leap_year(Y).
|
|
330
|
-
eyeprolog__days_in_month(_, M, 30) :-
|
|
331
|
-
eyeprolog__days_in_month(_, M, 31) :-
|
|
331
|
+
eyeprolog__days_in_month(_, M, 30) :- eyeprolog__member(M, [4,6,9,11]).
|
|
332
|
+
eyeprolog__days_in_month(_, M, 31) :- eyeprolog__member(M, [1,3,5,7,8,10,12]).
|
|
332
333
|
|
|
333
334
|
eyeprolog__leap_year(Y) :- 0 is Y mod 400.
|
|
334
335
|
eyeprolog__leap_year(Y) :- Y mod 100 =\= 0, 0 is Y mod 4.
|
|
@@ -340,15 +341,15 @@ eyeprolog__format_duration(Y, M, D, Duration) :-
|
|
|
340
341
|
eyeprolog__duration_part(Y, 'Y', YC),
|
|
341
342
|
eyeprolog__duration_part(M, 'M', MC),
|
|
342
343
|
eyeprolog__duration_part(D, 'D', DC),
|
|
343
|
-
|
|
344
|
-
|
|
344
|
+
eyeprolog__append(['P'|YC], MC, A),
|
|
345
|
+
eyeprolog__append(A, DC, Chars),
|
|
345
346
|
eyeprolog__text_chars(Duration, Chars).
|
|
346
347
|
|
|
347
348
|
eyeprolog__duration_part(0, _, []).
|
|
348
349
|
eyeprolog__duration_part(N, Unit, Chars) :-
|
|
349
350
|
N =\= 0,
|
|
350
351
|
number_chars(N, Digits),
|
|
351
|
-
|
|
352
|
+
eyeprolog__append(Digits, [Unit], Chars).
|
|
352
353
|
|
|
353
354
|
% ---------- portable named-capture matcher ----------
|
|
354
355
|
%
|
|
@@ -383,8 +384,8 @@ eyeprolog__regex_parse(Chars0, Start, End, Tokens) :-
|
|
|
383
384
|
eyeprolog__strip_start_anchor(['^'|Cs], yes, Cs).
|
|
384
385
|
eyeprolog__strip_start_anchor(Cs, no, Cs).
|
|
385
386
|
|
|
386
|
-
eyeprolog__strip_end_anchor(Cs0, yes, Cs) :-
|
|
387
|
-
eyeprolog__strip_end_anchor(Cs, no, Cs) :- \+
|
|
387
|
+
eyeprolog__strip_end_anchor(Cs0, yes, Cs) :- eyeprolog__append(Cs, ['$'], Cs0).
|
|
388
|
+
eyeprolog__strip_end_anchor(Cs, no, Cs) :- \+ eyeprolog__append(_, ['$'], Cs).
|
|
388
389
|
|
|
389
390
|
eyeprolog__regex_tokens_parse([], []).
|
|
390
391
|
eyeprolog__regex_tokens_parse(['('|Cs], [capture(Name, Kind, Optional)|Tokens]) :-
|
|
@@ -425,12 +426,12 @@ eyeprolog__capture_kind(['['|Body], class_exact(Class, Count)) :-
|
|
|
425
426
|
Class \= [].
|
|
426
427
|
eyeprolog__capture_kind(Body, literal(Body)) :-
|
|
427
428
|
Body \= [],
|
|
428
|
-
\+
|
|
429
|
-
\+
|
|
430
|
-
\+
|
|
431
|
-
\+
|
|
432
|
-
\+
|
|
433
|
-
\+
|
|
429
|
+
\+ eyeprolog__member('(', Body),
|
|
430
|
+
\+ eyeprolog__member(')', Body),
|
|
431
|
+
\+ eyeprolog__member('[', Body),
|
|
432
|
+
\+ eyeprolog__member(']', Body),
|
|
433
|
+
\+ eyeprolog__member('+', Body),
|
|
434
|
+
\+ eyeprolog__member('*', Body).
|
|
434
435
|
|
|
435
436
|
eyeprolog__has_capture([capture(_,_,_)|_]).
|
|
436
437
|
eyeprolog__has_capture([_|Ts]) :- eyeprolog__has_capture(Ts).
|
|
@@ -449,7 +450,7 @@ eyeprolog__regex_tokens([capture(Name,Kind,yes)|Ts], Chars, Rest, [capture(Name,
|
|
|
449
450
|
eyeprolog__regex_tokens([capture(_,_,yes)|Ts], Chars, Rest, Captures) :-
|
|
450
451
|
eyeprolog__regex_tokens(Ts, Chars, Rest, Captures).
|
|
451
452
|
|
|
452
|
-
eyeprolog__capture_match(literal(Literal), Chars, Rest, Literal) :-
|
|
453
|
+
eyeprolog__capture_match(literal(Literal), Chars, Rest, Literal) :- eyeprolog__append(Literal, Rest, Chars).
|
|
453
454
|
eyeprolog__capture_match(word_plus, Chars, Rest, Value) :- eyeprolog__take_class_plus(word, Chars, Value, Rest).
|
|
454
455
|
eyeprolog__capture_match(nonspace_plus, Chars, Rest, Value) :- eyeprolog__take_class_plus(nonspace, Chars, Value, Rest).
|
|
455
456
|
eyeprolog__capture_match(class_plus(Class), Chars, Rest, Value) :- eyeprolog__take_class_plus(class(Class), Chars, Value, Rest).
|
|
@@ -494,33 +495,30 @@ eyeprolog__captures_context([capture(Name,Value)|Rest], (Term,Context)) :-
|
|
|
494
495
|
|
|
495
496
|
% ---------- text/list processing ----------
|
|
496
497
|
|
|
497
|
-
append([], Ys, Ys).
|
|
498
|
-
append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).
|
|
499
|
-
|
|
500
498
|
string_concat(A, B, Whole) :-
|
|
501
499
|
nonvar(A), nonvar(B),
|
|
502
500
|
!,
|
|
503
501
|
eyeprolog__text_chars(A, AC),
|
|
504
502
|
eyeprolog__text_chars(B, BC),
|
|
505
|
-
|
|
503
|
+
eyeprolog__append(AC, BC, WC),
|
|
506
504
|
eyeprolog__text_chars(Whole, WC).
|
|
507
505
|
string_concat(A, B, Whole) :-
|
|
508
506
|
nonvar(Whole),
|
|
509
507
|
eyeprolog__text_chars(Whole, WC),
|
|
510
|
-
|
|
508
|
+
eyeprolog__append(AC, BC, WC),
|
|
511
509
|
eyeprolog__text_chars(A, AC),
|
|
512
510
|
eyeprolog__text_chars(B, BC).
|
|
513
511
|
|
|
514
512
|
contains(Text, Needle) :-
|
|
515
513
|
eyeprolog__text_chars(Text, TextChars),
|
|
516
514
|
eyeprolog__text_chars(Needle, NeedleChars),
|
|
517
|
-
|
|
518
|
-
|
|
515
|
+
eyeprolog__append(_, Tail, TextChars),
|
|
516
|
+
eyeprolog__append(NeedleChars, _, Tail),
|
|
519
517
|
!.
|
|
520
518
|
|
|
521
519
|
matches(Text, Pattern) :-
|
|
522
520
|
split(Pattern, '|', Alternatives),
|
|
523
|
-
|
|
521
|
+
eyeprolog__member(Needle, Alternatives),
|
|
524
522
|
contains(Text, Needle),
|
|
525
523
|
!.
|
|
526
524
|
|
|
@@ -533,8 +531,8 @@ split(Text, Separator, Parts) :-
|
|
|
533
531
|
eyeprolog__split_chars(Chars, [], Parts) :- eyeprolog__split_each_char(Chars, Parts).
|
|
534
532
|
eyeprolog__split_chars(Chars, Separator, [Prefix|Parts]) :-
|
|
535
533
|
Separator \= [],
|
|
536
|
-
|
|
537
|
-
|
|
534
|
+
eyeprolog__append(Prefix, Tail, Chars),
|
|
535
|
+
eyeprolog__append(Separator, Rest, Tail),
|
|
538
536
|
!,
|
|
539
537
|
eyeprolog__split_chars(Rest, Separator, Parts).
|
|
540
538
|
eyeprolog__split_chars(Chars, Separator, [Chars]) :- Separator \= [].
|
|
@@ -586,9 +584,9 @@ eyeprolog__upper_code(Code, Code) :- (Code < 97 ; Code > 122).
|
|
|
586
584
|
trim(Text, Trimmed) :-
|
|
587
585
|
eyeprolog__text_chars(Text, Chars),
|
|
588
586
|
eyeprolog__drop_space(Chars, Left),
|
|
589
|
-
|
|
587
|
+
eyeprolog__reverse(Left, [], Reversed),
|
|
590
588
|
eyeprolog__drop_space(Reversed, RightReversed),
|
|
591
|
-
|
|
589
|
+
eyeprolog__reverse(RightReversed, [], TrimmedChars),
|
|
592
590
|
eyeprolog__text_chars(Trimmed, TrimmedChars).
|
|
593
591
|
|
|
594
592
|
eyeprolog__drop_space([C|Cs], Out) :-
|
|
@@ -632,15 +630,15 @@ eyeprolog__term_chars(Term, Chars) :- atom(Term), atom_chars(Term, Chars).
|
|
|
632
630
|
eyeprolog__term_chars([], ['[',']']).
|
|
633
631
|
eyeprolog__term_chars([H|T], Chars) :-
|
|
634
632
|
eyeprolog__list_term_chars([H|T], Body),
|
|
635
|
-
|
|
633
|
+
eyeprolog__append(['['|Body], [']'], Chars).
|
|
636
634
|
eyeprolog__term_chars(Term, Chars) :-
|
|
637
635
|
compound(Term),
|
|
638
636
|
Term \= [_|_],
|
|
639
637
|
Term =.. [Name|Args],
|
|
640
638
|
atom_chars(Name, NameChars),
|
|
641
639
|
eyeprolog__term_args_chars(Args, ArgsChars),
|
|
642
|
-
|
|
643
|
-
|
|
640
|
+
eyeprolog__append(NameChars, ['('|ArgsChars], A),
|
|
641
|
+
eyeprolog__append(A, [')'], Chars).
|
|
644
642
|
|
|
645
643
|
eyeprolog__list_term_chars([H], Chars) :-
|
|
646
644
|
eyeprolog__term_chars(H, Chars).
|
|
@@ -648,19 +646,19 @@ eyeprolog__list_term_chars([H|T], Chars) :-
|
|
|
648
646
|
T = [_|_],
|
|
649
647
|
eyeprolog__term_chars(H, HC),
|
|
650
648
|
eyeprolog__list_term_chars(T, TC),
|
|
651
|
-
|
|
649
|
+
eyeprolog__append(HC, [',',' '|TC], Chars).
|
|
652
650
|
eyeprolog__list_term_chars([H|T], Chars) :-
|
|
653
651
|
T \= [], T \= [_|_],
|
|
654
652
|
eyeprolog__term_chars(H, HC),
|
|
655
653
|
eyeprolog__term_chars(T, TC),
|
|
656
|
-
|
|
654
|
+
eyeprolog__append(HC, [' ','|',' '|TC], Chars).
|
|
657
655
|
|
|
658
656
|
eyeprolog__term_args_chars([A], Chars) :- eyeprolog__term_chars(A, Chars).
|
|
659
657
|
eyeprolog__term_args_chars([A|As], Chars) :-
|
|
660
658
|
As \= [],
|
|
661
659
|
eyeprolog__term_chars(A, AC),
|
|
662
660
|
eyeprolog__term_args_chars(As, Rest),
|
|
663
|
-
|
|
661
|
+
eyeprolog__append(AC, [',',' '|Rest], Chars).
|
|
664
662
|
|
|
665
663
|
join([], _, Out) :- eyeprolog__text_chars(Out, []).
|
|
666
664
|
join([Item|Items], Separator, Out) :-
|
|
@@ -672,8 +670,8 @@ join([Item|Items], Separator, Out) :-
|
|
|
672
670
|
eyeprolog__join_chars([], _, Chars, Chars).
|
|
673
671
|
eyeprolog__join_chars([Item|Items], Separator, Prefix, Out) :-
|
|
674
672
|
eyeprolog__atomic_chars(Item, ItemChars),
|
|
675
|
-
|
|
676
|
-
|
|
673
|
+
eyeprolog__append(Prefix, Separator, A),
|
|
674
|
+
eyeprolog__append(A, ItemChars, B),
|
|
677
675
|
eyeprolog__join_chars(Items, Separator, B, Out).
|
|
678
676
|
|
|
679
677
|
substring(Text, Start, Count, Out) :-
|
|
@@ -685,21 +683,6 @@ substring(Text, Start, Count, Out) :-
|
|
|
685
683
|
|
|
686
684
|
% ---------- list relations ----------
|
|
687
685
|
|
|
688
|
-
member(X, [X|_]).
|
|
689
|
-
member(X, [_|Xs]) :- member(X, Xs).
|
|
690
|
-
|
|
691
|
-
select(X, [X|Xs], Xs).
|
|
692
|
-
select(X, [Y|Ys], [Y|Zs]) :- select(X, Ys, Zs).
|
|
693
|
-
|
|
694
|
-
last([X], X).
|
|
695
|
-
last([_|Xs], X) :- last(Xs, X).
|
|
696
|
-
|
|
697
|
-
nth0(0, [X|_], X).
|
|
698
|
-
nth0(N, [_|Xs], X) :- var(N), nth0(N0, Xs, X), N is N0 + 1.
|
|
699
|
-
nth0(N, [_|Xs], X) :- nonvar(N), N > 0, N1 is N - 1, nth0(N1, Xs, X).
|
|
700
|
-
|
|
701
|
-
nth1(N, List, X) :- nth0(N0, List, X), N is N0 + 1.
|
|
702
|
-
|
|
703
686
|
set_nth0(0, [_|Xs], X, [X|Xs]).
|
|
704
687
|
set_nth0(N, [Y|Ys], X, [Y|Zs]) :- N > 0, N1 is N - 1, set_nth0(N1, Ys, X, Zs).
|
|
705
688
|
|
|
@@ -711,33 +694,25 @@ drop(N, [_|Xs], Ys) :- N > 0, N1 is N - 1, drop(N1, Xs, Ys).
|
|
|
711
694
|
|
|
712
695
|
slice(Start, Count, List, Slice) :- drop(Start, List, Tail), take(Count, Tail, Slice).
|
|
713
696
|
|
|
714
|
-
reverse(List, Reversed) :- eyeprolog__reverse(List, [], Reversed).
|
|
715
697
|
eyeprolog__reverse([], Acc, Acc).
|
|
716
698
|
eyeprolog__reverse([X|Xs], Acc, Out) :- eyeprolog__reverse(Xs, [X|Acc], Out).
|
|
717
699
|
|
|
718
|
-
length(List, Length) :- nonvar(List), eyeprolog__length_count(List, 0, Length).
|
|
719
|
-
length(List, Length) :- var(List), integer(Length), Length >= 0, eyeprolog__length_make(Length, List).
|
|
720
|
-
|
|
721
700
|
eyeprolog__length_count([], N, N).
|
|
722
701
|
eyeprolog__length_count([_|Xs], N0, N) :- N1 is N0 + 1, eyeprolog__length_count(Xs, N1, N).
|
|
723
702
|
eyeprolog__length_make(0, []).
|
|
724
703
|
eyeprolog__length_make(N, [_|Xs]) :- N > 0, N1 is N - 1, eyeprolog__length_make(N1, Xs).
|
|
725
704
|
|
|
726
|
-
sum_list(List, Sum) :- eyeprolog__sum_list(List, 0, Sum).
|
|
727
705
|
eyeprolog__sum_list([], Sum, Sum).
|
|
728
706
|
eyeprolog__sum_list([X|Xs], Acc, Sum) :- Next is Acc + X, eyeprolog__sum_list(Xs, Next, Sum).
|
|
729
707
|
|
|
730
|
-
min_list([X|Xs], Min) :- eyeprolog__min_list(Xs, X, Min).
|
|
731
708
|
eyeprolog__min_list([], Min, Min).
|
|
732
709
|
eyeprolog__min_list([X|Xs], Current, Min) :- X @< Current, eyeprolog__min_list(Xs, X, Min).
|
|
733
710
|
eyeprolog__min_list([X|Xs], Current, Min) :- X @>= Current, eyeprolog__min_list(Xs, Current, Min).
|
|
734
711
|
|
|
735
|
-
max_list([X|Xs], Max) :- eyeprolog__max_list(Xs, X, Max).
|
|
736
712
|
eyeprolog__max_list([], Max, Max).
|
|
737
713
|
eyeprolog__max_list([X|Xs], Current, Max) :- X @> Current, eyeprolog__max_list(Xs, X, Max).
|
|
738
714
|
eyeprolog__max_list([X|Xs], Current, Max) :- X @=< Current, eyeprolog__max_list(Xs, Current, Max).
|
|
739
715
|
|
|
740
|
-
list_to_set(List, Set) :- eyeprolog__list_to_set(List, [], Set).
|
|
741
716
|
eyeprolog__list_to_set([], _, []).
|
|
742
717
|
eyeprolog__list_to_set([X|Xs], Seen, Set) :-
|
|
743
718
|
eyeprolog__identical_member(X, Seen),
|
|
@@ -749,7 +724,7 @@ eyeprolog__list_to_set([X|Xs], Seen, [X|Set]) :-
|
|
|
749
724
|
eyeprolog__identical_member(X, [Y|_]) :- X == Y.
|
|
750
725
|
eyeprolog__identical_member(X, [_|Ys]) :- eyeprolog__identical_member(X, Ys).
|
|
751
726
|
|
|
752
|
-
|
|
727
|
+
sort_unique(List, Sorted) :- eyeprolog__sort(List, [], Sorted).
|
|
753
728
|
eyeprolog__sort([], Sorted, Sorted).
|
|
754
729
|
eyeprolog__sort([X|Xs], Acc, Sorted) :-
|
|
755
730
|
eyeprolog__insert_sorted(X, Acc, Next),
|
|
@@ -762,11 +737,9 @@ eyeprolog__insert_sorted(X, [Y|Ys], [Y|Zs]) :- X @> Y, eyeprolog__insert_sorted(
|
|
|
762
737
|
|
|
763
738
|
% ---------- aggregation ----------
|
|
764
739
|
|
|
765
|
-
countall(Goal, Count) :- findall(1, Goal, Ones), length(Ones, Count).
|
|
766
|
-
|
|
767
740
|
sumall(Expression, Goal, Sum) :-
|
|
768
741
|
findall(Value, (Goal, Value is Expression), Values),
|
|
769
|
-
|
|
742
|
+
eyeprolog__sum_list(Values, 0, Sum).
|
|
770
743
|
|
|
771
744
|
aggregate_min(Key, Value, Goal, BestKey, BestValue) :-
|
|
772
745
|
findall(pair(Key, Value), Goal, Pairs),
|
|
@@ -775,7 +748,7 @@ aggregate_min(Key, Value, Goal, BestKey, BestValue) :-
|
|
|
775
748
|
eyeprolog__aggregate_min([pair(K,V)|Pairs], BestKey, BestValue) :-
|
|
776
749
|
eyeprolog__aggregate_min_rest(Pairs, K, V, BestKey, BestValue).
|
|
777
750
|
eyeprolog__aggregate_min_rest([], K, V, K, V).
|
|
778
|
-
eyeprolog__aggregate_min_rest([pair(K,V)|Pairs], CK,
|
|
751
|
+
eyeprolog__aggregate_min_rest([pair(K,V)|Pairs], CK, _, BK, BV) :-
|
|
779
752
|
K @< CK,
|
|
780
753
|
eyeprolog__aggregate_min_rest(Pairs, K, V, BK, BV).
|
|
781
754
|
eyeprolog__aggregate_min_rest([pair(K,_)|Pairs], CK, CV, BK, BV) :-
|
|
@@ -789,7 +762,7 @@ aggregate_max(Key, Value, Goal, BestKey, BestValue) :-
|
|
|
789
762
|
eyeprolog__aggregate_max([pair(K,V)|Pairs], BestKey, BestValue) :-
|
|
790
763
|
eyeprolog__aggregate_max_rest(Pairs, K, V, BestKey, BestValue).
|
|
791
764
|
eyeprolog__aggregate_max_rest([], K, V, K, V).
|
|
792
|
-
eyeprolog__aggregate_max_rest([pair(K,V)|Pairs], CK,
|
|
765
|
+
eyeprolog__aggregate_max_rest([pair(K,V)|Pairs], CK, _, BK, BV) :-
|
|
793
766
|
K @> CK,
|
|
794
767
|
eyeprolog__aggregate_max_rest(Pairs, K, V, BK, BV).
|
|
795
768
|
eyeprolog__aggregate_max_rest([pair(K,_)|Pairs], CK, CV, BK, BV) :-
|
package/src/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export {
|
|
|
18
18
|
eyePrologLibraryIndicators,
|
|
19
19
|
eyePrologNativeLibraryIndicators,
|
|
20
20
|
eyePrologPortableLibraryIndicators,
|
|
21
|
-
} from './eyeprolog-
|
|
21
|
+
} from './eyeprolog-autoload.js';
|
|
22
22
|
export { StreamManager } from './io.js';
|
|
23
23
|
|
|
24
24
|
import { ATOM, COMPOUND, VAR, Env, copyResolved, termIsGround, termToString } from './term.js';
|
|
@@ -26,7 +26,7 @@ import { Program } from './program.js';
|
|
|
26
26
|
import { Solver } from './solver.js';
|
|
27
27
|
import { whyNoProof, whyProof } from './explain.js';
|
|
28
28
|
import { HaltSignal, PrologError } from './iso.js';
|
|
29
|
-
import { getEyePrologRegistry } from './eyeprolog-
|
|
29
|
+
import { getEyePrologRegistry } from './eyeprolog-autoload.js';
|
|
30
30
|
import { parseGoalText } from './parser.js';
|
|
31
31
|
|
|
32
32
|
export function run(source, options = {}) {
|
package/src/playground-worker.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// Browser worker entry used by playground.html.
|
|
2
2
|
// Keep this module free of Node-only imports: it is fetched directly by the
|
|
3
3
|
// browser and is also exercised by test/run-playground.mjs.
|
|
4
|
-
import { run } from './index.js?playground=
|
|
5
|
-
import { createEyePrologRegistry } from './eyeprolog-library.js?playground=20260803c';
|
|
4
|
+
import { createEyePrologRegistry, run } from './index.js?playground=20260807b';
|
|
6
5
|
|
|
7
6
|
const registry = createEyePrologRegistry();
|
|
8
7
|
|
|
@@ -42,4 +41,5 @@ function defaultNow() {
|
|
|
42
41
|
|
|
43
42
|
if (typeof self !== 'undefined' && typeof self.postMessage === 'function') {
|
|
44
43
|
installPlaygroundWorker(self);
|
|
44
|
+
self.postMessage({ type: 'ready' });
|
|
45
45
|
}
|
package/src/solver.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
numberTerm, numberTextFromDouble, termIsGround, termToString, unify, variantTerms,
|
|
6
6
|
} from './term.js';
|
|
7
7
|
import { PrologError } from './iso.js';
|
|
8
|
-
import { ensureEyePrologLibrary, getEyePrologRegistry } from './eyeprolog-
|
|
8
|
+
import { ensureEyePrologLibrary, getEyePrologRegistry } from './eyeprolog-autoload.js';
|
|
9
9
|
import { selectClauseCandidates, selectClauseCandidatesForValues, selectGroundClauseCandidates } from './program.js';
|
|
10
10
|
import { StreamManager } from './io.js';
|
|
11
11
|
|
|
@@ -6,6 +6,5 @@ answer(set_nth0, X) :- set_nth0(1, [a, b, c], x, X).
|
|
|
6
6
|
answer(reverse, X) :- reverse([a, b, c], X).
|
|
7
7
|
answer(length, N) :- length([a, b, c], N).
|
|
8
8
|
answer(findall, X) :- findall(N, between(1, 3, N), X).
|
|
9
|
-
answer(sort, X) :-
|
|
9
|
+
answer(sort, X) :- sort_unique([b, a, b], X).
|
|
10
10
|
%% goal: answer(X0, X1)
|
|
11
|
-
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
answer(length_empty, X) :- length([], X).
|
|
5
5
|
answer(length_nested, X) :- length([[a], [b, c], []], X).
|
|
6
6
|
answer(reverse_atoms, X) :- reverse([a, b, c], X).
|
|
7
|
-
answer(sort_numbers, X) :-
|
|
8
|
-
answer(sort_mixed_terms, X) :-
|
|
7
|
+
answer(sort_numbers, X) :- sort_unique([3, 1, 2, 1], X).
|
|
8
|
+
answer(sort_mixed_terms, X) :- sort_unique([b, 2, a, 1, pair(a), "s"], X).
|
|
9
9
|
answer(reverse_empty, X) :- reverse([], X).
|
package/test/run-playground.mjs
CHANGED
|
@@ -22,6 +22,8 @@ export async function runPlayground(reporter = new TestReporter()) {
|
|
|
22
22
|
const html = fs.readFileSync(path.join(packageRoot, 'playground.html'), 'utf8');
|
|
23
23
|
assertIncludes(html, "new URL('./src/playground-worker.js?playground=", 'playground worker URL');
|
|
24
24
|
assertIncludes(html, "new Worker(workerUrl, { type: 'module' })", 'module worker construction');
|
|
25
|
+
assertIncludes(html, "event.data?.type === 'ready'", 'worker readiness handshake');
|
|
26
|
+
assertIncludes(html, 'did not finish loading', 'worker startup timeout');
|
|
25
27
|
assertNotIncludes(html, 'URL.createObjectURL(new Blob([workerCode]', 'inline blob worker');
|
|
26
28
|
});
|
|
27
29
|
|
|
@@ -80,6 +82,8 @@ export async function runPlayground(reporter = new TestReporter()) {
|
|
|
80
82
|
['playground.html', 'text/html'],
|
|
81
83
|
['src/playground-worker.js', 'text/javascript'],
|
|
82
84
|
['src/index.js', 'text/javascript'],
|
|
85
|
+
['src/eyeprolog-library.pl', 'text/plain'],
|
|
86
|
+
['src/eyeprolog-common-library.pl', 'text/plain'],
|
|
83
87
|
['examples/socrates.pl', 'text/plain'],
|
|
84
88
|
];
|
|
85
89
|
for (const [relative, contentType] of expected) {
|
|
@@ -94,7 +98,7 @@ export async function runPlayground(reporter = new TestReporter()) {
|
|
|
94
98
|
await withStaticServer(async (baseUrl) => {
|
|
95
99
|
const modules = await crawlModuleGraph(new URL('src/playground-worker.js?playground=test', baseUrl));
|
|
96
100
|
assert(modules.size >= 10, `expected a substantial worker module graph, got ${modules.size}`);
|
|
97
|
-
assert([...modules].some((url) => url.includes('/src/eyeprolog-
|
|
101
|
+
assert([...modules].some((url) => url.includes('/src/eyeprolog-autoload.js')), 'EyeProlog autoloader missing from worker graph');
|
|
98
102
|
assert([...modules].some((url) => url.includes('/src/solver.js')), 'solver missing from worker graph');
|
|
99
103
|
});
|
|
100
104
|
});
|
package/test/run-regression.mjs
CHANGED
|
@@ -584,7 +584,7 @@ function documentationSyncCases() {
|
|
|
584
584
|
'[Book — *The Art of EyeProlog*](https://eyereasoner.github.io/eyeprolog/the-art-of-eyeprolog)',
|
|
585
585
|
'README links to the book',
|
|
586
586
|
);
|
|
587
|
-
for (const filename of ['src/iso.js', 'src/eyeprolog-
|
|
587
|
+
for (const filename of ['src/iso.js', 'src/eyeprolog-autoload.js', 'src/playground-worker.js']) {
|
|
588
588
|
assertEqual(fs.existsSync(path.join(packageRoot, filename)), true, `${filename} exists`);
|
|
589
589
|
assertIncludes(book, filename, `book documents ${filename}`);
|
|
590
590
|
}
|
|
@@ -1047,7 +1047,8 @@ open(X) :- candidate(X), \\+ closed(X).
|
|
|
1047
1047
|
assertEqual(betweenGenerator.cutRecursive, true, 'portable between generator has deterministic recursive control');
|
|
1048
1048
|
assertEqual(betweenGenerator.tabled, false, 'portable between generator avoids suffix answer tables');
|
|
1049
1049
|
assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'eyeprolog-library.pl')), true, 'portable Prolog source exists');
|
|
1050
|
-
assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'eyeprolog-
|
|
1050
|
+
assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'eyeprolog-autoload.js')), true, 'portable source autoloader exists');
|
|
1051
|
+
assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'eyeprolog-common-library.pl')), true, 'pure-Prolog common library exists');
|
|
1051
1052
|
assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'library-source.js')), false, 'duplicate source loader is absent');
|
|
1052
1053
|
assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'portable-library.js')), false, 'obsolete duplicate module remains absent');
|
|
1053
1054
|
assertEqual(run(program, { goal: 'answer(X)' }).stdout, 'answer([a, b]).\n', 'autoloaded append execution');
|
|
@@ -1058,7 +1059,9 @@ open(X) :- candidate(X), \\+ closed(X).
|
|
|
1058
1059
|
name: 'portable library executes against the ISO-only registry',
|
|
1059
1060
|
run: () => {
|
|
1060
1061
|
const portableSource = fs.readFileSync(path.join(packageRoot, 'src', 'eyeprolog-library.pl'), 'utf8');
|
|
1062
|
+
const commonSource = fs.readFileSync(path.join(packageRoot, 'src', 'eyeprolog-common-library.pl'), 'utf8');
|
|
1061
1063
|
const program = Program.parse(`${portableSource}
|
|
1064
|
+
${commonSource}
|
|
1062
1065
|
portable_check(A, B, C) :- lowercase('HELLO', A), replace('banana', 'na', 'NA', B), append([x], [y], C).
|
|
1063
1066
|
`);
|
|
1064
1067
|
const solver = new Solver(program, { registry: createDefaultRegistry() });
|
|
@@ -1712,7 +1715,7 @@ function playgroundStaticIssues() {
|
|
|
1712
1715
|
if (!html.includes("new URL('./src/playground-worker.js?playground=")) issues.push('playground must cache-bust its dedicated module worker');
|
|
1713
1716
|
if (!html.includes("new Worker(workerUrl, { type: 'module' })")) issues.push('playground must launch the dedicated module worker');
|
|
1714
1717
|
const workerText = fs.readFileSync(path.join(packageRoot, 'src', 'playground-worker.js'), 'utf8');
|
|
1715
|
-
|
|
1718
|
+
if (!workerText.includes("from './index.js?playground=") ||
|
|
1716
1719
|
!workerText.includes('createEyePrologRegistry') ||
|
|
1717
1720
|
!workerText.includes('executePlaygroundRequest')) {
|
|
1718
1721
|
issues.push('playground worker must install the EyeProlog library registry');
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -907,7 +907,7 @@ reverse_go([X | Xs], Acc, Reversed) :-
|
|
|
907
907
|
|
|
908
908
|
No mutation occurs; every call receives a new term. EyeProlog also includes
|
|
909
909
|
`member/2`, `append/3`, `select/3`, `nth0/3`, `reverse/2`, `length/2`,
|
|
910
|
-
`
|
|
910
|
+
`sort_unique/2`, slicing helpers, and numeric summaries. Improper lists such as
|
|
911
911
|
`[a | Tail]` are valid terms, but operations requiring a proper finite list
|
|
912
912
|
fail unless the tail is `[]`.
|
|
913
913
|
|
|
@@ -1109,7 +1109,7 @@ Choose a collector from the question, not from convenience:
|
|
|
1109
1109
|
Counting solutions is not necessarily counting distinct domain objects: two
|
|
1110
1110
|
proofs may resolve the visible value in the same way. When identity matters,
|
|
1111
1111
|
collect the identifying template and deliberately canonicalize it with
|
|
1112
|
-
`
|
|
1112
|
+
`sort_unique/2`; when derivation multiplicity matters, retain the duplicates. Making
|
|
1113
1113
|
that decision explicit prevents a database-style summary from silently
|
|
1114
1114
|
changing the question.
|
|
1115
1115
|
|
|
@@ -1709,11 +1709,12 @@ truncate search; it does not prove that no further answer exists.
|
|
|
1709
1709
|
### Implementation boundary
|
|
1710
1710
|
|
|
1711
1711
|
The source layout mirrors the language boundary. `src/iso.js` contains the
|
|
1712
|
-
isolated ISO processor predicates and registry. `src/eyeprolog-library.pl`
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1712
|
+
isolated ISO processor predicates and registry. `src/eyeprolog-library.pl`
|
|
1713
|
+
contains collision-free, self-contained portable extensions.
|
|
1714
|
+
`src/eyeprolog-common-library.pl` supplies common list and aggregation
|
|
1715
|
+
predicates that Trealla, Scryer, and many other systems already provide. The
|
|
1716
|
+
paired `src/eyeprolog-autoload.js` module autoloads both pure-Prolog files in
|
|
1717
|
+
Node and the browser.
|
|
1717
1718
|
The RDF tools emit IRI and literal lexical values as ISO atoms, matching the
|
|
1718
1719
|
portable text API without a host representation adapter.
|
|
1719
1720
|
|
|
@@ -5254,14 +5255,17 @@ so side effects occur in Prolog execution order.
|
|
|
5254
5255
|
|
|
5255
5256
|
EyeProlog exposes **50 library predicate indicators** in addition to the 115
|
|
5256
5257
|
indicators in its isolated ISO profile. **All 50 are ordinary Prolog clauses**
|
|
5257
|
-
|
|
5258
|
+
across `src/eyeprolog-library.pl` and `src/eyeprolog-common-library.pl`; none
|
|
5259
|
+
is a native host predicate. The resulting
|
|
5258
5260
|
normal EyeProlog language surface is therefore **165 public predicate
|
|
5259
5261
|
indicators**. Internally, the runtime registry contains only the 115 ISO
|
|
5260
5262
|
definitions; the EyeProlog relations are autoloaded source clauses.
|
|
5261
5263
|
|
|
5262
|
-
The
|
|
5263
|
-
EyeProlog registry. `src/eyeprolog-
|
|
5264
|
-
or through `fetch()` in the browser and performs the autoload.
|
|
5264
|
+
The two Prolog files are autoloaded once into every `Program` used with the
|
|
5265
|
+
EyeProlog registry. `src/eyeprolog-autoload.js` loads them from the package in
|
|
5266
|
+
Node or through `fetch()` in the browser and performs the autoload. External
|
|
5267
|
+
Prolog systems load only `src/eyeprolog-library.pl`; this avoids redefining
|
|
5268
|
+
their protected or preloaded list predicates.
|
|
5265
5269
|
The isolated ISO-only registry remains
|
|
5266
5270
|
available through `createDefaultRegistry()` and `getDefaultRegistry()` for
|
|
5267
5271
|
conformance work and advanced embedders. Source clauses sharing a portable
|
|
@@ -5276,18 +5280,18 @@ standard fallback relation.
|
|
|
5276
5280
|
| `nth0/3`, `nth1/3`, `set_nth0/4`, `take/3`, `drop/3`, `slice/4` |
|
|
5277
5281
|
| `reverse/2`, `length/2`, `sum_list/2` |
|
|
5278
5282
|
| `min_list/2`, `max_list/2` |
|
|
5279
|
-
| `list_to_set/2`, `
|
|
5283
|
+
| `list_to_set/2`, `sort_unique/2` |
|
|
5280
5284
|
| `string_concat/3`, `contains/2`, `matches/2` |
|
|
5281
5285
|
| `join/3`, `substring/4` |
|
|
5282
5286
|
| `countall/2`, `sumall/3` |
|
|
5283
5287
|
| `aggregate_min/5`, `aggregate_max/5` |
|
|
5284
5288
|
| `between/3`, `smallest_divisor_from/3`, `random/3` |
|
|
5289
|
+
| `apply/3` |
|
|
5285
5290
|
| `maplist/3` |
|
|
5286
5291
|
| `acos/2`, `asin/2`, `atan2/3`, `tan/2` |
|
|
5287
5292
|
| `lt/2`, `le/2`, `gt/2`, `ge/2` |
|
|
5288
5293
|
| `uuid/3`, `difference/3` |
|
|
5289
5294
|
| `matches/3` |
|
|
5290
|
-
| `call/3` |
|
|
5291
5295
|
| `split/3`, `replace/4` |
|
|
5292
5296
|
| `lowercase/2`, `uppercase/2`, `trim/2` |
|
|
5293
5297
|
| `number_string/2`, `atom_string/2`, `term_string/2` |
|
|
@@ -5374,7 +5378,8 @@ zero-based, nonnegative safe integers.
|
|
|
5374
5378
|
| `\+ member(+Item,+List)` | Succeeds only when `Item` does not unify with any member. Use it after binding the item and list. |
|
|
5375
5379
|
| `nth0(?Index,+List,?Item)` | Checks a bound zero-based index or enumerates indexes and their items. |
|
|
5376
5380
|
| `nth1(?Index,+List,?Item)` | Checks a bound one-based index or enumerates one-based indexes and items. |
|
|
5377
|
-
| `
|
|
5381
|
+
| `apply(+Closure,+A,+B)` | Extends a closure with two arguments using ISO `=../2`, then invokes it through ISO `call/1`. |
|
|
5382
|
+
| `maplist(+Closure,+List1,?List2)` | Applies a two-argument closure pairwise through `apply/3`; partially applied compound closures are supported. |
|
|
5378
5383
|
| `[Head|Tail] = List` | Decomposes a nonempty list directly with ISO unification; no library wrapper is needed. |
|
|
5379
5384
|
| `set_nth0(+Index,+List,+Item,-NewList)` | Replaces one existing position without mutating the input list. |
|
|
5380
5385
|
| `last(+List,?Last)` | Returns the final element of a nonempty proper list. |
|
|
@@ -5385,7 +5390,7 @@ zero-based, nonnegative safe integers.
|
|
|
5385
5390
|
| `sum_list(+List,-Sum)` | Sums numeric elements with ISO `is/2`. The empty sum is `0`; invalid arithmetic raises the corresponding ISO error. |
|
|
5386
5391
|
| `min_list(+List,-Min)`, `max_list(+List,-Max)` | Select by EyeProlog term order, not numeric coercion. Empty lists fail. |
|
|
5387
5392
|
| `list_to_set(+List,-Set)` | Removes later structural duplicates while preserving first-occurrence order. |
|
|
5388
|
-
| `
|
|
5393
|
+
| `sort_unique(+List,-Set)` | Sorts by standard term order and removes structural duplicates without colliding with engines that protect `sort/2`. |
|
|
5389
5394
|
|
|
5390
5395
|
```eyeprolog
|
|
5391
5396
|
answer(split, pair(Prefix, Suffix)) :-
|
|
@@ -5739,7 +5744,7 @@ mode at a time.
|
|
|
5739
5744
|
|
|
5740
5745
|
| Program | Standard facility | Checked answer |
|
|
5741
5746
|
| --- | --- | --- |
|
|
5742
|
-
| [Combinatorics Findall Sort](https://github.com/eyereasoner/eyeprolog/blob/main/examples/combinatorics-findall-sort.pl) | Eyelet-inspired combinations example using findall/3 and
|
|
5747
|
+
| [Combinatorics Findall Sort](https://github.com/eyereasoner/eyeprolog/blob/main/examples/combinatorics-findall-sort.pl) | Eyelet-inspired combinations example using findall/3 and `sort_unique/2`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/combinatorics-findall-sort.pl) |
|
|
5743
5748
|
| [Floating Point](https://github.com/eyereasoner/eyeprolog/blob/main/examples/floating-point.pl) | Floating-point arithmetic and comparisons. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/floating-point.pl) · [proof](https://github.com/eyereasoner/eyeprolog/blob/main/examples/proof/floating-point.pl) |
|
|
5744
5749
|
| [Atomic conversion](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-atomic-conversion.pl) | Atom splitting, character atoms, Unicode codes, and numeric parsing. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/iso-atomic-conversion.pl) |
|
|
5745
5750
|
| [Control and errors](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-control-and-errors.pl) | `call/1`, `once/1`, cut, if-then-else, `throw/1`, and `catch/3`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/iso-control-and-errors.pl) |
|
|
@@ -5817,7 +5822,7 @@ finite search space, and which constraint removes which branches?
|
|
|
5817
5822
|
|
|
5818
5823
|
| Program | Search design | Checked answer |
|
|
5819
5824
|
| --- | --- | --- |
|
|
5820
|
-
| [Dijkstra Findall Sort](https://github.com/eyereasoner/eyeprolog/blob/main/examples/dijkstra-findall-sort.pl) | Eyelet-inspired Dijkstra example using findall/3 and
|
|
5825
|
+
| [Dijkstra Findall Sort](https://github.com/eyereasoner/eyeprolog/blob/main/examples/dijkstra-findall-sort.pl) | Eyelet-inspired Dijkstra example using findall/3 and `sort_unique/2`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/dijkstra-findall-sort.pl) |
|
|
5821
5826
|
| [Dijkstra](https://github.com/eyereasoner/eyeprolog/blob/main/examples/dijkstra.pl) | Weighted path enumeration adapted from Eyeling dijkstra.n3. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/dijkstra.pl) |
|
|
5822
5827
|
| [DONALD + GERALD = ROBERT](https://github.com/eyereasoner/eyeprolog/blob/main/examples/donald-gerald-robert.pl) | All ten decimal digits are assigned to ten distinct letters. Right-to-left carry propagation cuts a naive 10! search space to one solution. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/donald-gerald-robert.pl) |
|
|
5823
5828
|
| [Enigma1225](https://github.com/eyereasoner/eyeprolog/blob/main/examples/enigma1225.pl) | New Scientist Enigma 1225, retaining the best board in one pass with `aggregate_max/5`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/enigma1225.pl) |
|