eyeprolog 1.2.39 → 1.2.40

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.2.39",
6
+ "version": "1.2.40",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/iso.js CHANGED
@@ -1076,6 +1076,7 @@ function* nlBuiltin({ solver, goal, env }) {
1076
1076
 
1077
1077
  function* termTextCandidates(stream) {
1078
1078
  const source = String(stream.content);
1079
+ const lastBufferedNonLayout = lastNonLayoutIndex(source, stream.position);
1079
1080
  let quote = null, lineComment = false, blockComment = false;
1080
1081
  for (let i = stream.position; i < source.length; i++) {
1081
1082
  const ch = source[i], next = source[i + 1];
@@ -1106,14 +1107,29 @@ function* termTextCandidates(stream) {
1106
1107
  }
1107
1108
  if (ch === "'" || ch === '"') { quote = ch; continue; }
1108
1109
  if (ch === '.' && isTerminatingFullStop(source, i)) {
1110
+ // A buffered stream exposes what follows the layout after this dot. If
1111
+ // the dot continues a graphic token and later non-layout input exists,
1112
+ // it is part of that maximal token rather than an early end char. The
1113
+ // interactive reader makes the corresponding decision incrementally.
1114
+ if (continuesGraphicToken(source, i) && lastBufferedNonLayout > i) continue;
1109
1115
  yield { text: source.slice(stream.position, i + 1), end: i + 1 };
1110
1116
  }
1111
1117
  }
1112
1118
  }
1113
1119
  function hasNonLayoutRemainder(source, start) {
1114
- return source.slice(start)
1115
- .replace(/[\u0009-\u000d\u0020]+|%[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\//g, '')
1116
- .length > 0;
1120
+ return lastNonLayoutIndex(source, start) >= start;
1121
+ }
1122
+ function lastNonLayoutIndex(source, start = 0) {
1123
+ const ignored = /[\u0009-\u000d\u0020]+|%[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\//g;
1124
+ ignored.lastIndex = start;
1125
+ let cursor = start;
1126
+ let last = -1;
1127
+ for (let match = ignored.exec(source); match != null; match = ignored.exec(source)) {
1128
+ if (match.index > cursor) last = match.index - 1;
1129
+ cursor = match.index + match[0].length;
1130
+ }
1131
+ if (cursor < source.length) last = source.length - 1;
1132
+ return last;
1117
1133
  }
1118
1134
  function convertedTermText(text, solver) {
1119
1135
  if (solver.prologFlags.get('char_conversion')?.value?.name !== 'on' || solver.charConversions.size === 0) return text;
@@ -777,6 +777,37 @@ c4 ?- call((!;1)).
777
777
  assertEqual(term.args[1].name, `${name}.`, `graphic operator atom for ${name}`);
778
778
  }
779
779
 
780
+ // Unlike an interactive reader waiting at a line boundary, a buffered
781
+ // stream can see later non-layout input. Its first dot therefore
782
+ // remains in the maximal graphic token, making the adjacent ! invalid
783
+ // rather than prematurely returning the shorter atom.
784
+ for (const name of [...graphicOperators, '?', '#', '@', './*', '//*']) {
785
+ let bufferedError = null;
786
+ try {
787
+ runEyeProlog('', {
788
+ goal: 'read_term(T, [])',
789
+ ioOptions: { input: `${name}.\n!\n.` },
790
+ });
791
+ } catch (caught) {
792
+ bufferedError = caught;
793
+ }
794
+ assertEqual(
795
+ bufferedError?.message,
796
+ 'error(syntax_error(read_term))',
797
+ `buffered graphic atom boundary for ${name}`,
798
+ );
799
+ }
800
+
801
+ const bufferedPath = path.join(tmp, 'graphic-atom-boundary.pl');
802
+ fs.writeFileSync(bufferedPath, '*.\n!\n.');
803
+ const namedStream = runEyeProlog([
804
+ `caught(ok) :- open(${sourceAtom(bufferedPath)}, read, S),`,
805
+ ' catch(read_term(S, _, []), error(syntax_error(read_term), _), true),',
806
+ ' close(S).',
807
+ '',
808
+ ].join('\n'), { goal: 'caught(ok)' });
809
+ assertEqual(namedStream.stdout, 'caught(ok).\n', 'named buffered stream syntax error');
810
+
780
811
  let error = null;
781
812
  try {
782
813
  runEyeProlog('', { goal: 'read(T)', ioOptions: { input: '!.!.' } });