eyeprolog 1.5.75 → 1.5.76

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.75",
6
+ "version": "1.5.76",
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
@@ -1153,24 +1153,32 @@ function streamHandle(id) {
1153
1153
  return compound('$stream', [numberTerm(id)]);
1154
1154
  }
1155
1155
 
1156
+ function streamHandleId(value, env) {
1157
+ // A `$stream(Id)` term is an ordinary compound, not an opaque blob, so
1158
+ // general term-construction predicates such as functor/3 and arg/3 can
1159
+ // rebuild one whose argument is a variable that only later gets unified
1160
+ // with the real stream number (issue #109). Deref that argument, the same
1161
+ // as any other compound argument, instead of inspecting the raw arg term
1162
+ // and rejecting an equally valid stream reference as malformed.
1163
+ if (value.type !== COMPOUND || value.name !== '$stream' || value.arity !== 1) return null;
1164
+ const id = deref(value.args[0], env);
1165
+ return id.type === NUMBER && isDecimalInteger(id.name) ? Number(id.name) : null;
1166
+ }
1167
+
1156
1168
  function streamReference(term, env) {
1157
1169
  const value = deref(term, env);
1158
1170
  if (value.type === VAR) throw new PrologError('instantiation_error');
1159
1171
  if (value.type === ATOM) return value.name;
1160
- if (value.type === COMPOUND && value.name === '$stream' && value.arity === 1 &&
1161
- value.args[0].type === NUMBER && isDecimalInteger(value.args[0].name)) {
1162
- return Number(value.args[0].name);
1163
- }
1172
+ const id = streamHandleId(value, env);
1173
+ if (id != null) return id;
1164
1174
  throw new PrologError('domain_error(stream_or_alias)', value);
1165
1175
  }
1166
1176
 
1167
1177
  function streamTermReference(term, env) {
1168
1178
  const value = deref(term, env);
1169
1179
  if (value.type === VAR) return null;
1170
- if (value.type === COMPOUND && value.name === '$stream' && value.arity === 1 &&
1171
- value.args[0].type === NUMBER && isDecimalInteger(value.args[0].name)) {
1172
- return Number(value.args[0].name);
1173
- }
1180
+ const id = streamHandleId(value, env);
1181
+ if (id != null) return id;
1174
1182
  throw new PrologError('domain_error(stream)', value);
1175
1183
  }
1176
1184
 
@@ -28,6 +28,24 @@ import {
28
28
 
29
29
  export function regressionCases() {
30
30
  return [
31
+ {
32
+ name: 'close/1 accepts a $stream/1 term rebuilt via functor/3 and arg/3 that is structurally == to the real handle (issue #109)',
33
+ run: () => {
34
+ const file = sourceAtom(path.join(temp.dir, `stream-109-${++temp.counter}.txt`));
35
+ const result = runCli([], { input:
36
+ `open(${file},write,S),functor(S,Fn,A),functor(S2,Fn,A),arg(1,S,A1),arg(1,S2,A1),S==S2,close(S2).\n` +
37
+ 'halt.\n',
38
+ });
39
+ assertEqual(result.status, 0, result.stderr);
40
+ // S2's argument only becomes the real stream number through
41
+ // unification rather than being built in place by open/3, so
42
+ // close/1 must deref it like any other compound argument instead of
43
+ // rejecting an equally valid, structurally-== stream reference as
44
+ // malformed.
45
+ assertNotIncludes(result.stdout + result.stderr, 'error(', 'no domain_error(stream_or_alias, ...) from close/1');
46
+ assertIncludes(result.stdout, 'A1 = ', 'query succeeds and reports the shared stream number');
47
+ },
48
+ },
31
49
  {
32
50
  name: 'top level never mints a generated variable name that collides with a query variable\'s own name (issue #108)',
33
51
  run: () => {