eyeprolog 1.3.76 → 1.3.78

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 (39) hide show
  1. package/README.md +6 -6
  2. package/examples/binomial-vandermonde.pl +1 -1
  3. package/examples/book/chapter-26/02-triple.pl +1 -1
  4. package/examples/book/chapter-28/02-triangle.pl +1 -1
  5. package/examples/book/chapter-28/04-integer_rectangle.pl +1 -1
  6. package/examples/book/chapter-29/01-counterexample_to_odd_square.pl +1 -1
  7. package/examples/book/chapter-31/03-double.pl +1 -1
  8. package/examples/book/chapter-32/04-reference_square.pl +1 -1
  9. package/examples/book/chapter-33/03-chosen_pair.pl +1 -1
  10. package/examples/book/chapter-39/01-answer.pl +1 -1
  11. package/examples/catalan-convolution.pl +1 -1
  12. package/examples/collatz-1000.pl +1 -1
  13. package/examples/continued-fraction-sqrt2.pl +1 -1
  14. package/examples/easter-computus.pl +1 -1
  15. package/examples/equality-saturation.pl +1 -1
  16. package/examples/goldbach.pl +1 -1
  17. package/examples/integer-partitions.pl +1 -1
  18. package/examples/job-shop-scheduling.pl +1 -1
  19. package/examples/matrix-chain-order.pl +1 -1
  20. package/examples/missionaries-cannibals.pl +1 -1
  21. package/examples/pell-equation.pl +1 -1
  22. package/examples/polynomial.pl +1 -1
  23. package/examples/prime-range.pl +1 -1
  24. package/examples/stirling-bell-numbers.pl +1 -1
  25. package/examples/totient-summatory.pl +1 -1
  26. package/package.json +1 -1
  27. package/src/iso.js +74 -25
  28. package/src/lib/lists.pl +15 -5
  29. package/src/lib/prologue.pl +30 -166
  30. package/src/program.js +20 -8
  31. package/src/quads.js +24 -3
  32. package/src/repl.js +4 -2
  33. package/src/solver.js +75 -24
  34. package/src/standard-library.js +5 -6
  35. package/test/fixtures/README.md +8 -0
  36. package/test/fixtures/variable_names_quad.pl +163 -0
  37. package/test/run-regression.mjs +106 -6
  38. package/test/test-support.mjs +1 -1
  39. package/the-art-of-eyeprolog.md +30 -23
@@ -1565,6 +1565,18 @@ c4 ?- call((!;1)).
1565
1565
  assertEqual(result.stderr, '', 'quad stderr');
1566
1566
  },
1567
1567
  },
1568
+ {
1569
+ name: 'CLI passes the complete authoritative variable_names quad corpus (issue #69)',
1570
+ run: () => {
1571
+ const filename = path.join(testRoot, 'fixtures', 'variable_names_quad.pl');
1572
+ const source = fs.readFileSync(filename, 'utf8');
1573
+ assertEqual(Program.parse(source).quads.length, 74, 'vendored query-record total');
1574
+ const result = runCli(['-q', filename], { cwd: tmp });
1575
+ assertEqual(result.status, 0, 'quad exit status');
1576
+ assertEqual(result.stdout, 'quads: 75 run, 75 passed, 0 failed.\n', 'quad report');
1577
+ assertEqual(result.stderr, '', 'quad stderr');
1578
+ },
1579
+ },
1568
1580
  {
1569
1581
  name: 'REPL advances anonymous Prologue length checks through I = 28',
1570
1582
  run: () => {
@@ -1814,6 +1826,41 @@ c4 ?- call((!;1)).
1814
1826
  assertEqual(result.stderr, '', 'stderr');
1815
1827
  },
1816
1828
  },
1829
+ {
1830
+ name: 'REPL omits exhausted choicepoints without probing future branches (issue #74)',
1831
+ run: () => {
1832
+ const result = runCli([], {
1833
+ input:
1834
+ 'X=1;X=2.\n;\n' +
1835
+ 'setof(X,true,Xs).\n' +
1836
+ 'findall(t,false,Xs).\n' +
1837
+ 'use_module(library(lists)).\n' +
1838
+ 'member(a,"ba").\n' +
1839
+ 'append("a",Xs,Xs0).\n' +
1840
+ 'halt.\n',
1841
+ });
1842
+ assertEqual(result.status, 0, 'exit status');
1843
+ assertEqual(result.stdout,
1844
+ '?- X = 1\n' +
1845
+ '; X = 2.\n' +
1846
+ '?- Xs = [_A].\n' +
1847
+ '?- Xs = [].\n' +
1848
+ '?- true.\n' +
1849
+ '?- true.\n' +
1850
+ '?- Xs0 = [a | Xs].\n' +
1851
+ '?- ',
1852
+ 'stdout');
1853
+ assertEqual(result.stderr, '', 'stderr');
1854
+
1855
+ const grouped = new Solver(Program.parse(''));
1856
+ const groupedSolutions = grouped.solve([
1857
+ parseGoalText('setof(X,(Y=1,X=a;Y=2,X=b),Xs)'),
1858
+ ], new Env(), 0);
1859
+ assertEqual(groupedSolutions.next().done, false, 'first grouped setof/3 answer');
1860
+ assertEqual(grouped.hasPendingAlternatives(), true, 'genuine grouped setof/3 choicepoint');
1861
+ groupedSolutions.return();
1862
+ },
1863
+ },
1817
1864
  {
1818
1865
  name: 'REPL hides aliases to fresh throw variables while preserving query aliases',
1819
1866
  run: () => {
@@ -1825,6 +1872,15 @@ c4 ?- call((!;1)).
1825
1872
  assertEqual(result.stderr, '', 'stderr');
1826
1873
  },
1827
1874
  },
1875
+ {
1876
+ name: 'REPL displays uncaught user exceptions as throw terms (issue #75)',
1877
+ run: () => {
1878
+ const result = runCli([], { input: 'throw(stopped).\nhalt.\n' });
1879
+ assertEqual(result.status, 0, 'exit status');
1880
+ assertEqual(result.stdout, '?- throw(stopped).\n?- ', 'stdout');
1881
+ assertEqual(result.stderr, '', 'stderr');
1882
+ },
1883
+ },
1828
1884
  {
1829
1885
  name: 'REPL freshens variables displayed in uncaught ISO errors',
1830
1886
  run: () => {
@@ -2830,6 +2886,22 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
2830
2886
  assertEqual(result.stderr, '', 'stderr');
2831
2887
  },
2832
2888
  },
2889
+ {
2890
+ name: 'REPL combines canonical libraries with the Prologue facade',
2891
+ run: () => {
2892
+ const result = runCli([], {
2893
+ input:
2894
+ 'use_module(library(freeze)).\n' +
2895
+ 'use_module(library(lists)).\n' +
2896
+ 'use_module(library(iso_ext)).\n' +
2897
+ 'use_module(library(prologue)).\n' +
2898
+ 'halt.\n',
2899
+ });
2900
+ assertEqual(result.status, 0, 'exit status');
2901
+ assertEqual(result.stdout, '?- true.\n?- true.\n?- true.\n?- true.\n?- ', 'stdout');
2902
+ assertEqual(result.stderr, '', 'stderr');
2903
+ },
2904
+ },
2833
2905
  {
2834
2906
  name: 'REPL halt status is returned by the CLI',
2835
2907
  run: () => {
@@ -4566,7 +4638,7 @@ answer(ok) :-
4566
4638
  const program = Program.parse('answer(X) :- member(X, [a]), between(1, 1, _).\n');
4567
4639
  assertEqual(program.autoloadedPredicates.length, 2, 'autoloaded predicate count');
4568
4640
  assertEqual(program.autoloadedPredicates.map((entry) => `${entry.indicator}:${entry.library}`).sort().join(','),
4569
- 'between/3:prologue,member/2:lists', 'autoload mapping');
4641
+ 'between/3:between,member/2:lists', 'autoload mapping');
4570
4642
  assertEqual(program.interopPortabilityWarnings.length, 0, 'autoloaded portable calls are warning-free');
4571
4643
  },
4572
4644
  },
@@ -5050,9 +5122,9 @@ answer(ok) :-
5050
5122
  assertEqual(registeredNativeEyePrologLibraryNames().length, 49, 'public native EyeProlog builtin count');
5051
5123
  assertEqual(eyePrologPortableLibraryIndicators.length, 87, 'portable Prolog library count');
5052
5124
  assertEqual(eyePrologInteropLibraryIndicators.length, 30, 'cross-implementation interop profile count');
5053
- assertEqual(eyePrologInteropLibraryModules.join(','), 'lists,iso_ext,lambda,atts', 'common explicit library module profile');
5125
+ assertEqual(eyePrologInteropLibraryModules.join(','), 'lists,iso_ext,lambda,atts,freeze', 'common explicit library module profile');
5054
5126
  assertEqual(eyePrologInteropAutoload['member/2'], 'lists', 'member/2 canonical autoload');
5055
- assertEqual(eyePrologInteropAutoload['between/3'], 'prologue', 'between/3 canonical internal autoload');
5127
+ assertEqual(eyePrologInteropAutoload['between/3'], 'between', 'between/3 canonical internal autoload');
5056
5128
  assertEqual(eyePrologInteropAutoload['call_nth/2'], 'iso_ext', 'call_nth/2 canonical interop autoload');
5057
5129
  assertEqual(eyePrologInteropAutoload['call_residue_vars/2'], 'atts', 'call_residue_vars/2 canonical interop autoload');
5058
5130
  assertEqual(eyePrologInteropAutoload['time/1'], 'iso_ext', 'time/1 canonical interop autoload');
@@ -5113,13 +5185,41 @@ answer(ok) :-
5113
5185
  assertEqual(program.findGroup('random', 3)?.module, 'random', 'random/3 is imported from library(random)');
5114
5186
  },
5115
5187
  },
5188
+ {
5189
+ name: 'Prologue facade re-exports canonical library predicates without conflicts',
5190
+ run: () => {
5191
+ const sources = [
5192
+ ':- use_module(library(freeze)).\n:- use_module(library(lists)).\n:- use_module(library(iso_ext)).\n:- use_module(library(prologue)).',
5193
+ ':- use_module(library(prologue)).\n:- use_module(library(freeze)).\n:- use_module(library(lists)).\n:- use_module(library(iso_ext)).',
5194
+ ];
5195
+ for (const source of sources) {
5196
+ const program = Program.parse(source);
5197
+ assertEqual(program.findGroup('freeze', 2)?.module, 'freeze', 'freeze/2 canonical owner');
5198
+ assertEqual(program.findGroup('member', 2)?.module, 'lists', 'member/2 canonical owner');
5199
+ assertEqual(program.findGroup('call_nth', 2)?.module, 'iso_ext', 'call_nth/2 canonical owner');
5200
+ assertEqual(program.findGroup('between', 3)?.module, 'between', 'between/3 canonical owner');
5201
+ }
5202
+
5203
+ const conflicting = new Program();
5204
+ conflicting.defineModule('first', [{ name: 'shared', arity: 1 }]);
5205
+ conflicting.defineModule('second', [{ name: 'shared', arity: 1 }]);
5206
+ conflicting.importModule('user', 'first');
5207
+ let conflict = null;
5208
+ try {
5209
+ conflicting.importModule('user', 'second');
5210
+ } catch (error) {
5211
+ conflict = error;
5212
+ }
5213
+ assertEqual(conflict?.formal, 'permission_error(import, procedure)', 'distinct implementations still conflict');
5214
+ },
5215
+ },
5116
5216
  {
5117
5217
  name: 'aligned bundled libraries have no overlapping full-module exports',
5118
5218
  run: () => {
5119
5219
  const owners = new Map();
5120
5220
  for (const [moduleName, entry] of standardLibrarySources) {
5121
- // library(prologue) is the documented legacy compatibility umbrella;
5122
- // aligned libraries must remain pairwise collision-free so full
5221
+ // library(prologue) is a facade whose nominal exports are re-exports;
5222
+ // canonical libraries must remain pairwise collision-free so full
5123
5223
  // use_module/1 imports can be combined safely.
5124
5224
  if (moduleName === 'prologue') continue;
5125
5225
  const parsed = Program.parse(entry.source);
@@ -6541,7 +6641,7 @@ child.on('close', (status, signal) => {
6541
6641
 
6542
6642
  function runCli(args, options = {}) {
6543
6643
  return spawnSync(process.execPath, [bin, ...args], {
6544
- cwd: packageRoot,
6644
+ cwd: options.cwd ?? packageRoot,
6545
6645
  encoding: 'utf8',
6546
6646
  env: options.env ? { ...process.env, ...options.env } : process.env,
6547
6647
  input: options.input ?? undefined,
@@ -10,7 +10,7 @@ const standardModulePrelude = `:- use_module(library(aggregate)).
10
10
  :- use_module(library(iso_ext)).
11
11
  :- use_module(library(lists)).
12
12
  :- use_module(library(primes)).
13
- :- use_module(library(prologue), [between/3]).
13
+ :- use_module(library(between), [between/3]).
14
14
  :- use_module(library(random)).
15
15
  :- use_module(library(strings)).
16
16
  :- use_module(library(uuid)).
@@ -3568,7 +3568,7 @@ methods.
3568
3568
  Consider a relation for a Pythagorean triple:
3569
3569
 
3570
3570
  ```eyeprolog
3571
- :- use_module(library(prologue), [between/3]).
3571
+ :- use_module(library(between), [between/3]).
3572
3572
  :- use_module(library(lists)).
3573
3573
 
3574
3574
  triple(A, B, C) :-
@@ -3880,7 +3880,7 @@ permutations repeats the same geometric object six times. Ordering the sides
3880
3880
  removes the symmetry:
3881
3881
 
3882
3882
  ```eyeprolog
3883
- :- use_module(library(prologue), [between/3]).
3883
+ :- use_module(library(between), [between/3]).
3884
3884
  :- use_module(library(lists)).
3885
3885
 
3886
3886
  triangle(A, B, C) :-
@@ -3918,7 +3918,7 @@ it from width and height. With a finite generator it can also search for
3918
3918
  factorizations:
3919
3919
 
3920
3920
  ```eyeprolog
3921
- :- use_module(library(prologue), [between/3]).
3921
+ :- use_module(library(between), [between/3]).
3922
3922
  :- use_module(library(lists)).
3923
3923
 
3924
3924
  integer_rectangle(Area, W, H) :-
@@ -4031,7 +4031,7 @@ theorem over an unbounded domain.
4031
4031
  This boundary can be written directly:
4032
4032
 
4033
4033
  ```eyeprolog
4034
- :- use_module(library(prologue), [between/3]).
4034
+ :- use_module(library(between), [between/3]).
4035
4035
  :- use_module(library(lists)).
4036
4036
 
4037
4037
  counterexample_to_odd_square(N) :-
@@ -4470,7 +4470,7 @@ Examples test selected points. A finite generated property tests every point
4470
4470
  in a declared scope:
4471
4471
 
4472
4472
  ```eyeprolog
4473
- :- use_module(library(prologue), [between/3]).
4473
+ :- use_module(library(between), [between/3]).
4474
4474
  :- use_module(library(lists)).
4475
4475
 
4476
4476
  double(N, D) :- (D is N + N).
@@ -4724,7 +4724,7 @@ compare it with the optimized one. The reference may be slow; its purpose is
4724
4724
  clarity.
4725
4725
 
4726
4726
  ```eyeprolog
4727
- :- use_module(library(prologue), [between/3]).
4727
+ :- use_module(library(between), [between/3]).
4728
4728
  :- use_module(library(lists)).
4729
4729
 
4730
4730
  reference_square(N, S) :-
@@ -4865,7 +4865,7 @@ explanation until none can be reasoned about separately.
4865
4865
  in dependency order, then construct a witness or reason.
4866
4866
 
4867
4867
  ```eyeprolog
4868
- :- use_module(library(prologue), [between/3]).
4868
+ :- use_module(library(between), [between/3]).
4869
4869
  :- use_module(library(lists)).
4870
4870
 
4871
4871
  chosen_pair(pair(X, Y), reason(sum_is_ten)) :-
@@ -6222,17 +6222,23 @@ clauses over private adapters. `time/1` is additionally registered directly in
6222
6222
  the normal EyeProlog runtime so Trealla-style timing works at the interactive
6223
6223
  top level without an import; it is absent from the strict ISO registry.
6224
6224
 
6225
- The sources are `src/lib/aggregate.pl`, `src/lib/atts.pl`, `src/lib/clpz.pl`, `src/lib/comparison.pl`,
6226
- `src/lib/dates.pl`, `src/lib/iso_ext.pl`, `src/lib/lambda.pl`,
6227
- `src/lib/lists.pl`, `src/lib/primes.pl`, `src/lib/prologue.pl`,
6228
- `src/lib/random.pl`, `src/lib/strings.pl`, and `src/lib/uuid.pl`. Each declares a same-named module
6225
+ The sources are `src/lib/aggregate.pl`, `src/lib/arithmetic.pl`,
6226
+ `src/lib/assoc.pl`, `src/lib/atts.pl`, `src/lib/between.pl`,
6227
+ `src/lib/clpz.pl`, `src/lib/comparison.pl`, `src/lib/dates.pl`,
6228
+ `src/lib/dcgs.pl`, `src/lib/debug.pl`, `src/lib/error.pl`,
6229
+ `src/lib/format.pl`, `src/lib/freeze.pl`, `src/lib/iso_ext.pl`,
6230
+ `src/lib/lambda.pl`, `src/lib/lists.pl`, `src/lib/pairs.pl`,
6231
+ `src/lib/primes.pl`, `src/lib/prologue.pl`, `src/lib/random.pl`,
6232
+ `src/lib/si.pl`, `src/lib/strings.pl`, `src/lib/terms.pl`, and
6233
+ `src/lib/uuid.pl`. Each declares a same-named module
6229
6234
  with `module/2`; there is no catch-all `library(eyeprolog)`. A program imports
6230
6235
  only the modules it needs, and
6231
6236
  `use_module/2` can select an even smaller indicator list. The Prologue
6232
- module implements p.p.1 through p.p.11 of the
6237
+ module exposes p.p.1 through p.p.11 of the
6233
6238
  [working-draft Prologue](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue),
6234
- with the published Prologue, `call_nth/2`, and `length/2` quads retained as
6235
- offline regressions. `src/standard-library.js` registers the module sources and private control and
6239
+ as a compatibility facade over the canonical `lists`, `between`, `iso_ext`,
6240
+ and `freeze` modules. The published Prologue, `call_nth/2`, and `length/2`
6241
+ quads are retained as offline regressions. `src/standard-library.js` registers the module sources and private control and
6236
6242
  constraint adapters for Node and browser resolution. Explicit `use_module/1-2`
6237
6243
  loads remain supported; outside strict ISO mode, the conservative interop
6238
6244
  autoloader may also load a module for one uniquely mapped common predicate.
@@ -6294,7 +6300,9 @@ The current interoperability profile recognizes these library roles:
6294
6300
  | `library(lists)` | Common list module. A conservative subset of its exports is in the shared predicate profile. |
6295
6301
  | `library(iso_ext)` | Common extension-module name. `call_nth/2`, `time/1`, and the `... //0` arbitrary-sequence helper are conservatively autoloaded for cross-engine source. |
6296
6302
  | `library(lambda)` | Scryer-aligned higher-order notation. It is imported explicitly because loading it also installs the `+\` operator. |
6297
- | `library(prologue)` | EyeProlog compatibility module, not a common interop library name. `between/3` is nevertheless autoloaded from it so portable source need not name this EyeProlog-specific provider. |
6303
+ | `library(atts)` | Common attributed-variable interface used by constraint libraries. |
6304
+ | `library(freeze)` | Common delayed-goal module backed by attributed variables. |
6305
+ | `library(prologue)` | EyeProlog compatibility facade, not a common interop library name. Its exports resolve to canonical focused modules instead of duplicate implementations. |
6298
6306
 
6299
6307
  Other modules from the catalog, including `library(clpz)`, `library(strings)`,
6300
6308
  `library(random)`, and `library(uuid)`, remain normal EyeProlog libraries. They
@@ -6323,11 +6331,10 @@ for example `% Time elapsed 0.832s, 65551 Inferences, 0.079 MLips`; `... //0`
6323
6331
  describes an arbitrary number of input elements. Together they let the
6324
6332
  Trealla/Scryer DCG hand-off benchmark run in EyeProlog without source changes
6325
6333
  (assuming the usual list library is already imported in an interactive
6326
- session). The interop exports of `library(lists)` and
6327
- `library(iso_ext)` are kept disjoint, so both modules can be imported together
6328
- without an accidental collision. `library(prologue)` remains a compatibility
6329
- umbrella and overlaps them; use selective imports when legacy code combines it
6330
- with the aligned modules.
6334
+ session). The focused modules have one canonical implementation owner per
6335
+ predicate. `library(prologue)` re-exports those same owners, so legacy code can
6336
+ combine the facade with `library(lists)`, `library(iso_ext)`, and
6337
+ `library(freeze)` in either import order without an accidental collision.
6331
6338
 
6332
6339
  `library(lambda)` follows Scryer's higher-order notation, adapted from Ulrich
6333
6340
  Neumerkel's permissively licensed implementation. Its public syntax is:
@@ -6372,7 +6379,7 @@ extension. For example, the canonical build-time providers are:
6372
6379
  | `call_nth/2` | `library(iso_ext)` |
6373
6380
  | `time/1` | `library(iso_ext)` |
6374
6381
  | `.../2` | `library(iso_ext)` |
6375
- | `between/3` | `library(prologue)` |
6382
+ | `between/3` | `library(between)` |
6376
6383
 
6377
6384
  Predicates outside that table require an explicit import even when EyeProlog
6378
6385
  provides them. Explicit imports therefore remain the clearest way to state
@@ -6494,7 +6501,7 @@ before calling the corresponding predicate.
6494
6501
 
6495
6502
  ```eyeprolog
6496
6503
  :- use_module(library(dates)).
6497
- :- use_module(library(prologue), [between/3]).
6504
+ :- use_module(library(between), [between/3]).
6498
6505
  :- use_module(library(random)).
6499
6506
  :- use_module(library(lists)).
6500
6507
 
@@ -6515,7 +6522,7 @@ expresses them: for example, `R is A + B`, `R is abs(A)`, and
6515
6522
  modulo, powers, sine, cosine, exponential, logarithm, and the ISO rounding
6516
6523
  functions.
6517
6524
 
6518
- The bundled library layer defines `between/3` in `library(prologue)` and
6525
+ The bundled library layer defines `between/3` in `library(between)` and
6519
6526
  `smallest_divisor_from/3` in `library(primes)` as ordinary Prolog clauses.
6520
6527
  Choose the smaller or
6521
6528
  larger of two arithmetic values directly with ISO control, for example