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.
Files changed (43) hide show
  1. package/README.md +5 -3
  2. package/examples/age.pl +7 -7
  3. package/examples/blocks-world-planning.pl +3 -3
  4. package/examples/combinatorics-findall-sort.pl +4 -4
  5. package/examples/d3-group.pl +3 -3
  6. package/examples/dijkstra-findall-sort.pl +3 -3
  7. package/examples/enigma1225.pl +1 -2
  8. package/examples/eulerian-path.pl +4 -4
  9. package/examples/four-color-map.pl +2 -2
  10. package/examples/input/odrl-dpv-healthcare-risk-ranked-rules.pl +1 -1
  11. package/examples/input/odrl-dpv-risk-ranked-rules.pl +1 -1
  12. package/examples/input/rdf12-annotated-claims-rules.pl +1 -1
  13. package/examples/odrl-dpv-healthcare-risk-ranked.pl +1 -1
  14. package/examples/odrl-dpv-risk-ranked.pl +1 -1
  15. package/examples/proof/age.pl +6 -6
  16. package/examples/proof/d3-group.pl +4 -4
  17. package/examples/proof/iso-dynamic-database.pl +1 -1
  18. package/examples/quine-mccluskey.pl +7 -7
  19. package/examples/rdf12-annotated-claims.pl +1 -1
  20. package/examples/sudoku.pl +1 -1
  21. package/examples/uuid.pl +4 -4
  22. package/package.json +1 -1
  23. package/playground.html +28 -8
  24. package/src/cli.js +1 -1
  25. package/src/explain.js +1 -1
  26. package/src/eyeprolog-autoload.js +133 -0
  27. package/src/eyeprolog-common-library.pl +44 -0
  28. package/src/eyeprolog-library.pl +113 -74
  29. package/src/index.js +2 -2
  30. package/src/playground-worker.js +2 -2
  31. package/src/solver.js +1 -1
  32. package/test/conformance/cases/builtins/extra_uuid_v4.pl +2 -2
  33. package/test/conformance/cases/lists/031_lists_aggregation_ordering.pl +1 -2
  34. package/test/conformance/cases/lists/045_sort_deduplicates_atoms.pl +2 -3
  35. package/test/conformance/cases/lists/087_sort_reverse_length.pl +2 -2
  36. package/test/conformance/cases/lists/extra_sort_iri_atoms.pl +1 -1
  37. package/test/conformance/cases/lists/sort_structured_terms.pl +1 -1
  38. package/test/run-examples.mjs +11 -19
  39. package/test/run-playground.mjs +5 -1
  40. package/test/run-regression.mjs +35 -19
  41. package/the-art-of-eyeprolog.md +37 -32
  42. package/src/library-source.js +0 -18
  43. 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).
@@ -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. Only uuid/1 and local_time/1 remain native
7
- % host built-ins because ISO Prolog has no entropy or wall-clock primitive.
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
- call(Closure, A, B) :-
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
- append(Parts, [A, B], CallParts),
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
- member(Unit, ['Y', 'M', 'D']).
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, Start, 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) :- member(M, [4,6,9,11]).
265
- eyeprolog__days_in_month(_, M, 31) :- member(M, [1,3,5,7,8,10,12]).
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
- append(['P'|YC], MC, A),
278
- append(A, DC, Chars),
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
- append(Digits, [Unit], Chars).
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) :- append(Cs, ['$'], Cs0).
321
- eyeprolog__strip_end_anchor(Cs, no, Cs) :- \+ append(_, ['$'], 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
- \+ member('(', Body),
363
- \+ member(')', Body),
364
- \+ member('[', Body),
365
- \+ member(']', Body),
366
- \+ member('+', Body),
367
- \+ member('*', Body).
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) :- append(Literal, Rest, Chars).
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
- append(AC, BC, WC),
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
- append(AC, BC, WC),
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
- append(_, Tail, TextChars),
452
- append(NeedleChars, _, Tail),
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
- member(Needle, Alternatives),
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
- append(Prefix, Tail, Chars),
471
- append(Separator, Rest, Tail),
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
- reverse(Left, Reversed),
587
+ eyeprolog__reverse(Left, [], Reversed),
524
588
  eyeprolog__drop_space(Reversed, RightReversed),
525
- reverse(RightReversed, TrimmedChars),
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
- append(['['|Body], [']'], Chars).
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
- append(NameChars, ['('|ArgsChars], A),
577
- append(A, [')'], Chars).
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
- append(HC, [',',' '|TC], Chars).
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
- append(HC, [' ','|',' '|TC], Chars).
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
- append(AC, [',',' '|Rest], Chars).
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
- append(Prefix, Separator, A),
610
- append(A, ItemChars, B),
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
- sort(List, Sorted) :- eyeprolog__sort(List, [], Sorted).
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
- sum_list(Values, Sum).
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, CV, BK, BV) :-
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, CV, BK, BV) :-
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 './library.js';
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 './library.js';
29
+ import { getEyePrologRegistry } from './eyeprolog-autoload.js';
30
30
  import { parseGoalText } from './parser.js';
31
31
 
32
32
  export function run(source, options = {}) {
@@ -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=20260803c';
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 './library.js';
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 is deterministic and creates a canonical version 4 UUID atom.
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) :- sort([b, a, b], X).
9
+ answer(sort, X) :- sort_unique([b, a, b], X).
10
10
  %% goal: answer(X0, X1)
11
-
@@ -1,4 +1,3 @@
1
- % Reference 9.8: sort/2 sorts and deduplicates a proper list.
2
- answer(sorted, X) :- sort([c, a, b, a], X).
1
+ % Reference 9.8: sort_unique/2 sorts and deduplicates a proper list.
2
+ answer(sorted, X) :- sort_unique([c, a, b, a], X).
3
3
  %% goal: answer(X0, X1)
4
-
@@ -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) :- sort([3, 1, 2, 1], X).
8
- answer(sort_mixed_terms, X) :- sort([b, 2, a, 1, pair(a), "s"], 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).
@@ -1,3 +1,3 @@
1
1
  %% goal: answer(X0, X1)
2
2
 
3
- answer(sort_iri_atoms, X) :- sort(['<urn:example:b>', '<urn:example:a>'], X).
3
+ answer(sort_iri_atoms, X) :- sort_unique(['<urn:example:b>', '<urn:example:a>'], X).
@@ -1,3 +1,3 @@
1
1
  %% goal: answer(X0)
2
2
 
3
- answer(X) :- sort([b(2), a(3), a(1)], X).
3
+ answer(X) :- sort_unique([b(2), a(3), a(1)], X).
@@ -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 oldLocalTime = process.env.EYEPROLOG_LOCAL_TIME;
128
- process.env.EYEPROLOG_LOCAL_TIME = fixedExampleDate;
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 text = fs.readFileSync(programFile, 'utf8');
131
- const expectedExit = text.match(/^%\s*expect-exit:\s*(\d+)\s*$/m);
132
- const program = Program.parseSources([{ text, filename }], {
133
- sourceMetadata: options.proof,
134
- });
135
- try {
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
 
@@ -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/library.js')), 'EyeProlog library missing from worker graph');
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
  });