eyeprolog 0.5.11 → 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 +7 -7
- 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 +6 -6
- 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/examples/uuid.pl +4 -4
- package/package.json +1 -1
- package/playground.html +28 -8
- package/src/cli.js +1 -1
- package/src/explain.js +1 -1
- package/src/eyeprolog-autoload.js +133 -0
- package/src/eyeprolog-common-library.pl +44 -0
- package/src/eyeprolog-library.pl +113 -74
- package/src/index.js +2 -2
- package/src/playground-worker.js +2 -2
- package/src/solver.js +1 -1
- package/test/conformance/cases/builtins/extra_uuid_v4.pl +2 -2
- 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-examples.mjs +11 -19
- package/test/run-playground.mjs +5 -1
- package/test/run-regression.mjs +35 -19
- package/the-art-of-eyeprolog.md +37 -32
- package/src/library-source.js +0 -18
- package/src/library.js +0 -171
|
@@ -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
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
% This file is autoloaded for the EyeProlog runtime and is intentionally
|
|
4
4
|
% written against the project's ISO compatibility profile. Text-facing
|
|
5
5
|
% predicates accept ISO atoms or proper lists of one-character atoms; newly
|
|
6
|
-
% produced text defaults to atoms.
|
|
7
|
-
%
|
|
6
|
+
% produced text defaults to atoms. Every public predicate in this file uses
|
|
7
|
+
% only the project's ISO Prolog profile; none requires host support.
|
|
8
8
|
|
|
9
9
|
% ---------- text representation helpers ----------
|
|
10
10
|
|
|
@@ -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).
|
|
@@ -140,6 +141,72 @@ eyeprolog__between(Low, High, Value) :-
|
|
|
140
141
|
!,
|
|
141
142
|
eyeprolog__between(Next, High, Value).
|
|
142
143
|
|
|
144
|
+
% A Park-Miller generator with explicit state. Threading Seed into the next
|
|
145
|
+
% call makes a sequence reproducible without mutable runtime state. Schrage's
|
|
146
|
+
% method keeps every intermediate integer within the exact 32-bit range.
|
|
147
|
+
random(Seed0, Value, Seed) :-
|
|
148
|
+
integer(Seed0),
|
|
149
|
+
eyeprolog__random_normalize_seed(Seed0, Normalized),
|
|
150
|
+
High is Normalized // 44488,
|
|
151
|
+
Low is Normalized mod 44488,
|
|
152
|
+
Candidate is 48271 * Low - 3399 * High,
|
|
153
|
+
eyeprolog__random_wrap(Candidate, Seed),
|
|
154
|
+
Value is (Seed - 1) / 2147483646.
|
|
155
|
+
|
|
156
|
+
eyeprolog__random_normalize_seed(Seed, 1) :-
|
|
157
|
+
0 is Seed mod 2147483647,
|
|
158
|
+
!.
|
|
159
|
+
eyeprolog__random_normalize_seed(Seed, Normalized) :-
|
|
160
|
+
Normalized is Seed mod 2147483647.
|
|
161
|
+
|
|
162
|
+
eyeprolog__random_wrap(Candidate, Candidate) :- Candidate > 0, !.
|
|
163
|
+
eyeprolog__random_wrap(Candidate, Seed) :- Seed is Candidate + 2147483647.
|
|
164
|
+
|
|
165
|
+
% Seed-threaded UUID version 4 generation. The version and variant nibbles are
|
|
166
|
+
% fixed by RFC 9562; all other nibbles come from random/3. Reusing Seed0
|
|
167
|
+
% reproduces the same UUID, while threading Seed produces a deterministic
|
|
168
|
+
% sequence of different UUIDs.
|
|
169
|
+
uuid(Seed0, UUID, Seed) :-
|
|
170
|
+
eyeprolog__uuid_hex(8, Seed0, Group1, Seed1),
|
|
171
|
+
eyeprolog__uuid_hex(4, Seed1, Group2, Seed2),
|
|
172
|
+
eyeprolog__uuid_hex(3, Seed2, Group3, Seed3),
|
|
173
|
+
random(Seed3, _, Seed4),
|
|
174
|
+
VariantValue is 8 + Seed4 mod 4,
|
|
175
|
+
eyeprolog__hex_digit(VariantValue, Variant),
|
|
176
|
+
eyeprolog__uuid_hex(3, Seed4, Group4, Seed5),
|
|
177
|
+
eyeprolog__uuid_hex(12, Seed5, Group5, Seed),
|
|
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),
|
|
182
|
+
atom_chars(UUID, Chars).
|
|
183
|
+
|
|
184
|
+
eyeprolog__uuid_hex(0, Seed, [], Seed).
|
|
185
|
+
eyeprolog__uuid_hex(Count, Seed0, [Digit|Digits], Seed) :-
|
|
186
|
+
Count > 0,
|
|
187
|
+
random(Seed0, _, Seed1),
|
|
188
|
+
Value is Seed1 mod 16,
|
|
189
|
+
eyeprolog__hex_digit(Value, Digit),
|
|
190
|
+
NextCount is Count - 1,
|
|
191
|
+
eyeprolog__uuid_hex(NextCount, Seed1, Digits, Seed).
|
|
192
|
+
|
|
193
|
+
eyeprolog__hex_digit(0, '0').
|
|
194
|
+
eyeprolog__hex_digit(1, '1').
|
|
195
|
+
eyeprolog__hex_digit(2, '2').
|
|
196
|
+
eyeprolog__hex_digit(3, '3').
|
|
197
|
+
eyeprolog__hex_digit(4, '4').
|
|
198
|
+
eyeprolog__hex_digit(5, '5').
|
|
199
|
+
eyeprolog__hex_digit(6, '6').
|
|
200
|
+
eyeprolog__hex_digit(7, '7').
|
|
201
|
+
eyeprolog__hex_digit(8, '8').
|
|
202
|
+
eyeprolog__hex_digit(9, '9').
|
|
203
|
+
eyeprolog__hex_digit(10, a).
|
|
204
|
+
eyeprolog__hex_digit(11, b).
|
|
205
|
+
eyeprolog__hex_digit(12, c).
|
|
206
|
+
eyeprolog__hex_digit(13, d).
|
|
207
|
+
eyeprolog__hex_digit(14, e).
|
|
208
|
+
eyeprolog__hex_digit(15, f).
|
|
209
|
+
|
|
143
210
|
smallest_divisor_from(N, Start, Divisor) :-
|
|
144
211
|
N >= 0,
|
|
145
212
|
Start > 0,
|
|
@@ -149,7 +216,7 @@ smallest_divisor_from(N, Start, Divisor) :-
|
|
|
149
216
|
% in the exact range covered by bases 2,3,5,7,11,13,17. Above that range the
|
|
150
217
|
% implementation falls back to exact trial division, preserving semantics for
|
|
151
218
|
% arbitrary-size integers.
|
|
152
|
-
eyeprolog__smallest_divisor_fast(N,
|
|
219
|
+
eyeprolog__smallest_divisor_fast(N, _, N) :-
|
|
153
220
|
N >= 2,
|
|
154
221
|
N < 341550071728321,
|
|
155
222
|
eyeprolog__mr_prime(N),
|
|
@@ -261,8 +328,8 @@ eyeprolog__previous_month(Y, 1, PY, 12) :- PY is Y - 1.
|
|
|
261
328
|
|
|
262
329
|
eyeprolog__days_in_month(Y, 2, 29) :- eyeprolog__leap_year(Y).
|
|
263
330
|
eyeprolog__days_in_month(Y, 2, 28) :- \+ eyeprolog__leap_year(Y).
|
|
264
|
-
eyeprolog__days_in_month(_, M, 30) :-
|
|
265
|
-
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]).
|
|
266
333
|
|
|
267
334
|
eyeprolog__leap_year(Y) :- 0 is Y mod 400.
|
|
268
335
|
eyeprolog__leap_year(Y) :- Y mod 100 =\= 0, 0 is Y mod 4.
|
|
@@ -274,15 +341,15 @@ eyeprolog__format_duration(Y, M, D, Duration) :-
|
|
|
274
341
|
eyeprolog__duration_part(Y, 'Y', YC),
|
|
275
342
|
eyeprolog__duration_part(M, 'M', MC),
|
|
276
343
|
eyeprolog__duration_part(D, 'D', DC),
|
|
277
|
-
|
|
278
|
-
|
|
344
|
+
eyeprolog__append(['P'|YC], MC, A),
|
|
345
|
+
eyeprolog__append(A, DC, Chars),
|
|
279
346
|
eyeprolog__text_chars(Duration, Chars).
|
|
280
347
|
|
|
281
348
|
eyeprolog__duration_part(0, _, []).
|
|
282
349
|
eyeprolog__duration_part(N, Unit, Chars) :-
|
|
283
350
|
N =\= 0,
|
|
284
351
|
number_chars(N, Digits),
|
|
285
|
-
|
|
352
|
+
eyeprolog__append(Digits, [Unit], Chars).
|
|
286
353
|
|
|
287
354
|
% ---------- portable named-capture matcher ----------
|
|
288
355
|
%
|
|
@@ -317,8 +384,8 @@ eyeprolog__regex_parse(Chars0, Start, End, Tokens) :-
|
|
|
317
384
|
eyeprolog__strip_start_anchor(['^'|Cs], yes, Cs).
|
|
318
385
|
eyeprolog__strip_start_anchor(Cs, no, Cs).
|
|
319
386
|
|
|
320
|
-
eyeprolog__strip_end_anchor(Cs0, yes, Cs) :-
|
|
321
|
-
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).
|
|
322
389
|
|
|
323
390
|
eyeprolog__regex_tokens_parse([], []).
|
|
324
391
|
eyeprolog__regex_tokens_parse(['('|Cs], [capture(Name, Kind, Optional)|Tokens]) :-
|
|
@@ -359,12 +426,12 @@ eyeprolog__capture_kind(['['|Body], class_exact(Class, Count)) :-
|
|
|
359
426
|
Class \= [].
|
|
360
427
|
eyeprolog__capture_kind(Body, literal(Body)) :-
|
|
361
428
|
Body \= [],
|
|
362
|
-
\+
|
|
363
|
-
\+
|
|
364
|
-
\+
|
|
365
|
-
\+
|
|
366
|
-
\+
|
|
367
|
-
\+
|
|
429
|
+
\+ eyeprolog__member('(', Body),
|
|
430
|
+
\+ eyeprolog__member(')', Body),
|
|
431
|
+
\+ eyeprolog__member('[', Body),
|
|
432
|
+
\+ eyeprolog__member(']', Body),
|
|
433
|
+
\+ eyeprolog__member('+', Body),
|
|
434
|
+
\+ eyeprolog__member('*', Body).
|
|
368
435
|
|
|
369
436
|
eyeprolog__has_capture([capture(_,_,_)|_]).
|
|
370
437
|
eyeprolog__has_capture([_|Ts]) :- eyeprolog__has_capture(Ts).
|
|
@@ -383,7 +450,7 @@ eyeprolog__regex_tokens([capture(Name,Kind,yes)|Ts], Chars, Rest, [capture(Name,
|
|
|
383
450
|
eyeprolog__regex_tokens([capture(_,_,yes)|Ts], Chars, Rest, Captures) :-
|
|
384
451
|
eyeprolog__regex_tokens(Ts, Chars, Rest, Captures).
|
|
385
452
|
|
|
386
|
-
eyeprolog__capture_match(literal(Literal), Chars, Rest, Literal) :-
|
|
453
|
+
eyeprolog__capture_match(literal(Literal), Chars, Rest, Literal) :- eyeprolog__append(Literal, Rest, Chars).
|
|
387
454
|
eyeprolog__capture_match(word_plus, Chars, Rest, Value) :- eyeprolog__take_class_plus(word, Chars, Value, Rest).
|
|
388
455
|
eyeprolog__capture_match(nonspace_plus, Chars, Rest, Value) :- eyeprolog__take_class_plus(nonspace, Chars, Value, Rest).
|
|
389
456
|
eyeprolog__capture_match(class_plus(Class), Chars, Rest, Value) :- eyeprolog__take_class_plus(class(Class), Chars, Value, Rest).
|
|
@@ -428,33 +495,30 @@ eyeprolog__captures_context([capture(Name,Value)|Rest], (Term,Context)) :-
|
|
|
428
495
|
|
|
429
496
|
% ---------- text/list processing ----------
|
|
430
497
|
|
|
431
|
-
append([], Ys, Ys).
|
|
432
|
-
append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).
|
|
433
|
-
|
|
434
498
|
string_concat(A, B, Whole) :-
|
|
435
499
|
nonvar(A), nonvar(B),
|
|
436
500
|
!,
|
|
437
501
|
eyeprolog__text_chars(A, AC),
|
|
438
502
|
eyeprolog__text_chars(B, BC),
|
|
439
|
-
|
|
503
|
+
eyeprolog__append(AC, BC, WC),
|
|
440
504
|
eyeprolog__text_chars(Whole, WC).
|
|
441
505
|
string_concat(A, B, Whole) :-
|
|
442
506
|
nonvar(Whole),
|
|
443
507
|
eyeprolog__text_chars(Whole, WC),
|
|
444
|
-
|
|
508
|
+
eyeprolog__append(AC, BC, WC),
|
|
445
509
|
eyeprolog__text_chars(A, AC),
|
|
446
510
|
eyeprolog__text_chars(B, BC).
|
|
447
511
|
|
|
448
512
|
contains(Text, Needle) :-
|
|
449
513
|
eyeprolog__text_chars(Text, TextChars),
|
|
450
514
|
eyeprolog__text_chars(Needle, NeedleChars),
|
|
451
|
-
|
|
452
|
-
|
|
515
|
+
eyeprolog__append(_, Tail, TextChars),
|
|
516
|
+
eyeprolog__append(NeedleChars, _, Tail),
|
|
453
517
|
!.
|
|
454
518
|
|
|
455
519
|
matches(Text, Pattern) :-
|
|
456
520
|
split(Pattern, '|', Alternatives),
|
|
457
|
-
|
|
521
|
+
eyeprolog__member(Needle, Alternatives),
|
|
458
522
|
contains(Text, Needle),
|
|
459
523
|
!.
|
|
460
524
|
|
|
@@ -467,8 +531,8 @@ split(Text, Separator, Parts) :-
|
|
|
467
531
|
eyeprolog__split_chars(Chars, [], Parts) :- eyeprolog__split_each_char(Chars, Parts).
|
|
468
532
|
eyeprolog__split_chars(Chars, Separator, [Prefix|Parts]) :-
|
|
469
533
|
Separator \= [],
|
|
470
|
-
|
|
471
|
-
|
|
534
|
+
eyeprolog__append(Prefix, Tail, Chars),
|
|
535
|
+
eyeprolog__append(Separator, Rest, Tail),
|
|
472
536
|
!,
|
|
473
537
|
eyeprolog__split_chars(Rest, Separator, Parts).
|
|
474
538
|
eyeprolog__split_chars(Chars, Separator, [Chars]) :- Separator \= [].
|
|
@@ -520,9 +584,9 @@ eyeprolog__upper_code(Code, Code) :- (Code < 97 ; Code > 122).
|
|
|
520
584
|
trim(Text, Trimmed) :-
|
|
521
585
|
eyeprolog__text_chars(Text, Chars),
|
|
522
586
|
eyeprolog__drop_space(Chars, Left),
|
|
523
|
-
|
|
587
|
+
eyeprolog__reverse(Left, [], Reversed),
|
|
524
588
|
eyeprolog__drop_space(Reversed, RightReversed),
|
|
525
|
-
|
|
589
|
+
eyeprolog__reverse(RightReversed, [], TrimmedChars),
|
|
526
590
|
eyeprolog__text_chars(Trimmed, TrimmedChars).
|
|
527
591
|
|
|
528
592
|
eyeprolog__drop_space([C|Cs], Out) :-
|
|
@@ -566,15 +630,15 @@ eyeprolog__term_chars(Term, Chars) :- atom(Term), atom_chars(Term, Chars).
|
|
|
566
630
|
eyeprolog__term_chars([], ['[',']']).
|
|
567
631
|
eyeprolog__term_chars([H|T], Chars) :-
|
|
568
632
|
eyeprolog__list_term_chars([H|T], Body),
|
|
569
|
-
|
|
633
|
+
eyeprolog__append(['['|Body], [']'], Chars).
|
|
570
634
|
eyeprolog__term_chars(Term, Chars) :-
|
|
571
635
|
compound(Term),
|
|
572
636
|
Term \= [_|_],
|
|
573
637
|
Term =.. [Name|Args],
|
|
574
638
|
atom_chars(Name, NameChars),
|
|
575
639
|
eyeprolog__term_args_chars(Args, ArgsChars),
|
|
576
|
-
|
|
577
|
-
|
|
640
|
+
eyeprolog__append(NameChars, ['('|ArgsChars], A),
|
|
641
|
+
eyeprolog__append(A, [')'], Chars).
|
|
578
642
|
|
|
579
643
|
eyeprolog__list_term_chars([H], Chars) :-
|
|
580
644
|
eyeprolog__term_chars(H, Chars).
|
|
@@ -582,19 +646,19 @@ eyeprolog__list_term_chars([H|T], Chars) :-
|
|
|
582
646
|
T = [_|_],
|
|
583
647
|
eyeprolog__term_chars(H, HC),
|
|
584
648
|
eyeprolog__list_term_chars(T, TC),
|
|
585
|
-
|
|
649
|
+
eyeprolog__append(HC, [',',' '|TC], Chars).
|
|
586
650
|
eyeprolog__list_term_chars([H|T], Chars) :-
|
|
587
651
|
T \= [], T \= [_|_],
|
|
588
652
|
eyeprolog__term_chars(H, HC),
|
|
589
653
|
eyeprolog__term_chars(T, TC),
|
|
590
|
-
|
|
654
|
+
eyeprolog__append(HC, [' ','|',' '|TC], Chars).
|
|
591
655
|
|
|
592
656
|
eyeprolog__term_args_chars([A], Chars) :- eyeprolog__term_chars(A, Chars).
|
|
593
657
|
eyeprolog__term_args_chars([A|As], Chars) :-
|
|
594
658
|
As \= [],
|
|
595
659
|
eyeprolog__term_chars(A, AC),
|
|
596
660
|
eyeprolog__term_args_chars(As, Rest),
|
|
597
|
-
|
|
661
|
+
eyeprolog__append(AC, [',',' '|Rest], Chars).
|
|
598
662
|
|
|
599
663
|
join([], _, Out) :- eyeprolog__text_chars(Out, []).
|
|
600
664
|
join([Item|Items], Separator, Out) :-
|
|
@@ -606,8 +670,8 @@ join([Item|Items], Separator, Out) :-
|
|
|
606
670
|
eyeprolog__join_chars([], _, Chars, Chars).
|
|
607
671
|
eyeprolog__join_chars([Item|Items], Separator, Prefix, Out) :-
|
|
608
672
|
eyeprolog__atomic_chars(Item, ItemChars),
|
|
609
|
-
|
|
610
|
-
|
|
673
|
+
eyeprolog__append(Prefix, Separator, A),
|
|
674
|
+
eyeprolog__append(A, ItemChars, B),
|
|
611
675
|
eyeprolog__join_chars(Items, Separator, B, Out).
|
|
612
676
|
|
|
613
677
|
substring(Text, Start, Count, Out) :-
|
|
@@ -619,21 +683,6 @@ substring(Text, Start, Count, Out) :-
|
|
|
619
683
|
|
|
620
684
|
% ---------- list relations ----------
|
|
621
685
|
|
|
622
|
-
member(X, [X|_]).
|
|
623
|
-
member(X, [_|Xs]) :- member(X, Xs).
|
|
624
|
-
|
|
625
|
-
select(X, [X|Xs], Xs).
|
|
626
|
-
select(X, [Y|Ys], [Y|Zs]) :- select(X, Ys, Zs).
|
|
627
|
-
|
|
628
|
-
last([X], X).
|
|
629
|
-
last([_|Xs], X) :- last(Xs, X).
|
|
630
|
-
|
|
631
|
-
nth0(0, [X|_], X).
|
|
632
|
-
nth0(N, [_|Xs], X) :- var(N), nth0(N0, Xs, X), N is N0 + 1.
|
|
633
|
-
nth0(N, [_|Xs], X) :- nonvar(N), N > 0, N1 is N - 1, nth0(N1, Xs, X).
|
|
634
|
-
|
|
635
|
-
nth1(N, List, X) :- nth0(N0, List, X), N is N0 + 1.
|
|
636
|
-
|
|
637
686
|
set_nth0(0, [_|Xs], X, [X|Xs]).
|
|
638
687
|
set_nth0(N, [Y|Ys], X, [Y|Zs]) :- N > 0, N1 is N - 1, set_nth0(N1, Ys, X, Zs).
|
|
639
688
|
|
|
@@ -645,33 +694,25 @@ drop(N, [_|Xs], Ys) :- N > 0, N1 is N - 1, drop(N1, Xs, Ys).
|
|
|
645
694
|
|
|
646
695
|
slice(Start, Count, List, Slice) :- drop(Start, List, Tail), take(Count, Tail, Slice).
|
|
647
696
|
|
|
648
|
-
reverse(List, Reversed) :- eyeprolog__reverse(List, [], Reversed).
|
|
649
697
|
eyeprolog__reverse([], Acc, Acc).
|
|
650
698
|
eyeprolog__reverse([X|Xs], Acc, Out) :- eyeprolog__reverse(Xs, [X|Acc], Out).
|
|
651
699
|
|
|
652
|
-
length(List, Length) :- nonvar(List), eyeprolog__length_count(List, 0, Length).
|
|
653
|
-
length(List, Length) :- var(List), integer(Length), Length >= 0, eyeprolog__length_make(Length, List).
|
|
654
|
-
|
|
655
700
|
eyeprolog__length_count([], N, N).
|
|
656
701
|
eyeprolog__length_count([_|Xs], N0, N) :- N1 is N0 + 1, eyeprolog__length_count(Xs, N1, N).
|
|
657
702
|
eyeprolog__length_make(0, []).
|
|
658
703
|
eyeprolog__length_make(N, [_|Xs]) :- N > 0, N1 is N - 1, eyeprolog__length_make(N1, Xs).
|
|
659
704
|
|
|
660
|
-
sum_list(List, Sum) :- eyeprolog__sum_list(List, 0, Sum).
|
|
661
705
|
eyeprolog__sum_list([], Sum, Sum).
|
|
662
706
|
eyeprolog__sum_list([X|Xs], Acc, Sum) :- Next is Acc + X, eyeprolog__sum_list(Xs, Next, Sum).
|
|
663
707
|
|
|
664
|
-
min_list([X|Xs], Min) :- eyeprolog__min_list(Xs, X, Min).
|
|
665
708
|
eyeprolog__min_list([], Min, Min).
|
|
666
709
|
eyeprolog__min_list([X|Xs], Current, Min) :- X @< Current, eyeprolog__min_list(Xs, X, Min).
|
|
667
710
|
eyeprolog__min_list([X|Xs], Current, Min) :- X @>= Current, eyeprolog__min_list(Xs, Current, Min).
|
|
668
711
|
|
|
669
|
-
max_list([X|Xs], Max) :- eyeprolog__max_list(Xs, X, Max).
|
|
670
712
|
eyeprolog__max_list([], Max, Max).
|
|
671
713
|
eyeprolog__max_list([X|Xs], Current, Max) :- X @> Current, eyeprolog__max_list(Xs, X, Max).
|
|
672
714
|
eyeprolog__max_list([X|Xs], Current, Max) :- X @=< Current, eyeprolog__max_list(Xs, Current, Max).
|
|
673
715
|
|
|
674
|
-
list_to_set(List, Set) :- eyeprolog__list_to_set(List, [], Set).
|
|
675
716
|
eyeprolog__list_to_set([], _, []).
|
|
676
717
|
eyeprolog__list_to_set([X|Xs], Seen, Set) :-
|
|
677
718
|
eyeprolog__identical_member(X, Seen),
|
|
@@ -683,7 +724,7 @@ eyeprolog__list_to_set([X|Xs], Seen, [X|Set]) :-
|
|
|
683
724
|
eyeprolog__identical_member(X, [Y|_]) :- X == Y.
|
|
684
725
|
eyeprolog__identical_member(X, [_|Ys]) :- eyeprolog__identical_member(X, Ys).
|
|
685
726
|
|
|
686
|
-
|
|
727
|
+
sort_unique(List, Sorted) :- eyeprolog__sort(List, [], Sorted).
|
|
687
728
|
eyeprolog__sort([], Sorted, Sorted).
|
|
688
729
|
eyeprolog__sort([X|Xs], Acc, Sorted) :-
|
|
689
730
|
eyeprolog__insert_sorted(X, Acc, Next),
|
|
@@ -696,11 +737,9 @@ eyeprolog__insert_sorted(X, [Y|Ys], [Y|Zs]) :- X @> Y, eyeprolog__insert_sorted(
|
|
|
696
737
|
|
|
697
738
|
% ---------- aggregation ----------
|
|
698
739
|
|
|
699
|
-
countall(Goal, Count) :- findall(1, Goal, Ones), length(Ones, Count).
|
|
700
|
-
|
|
701
740
|
sumall(Expression, Goal, Sum) :-
|
|
702
741
|
findall(Value, (Goal, Value is Expression), Values),
|
|
703
|
-
|
|
742
|
+
eyeprolog__sum_list(Values, 0, Sum).
|
|
704
743
|
|
|
705
744
|
aggregate_min(Key, Value, Goal, BestKey, BestValue) :-
|
|
706
745
|
findall(pair(Key, Value), Goal, Pairs),
|
|
@@ -709,7 +748,7 @@ aggregate_min(Key, Value, Goal, BestKey, BestValue) :-
|
|
|
709
748
|
eyeprolog__aggregate_min([pair(K,V)|Pairs], BestKey, BestValue) :-
|
|
710
749
|
eyeprolog__aggregate_min_rest(Pairs, K, V, BestKey, BestValue).
|
|
711
750
|
eyeprolog__aggregate_min_rest([], K, V, K, V).
|
|
712
|
-
eyeprolog__aggregate_min_rest([pair(K,V)|Pairs], CK,
|
|
751
|
+
eyeprolog__aggregate_min_rest([pair(K,V)|Pairs], CK, _, BK, BV) :-
|
|
713
752
|
K @< CK,
|
|
714
753
|
eyeprolog__aggregate_min_rest(Pairs, K, V, BK, BV).
|
|
715
754
|
eyeprolog__aggregate_min_rest([pair(K,_)|Pairs], CK, CV, BK, BV) :-
|
|
@@ -723,7 +762,7 @@ aggregate_max(Key, Value, Goal, BestKey, BestValue) :-
|
|
|
723
762
|
eyeprolog__aggregate_max([pair(K,V)|Pairs], BestKey, BestValue) :-
|
|
724
763
|
eyeprolog__aggregate_max_rest(Pairs, K, V, BestKey, BestValue).
|
|
725
764
|
eyeprolog__aggregate_max_rest([], K, V, K, V).
|
|
726
|
-
eyeprolog__aggregate_max_rest([pair(K,V)|Pairs], CK,
|
|
765
|
+
eyeprolog__aggregate_max_rest([pair(K,V)|Pairs], CK, _, BK, BV) :-
|
|
727
766
|
K @> CK,
|
|
728
767
|
eyeprolog__aggregate_max_rest(Pairs, K, V, BK, BV).
|
|
729
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 './
|
|
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 './
|
|
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 './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 './
|
|
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
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
% uuid/
|
|
1
|
+
% uuid/3 deterministically creates a canonical version 4 UUID atom from a seed.
|
|
2
2
|
answer(uuid_v4, true) :-
|
|
3
|
-
uuid(UUID),
|
|
3
|
+
uuid(1, UUID, _),
|
|
4
4
|
atom(UUID),
|
|
5
5
|
atom_length(UUID, 36),
|
|
6
6
|
sub_atom(UUID, 8, 1, 27, '-'),
|
|
@@ -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-examples.mjs
CHANGED
|
@@ -14,7 +14,6 @@ const packageRoot = path.resolve(root, '..');
|
|
|
14
14
|
const examplesDir = path.join(packageRoot, 'examples');
|
|
15
15
|
const expectedDir = path.join(examplesDir, 'output');
|
|
16
16
|
const expectedProofDir = path.join(examplesDir, 'proof');
|
|
17
|
-
const fixedExampleDate = '2026-05-30';
|
|
18
17
|
|
|
19
18
|
export const proofExamples = [
|
|
20
19
|
'access-control-policy.pl',
|
|
@@ -124,25 +123,18 @@ function runProofExample(name) {
|
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
function runProgramExample(programFile, filename, options) {
|
|
127
|
-
const
|
|
128
|
-
|
|
126
|
+
const text = fs.readFileSync(programFile, 'utf8');
|
|
127
|
+
const expectedExit = text.match(/^%\s*expect-exit:\s*(\d+)\s*$/m);
|
|
128
|
+
const program = Program.parseSources([{ text, filename }], {
|
|
129
|
+
sourceMetadata: options.proof,
|
|
130
|
+
});
|
|
129
131
|
try {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const result = run(program, { ...options, goals: goalsInProgramOrder(program, text) });
|
|
137
|
-
if (expectedExit) throw new Error(`${filename} expected exit ${expectedExit[1]}, but reasoning succeeded`);
|
|
138
|
-
return result.stdout;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
if (expectedExit && error?.code === Number(expectedExit[1])) return error.stdout ?? '';
|
|
141
|
-
throw error;
|
|
142
|
-
}
|
|
143
|
-
} finally {
|
|
144
|
-
if (oldLocalTime == null) delete process.env.EYEPROLOG_LOCAL_TIME;
|
|
145
|
-
else process.env.EYEPROLOG_LOCAL_TIME = oldLocalTime;
|
|
132
|
+
const result = run(program, { ...options, goals: goalsInProgramOrder(program, text) });
|
|
133
|
+
if (expectedExit) throw new Error(`${filename} expected exit ${expectedExit[1]}, but reasoning succeeded`);
|
|
134
|
+
return result.stdout;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (expectedExit && error?.code === Number(expectedExit[1])) return error.stdout ?? '';
|
|
137
|
+
throw error;
|
|
146
138
|
}
|
|
147
139
|
}
|
|
148
140
|
|
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/
|
|
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
|
});
|