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
package/src/program.js CHANGED
@@ -180,12 +180,18 @@ export class Program {
180
180
  for (let i = 0; i < head.arity; i++) indexOne(group.argIndexes[i], head.args[i], clause, group.clauses, clausePosition);
181
181
  }
182
182
  findGroup(name, arity, module = 'user') {
183
- const direct = this.groups.get(modulePredicateKey(module, name, arity));
184
- if (direct) return direct;
185
- const importedModule = this.moduleImports.get(module)?.get(`${name}/${arity}`);
186
- return importedModule == null
187
- ? null
188
- : this.groups.get(modulePredicateKey(importedModule, name, arity)) ?? null;
183
+ const indicator = `${name}/${arity}`;
184
+ const visited = new Set();
185
+ let current = module;
186
+ while (!visited.has(current)) {
187
+ visited.add(current);
188
+ const direct = this.groups.get(modulePredicateKey(current, name, arity));
189
+ if (direct) return direct;
190
+ const importedModule = this.moduleImports.get(current)?.get(indicator);
191
+ if (importedModule == null) return null;
192
+ current = importedModule;
193
+ }
194
+ return null;
189
195
  }
190
196
  defineModule(name, exports, filename = '<input>') {
191
197
  const indicators = new Map(exports.map((indicator) => [`${indicator.name}/${indicator.arity}`, indicator]));
@@ -202,10 +208,16 @@ export class Program {
202
208
  throw new PrologError('existence_error(procedure)', compound('/', [atom(indicator.name), numberTerm(indicator.arity)]));
203
209
  }
204
210
  const previous = imports.get(key);
205
- if (previous != null && previous !== source) {
211
+ const resolved = this.findGroup(indicator.name, indicator.arity, source);
212
+ const canonicalSource = resolved?.module ?? source;
213
+ const previousResolved = previous == null
214
+ ? null
215
+ : this.findGroup(indicator.name, indicator.arity, previous);
216
+ const canonicalPrevious = previousResolved?.module ?? previous;
217
+ if (previous != null && canonicalPrevious !== canonicalSource) {
206
218
  throw new PrologError('permission_error(import, procedure)', compound('/', [atom(indicator.name), numberTerm(indicator.arity)]));
207
219
  }
208
- imports.set(key, source);
220
+ imports.set(key, canonicalSource);
209
221
  }
210
222
  this.moduleImports.set(target, imports);
211
223
  }
package/src/quads.js CHANGED
@@ -110,10 +110,15 @@ function checkAlternative(program, quad, alternative, options, context) {
110
110
  const actual = executeQuery(program, quad.query, input, maxSolutions, {
111
111
  ...options,
112
112
  detectLoops: leaves.some((leaf) => leaf.loops),
113
+ detectWaits: leaves.some((leaf) => leaf.waits),
113
114
  inputBoundary: hasInputSpec,
114
115
  inputPeek: peek,
115
116
  });
116
117
 
118
+ if (leaves.some((leaf) => leaf.waits)) {
119
+ return { ok: leaves.length === 1 && actual.inputWaitObserved };
120
+ }
121
+
117
122
  if (requiresSto) {
118
123
  // Trealla currently treats the answer part of an `sto`-annotated leaf as
119
124
  // implementation-dependent and skips it. EyeProlog can strengthen that
@@ -199,6 +204,7 @@ function describeLeaf(term) {
199
204
  false: false,
200
205
  truth: false,
201
206
  loops: false,
207
+ waits: false,
202
208
  error: null,
203
209
  input: null,
204
210
  peek: null,
@@ -213,7 +219,8 @@ function describeLeaf(term) {
213
219
  else if (item.name === '...' || item.name === 'ad_infinitum') leaf.more = true;
214
220
  else if (item.name === 'sto') leaf.sto = true;
215
221
  else if (item.name === 'loops') leaf.loops = true;
216
- else if (item.name === 'waits' || item.name === 'other_answer_sequence') {
222
+ else if (item.name === 'waits') leaf.waits = true;
223
+ else if (item.name === 'other_answer_sequence') {
217
224
  leaf.unsupported ??= item;
218
225
  } else if (item.name === 'false') leaf.false = true;
219
226
  else if (item.name === 'true') leaf.truth = true;
@@ -246,7 +253,8 @@ function describeLeaf(term) {
246
253
  if (isErrorDescription(item)) leaf.error = item;
247
254
  else leaf.malformed ??= item;
248
255
  }
249
- leaf.hasExpectation = leaf.bindings.length > 0 || leaf.truth || leaf.false || leaf.loops || leaf.error != null || leaf.output != null;
256
+ leaf.hasExpectation = leaf.bindings.length > 0 || leaf.truth || leaf.false || leaf.loops || leaf.waits ||
257
+ leaf.error != null || leaf.output != null;
250
258
  if (!leaf.hasExpectation && !leaf.more && !leaf.sto && leaf.unsupported == null) leaf.malformed ??= term;
251
259
  if ([leaf.false, leaf.truth, leaf.error != null].filter(Boolean).length > 1) leaf.malformed ??= term;
252
260
  return leaf;
@@ -254,6 +262,7 @@ function describeLeaf(term) {
254
262
 
255
263
  function executeQuery(program, query, input, maxSolutions, options) {
256
264
  let pendingOutput = '';
265
+ let inputWaitObserved = false;
257
266
  const boundedInput = options.inputBoundary
258
267
  ? `${input}${options.inputPeek ?? ''}${INVALID_UTF8_SENTINEL}`
259
268
  : input;
@@ -274,13 +283,24 @@ function executeQuery(program, query, input, maxSolutions, options) {
274
283
  write: (text) => { pendingOutput += String(text); },
275
284
  },
276
285
  });
286
+ if (options.detectWaits) {
287
+ const inputStream = solver.io.resolve('user_input');
288
+ const observeWait = () => {
289
+ inputWaitObserved = true;
290
+ return null;
291
+ };
292
+ inputStream.interactiveReadTerm = observeWait;
293
+ inputStream.interactiveReadUnit = observeWait;
294
+ }
277
295
  if (options.inputBoundary) {
278
296
  // Quads need a boundary that is neither EOF nor a valid character. This
279
297
  // mirrors Trealla's sentinel technique: inputs/1 says exactly what was
280
298
  // consumed, while peeks/1 supplies one additional character that must
281
299
  // remain unread. If the query asks for anything beyond that boundary it
282
300
  // sees representation_error(character), not an artificial EOF.
283
- solver.io.resolve('user_input').strictUtf8 = true;
301
+ const inputStream = solver.io.resolve('user_input');
302
+ inputStream.strictUtf8 = true;
303
+ inputStream.syntheticInputBoundary = input.length;
284
304
  }
285
305
  // Undefined predicates are test failures rather than silent negative
286
306
  // answers unless the source explicitly selected another unknown policy.
@@ -338,6 +358,7 @@ function executeQuery(program, query, input, maxSolutions, options) {
338
358
  inputRemainder,
339
359
  complete,
340
360
  loopObserved,
361
+ inputWaitObserved,
341
362
  stoObserved: solver.occursCheckObserved,
342
363
  nstoObserved: complete && !solver.occursCheckObserved && !loopObserved &&
343
364
  !bounded && !resourceInterrupted,
package/src/repl.js CHANGED
@@ -803,7 +803,7 @@ function letterName(index) {
803
803
  }
804
804
 
805
805
  function formatError(engine, state, error) {
806
- if (error?.name === 'PrologError') {
806
+ if (error?.name === 'PrologError' || (error?.name === 'ThrownTerm' && error.term != null)) {
807
807
  const env = new engine.Env();
808
808
  const variableNames = new Map();
809
809
  let generated = 0;
@@ -811,7 +811,9 @@ function formatError(engine, state, error) {
811
811
  // Reuse the same conversion as catch/3 so uncaught errors at the top
812
812
  // level cannot lose the implementation-defined context or misplace a
813
813
  // culprit as the second argument of error/2.
814
- const term = formalErrorTerm(error);
814
+ const term = error.name === 'ThrownTerm'
815
+ ? engine.compound('throw', [error.term])
816
+ : formalErrorTerm(error);
815
817
  collectUnboundVariables(engine, term, env, variableNames, () => `_${letterName(generated++)}`);
816
818
  return `${engine.formatTermForWrite(term, env, {
817
819
  quoted: true,
package/src/solver.js CHANGED
@@ -607,14 +607,16 @@ export class Solver {
607
607
  const def = callable ? this.registry.get(goal.name, goal.arity) : null;
608
608
  this.active = active;
609
609
  if (def && builtinIsReadyOrAuthoritative(def, this, goal, env)) {
610
+ const deterministic = def.deterministic ||
611
+ def.deterministicWhen?.({ solver: this, goal, env }) === true;
610
612
  const iterator = def.handler({ solver: this, goal, env });
611
613
  const firstResult = iterator.next();
612
- if (def.deterministic) {
614
+ if (deterministic) {
613
615
  if (!firstResult.done) this.stats.deterministic_builtin_successes++;
614
616
  else this.stats.deterministic_builtin_failures++;
615
617
  }
616
618
  if (firstResult.done) break;
617
- if (!def.deterministic) {
619
+ if (!deterministic) {
618
620
  stack.push({
619
621
  kind: 'resumeBuiltin',
620
622
  iterator,
@@ -819,11 +821,22 @@ export class Solver {
819
821
  }
820
822
  }
821
823
 
822
- hasPendingAlternatives() {
823
- // When solve() is suspended at an answer, active solve stacks contain only
824
- // unexplored work. The demand-driven REPL uses this without speculatively
825
- // pulling the next answer.
826
- return this.solveStacks.some((stack) => stack.length !== 0);
824
+ hasPendingAlternatives(excludedStacks = null) {
825
+ // When solve() is suspended at an answer, active solve stacks contain
826
+ // unexplored work or an iterator frame that can prove it just yielded its
827
+ // final answer. The demand-driven REPL uses this without speculatively
828
+ // pulling the next answer. Control builtins can exclude their caller's
829
+ // pre-existing stacks when inspecting a nested branch.
830
+ return this.solveStacks.some((stack) => !excludedStacks?.has(stack) && stack.some((frame) => {
831
+ if (frame.kind === 'resumeBuiltin' &&
832
+ typeof frame.iterator?.hasPendingAlternatives === 'function') {
833
+ return frame.iterator.hasPendingAlternatives();
834
+ }
835
+ if (frame.kind === 'userClause' && headCannotMatch(frame.goal, frame.clause.head, frame.env)) {
836
+ return false;
837
+ }
838
+ return true;
839
+ }));
827
840
  }
828
841
 
829
842
  fastCountGoal(goal, env) {
@@ -1428,19 +1441,22 @@ function pushWfsAnswerFrames(stack, model, group, goal, rest, env, depth, active
1428
1441
 
1429
1442
  function bundledBetweenIterator(solver, group, goal, env) {
1430
1443
  if (solver.registry.eyePrologLibrary !== true ||
1431
- group.module !== 'prologue' || group.name !== 'between' || group.arity !== 3 ||
1444
+ group.module !== 'between' || group.name !== 'between' || group.arity !== 3 ||
1432
1445
  group.bundledLibrary !== true || group.clauses.length !== 1) {
1433
1446
  return null;
1434
1447
  }
1435
1448
 
1436
- // The portable Prologue definition is intentionally kept as the semantic
1449
+ // The portable library(between) definition is kept as the semantic
1437
1450
  // source of between/3. Its recursive helper, however, carries the output
1438
1451
  // variable through one fresh clause instance per integer. With persistent
1439
1452
  // environments that builds a growing variable-alias chain, so dereferencing
1440
1453
  // the generated value in the caller repeatedly revisits all earlier frames.
1441
1454
  // Enumerate the canonical bundled relation directly while leaving user
1442
1455
  // definitions and non-EyeProlog registries on the ordinary Prolog path.
1443
- return bundledBetweenSolutions(solver, goal, env);
1456
+ const state = { pending: true };
1457
+ const iterator = bundledBetweenSolutions(solver, goal, env, state);
1458
+ iterator.hasPendingAlternatives = () => state.pending;
1459
+ return iterator;
1444
1460
  }
1445
1461
 
1446
1462
  function requireBetweenInteger(term, env) {
@@ -1452,7 +1468,7 @@ function requireBetweenInteger(term, env) {
1452
1468
  return BigInt(value.name);
1453
1469
  }
1454
1470
 
1455
- function* bundledBetweenSolutions(solver, goal, env) {
1471
+ function* bundledBetweenSolutions(solver, goal, env, state) {
1456
1472
  const lower = requireBetweenInteger(goal.args[0], env);
1457
1473
  const upper = requireBetweenInteger(goal.args[1], env);
1458
1474
  const requested = deref(goal.args[2], env);
@@ -1462,20 +1478,28 @@ function* bundledBetweenSolutions(solver, goal, env) {
1462
1478
  throw new PrologError('type_error(integer)', requested);
1463
1479
  }
1464
1480
  const value = BigInt(requested.name);
1465
- if (value >= lower && value <= upper) yield env;
1481
+ if (value >= lower && value <= upper) {
1482
+ state.pending = false;
1483
+ yield env;
1484
+ }
1485
+ state.pending = false;
1466
1486
  return;
1467
1487
  }
1468
1488
 
1469
1489
  for (let value = lower; value <= upper; value++) {
1470
1490
  const next = env.clone();
1471
1491
  solver.stats.unify_calls++;
1472
- if (unify(goal.args[2], numberTerm(value), next)) yield next;
1492
+ if (unify(goal.args[2], numberTerm(value), next)) {
1493
+ state.pending = value < upper;
1494
+ yield next;
1495
+ }
1473
1496
  }
1497
+ state.pending = false;
1474
1498
  }
1475
1499
 
1476
1500
  function bundledMemberIterator(solver, group, goal, env) {
1477
1501
  if (solver.registry.eyePrologLibrary !== true ||
1478
- !['lists', 'prologue'].includes(group.module) || group.name !== 'member' || group.arity !== 2 ||
1502
+ group.module !== 'lists' || group.name !== 'member' || group.arity !== 2 ||
1479
1503
  group.bundledLibrary !== true || group.clauses.length !== 2) {
1480
1504
  return null;
1481
1505
  }
@@ -1487,17 +1511,33 @@ function bundledMemberIterator(solver, group, goal, env) {
1487
1511
  let cursor = deref(goal.args[1], env);
1488
1512
  while (isCons(cursor)) cursor = deref(cursor.args[1], env);
1489
1513
  if (!isEmptyList(cursor)) return null;
1490
- return bundledMemberSolutions(solver, goal, env);
1514
+ const state = { pending: true };
1515
+ const iterator = bundledMemberSolutions(solver, goal, env, state);
1516
+ iterator.hasPendingAlternatives = () => state.pending;
1517
+ return iterator;
1491
1518
  }
1492
1519
 
1493
- function* bundledMemberSolutions(solver, goal, env) {
1520
+ function* bundledMemberSolutions(solver, goal, env, state) {
1494
1521
  let cursor = deref(goal.args[1], env);
1495
1522
  while (isCons(cursor)) {
1523
+ const item = cursor.args[0];
1524
+ cursor = deref(cursor.args[1], env);
1496
1525
  const next = env.clone();
1497
1526
  solver.stats.unify_calls++;
1498
- if (unify(goal.args[0], cursor.args[0], next)) yield next;
1527
+ if (unify(goal.args[0], item, next)) {
1528
+ state.pending = bundledMemberMayMatch(goal.args[0], cursor, env);
1529
+ yield next;
1530
+ }
1531
+ }
1532
+ state.pending = false;
1533
+ }
1534
+
1535
+ function bundledMemberMayMatch(target, cursor, env) {
1536
+ while (isCons(cursor)) {
1537
+ if (!goalHeadTermsCannotMatch(target, cursor.args[0], env)) return true;
1499
1538
  cursor = deref(cursor.args[1], env);
1500
1539
  }
1540
+ return false;
1501
1541
  }
1502
1542
 
1503
1543
  function bundledEllipsisPlan(solver, group, goal, rest, env) {
@@ -1588,7 +1628,7 @@ function* bundledEllipsisSolutions(solver, output, input, env) {
1588
1628
 
1589
1629
  function bundledLengthIterator(solver, group, goal, env) {
1590
1630
  if (solver.registry.eyePrologLibrary !== true ||
1591
- !['lists', 'prologue'].includes(group.module) || group.name !== 'length' || group.arity !== 2 ||
1631
+ group.module !== 'lists' || group.name !== 'length' || group.arity !== 2 ||
1592
1632
  group.bundledLibrary !== true || group.clauses.length !== 2) {
1593
1633
  return null;
1594
1634
  }
@@ -1900,7 +1940,7 @@ function findallLengthFusion(solver, goal, rest, env) {
1900
1940
  if (lengthGoal.type !== COMPOUND || lengthGoal.name !== 'length' || lengthGoal.arity !== 2) return null;
1901
1941
  const lengthGroup = solver.program.findGroup('length', 2, lengthGoal.module ?? 'user');
1902
1942
  if (!lengthGroup || lengthGroup.bundledLibrary !== true ||
1903
- !['lists', 'prologue'].includes(lengthGroup.module)) return null;
1943
+ lengthGroup.module !== 'lists') return null;
1904
1944
  const lengthList = derefForLocal(lengthGoal.args[0], env);
1905
1945
  const countArg = derefForLocal(lengthGoal.args[1], env);
1906
1946
  if (lengthList.type !== 'var' || lengthList.name !== bag.name || countArg.type !== 'var') return null;
@@ -2849,11 +2889,22 @@ function headCannotMatch(goal, head, env) {
2849
2889
  if (goal.type !== COMPOUND || head.type !== COMPOUND) return false;
2850
2890
  if (goal.name !== head.name || goal.arity !== head.arity) return true;
2851
2891
  for (let i = 0; i < goal.arity; i++) {
2852
- const a = goal.args[i];
2853
- const b = head.args[i];
2854
- // Keep this only as a cheap scalar rejection. unify() remains authoritative.
2855
- const da = derefForLocal(a, env);
2856
- if (isScalarTerm(da) && isScalarTerm(b) && !sameScalarTerm(da, b)) return true;
2892
+ if (goalHeadTermsCannotMatch(goal.args[i], head.args[i], env)) return true;
2893
+ }
2894
+ return false;
2895
+ }
2896
+
2897
+ function goalHeadTermsCannotMatch(goalTerm, headTerm, env) {
2898
+ const pending = [[goalTerm, headTerm]];
2899
+ while (pending.length > 0) {
2900
+ const [goalItem, headItem] = pending.pop();
2901
+ const actual = derefForLocal(goalItem, env);
2902
+ if (actual.type === VAR || headItem.type === VAR) continue;
2903
+ if (actual.type !== headItem.type || actual.name !== headItem.name || actual.arity !== headItem.arity) return true;
2904
+ if (actual.type !== COMPOUND) continue;
2905
+ for (let index = 0; index < actual.arity; index++) {
2906
+ pending.push([actual.args[index], headItem.args[index]]);
2907
+ }
2857
2908
  }
2858
2909
  return false;
2859
2910
  }
@@ -141,11 +141,10 @@ export const eyePrologInteropAutoload = Object.freeze({
141
141
  // unchanged while their implementations remain outside the ISO core.
142
142
  'time/1': 'iso_ext',
143
143
  '.../2': 'iso_ext',
144
- // Trealla and Scryer expose between/3 without an EyeProlog-style
145
- // library(prologue) dependency. EyeProlog keeps its implementation in the
146
- // Prologue module but autoloads it so portable source need not name that
147
- // implementation-specific library.
148
- 'between/3': 'prologue',
144
+ // Scryer provides library(between), while Trealla exposes the relation as a
145
+ // system predicate. Keep EyeProlog's canonical portable owner in the
146
+ // specialized module; library(prologue) re-exports it for compatibility.
147
+ 'between/3': 'between',
149
148
  });
150
149
 
151
150
  export const eyePrologInteropLibraryIndicators = Object.freeze(
@@ -155,7 +154,7 @@ export const eyePrologInteropLibraryIndicators = Object.freeze(
155
154
  // Libraries whose *name* is part of the current interop profile. A program
156
155
  // may freely use_module/1 with these common module names; predicates in those
157
156
  // modules outside eyePrologInteropLibraryIndicators are still diagnosed when used.
158
- export const eyePrologInteropLibraryModules = Object.freeze(['lists', 'iso_ext', 'lambda', 'atts']);
157
+ export const eyePrologInteropLibraryModules = Object.freeze(['lists', 'iso_ext', 'lambda', 'atts', 'freeze']);
159
158
 
160
159
  function* tabledNegationBuiltin({ solver, goal, env }) {
161
160
  yield* solver.solveTabledNegation(goal.args[0], env);
@@ -9,6 +9,14 @@ 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
11
 
12
+ `variable_names_quad.pl` is an unmodified snapshot of the 75 machine-readable
13
+ quads for the ISO read and write option `variable_names/1`:
14
+
15
+ <https://www.complang.tuwien.ac.at/ulrich/iso-prolog/variable_names_quad.pl>
16
+
17
+ Retrieved on 2026-08-25. It is vendored so all input, output, waiting, and
18
+ error cases remain release-gated.
19
+
12
20
  `prologue_quad.pl` is an unmodified snapshot of the 33 machine-readable quads
13
21
  for the predicates proposed by the Prolog Prologue working draft:
14
22
 
@@ -0,0 +1,163 @@
1
+ 1 ?- write_term(T,[quoted(true),variable_names([N=T])]).
2
+ instantiation_error.
3
+ 2 ?- N = 'X', /**/write_term(T,[quoted(true),variable_names([N=T])]).
4
+ outputs("X"), N = 'X'.
5
+ outputs("X"), N = ... . % Same, ignoring 'X'
6
+ 3 ?- N = T, /**/write_term(T,[quoted(true),variable_names([N=T])]).
7
+ instantiation_error.
8
+ 4 ?- N = '_', /**/write_term(T,[quoted(true),variable_names([N=T])]).
9
+ outputs("_"), N = ... .
10
+ 65 ?- N = '_/*.*/', /**/write_term(T,[quoted(true),variable_names([N=T])]).
11
+ outputs("_/*.*/"), N = ... .
12
+ 5 ?- N = x, /**/write_term(T,[quoted(true),variable_names([N=T])]).
13
+ outputs("x"), N = ... .
14
+ 6 ?- N = 'x+y', /**/write_term(T,[quoted(true),variable_names([N=T])]).
15
+ outputs("x+y"), N = ... .
16
+ 50 ?- N = '))', /**/write_term(T,[quoted(true),variable_names([N=T])]).
17
+ outputs("))"), N = ... .
18
+ 7 ?- N = 7, /**/write_term(T,[quoted(true),variable_names([N=T])]).
19
+ domain_error(write_option, variable_names(...)).
20
+ 8 ?- N = 1+2, /**/write_term(T,[quoted(true),variable_names([N=T])]).
21
+ domain_error(write_option, variable_names(...)).
22
+ 9 ?- N = '$VAR'(9), /**/write_term(T,[quoted(true),variable_names([N=T])]).
23
+ domain_error(write_option, variable_names(...)).
24
+ 10 ?- T = a, /**/write_term(T,[quoted(true),variable_names([N=T])]).
25
+ instantiation_error.
26
+ 11 ?- T = a, N = 'Any', /**/write_term(T,[quoted(true),variable_names([N=T])]).
27
+ outputs("a"), T = ..., N = ... .
28
+ 12 ?- T = '$VAR'(9), N = '_', /**/write_term(T,[quoted(true),variable_names([N=T])]).
29
+ outputs("'$VAR'(9)"), T = ..., N = ... .
30
+ 74 ?- T = f(_), N = 'Bad', /**/write_term(T,[quoted(true),variable_names([N=T])]).
31
+ outputs(("f(_",...,")")), T = ..., N = ... .
32
+ 28 ?- freeze(T,throw(g(T))), N = 'X', /**/write_term(T,[quoted(true),variable_names([N=T])]), false.
33
+ outputs("X"), false.
34
+ 13 ?- write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
35
+ outputs(("_",...)).
36
+ 14 ?- T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
37
+ outputs("X,Y,Z"), T = ... .
38
+ 15 ?- Z=Y, T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
39
+ outputs("X,Y,Y"), Z = Y, T = ... .
40
+ 16 ?- Z=Y, Y=X, T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
41
+ outputs("X,X,X"), Z = X, Y = X, T = ... .
42
+ 17 ?- T=(Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
43
+ outputs("Y,Z"), T = ... .
44
+ 18 ?- T=(Z,Y), /**/write_term(T,[quoted(true),variable_names(['X'=X,'Y'=Y,'Z'=Z])]).
45
+ outputs("Z,Y"), T = ... .
46
+ 19 ?- write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
47
+ outputs(("_",...)).
48
+ 20 ?- T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
49
+ outputs("X,Y,Z"), T = ... .
50
+ 21 ?- Z=Y, T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
51
+ outputs("X,Z,Z"), Z = Y, T = ... .
52
+ 22 ?- Z=Y, Y=X, T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
53
+ outputs("Z,Z,Z"), Z = X, Y = X, T = ... .
54
+ 23 ?- T=(Y,Z), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
55
+ outputs("Y,Z"), T = ... .
56
+ 24 ?- T=(Z,Y), /**/write_term(T,[quoted(true),variable_names(['Z'=Z,'Y'=Y,'X'=X])]).
57
+ outputs("Z,Y"), T = ... .
58
+ 25 ?- write_term(T,[quoted(true),variable_names(['X'=Z,'X'=Y,'X'=X])]).
59
+ outputs(("_",...)).
60
+ 26 ?- T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=Z,'X'=Y,'X'=X])]).
61
+ outputs("X,X,X"), T = ... .
62
+ 27 ?- T=(1,2,3), T=(X,Y,Z), /**/write_term(T,[quoted(true),variable_names(['X'=Z,'X'=Y,'X'=X])]).
63
+ outputs("1,2,3"), T = ..., X = 1, Y = 2, Z = 3.
64
+ 32 ?- read_term(T,[variable_names(VN_list)]),VN_list=[_=1,_=2,_=3],writeq(VN_list).
65
+ waits.
66
+ 29 ?- /**/read_term(T,[variable_names(VN_list)]),VN_list=[_=1,_=2,_=3],writeq(VN_list).
67
+ inputs("B+C+A+B+C+A."), peeks("\n"),
68
+ outputs("['B'=1,'C'=2,'A'=3]"),
69
+ T = 1+2+3+1+2+3,
70
+ VN_list=['B'=1,'C'=2,'A'=3].
71
+ 30 ?- write_term(T,[variable_names(VN_list)]).
72
+ instantiation_error.
73
+ 31 ?- VN_list = 1, /**/write_term(T,[variable_names(VN_list)]).
74
+ domain_error(write_option, variable_names(1)).
75
+ 33 ?- VN_list = [[]], /**/write_term(T,[variable_names(VN_list)]).
76
+ domain_error(write_option, variable_names([[]])).
77
+ 34 ?- VN_list = non_list, /**/write_term(T,[variable_names(VN_list)]).
78
+ domain_error(write_option, variable_names(non_list)).
79
+ 35 ?- VN_list = [T='T'|non_list], /**/write_term(T,[variable_names(VN_list)]).
80
+ domain_error(write_option, variable_names([_='T'|non_list]))
81
+ | instantiation_error.
82
+ 52 ?- VN_list = ['T'=T|_], /**/write_term(T,[variable_names(VN_list)]).
83
+ instantiation_error.
84
+ 51 ?- VN_list = ['T'=T|non_list], /**/write_term(T,[variable_names(VN_list)]).
85
+ domain_error(write_option, variable_names(...)).
86
+ 36 ?- VN_list = [T-'T'], /**/write_term(T,[variable_names(VN_list)]).
87
+ domain_error(write_option, variable_names(...)).
88
+ 63 ?- VN_list = [_,a], /**/write_term(T,[variable_names(VN_list)]).
89
+ domain_error(write_option, variable_names(...))
90
+ | instantiation_error.
91
+ 64 ?- VN_list = [a,_], /**/write_term(T,[variable_names(VN_list)]).
92
+ domain_error(write_option, variable_names(...))
93
+ | instantiation_error.
94
+ 66 ?- VN_list = [a|_], /**/write_term(T,[variable_names(VN_list)]).
95
+ domain_error(write_option, variable_names(...))
96
+ | instantiation_error.
97
+ 67 ?- VN_list = [i=i,7=i], /**/write_term(T,[variable_names(VN_list)]).
98
+ domain_error(write_option,variable_names([i=i,7=i])).
99
+ 68 ?- VN_list = [_,_], /**/write_term(T,[variable_names(VN_list)]).
100
+ instantiation_error.
101
+ 43 ?- write_term(-X^2,[variable_names(['X'=X])]).
102
+ outputs("- (X^2)").
103
+ 44 ?- X=1, /**/write_term(-X^2,[variable_names(['X'=X])]).
104
+ outputs("- (1^2)"), X = ... .
105
+ 37 ?- open(f,write,_,[O]).
106
+ instantiation_error.
107
+ 38 ?- O = 1, /**/open(f,write,_,[O]).
108
+ domain_error(stream_option,1).
109
+ 56 ?- O = typex(_), /**/open(f,write,_,[O]).
110
+ domain_error(stream_option,typex(_)).
111
+ 57 ?- O = typex(1), /**/open(f,write,_,[O]).
112
+ domain_error(stream_option,typex(1)).
113
+ 62 ?- O = typex(s(_)), /**/open(f,write,_,[O]).
114
+ domain_error(stream_option,typex(s(_))).
115
+ 39 ?- O = type(text), /**/open(f,write,_,[O]).
116
+ O = ... .
117
+ 40 ?- O = type(1), /**/open(f,write,_,[O]).
118
+ domain_error(stream_option,type(1)).
119
+ 41 ?- O = type(_), /**/open(f,write,_,[O]).
120
+ instantiation_error.
121
+ 60 ?- O = alias(_), /**/open(f,write,_,[O]).
122
+ instantiation_error.
123
+ 42 ?- O = type(nontype), /**/open(f,write,_,[O]).
124
+ domain_error(stream_option,type(nontype)).
125
+ 61 ?- O = alias(1), /**/open(f,write,_,[O]).
126
+ domain_error(stream_option,alias(1)).
127
+ 45 ?- read_term(T,[variable_names(VN_list)]).
128
+ waits.
129
+ 46 ?- /**/read_term(T,[variable_names(VN_list)]).
130
+ inputs("a."), peeks("\n"),
131
+ T = a, VN_list = [].
132
+ 47 ?- VN_list = 42, /**/read_term(T,[variable_names(VN_list)]).
133
+ waits.
134
+ 48 ?- VN_list = 42, /**/read_term(T,[variable_names(VN_list)]).
135
+ inputs("a."), peeks("\n"),
136
+ false.
137
+ 49 ?- VN_list = 42, /**/read_term(T,[variable_names(VN_list)]).
138
+ inputs("a b."), peeks("\n"),
139
+ syntax_error(...).
140
+ 53 ?- write_term(S,[quoted(true),variable_names([N=T])]).
141
+ instantiation_error.
142
+ 54 ?- S=1+T,N='/*r*/V',/**/write_term(S,[quoted(true),variable_names([N=T])]).
143
+ outputs("1+/*r*/V"), S = ..., N = ...
144
+ | outputs("1+ /*r*/V"), S = ..., N = ... .
145
+ 55 ?- S=1+T,N=' /*r*/V',/**/write_term(S,[quoted(true),variable_names([N=T])]).
146
+ outputs("1+ /*r*/V"), S = ..., N = ... .
147
+ 58 ?- S=1+T,N=(+),/**/write_term(S,[quoted(true),variable_names([N=T])]).
148
+ outputs("1++"), S = ..., N = ...
149
+ | outputs("1+ +"), S = ..., N = ... .
150
+ 59 ?- S=T+1,N=(+),/**/write_term(S,[quoted(true),variable_names([N=T])]).
151
+ outputs("++1"), S = ..., N = ...
152
+ | outputs("+ +1"), S = ..., N = ... .
153
+ 73 ?- S=(1 is T),N='X',/**/write_term(S,[quoted(true),variable_names([N=T])]).
154
+ outputs("1 is X"), S = ..., N = ... .
155
+ 69 ?- read_term(T,[singletons(1)]).
156
+ waits.
157
+ 70 ?- /**/read_term(T,[singletons(1)]).
158
+ inputs("a."), peeks("\n"),
159
+ false.
160
+ 71 ?- write_term(T,[variable_names(['Bad'=T]),variable_names(['Good'=T])]).
161
+ outputs("Good").
162
+ 72 ?- read_term(T,[singletons([])]).
163
+ waits.