eyeprolog 1.5.80 → 1.5.81

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.80",
6
+ "version": "1.5.81",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/explain.js CHANGED
@@ -69,7 +69,7 @@ function* proveGoalAll(program, goal, env, depth, maxDepth, registry, active, de
69
69
  env: proofEnv,
70
70
  node: {
71
71
  goal: resolveForProof(goal, proofEnv),
72
- method: builtinMethod(goal),
72
+ method: goalMethod('builtin', goal),
73
73
  sourceHead: resolveForProof(goal, proofEnv),
74
74
  sourceBody: [],
75
75
  bindings: [],
@@ -97,7 +97,7 @@ function* proveGoalAll(program, goal, env, depth, maxDepth, registry, active, de
97
97
  env: proofEnv,
98
98
  node: {
99
99
  goal: resolveForProof(goal, proofEnv),
100
- method: libraryMethod(goal),
100
+ method: goalMethod('library', goal),
101
101
  sourceHead: resolveForProof(goal, proofEnv),
102
102
  sourceBody: [],
103
103
  bindings: [],
@@ -254,17 +254,9 @@ function sourceMethod(clause, kind) {
254
254
  };
255
255
  }
256
256
 
257
- function builtinMethod(goal) {
257
+ function goalMethod(type, goal) {
258
258
  return {
259
- type: 'builtin',
260
- name: goal.type === COMPOUND ? goal.name : 'goal',
261
- arity: goal.type === COMPOUND ? goal.arity : 0,
262
- };
263
- }
264
-
265
- function libraryMethod(goal) {
266
- return {
267
- type: 'library',
259
+ type,
268
260
  name: goal.type === COMPOUND ? goal.name : 'goal',
269
261
  arity: goal.type === COMPOUND ? goal.arity : 0,
270
262
  };
@@ -380,15 +372,15 @@ function certificateText(term) {
380
372
  throw new Error('expected certificate text');
381
373
  }
382
374
 
383
- function containsLibraryBoundary(node) {
384
- if (node.method?.type === 'library') return true;
385
- return node.children.some(containsLibraryBoundary);
375
+ function proofTreeContains(node, predicate) {
376
+ return predicate(node) || node.children.some((child) => proofTreeContains(child, predicate));
386
377
  }
387
378
 
388
- function containsExpandedLibrarySource(node) {
389
- if (node.method?.type === 'source' && String(node.method.filename ?? '').startsWith('src/lib/')) return true;
390
- return node.children.some(containsExpandedLibrarySource);
391
- }
379
+ const containsLibraryBoundary = (node) => proofTreeContains(node, (n) => n.method?.type === 'library');
380
+
381
+ const containsExpandedLibrarySource = (node) => proofTreeContains(
382
+ node, (n) => n.method?.type === 'source' && String(n.method.filename ?? '').startsWith('src/lib/'),
383
+ );
392
384
 
393
385
  export function verifyProof(program, input, options = {}) {
394
386
  const certificate = input?.certificate ?? input;
@@ -557,8 +549,9 @@ function verifyBindings(bindings, variables, env, program) {
557
549
 
558
550
  function renderMethodTerm(method) {
559
551
  if (method && method.type === 'source') return `${method.kind}(${quoteString(method.filename)}, clause(${method.clause}))`;
560
- if (method && method.type === 'builtin') return `builtin(${quoteAtomText(method.name)}, ${method.arity})`;
561
- if (method && method.type === 'library') return `library(${quoteAtomText(method.name)}, ${method.arity})`;
552
+ if (method && (method.type === 'builtin' || method.type === 'library')) {
553
+ return `${method.type}(${quoteAtomText(method.name)}, ${method.arity})`;
554
+ }
562
555
  return String(method);
563
556
  }
564
557
 
package/src/iso.js CHANGED
@@ -680,10 +680,7 @@ function validPredicateIndicator(term) {
680
680
  (term.args[1].type === NUMBER && isDecimalInteger(term.args[1].name) && BigInt(term.args[1].name) >= 0n));
681
681
  }
682
682
 
683
- function currentPredicateBuiltin(context) {
684
- const state = { pending: false };
685
- return withPendingState(currentPredicateSolutions(context, state), state);
686
- }
683
+ const currentPredicateBuiltin = pendingBuiltin(currentPredicateSolutions);
687
684
 
688
685
  function* currentPredicateSolutions({ solver, goal, env }, state) {
689
686
  const indicator = copyResolved(goal.args[0], env);
@@ -719,10 +716,7 @@ function clauseBodyTerm(body) {
719
716
  return result;
720
717
  }
721
718
 
722
- function clauseBuiltin(context) {
723
- const state = { pending: false };
724
- return withPendingState(clauseSolutions(context, state), state);
725
- }
719
+ const clauseBuiltin = pendingBuiltin(clauseSolutions);
726
720
 
727
721
  function* clauseSolutions({ solver, goal, env }, state) {
728
722
  const head = deref(goal.args[0], env);
@@ -851,12 +845,7 @@ function assertBuiltin(atStart) {
851
845
  };
852
846
  }
853
847
 
854
- function retractBuiltin(context) {
855
- const state = { pending: true };
856
- const iterator = retractSolutions(context, state);
857
- iterator.hasPendingAlternatives = () => state.pending;
858
- return iterator;
859
- }
848
+ const retractBuiltin = pendingBuiltin(retractSolutions, true);
860
849
 
861
850
  function* retractSolutions({ solver, goal, env }, state) {
862
851
  const parts = clauseParts(goal.args[0], env);
@@ -941,10 +930,7 @@ function* abolishBuiltin({ solver, goal, env }) {
941
930
  yield env;
942
931
  }
943
932
 
944
- function currentPrologFlagBuiltin(context) {
945
- const state = { pending: false };
946
- return withPendingState(currentPrologFlagSolutions(context, state), state);
947
- }
933
+ const currentPrologFlagBuiltin = pendingBuiltin(currentPrologFlagSolutions);
948
934
 
949
935
  function* currentPrologFlagSolutions({ solver, goal, env }, state) {
950
936
  const flag = deref(goal.args[0], env);
@@ -1058,10 +1044,7 @@ function* opBuiltin({ solver, goal, env }) {
1058
1044
  yield env;
1059
1045
  }
1060
1046
 
1061
- function currentOpBuiltin(context) {
1062
- const state = { pending: false };
1063
- return withPendingState(currentOpSolutions(context, state), state);
1064
- }
1047
+ const currentOpBuiltin = pendingBuiltin(currentOpSolutions);
1065
1048
 
1066
1049
  function* currentOpSolutions({ solver, goal, env }, state) {
1067
1050
  const priority = deref(goal.args[0], env);
@@ -1124,10 +1107,7 @@ function* charConversionBuiltin({ solver, goal, env }) {
1124
1107
  else solver.charConversions.set(input.name, output.name);
1125
1108
  yield env;
1126
1109
  }
1127
- function currentCharConversionBuiltin(context) {
1128
- const state = { pending: false };
1129
- return withPendingState(currentCharConversionSolutions(context, state), state);
1130
- }
1110
+ const currentCharConversionBuiltin = pendingBuiltin(currentCharConversionSolutions);
1131
1111
 
1132
1112
  function* currentCharConversionSolutions({ solver, goal, env }, state) {
1133
1113
  const input = conversionCharacter(goal.args[0], env, true, solver);
@@ -1355,28 +1335,23 @@ function* closeBuiltin({ solver, goal, env }) {
1355
1335
  yield env;
1356
1336
  }
1357
1337
 
1358
- function* currentInputBuiltin({ solver, goal, env }) {
1359
- const value = deref(goal.args[0], env);
1360
- if (value.type !== VAR) {
1361
- const id = streamTermReference(goal.args[0], env);
1362
- // A closed handle is still a stream-term, but cannot be current (#107).
1363
- // Validate its shape, then test identity rather than requiring an open stream.
1364
- if (id === solver.io.currentInput) yield env;
1365
- return;
1366
- }
1367
- const next = env.clone();
1368
- if (unify(goal.args[0], streamHandle(solver.io.currentInput), next)) yield next;
1369
- }
1370
- function* currentOutputBuiltin({ solver, goal, env }) {
1371
- const value = deref(goal.args[0], env);
1372
- if (value.type !== VAR) {
1373
- const id = streamTermReference(goal.args[0], env);
1374
- if (id === solver.io.currentOutput) yield env;
1375
- return;
1376
- }
1377
- const next = env.clone();
1378
- if (unify(goal.args[0], streamHandle(solver.io.currentOutput), next)) yield next;
1338
+ function currentStreamBuiltin(which) {
1339
+ // A closed handle is still a stream-term, but cannot be current (#107).
1340
+ // Validate its shape, then test identity rather than requiring an open stream.
1341
+ return function* ({ solver, goal, env }) {
1342
+ const value = deref(goal.args[0], env);
1343
+ const current = solver.io[which];
1344
+ if (value.type !== VAR) {
1345
+ const id = streamTermReference(goal.args[0], env);
1346
+ if (id === current) yield env;
1347
+ return;
1348
+ }
1349
+ const next = env.clone();
1350
+ if (unify(goal.args[0], streamHandle(current), next)) yield next;
1351
+ };
1379
1352
  }
1353
+ const currentInputBuiltin = currentStreamBuiltin('currentInput');
1354
+ const currentOutputBuiltin = currentStreamBuiltin('currentOutput');
1380
1355
 
1381
1356
  function setCurrentStreamBuiltin(mode) {
1382
1357
  return function* ({ solver, goal, env }) {
@@ -1456,10 +1431,7 @@ function isStreamPropertyPattern(value) {
1456
1431
  ].includes(value.name);
1457
1432
  }
1458
1433
 
1459
- function streamPropertyBuiltin(context) {
1460
- const state = { pending: false };
1461
- return withPendingState(streamPropertySolutions(context, state), state);
1462
- }
1434
+ const streamPropertyBuiltin = pendingBuiltin(streamPropertySolutions);
1463
1435
 
1464
1436
  function* streamPropertySolutions({ solver, goal, env }, state) {
1465
1437
  const reference = deref(goal.args[0], env);
@@ -2068,14 +2040,6 @@ function* writeTermBuiltin({ solver, goal, env }) {
2068
2040
  yield env;
2069
2041
  }
2070
2042
 
2071
- function resolvedOrVariable(term, env, expected) {
2072
- const value = deref(term, env);
2073
- if (value.type !== VAR && value.type !== expected) {
2074
- throw new PrologError(`type_error(${expected === ATOM ? 'atom' : 'number'})`, value);
2075
- }
2076
- return value;
2077
- }
2078
-
2079
2043
  function characters(text) {
2080
2044
  return Array.from(text);
2081
2045
  }
@@ -2095,10 +2059,7 @@ function* atomLengthBuiltin({ goal, env }) {
2095
2059
  if (unify(goal.args[1], numberTerm(characters(value.name).length), next)) yield next;
2096
2060
  }
2097
2061
 
2098
- function atomConcatBuiltin(context) {
2099
- const state = { pending: false };
2100
- return withPendingState(atomConcatSolutions(context, state), state);
2101
- }
2062
+ const atomConcatBuiltin = pendingBuiltin(atomConcatSolutions);
2102
2063
 
2103
2064
  function* atomConcatSolutions({ goal, env }, state) {
2104
2065
  const first = deref(goal.args[0], env);
@@ -2142,10 +2103,7 @@ function optionalInteger(term, env) {
2142
2103
  return BigInt(value.name);
2143
2104
  }
2144
2105
 
2145
- function subAtomBuiltin(context) {
2146
- const state = { pending: false };
2147
- return withPendingState(subAtomSolutions(context, state), state);
2148
- }
2106
+ const subAtomBuiltin = pendingBuiltin(subAtomSolutions);
2149
2107
 
2150
2108
  function* subAtomSolutions({ goal, env }, state) {
2151
2109
  const source = deref(goal.args[0], env);
@@ -2625,10 +2583,7 @@ function sortedUnique(items) {
2625
2583
  }
2626
2584
 
2627
2585
  function allSolutionsBuiltin(asSet) {
2628
- return function allSolutions(context) {
2629
- const state = { pending: false };
2630
- return withPendingState(allSolutionsGroups(context, asSet, state), state);
2631
- };
2586
+ return pendingBuiltin((context, state) => allSolutionsGroups(context, asSet, state));
2632
2587
  }
2633
2588
 
2634
2589
  function* allSolutionsGroups({ solver, goal, env }, asSet, state) {
@@ -2697,6 +2652,15 @@ function withPendingState(iterator, state) {
2697
2652
  iterator.hasPendingAlternatives = () => state.pending;
2698
2653
  return iterator;
2699
2654
  }
2655
+
2656
+ // Most builtins share this shape: create a { pending } cell, run a solutions
2657
+ // generator against it, and expose that cell through hasPendingAlternatives.
2658
+ function pendingBuiltin(solutionsFn, initialPending = false) {
2659
+ return (context) => {
2660
+ const state = { pending: initialPending };
2661
+ return withPendingState(solutionsFn(context, state), state);
2662
+ };
2663
+ }
2700
2664
  function validateControlCallable(term, culprit, env) {
2701
2665
  // Only control constructs need their nested goals validated at meta-call
2702
2666
  // entry. Walk them iteratively and dereference each nested goal lazily so
@@ -2770,9 +2734,9 @@ function expandCallGoal({ goal, env }) {
2770
2734
  if (converted.module == null) converted.module = module;
2771
2735
  return converted;
2772
2736
  }
2773
- function* callBuiltin(context) {
2737
+ function* invokeExpandedGoal(context, expand) {
2774
2738
  const { solver, env } = context;
2775
- const invoked = expandCallGoal(context);
2739
+ const invoked = expand(context);
2776
2740
  const child = solver.cloneForInnerGoal();
2777
2741
  try {
2778
2742
  yield* child.solve([invoked], env, 0);
@@ -2780,6 +2744,9 @@ function* callBuiltin(context) {
2780
2744
  solver.absorbStatsFrom(child);
2781
2745
  }
2782
2746
  }
2747
+ function* callBuiltin(context) {
2748
+ yield* invokeExpandedGoal(context, expandCallGoal);
2749
+ }
2783
2750
  function expandCallClosureGoal({ goal, env }) {
2784
2751
  const closure = callable(goal.args[0], env);
2785
2752
  const existing = closure.type === COMPOUND ? closure.args : [];
@@ -2792,14 +2759,7 @@ function expandCallClosureGoal({ goal, env }) {
2792
2759
  return invoked;
2793
2760
  }
2794
2761
  function* callClosureBuiltin(context) {
2795
- const { solver, env } = context;
2796
- const invoked = expandCallClosureGoal(context);
2797
- const child = solver.cloneForInnerGoal();
2798
- try {
2799
- yield* child.solve([invoked], env, 0);
2800
- } finally {
2801
- solver.absorbStatsFrom(child);
2802
- }
2762
+ yield* invokeExpandedGoal(context, expandCallClosureGoal);
2803
2763
  }
2804
2764
 
2805
2765
  export function* countAllBuiltin({ solver, goal, env }) {
@@ -2846,10 +2806,7 @@ function writeElapsedTime(solver, startedAt, inferences) {
2846
2806
  );
2847
2807
  }
2848
2808
 
2849
- export function timeBuiltin(context) {
2850
- const state = { pending: true };
2851
- return withPendingState(timeSolutions(context, state), state);
2852
- }
2809
+ export const timeBuiltin = pendingBuiltin(timeSolutions, true);
2853
2810
 
2854
2811
  function* timeSolutions({ solver, goal, env }, state) {
2855
2812
  const invoked = callable(goal.args[0], env);
@@ -2877,10 +2834,7 @@ function* timeSolutions({ solver, goal, env }, state) {
2877
2834
  }
2878
2835
  }
2879
2836
 
2880
- export function callNthBuiltin(context) {
2881
- const state = { pending: true };
2882
- return withPendingState(callNthSolutions(context, state), state);
2883
- }
2837
+ export const callNthBuiltin = pendingBuiltin(callNthSolutions, true);
2884
2838
 
2885
2839
  function* callNthSolutions({ solver, goal, env }, state) {
2886
2840
  const requestedTerm = deref(goal.args[1], env);
@@ -3005,37 +2959,7 @@ export function callResidueVarsBuiltin({ solver, goal, env }) {
3005
2959
  return iterator;
3006
2960
  }
3007
2961
 
3008
- function freezeBuiltin(context) {
3009
- const state = { pending: true };
3010
- return withPendingState(freezeSolutions(context, state), state);
3011
- }
3012
-
3013
- function* freezeSolutions({ solver, goal, env }, state) {
3014
- const watched = deref(goal.args[0], env);
3015
- if (watched.type !== VAR) {
3016
- const child = solver.cloneForInnerGoal();
3017
- try {
3018
- for (const answerEnv of child.solve([callable(goal.args[1], env)], env, 0)) {
3019
- state.pending = child.hasPendingAlternatives();
3020
- yield answerEnv;
3021
- if (!state.pending) return;
3022
- }
3023
- state.pending = false;
3024
- } finally {
3025
- solver.absorbStatsFrom(child);
3026
- }
3027
- return;
3028
- }
3029
- const next = env.clone();
3030
- next.delay(watched.name, goal.args[1], goal.module ?? 'user');
3031
- state.pending = false;
3032
- yield next;
3033
- }
3034
-
3035
- function phraseBuiltin(context) {
3036
- const state = { pending: true };
3037
- return withPendingState(phraseSolutions(context, state), state);
3038
- }
2962
+ const phraseBuiltin = pendingBuiltin(phraseSolutions, true);
3039
2963
 
3040
2964
  function* phraseSolutions({ solver, goal, env }, state) {
3041
2965
  const grammarBody0 = deref(goal.args[0], env);
@@ -3161,10 +3085,7 @@ function prologErrorBall(error) {
3161
3085
  if (hasDefaultGroundErrorShape(error) || termIsGround(term)) return term;
3162
3086
  return freshCopy(term, new Env());
3163
3087
  }
3164
- function catchBuiltin(context) {
3165
- const state = { pending: true };
3166
- return withPendingState(catchSolutions(context, state), state);
3167
- }
3088
+ const catchBuiltin = pendingBuiltin(catchSolutions, true);
3168
3089
 
3169
3090
  function* catchSolutions({ solver, goal, env }, state) {
3170
3091
  let child = null;
@@ -3293,12 +3214,7 @@ function* solveControlBranch(solver, goal, env, observePending = null) {
3293
3214
  yield answer;
3294
3215
  }
3295
3216
  }
3296
- function disjunctionBuiltin(context) {
3297
- const state = { pending: true };
3298
- const iterator = disjunctionSolutions(context, state);
3299
- iterator.hasPendingAlternatives = () => state.pending;
3300
- return iterator;
3301
- }
3217
+ const disjunctionBuiltin = pendingBuiltin(disjunctionSolutions, true);
3302
3218
  function* disjunctionSolutions({ solver, goal, env }, state) {
3303
3219
  const left = deref(goal.args[0], env);
3304
3220
  if (left.type === COMPOUND && left.name === '->' && left.arity === 2) {
@@ -3333,10 +3249,7 @@ function* disjunctionSolutions({ solver, goal, env }, state) {
3333
3249
  (pending) => { state.pending = pending; });
3334
3250
  state.pending = false;
3335
3251
  }
3336
- function ifThenBuiltin(context) {
3337
- const state = { pending: true };
3338
- return withPendingState(ifThenSolutions(context, state), state);
3339
- }
3252
+ const ifThenBuiltin = pendingBuiltin(ifThenSolutions, true);
3340
3253
 
3341
3254
  function* ifThenSolutions({ solver, goal, env }, state) {
3342
3255
  for (const conditionEnv of solver.cloneForInnerGoal(1).solve([callable(goal.args[0], env)], env.clone(), 0)) {