eyeprolog 1.5.30 → 1.5.31

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.30",
6
+ "version": "1.5.31",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/parser.js CHANGED
@@ -1382,10 +1382,14 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
1382
1382
  return atom(text);
1383
1383
  };
1384
1384
 
1385
- const trimRange = (text, start, end) => {
1385
+ const trimRangeStart = (text, start, end) => {
1386
1386
  while (start < end && isWhitespaceCode(text.charCodeAt(start))) start++;
1387
+ return start;
1388
+ };
1389
+
1390
+ const trimRangeEnd = (text, start, end) => {
1387
1391
  while (end > start && isWhitespaceCode(text.charCodeAt(end - 1))) end--;
1388
- return [start, end];
1392
+ return end;
1389
1393
  };
1390
1394
 
1391
1395
  const tokenKindInRange = (text, start, end) => {
@@ -1427,7 +1431,8 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
1427
1431
  };
1428
1432
 
1429
1433
  const scalarOrVariableRange = (text, start, end) => {
1430
- [start, end] = trimRange(text, start, end);
1434
+ start = trimRangeStart(text, start, end);
1435
+ end = trimRangeEnd(text, start, end);
1431
1436
  const kind = tokenKindInRange(text, start, end);
1432
1437
  const value = text.slice(start, end);
1433
1438
  if (kind === 'var') {
@@ -1447,20 +1452,27 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
1447
1452
  const rawBody = {};
1448
1453
 
1449
1454
  const scalarTokenRange = (text, start, end, out, slot) => {
1450
- [start, end] = trimRange(text, start, end);
1455
+ start = trimRangeStart(text, start, end);
1456
+ end = trimRangeEnd(text, start, end);
1451
1457
  const kind = tokenKindInRange(text, start, end);
1452
1458
  let type = kind;
1453
1459
  if (kind == null && simpleNumberInRange(text, start, end)) type = 'number';
1454
1460
  if (type == null) return false;
1455
1461
  let name = text.slice(start, end);
1456
1462
  if (type === 'var' && name === '_') name = `__anon${anonymous++}`;
1457
- out[`arg${slot}Type`] = type;
1458
- out[`arg${slot}Name`] = name;
1463
+ if (slot === 0) {
1464
+ out.arg0Type = type;
1465
+ out.arg0Name = name;
1466
+ } else {
1467
+ out.arg1Type = type;
1468
+ out.arg1Name = name;
1469
+ }
1459
1470
  return true;
1460
1471
  };
1461
1472
 
1462
1473
  const parseBinaryRawRange = (text, start, end, out) => {
1463
- [start, end] = trimRange(text, start, end);
1474
+ start = trimRangeStart(text, start, end);
1475
+ end = trimRangeEnd(text, start, end);
1464
1476
  let i = start;
1465
1477
  const first = text.charCodeAt(i);
1466
1478
  if (!(first >= 97 && first <= 122)) return false;
@@ -1487,7 +1499,8 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
1487
1499
  };
1488
1500
 
1489
1501
  const parseBinaryCompoundRange = (text, start = 0, end = text.length) => {
1490
- [start, end] = trimRange(text, start, end);
1502
+ start = trimRangeStart(text, start, end);
1503
+ end = trimRangeEnd(text, start, end);
1491
1504
  let i = start;
1492
1505
  const first = text.charCodeAt(i);
1493
1506
  if (!(first >= 97 && first <= 122)) return null;
@@ -1680,7 +1693,8 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
1680
1693
  let contentStart = lineStart;
1681
1694
  let contentEnd = lineEnd;
1682
1695
  if (contentEnd > contentStart && source.charCodeAt(contentEnd - 1) === 13) contentEnd--;
1683
- [contentStart, contentEnd] = trimRange(source, contentStart, contentEnd);
1696
+ contentStart = trimRangeStart(source, contentStart, contentEnd);
1697
+ contentEnd = trimRangeEnd(source, contentStart, contentEnd);
1684
1698
  if (contentStart < contentEnd && source.charCodeAt(contentStart) !== 37) {
1685
1699
  if (!chunk && source.charCodeAt(contentEnd - 1) === 46 && !preparationConversionActive()) {
1686
1700
  if (emitFastBinaryRange(source, contentStart, contentEnd, lineNumber)) {
package/src/program.js CHANGED
@@ -36,6 +36,11 @@ const DEFER_PROGRAM_BUILD = Symbol('deferProgramBuild');
36
36
  const FAST_PARSE_ABORT = Symbol('fastParseAbort');
37
37
  const PROGRAM_BUILD_BATCH_SIZE = 16384;
38
38
  const preparedBundledLibraryCache = new Map();
39
+ const NORMAL_OPERATOR_DEFINITIONS = [
40
+ ...ISO_OPERATOR_DEFINITIONS,
41
+ ...PART3_OPERATOR_DEFINITIONS,
42
+ ...QUAD_OPERATOR_DEFINITIONS,
43
+ ];
39
44
 
40
45
  function collectMultiplicitySensitiveDependencies(goal, out = []) {
41
46
  if (goal?.type !== COMPOUND) return out;
@@ -151,13 +156,12 @@ export class Program {
151
156
  // recursive predicates retain standard depth-first Prolog control.
152
157
  this.tabledPredicates = new Set();
153
158
  this.operators = new Map();
154
- const predefinedOperatorSets = this.strictIso
155
- ? [ISO_OPERATOR_DEFINITIONS]
156
- : [ISO_OPERATOR_DEFINITIONS, PART3_OPERATOR_DEFINITIONS, QUAD_OPERATOR_DEFINITIONS];
157
- for (const definitions of predefinedOperatorSets) {
158
- for (const [priority, specifier, name] of definitions) {
159
- this.defineOperator(priority, specifier, name);
160
- }
159
+ const predefinedOperators = this.strictIso ? ISO_OPERATOR_DEFINITIONS : NORMAL_OPERATOR_DEFINITIONS;
160
+ // The predefined lists contain no competing declarations in the same
161
+ // prefix, infix, or postfix class. Populate them directly; defineOperator
162
+ // still performs replacement semantics for source-level op/3 directives.
163
+ for (const [priority, specifier, name] of predefinedOperators) {
164
+ this.operators.set(`${specifier}\u0000${name}`, { priority, specifier, name });
161
165
  }
162
166
  this.initializations = [];
163
167
  // Track how many initialization/1 directives have run so a REPL can
@@ -251,12 +255,15 @@ export class Program {
251
255
  }
252
256
  findGroup(name, arity, module = 'user') {
253
257
  const indicator = `${name}/${arity}`;
254
- const visited = new Set();
255
- let current = module;
258
+ const direct = this.groups.get(module === 'user' ? indicator : `${module}:${indicator}`);
259
+ if (direct) return direct;
260
+ let current = this.moduleImports.get(module)?.get(indicator);
261
+ if (current == null) return null;
262
+ const visited = new Set([module]);
256
263
  while (!visited.has(current)) {
257
264
  visited.add(current);
258
- const direct = this.groups.get(modulePredicateKey(current, name, arity));
259
- if (direct) return direct;
265
+ const imported = this.groups.get(current === 'user' ? indicator : `${current}:${indicator}`);
266
+ if (imported) return imported;
260
267
  const importedModule = this.moduleImports.get(current)?.get(indicator);
261
268
  if (importedModule == null) return null;
262
269
  current = importedModule;
package/src/solver.js CHANGED
@@ -115,7 +115,11 @@ export class Solver {
115
115
  reservationBytes: 0,
116
116
  checks: 0,
117
117
  };
118
- this.nextMemoryCheck = 0;
118
+ // Nested meta-call solvers contribute to one execution and inspect the
119
+ // same process heap. Share their sampling deadline so each short-lived
120
+ // child does not repeat an expensive host memory query before the parent
121
+ // has advanced the guard interval.
122
+ this.memoryCheckState = options.memoryCheckState ?? { nextObservation: 0 };
119
123
  // Do not impose an implicit answer cap. Infinite and very large searches are
120
124
  // part of normal Prolog semantics; callers that need a resource bound can
121
125
  // still supply solutionLimit explicitly.
@@ -215,6 +219,7 @@ export class Solver {
215
219
  maxInferences: this.maxInferences,
216
220
  maxMemoryBytes: this.maxMemoryBytes,
217
221
  memoryRecovery: this.memoryRecovery,
222
+ memoryCheckState: this.memoryCheckState,
218
223
  solutionLimit,
219
224
  isoStrict: this.isoStrict,
220
225
  prologFlags: this.prologFlags,
@@ -1005,8 +1010,9 @@ export class Solver {
1005
1010
  }
1006
1011
 
1007
1012
  checkMemoryLimit(force = false) {
1008
- if (!force && this.inferences < this.nextMemoryCheck) return;
1009
- this.nextMemoryCheck = this.inferences + 256;
1013
+ const observation = this.inferenceObservation.value;
1014
+ if (!force && observation < this.memoryCheckState.nextObservation) return;
1015
+ this.memoryCheckState.nextObservation = observation + 256;
1010
1016
  if (!Number.isFinite(this.maxMemoryBytes)) return;
1011
1017
  const used = usedHeapSize();
1012
1018
  if (used != null && used < this.maxMemoryBytes) this.finishMemoryRecovery();