eyeprolog 1.1.9 → 1.1.10

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 CHANGED
@@ -66,12 +66,12 @@ noun --> [world] | [prolog].
66
66
  %% goal: phrase(sentence, Words)
67
67
  ```
68
68
 
69
- EyeProlog also adds 44 public library predicates to its 129-entry ISO registry.
70
- **All 44 are ordinary Prolog clauses** in `src/lib/eyeprolog.pl` and
71
- `src/lib/lists.pl`. They are ISO/IEC 13211-2 modules, loaded explicitly with
72
- `use_module(library(eyeprolog))` or `use_module(library(lists))`, following the
73
- layout used by Scryer Prolog. None requires host support. Portable text
74
- predicates use ISO atoms or character lists.
69
+ EyeProlog also adds 46 public library predicates to its 129-entry ISO registry.
70
+ **All 46 are ordinary Prolog clauses** in `src/lib/eyeprolog.pl`,
71
+ `src/lib/lists.pl`, and `src/lib/prologue.pl`. They are ISO/IEC 13211-2 modules,
72
+ loaded explicitly with `use_module(library(eyeprolog))`,
73
+ `use_module(library(lists))`, or `use_module(library(prologue))`. None requires
74
+ host support. Portable text predicates use ISO atoms or character lists.
75
75
 
76
76
  ## Development
77
77
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.1.9",
6
+ "version": "1.1.10",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
@@ -0,0 +1,87 @@
1
+ /** Predicates proposed by the ISO Prolog Prologue working draft. */
2
+
3
+ :- module(prologue, [
4
+ member/2,
5
+ append/3,
6
+ length/2,
7
+ between/3,
8
+ select/3,
9
+ succ/2,
10
+ maplist/2
11
+ ]).
12
+
13
+ :- meta_predicate(maplist(1, '?')).
14
+
15
+ member(X, [X|_]).
16
+ member(X, [_|Xs]) :- member(X, Xs).
17
+
18
+ append([], Ys, Ys).
19
+ append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).
20
+
21
+ length(List, Length) :-
22
+ nonvar(Length), !,
23
+ prologue__integer(Length),
24
+ prologue__not_less_than_zero(Length),
25
+ prologue__length_fixed(Length, List).
26
+ length(List, Length) :-
27
+ prologue__length_generate(List, 0, Length).
28
+
29
+ prologue__length_fixed(0, []).
30
+ prologue__length_fixed(N, [_|Xs]) :-
31
+ N > 0,
32
+ Next is N - 1,
33
+ prologue__length_fixed(Next, Xs).
34
+
35
+ prologue__length_generate([], N, N).
36
+ prologue__length_generate([_|Xs], N0, N) :-
37
+ N1 is N0 + 1,
38
+ prologue__length_generate(Xs, N1, N).
39
+
40
+ between(Lower, Upper, X) :-
41
+ prologue__integer(Lower),
42
+ prologue__integer(Upper),
43
+ prologue__integer_or_variable(X),
44
+ prologue__between(Lower, Upper, X).
45
+
46
+ prologue__between(Lower, Upper, Lower) :- Lower =< Upper.
47
+ prologue__between(Lower, Upper, X) :-
48
+ Lower < Upper,
49
+ Next is Lower + 1,
50
+ prologue__between(Next, Upper, X).
51
+
52
+ select(X, [X|Xs], Xs).
53
+ select(X, [Y|Ys], [Y|Zs]) :- select(X, Ys, Zs).
54
+
55
+ succ(X, S) :-
56
+ var(X), !,
57
+ ( var(S) -> 0 is S
58
+ ; prologue__integer(S),
59
+ prologue__not_less_than_zero(S),
60
+ S > 0,
61
+ X is S - 1
62
+ ).
63
+ succ(X, S) :-
64
+ prologue__integer(X),
65
+ prologue__not_less_than_zero(X),
66
+ ( var(S) -> S is X + 1
67
+ ; prologue__integer(S),
68
+ prologue__not_less_than_zero(S),
69
+ S =:= X + 1
70
+ ).
71
+
72
+ maplist(_, []).
73
+ maplist(Closure, [X|Xs]) :-
74
+ call(Closure, X),
75
+ maplist(Closure, Xs).
76
+
77
+ prologue__integer_or_variable(X) :- var(X), !.
78
+ prologue__integer_or_variable(X) :- prologue__integer(X).
79
+
80
+ prologue__integer(X) :- integer(X), !.
81
+ prologue__integer(X) :- var(X), !, 0 is X.
82
+ % arg/3 performs the required integer type check before inspecting its term.
83
+ prologue__integer(X) :- arg(X, type_check, _).
84
+
85
+ prologue__not_less_than_zero(X) :- X >= 0, !.
86
+ % atom_length/2 reports domain_error(not_less_than_zero) for a negative value.
87
+ prologue__not_less_than_zero(X) :- atom_length('', X).
package/src/solver.js CHANGED
@@ -431,6 +431,8 @@ function defaultPrologFlags(unknown = 'error') {
431
431
  ['integer_rounding_function', { value: compound('toward_zero', []), allowed: ['toward_zero'], changeable: false }],
432
432
  ['char_conversion', { value: compound('on', []), allowed: ['on', 'off'], changeable: true }],
433
433
  ['debug', { value: compound('off', []), allowed: ['on', 'off'], changeable: true }],
434
+ ['max_integer', { value: compound('unbounded', []), allowed: ['unbounded'], changeable: false }],
435
+ ['min_integer', { value: compound('unbounded', []), allowed: ['unbounded'], changeable: false }],
434
436
  ['max_arity', { value: compound('unbounded', []), allowed: ['unbounded'], changeable: false }],
435
437
  ['unknown', { value: compound(unknown, []), allowed: ['error', 'fail', 'warning'], changeable: true }],
436
438
  ['double_quotes', { value: compound('chars', []), allowed: ['chars', 'codes', 'atom'], changeable: true }],
@@ -8,6 +8,7 @@ import { fs, isNode } from './platform.js';
8
8
  const moduleFiles = Object.freeze({
9
9
  eyeprolog: 'eyeprolog.pl',
10
10
  lists: 'lists.pl',
11
+ prologue: 'prologue.pl',
11
12
  });
12
13
 
13
14
  const cacheKey = isNode
@@ -36,14 +37,14 @@ function libraryUrl(filename) {
36
37
 
37
38
  export const eyePrologNativeLibraryIndicators = Object.freeze([]);
38
39
  export const eyePrologPortableLibraryIndicators = Object.freeze([
39
- 'uuid/3', 'difference/3', 'maplist/3', 'lt/2', 'gt/2', 'le/2', 'ge/2',
40
+ 'uuid/3', 'difference/3', 'maplist/2', 'maplist/3', 'lt/2', 'gt/2', 'le/2', 'ge/2',
40
41
  'between/3', 'smallest_divisor_from/3', 'random/3', 'matches/3', 'split/3',
41
42
  'replace/4', 'lowercase/2', 'uppercase/2', 'trim/2', 'number_string/2',
42
43
  'atom_string/2', 'term_string/2', 'append/3', 'string_concat/3', 'contains/2',
43
44
  'matches/2', 'join/3', 'substring/4', 'member/2', 'select/3', 'last/2',
44
45
  'nth0/3', 'nth1/3', 'set_nth0/4', 'take/3', 'drop/3', 'slice/4', 'reverse/2',
45
46
  'length/2', 'sum_list/2', 'min_list/2', 'max_list/2', 'list_to_set/2',
46
- 'countall/2', 'sumall/3', 'aggregate_min/5', 'aggregate_max/5',
47
+ 'countall/2', 'succ/2', 'sumall/3', 'aggregate_min/5', 'aggregate_max/5',
47
48
  ]);
48
49
  export const eyePrologLibraryIndicators = Object.freeze([...eyePrologPortableLibraryIndicators]);
49
50
 
@@ -1,3 +1,3 @@
1
1
  answer(7, red, caught(eyeprolog), first, caught(eyeprolog)).
2
2
  answer(7, red, caught(eyeprolog), second, caught(eyeprolog)).
3
- flags(off, on, [pair(bounded, false), pair(integer_rounding_function, toward_zero), pair(char_conversion, on), pair(debug, off), pair(max_arity, unbounded), pair(unknown, fail), pair(double_quotes, chars)]).
3
+ flags(off, on, [pair(bounded, false), pair(integer_rounding_function, toward_zero), pair(char_conversion, on), pair(debug, off), pair(max_integer, unbounded), pair(min_integer, unbounded), pair(max_arity, unbounded), pair(unknown, fail), pair(double_quotes, chars)]).
@@ -8,3 +8,16 @@
8
8
  Retrieved on 2026-08-11. It is vendored so the regression suite exercises all
9
9
  58 quads without depending on network access or availability of the source
10
10
  server.
11
+
12
+ `prologue_quad.pl` is an unmodified snapshot of the 33 machine-readable quads
13
+ for the predicates proposed by the Prolog Prologue working draft:
14
+
15
+ <https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue_quad.pl>
16
+
17
+ The corresponding working draft is at
18
+ <https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue>.
19
+ The corpus snapshot was retrieved on 2026-08-11.
20
+
21
+ `prologue_quad_runner.pl` loads EyeProlog's `library(prologue)` and includes
22
+ the unmodified corpus, mirroring the draft's requirement that a Prologue be
23
+ included before its examples are run.
@@ -0,0 +1,134 @@
1
+ % p.p.1 member/2
2
+
3
+ ?- member(X, [1,2]).
4
+ X = 1
5
+ ; X = 2.
6
+
7
+ ?- member(1, L).
8
+ L = [1|_]
9
+ ; L = [_,1|_]
10
+ ; L = [_,_,1|_]
11
+ ; ... . % Ad infinitum.
12
+
13
+ ?- member(X, [Y,Z|nonlist]).
14
+ X = Y
15
+ ; X = Z.
16
+
17
+ ?- member(X, nonlist).
18
+ false.
19
+
20
+ ?- member(X, X).
21
+ sto, % undefined, STO 7.3.3
22
+ true
23
+ | sto,
24
+ loops.
25
+
26
+ % p.p.2 append/3
27
+
28
+ ?- append([a,b],[c,d], Xs).
29
+ Xs = [a,b,c,d].
30
+
31
+ ?- append([a], nonlist, Xs).
32
+ Xs = [a|nonlist].
33
+
34
+ ?- append([a], Ys, Zs).
35
+ Zs = [a|Ys].
36
+
37
+ ?- append(Xs, Ys, [a,b,c]).
38
+ Xs = [], Ys = [a,b,c]
39
+ ; Xs = [a], Ys = [b,c]
40
+ ; Xs = [a,b], Ys = [c]
41
+ ; Xs = [a,b,c], Ys = [].
42
+
43
+ % p.p.3 length/2
44
+
45
+ ?- length([a,b,c], Length).
46
+ Length = 3.
47
+
48
+ ?- length(List, 5).
49
+ List = [_,_,_,_,_].
50
+
51
+ ?- length(List, Length).
52
+ List = [], Length = 0
53
+ ; List = [_], Length = 1
54
+ ; List = [_,_], Length = 2
55
+ ; ... . % Ad infinitum.
56
+
57
+ % p.p.4 between/3
58
+
59
+ ?- between(1, 2, 0).
60
+ false.
61
+
62
+ ?- between(1, 2, I).
63
+ I = 1
64
+ ; I = 2.
65
+
66
+ ?- between(2, 1, I).
67
+ false.
68
+
69
+ ?- between(I, I, 0).
70
+ instantiation_error.
71
+
72
+ ?- between(1, I, 0).
73
+ instantiation_error.
74
+
75
+ ?- between(I, -1, 0).
76
+ instantiation_error.
77
+
78
+ ?- between(1, c, 0).
79
+ type_error(integer,c).
80
+
81
+ ?- between(1+1,2,I).
82
+ type_error(integer,1+1).
83
+
84
+ % p.p.5 select/3
85
+
86
+ ?- select(X, [1,2], Xs).
87
+ X = 1, Xs = [2]
88
+ ; X = 2, Xs = [1].
89
+
90
+ ?- select(X, [Y|nonlist], Xs).
91
+ X = Y, Xs = nonlist.
92
+
93
+ ?- select(E, Xs, Xs).
94
+ sto.
95
+
96
+ % p.p.6 succ/2
97
+
98
+ ?- succ(X, S).
99
+ instantiation_error.
100
+
101
+ ?- succ(X, X).
102
+ instantiation_error.
103
+
104
+ ?- succ(0, S).
105
+ S = 1.
106
+
107
+ ?- succ(1, 1+1).
108
+ type_error(integer, 1+1).
109
+
110
+ ?- succ(X, 0).
111
+ false.
112
+
113
+ ?- succ(-1, S).
114
+ domain_error(not_less_than_zero, -1).
115
+
116
+ ?- current_prolog_flag(max_integer, Max),
117
+ ( integer(Max) -> succ(Max, S) ; true ).
118
+ evaluation_error(int_overflow)
119
+ | Max = unbounded.
120
+
121
+ % p.p.7
122
+
123
+ ?- maplist(>(3), [1, 2]).
124
+ true.
125
+
126
+ ?- maplist(>(3), [1, 2, 3]).
127
+ false.
128
+
129
+ ?- maplist(=(X), Xs).
130
+ Xs = []
131
+ ; Xs = [X]
132
+ ; Xs = [X, X]
133
+ ; Xs = [X, X, X]
134
+ ; ... . % Ad infinitum.
@@ -0,0 +1,2 @@
1
+ :- use_module(library(prologue)).
2
+ :- include('prologue_quad.pl').
@@ -322,6 +322,21 @@ c4 ?- call((!;1)).
322
322
  assertEqual(result.stdout, 'quads: 58 run, 58 passed, 0 failed.\n', 'quad report');
323
323
  },
324
324
  },
325
+ {
326
+ name: 'runQuads passes the complete vendored Prolog Prologue quad corpus',
327
+ run: () => {
328
+ const filename = path.join(testRoot, 'fixtures', 'prologue_quad_runner.pl');
329
+ const source = fs.readFileSync(filename, 'utf8');
330
+ const result = publicApi.runQuads(Program.parseSources([{
331
+ text: source,
332
+ filename,
333
+ baseDir: path.dirname(filename),
334
+ }]));
335
+ assertEqual(result.total, 33, 'quad total');
336
+ assertEqual(result.passed, 33, 'quad passed');
337
+ assertEqual(result.stdout, 'quads: 33 run, 33 passed, 0 failed.\n', 'quad report');
338
+ },
339
+ },
325
340
  {
326
341
  name: 'runQuads rejects malformed answer substitutions',
327
342
  run: () => {
@@ -861,7 +876,7 @@ function documentationSyncCases() {
861
876
  '<a href="https://eyereasoner.github.io/eyeprolog/the-art-of-eyeprolog">\n <img src="book-assets/title-page.svg" alt="Read The Art of EyeProlog"',
862
877
  'README cover links to the book',
863
878
  );
864
- for (const filename of ['src/iso.js', 'src/dcg.js', 'src/standard-library.js', 'src/lib/eyeprolog.pl', 'src/lib/lists.pl', 'src/playground-worker.js']) {
879
+ for (const filename of ['src/iso.js', 'src/dcg.js', 'src/standard-library.js', 'src/lib/eyeprolog.pl', 'src/lib/lists.pl', 'src/lib/prologue.pl', 'src/playground-worker.js']) {
865
880
  assertEqual(fs.existsSync(path.join(packageRoot, filename)), true, `${filename} exists`);
866
881
  assertIncludes(book, filename, `book documents ${filename}`);
867
882
  }
@@ -1300,10 +1315,10 @@ open(X) :- candidate(X), \\+ closed(X).
1300
1315
  assertEqual(Boolean(registry.get('phrase', 2)), true, 'Part 3 phrase/2 exists');
1301
1316
  assertEqual(Boolean(registry.get('phrase', 3)), true, 'Part 3 phrase/3 exists');
1302
1317
  assertEqual(registeredNativeEyePrologLibraryNames().length, 0, 'public native EyeProlog builtin count');
1303
- assertEqual(eyePrologPortableLibraryIndicators.length, 44, 'portable Prolog library count');
1318
+ assertEqual(eyePrologPortableLibraryIndicators.length, 46, 'portable Prolog library count');
1304
1319
  assertEqual(eyePrologNativeLibraryIndicators.length, 0, 'native host library count');
1305
1320
  assertEqual(eyePrologNativeLibraryIndicators.join(','), '', 'no EyeProlog library predicate requires host support');
1306
- assertEqual(eyePrologLibraryIndicators.length, 44, 'complete EyeProlog library surface');
1321
+ assertEqual(eyePrologLibraryIndicators.length, 46, 'complete EyeProlog library surface');
1307
1322
  assertEqual(library.get('between', 3), null, 'between/3 remains portable Prolog');
1308
1323
  assertEqual(library.get('smallest_divisor_from', 3), null, 'smallest_divisor_from/3 remains portable Prolog');
1309
1324
  assertEqual(library.get('random', 3), null, 'random/3 remains portable Prolog');
@@ -1331,6 +1346,7 @@ open(X) :- candidate(X), \\+ closed(X).
1331
1346
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'lib', 'eyeprolog.pl')), true, 'portable module exists');
1332
1347
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'standard-library.js')), true, 'standard module registry exists');
1333
1348
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'lib', 'lists.pl')), true, 'lists module exists');
1349
+ assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'lib', 'prologue.pl')), true, 'Prologue module exists');
1334
1350
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'eyeprolog-autoload.js')), false, 'obsolete autoloader is absent');
1335
1351
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'library-source.js')), false, 'duplicate source loader is absent');
1336
1352
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'portable-library.js')), false, 'obsolete duplicate module remains absent');
@@ -5580,6 +5580,8 @@ silently changing a static program.
5580
5580
  | `integer_rounding_function` | `toward_zero` | `toward_zero` | no |
5581
5581
  | `char_conversion` | `on` | `on`, `off` | yes |
5582
5582
  | `debug` | `off` | `on`, `off` | yes |
5583
+ | `max_integer` | `unbounded` | `unbounded` | no |
5584
+ | `min_integer` | `unbounded` | `unbounded` | no |
5583
5585
  | `max_arity` | `unbounded` | `unbounded` | no |
5584
5586
  | `unknown` | `fail` | `error`, `fail`, `warning` | yes |
5585
5587
  | `double_quotes` | `chars` | `chars`, `codes`, `atom` | yes |
@@ -5735,17 +5737,20 @@ so side effects occur in Prolog execution order.
5735
5737
 
5736
5738
  ### The EyeProlog library
5737
5739
 
5738
- EyeProlog exposes **44 library predicate indicators** in addition to the 129
5739
- indicators in its isolated ISO profile. **All 44 are ordinary Prolog clauses**
5740
- across `src/lib/eyeprolog.pl` and `src/lib/lists.pl`; none
5740
+ EyeProlog exposes **46 library predicate indicators** in addition to the 129
5741
+ indicators in its isolated ISO profile. **All 46 are ordinary Prolog clauses**
5742
+ across `src/lib/eyeprolog.pl`, `src/lib/lists.pl`, and `src/lib/prologue.pl`; none
5741
5743
  is a native host predicate. The resulting
5742
- normal EyeProlog language surface is therefore **173 public predicate
5744
+ normal EyeProlog language surface is therefore **175 public predicate
5743
5745
  indicators**. Internally, the runtime registry contains only the 129 ISO
5744
5746
  definitions; the EyeProlog relations are module source clauses.
5745
5747
 
5746
- The two Prolog files declare `eyeprolog` and `lists` with `module/2`. A program
5747
- loads them explicitly with `use_module(library(eyeprolog))` and
5748
- `use_module(library(lists))`; `use_module/2` can select a smaller import list.
5748
+ The three Prolog files declare `eyeprolog`, `lists`, and `prologue` with
5749
+ `module/2`. A program loads them explicitly with
5750
+ `use_module(library(eyeprolog))`, `use_module(library(lists))`, or
5751
+ `use_module(library(prologue))`; `use_module/2` can select a smaller import
5752
+ list. The last module implements the predicates exercised by the working-draft
5753
+ Prologue quad corpus.
5749
5754
  `src/standard-library.js` only registers the module sources for Node and browser
5750
5755
  resolution and never adds clauses implicitly.
5751
5756
  The isolated ISO-only registry remains
@@ -5759,6 +5764,7 @@ private helpers and same-named predicates in different modules separate.
5759
5764
  | --- | --- |
5760
5765
  | `library(lists)` | `maplist/3`, `append/3`, `member/2`, `select/3`, `last/2`, `nth0/3`, `nth1/3`, `reverse/2`, `length/2`, `sum_list/2`, `min_list/2`, `max_list/2`, `list_to_set/2`, `countall/2` |
5761
5766
  | `library(eyeprolog)` | `uuid/3`, `difference/3`, `lt/2`, `le/2`, `gt/2`, `ge/2`, `between/3`, `smallest_divisor_from/3`, `random/3`, `matches/3`, `split/3`, `replace/4`, `lowercase/2`, `uppercase/2`, `trim/2`, `number_string/2`, `atom_string/2`, `term_string/2`, `string_concat/3`, `contains/2`, `matches/2`, `join/3`, `substring/4`, `set_nth0/4`, `take/3`, `drop/3`, `slice/4`, `sumall/3`, `aggregate_min/5`, `aggregate_max/5` |
5767
+ | `library(prologue)` | `member/2`, `append/3`, `length/2`, `between/3`, `select/3`, `succ/2`, `maplist/2` |
5762
5768
 
5763
5769
  <!-- eyeprolog-library-catalog:end -->
5764
5770
 
@@ -5885,7 +5891,7 @@ eyeprolog --goal 'answer(Kind, Value)' program.pl
5885
5891
  The portable text API uses **ISO atoms or proper lists of one-character atoms**.
5886
5892
  A generated text result defaults to an atom. Double-quoted source text uses the
5887
5893
  ISO representation selected by `double_quotes`; with the default `chars`, it is
5888
- already a proper character list accepted by this API. The 44-predicate portable
5894
+ already a proper character list accepted by this API. The 46-predicate portable
5889
5895
  library itself has no STRING or JavaScript dependency.
5890
5896
 
5891
5897
  | Predicate and principal mode | Behavior |