eyeprolog 1.1.9 → 1.1.11

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
@@ -21,11 +21,22 @@ examples, proofs, conformance, and implementation.
21
21
 
22
22
  ## Quick start
23
23
 
24
- EyeProlog requires Node.js 18 or newer.
24
+ EyeProlog requires Node.js 18 or newer. Check the active runtime before
25
+ installing:
25
26
 
26
27
  ```sh
27
- npm install --global eyeprolog
28
- eyeprolog
28
+ node --version
29
+ ```
30
+
31
+ If it reports an older release, upgrade through a Node version manager or the
32
+ [official Node.js download](https://nodejs.org/en/download) and check again.
33
+ Distribution packages can provide an older Node.js even on a current operating
34
+ system.
35
+
36
+ Run EyeProlog without a global installation:
37
+
38
+ ```sh
39
+ npx --yes eyeprolog
29
40
  ?- use_module(library(lists)).
30
41
  true.
31
42
  ?- member(X, [prolog, logic]).
@@ -34,11 +45,25 @@ eyeprolog
34
45
  ?- halt.
35
46
  ```
36
47
 
48
+ For a persistent `eyeprolog` command without administrator access, install it
49
+ under a user-owned prefix and put that prefix's `bin` directory on `PATH`:
50
+
51
+ ```sh
52
+ npm install --global --prefix "$HOME/.local" eyeprolog
53
+ export PATH="$HOME/.local/bin:$PATH"
54
+ eyeprolog
55
+ ```
56
+
57
+ Add the `PATH` export to your shell startup file to keep it across sessions.
58
+ Do not use `sudo npm install`; npm's
59
+ [EACCES guidance](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally/)
60
+ also recommends a Node version manager or a user-owned npm prefix.
61
+
37
62
  For a non-interactive run:
38
63
 
39
64
  ```sh
40
65
  printf 'human(socrates).\nmortal(X) :- human(X).\n' |
41
- eyeprolog --proof --goal 'mortal(socrates)' -
66
+ npx --yes eyeprolog --proof --goal 'mortal(socrates)' -
42
67
  ```
43
68
 
44
69
  Programs may declare their default queries with `%% goal:` comments.
@@ -66,12 +91,12 @@ noun --> [world] | [prolog].
66
91
  %% goal: phrase(sentence, Words)
67
92
  ```
68
93
 
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.
94
+ EyeProlog also adds 46 public library predicates to its 129-entry ISO registry.
95
+ **All 46 are ordinary Prolog clauses** in `src/lib/eyeprolog.pl`,
96
+ `src/lib/lists.pl`, and `src/lib/prologue.pl`. They are ISO/IEC 13211-2 modules,
97
+ loaded explicitly with `use_module(library(eyeprolog))`,
98
+ `use_module(library(lists))`, or `use_module(library(prologue))`. None requires
99
+ host support. Portable text predicates use ISO atoms or character lists.
75
100
 
76
101
  ## Development
77
102
 
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.11",
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: () => {
@@ -501,6 +516,27 @@ c4 ?- call((!;1)).
501
516
  assertEqual(result.stderr, '', 'stderr');
502
517
  },
503
518
  },
519
+ {
520
+ name: 'npm can install the CLI under a user-owned prefix',
521
+ run: () => {
522
+ const prefix = path.join(tmp, `npm-prefix-${++tmpCounter}`);
523
+ const installed = spawnSync('npm', [
524
+ 'install', '--global', '--prefix', prefix, '--loglevel=silent', '--no-audit', '--no-fund', '.',
525
+ ], {
526
+ cwd: packageRoot,
527
+ encoding: 'utf8',
528
+ env: { ...process.env, npm_config_update_notifier: 'false' },
529
+ });
530
+ assertEqual(installed.status, 0, 'install exit status');
531
+ const executable = process.platform === 'win32'
532
+ ? path.join(prefix, 'eyeprolog.cmd')
533
+ : path.join(prefix, 'bin', 'eyeprolog');
534
+ const result = spawnSync(executable, ['--version'], { encoding: 'utf8' });
535
+ assertEqual(result.status, 0, 'installed CLI exit status');
536
+ assertEqual(result.stdout, `eyeprolog ${pkg.version}\n`, 'installed CLI stdout');
537
+ assertEqual(result.stderr, '', 'installed CLI stderr');
538
+ },
539
+ },
504
540
  {
505
541
  name: 'stdin input is accepted',
506
542
  run: () => {
@@ -861,7 +897,7 @@ function documentationSyncCases() {
861
897
  '<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
898
  'README cover links to the book',
863
899
  );
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']) {
900
+ 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
901
  assertEqual(fs.existsSync(path.join(packageRoot, filename)), true, `${filename} exists`);
866
902
  assertIncludes(book, filename, `book documents ${filename}`);
867
903
  }
@@ -964,6 +1000,22 @@ function documentationSyncCases() {
964
1000
  assertArrayEqual(misleadingDependencyInstallDocs(), [], 'misleading dependency install docs');
965
1001
  },
966
1002
  },
1003
+ {
1004
+ name: 'installation docs avoid unsupported Node and global npm permission traps',
1005
+ run: () => {
1006
+ assertEqual(pkg.engines?.node, '>=18', 'supported Node range');
1007
+ for (const filename of ['README.md', 'the-art-of-eyeprolog.md']) {
1008
+ const text = fs.readFileSync(path.join(packageRoot, filename), 'utf8');
1009
+ assertIncludes(text, 'node --version', `${filename} checks Node version`);
1010
+ assertIncludes(text, 'npx --yes eyeprolog', `${filename} offers a non-global launch`);
1011
+ assertIncludes(text, 'npm install --global --prefix "$HOME/.local" eyeprolog', `${filename} uses a user prefix`);
1012
+ assertIncludes(text, 'https://nodejs.org/en/download', `${filename} links Node upgrades`);
1013
+ assertIncludes(text, 'https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally/', `${filename} links npm EACCES guidance`);
1014
+ assertEqual(text.includes('sudo npm install'), true, `${filename} explicitly warns against sudo npm`);
1015
+ assertEqual(/^\s*sudo npm install/m.test(text), false, `${filename} never recommends sudo npm`);
1016
+ }
1017
+ },
1018
+ },
967
1019
  ];
968
1020
  }
969
1021
 
@@ -1300,10 +1352,10 @@ open(X) :- candidate(X), \\+ closed(X).
1300
1352
  assertEqual(Boolean(registry.get('phrase', 2)), true, 'Part 3 phrase/2 exists');
1301
1353
  assertEqual(Boolean(registry.get('phrase', 3)), true, 'Part 3 phrase/3 exists');
1302
1354
  assertEqual(registeredNativeEyePrologLibraryNames().length, 0, 'public native EyeProlog builtin count');
1303
- assertEqual(eyePrologPortableLibraryIndicators.length, 44, 'portable Prolog library count');
1355
+ assertEqual(eyePrologPortableLibraryIndicators.length, 46, 'portable Prolog library count');
1304
1356
  assertEqual(eyePrologNativeLibraryIndicators.length, 0, 'native host library count');
1305
1357
  assertEqual(eyePrologNativeLibraryIndicators.join(','), '', 'no EyeProlog library predicate requires host support');
1306
- assertEqual(eyePrologLibraryIndicators.length, 44, 'complete EyeProlog library surface');
1358
+ assertEqual(eyePrologLibraryIndicators.length, 46, 'complete EyeProlog library surface');
1307
1359
  assertEqual(library.get('between', 3), null, 'between/3 remains portable Prolog');
1308
1360
  assertEqual(library.get('smallest_divisor_from', 3), null, 'smallest_divisor_from/3 remains portable Prolog');
1309
1361
  assertEqual(library.get('random', 3), null, 'random/3 remains portable Prolog');
@@ -1331,6 +1383,7 @@ open(X) :- candidate(X), \\+ closed(X).
1331
1383
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'lib', 'eyeprolog.pl')), true, 'portable module exists');
1332
1384
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'standard-library.js')), true, 'standard module registry exists');
1333
1385
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'lib', 'lists.pl')), true, 'lists module exists');
1386
+ assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'lib', 'prologue.pl')), true, 'Prologue module exists');
1334
1387
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'eyeprolog-autoload.js')), false, 'obsolete autoloader is absent');
1335
1388
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'library-source.js')), false, 'duplicate source loader is absent');
1336
1389
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'portable-library.js')), false, 'obsolete duplicate module remains absent');
@@ -79,6 +79,30 @@ Then ask for the derivations:
79
79
  node bin/eyeprolog.js --proof examples/socrates.pl
80
80
  ```
81
81
 
82
+ To use the published package, first verify that `node --version` reports Node.js
83
+ 18 or newer. Upgrade an older runtime through a Node version manager or the
84
+ [official Node.js download](https://nodejs.org/en/download). A current Linux
85
+ distribution can still expose an older Node.js package.
86
+
87
+ The package can be launched without a global installation:
88
+
89
+ ```sh
90
+ npx --yes eyeprolog
91
+ ```
92
+
93
+ For a persistent command without administrator access, install into a
94
+ user-owned prefix:
95
+
96
+ ```sh
97
+ npm install --global --prefix "$HOME/.local" eyeprolog
98
+ export PATH="$HOME/.local/bin:$PATH"
99
+ ```
100
+
101
+ Persist the `PATH` export in the appropriate shell startup file. Do not use
102
+ `sudo npm install`; npm's
103
+ [EACCES guidance](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally/)
104
+ recommends a Node version manager or a user-owned npm prefix.
105
+
82
106
  Readers who do not want to install anything can begin in the
83
107
  [browser playground](https://eyereasoner.github.io/eyeprolog/playground). Paste
84
108
  the source of `examples/socrates.pl` into the editor and run it. The playground
@@ -5580,6 +5604,8 @@ silently changing a static program.
5580
5604
  | `integer_rounding_function` | `toward_zero` | `toward_zero` | no |
5581
5605
  | `char_conversion` | `on` | `on`, `off` | yes |
5582
5606
  | `debug` | `off` | `on`, `off` | yes |
5607
+ | `max_integer` | `unbounded` | `unbounded` | no |
5608
+ | `min_integer` | `unbounded` | `unbounded` | no |
5583
5609
  | `max_arity` | `unbounded` | `unbounded` | no |
5584
5610
  | `unknown` | `fail` | `error`, `fail`, `warning` | yes |
5585
5611
  | `double_quotes` | `chars` | `chars`, `codes`, `atom` | yes |
@@ -5735,17 +5761,20 @@ so side effects occur in Prolog execution order.
5735
5761
 
5736
5762
  ### The EyeProlog library
5737
5763
 
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
5764
+ EyeProlog exposes **46 library predicate indicators** in addition to the 129
5765
+ indicators in its isolated ISO profile. **All 46 are ordinary Prolog clauses**
5766
+ across `src/lib/eyeprolog.pl`, `src/lib/lists.pl`, and `src/lib/prologue.pl`; none
5741
5767
  is a native host predicate. The resulting
5742
- normal EyeProlog language surface is therefore **173 public predicate
5768
+ normal EyeProlog language surface is therefore **175 public predicate
5743
5769
  indicators**. Internally, the runtime registry contains only the 129 ISO
5744
5770
  definitions; the EyeProlog relations are module source clauses.
5745
5771
 
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.
5772
+ The three Prolog files declare `eyeprolog`, `lists`, and `prologue` with
5773
+ `module/2`. A program loads them explicitly with
5774
+ `use_module(library(eyeprolog))`, `use_module(library(lists))`, or
5775
+ `use_module(library(prologue))`; `use_module/2` can select a smaller import
5776
+ list. The last module implements the predicates exercised by the working-draft
5777
+ Prologue quad corpus.
5749
5778
  `src/standard-library.js` only registers the module sources for Node and browser
5750
5779
  resolution and never adds clauses implicitly.
5751
5780
  The isolated ISO-only registry remains
@@ -5759,6 +5788,7 @@ private helpers and same-named predicates in different modules separate.
5759
5788
  | --- | --- |
5760
5789
  | `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
5790
  | `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` |
5791
+ | `library(prologue)` | `member/2`, `append/3`, `length/2`, `between/3`, `select/3`, `succ/2`, `maplist/2` |
5762
5792
 
5763
5793
  <!-- eyeprolog-library-catalog:end -->
5764
5794
 
@@ -5885,7 +5915,7 @@ eyeprolog --goal 'answer(Kind, Value)' program.pl
5885
5915
  The portable text API uses **ISO atoms or proper lists of one-character atoms**.
5886
5916
  A generated text result defaults to an atom. Double-quoted source text uses the
5887
5917
  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
5918
+ already a proper character list accepted by this API. The 46-predicate portable
5889
5919
  library itself has no STRING or JavaScript dependency.
5890
5920
 
5891
5921
  | Predicate and principal mode | Behavior |