eyeprolog 1.3.51 → 1.3.53
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 +1 -1
- package/src/iso-arithmetic.js +76 -21
- package/src/iso-limits.js +6 -7
- package/src/iso.js +39 -10
- package/src/parser.js +3 -1
- package/src/program.js +6 -1
- package/src/solver.js +4 -2
- package/test/conformance/ISO-COMPLIANCE.md +61 -22
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +15 -10
- package/test/conformance/ISO-MATRIX.md +5 -4
- package/test/conformance/STC-DRAFT-STATUS.md +12 -4
- package/test/conformance/expected/iso/exceptions_and_flags.pl +1 -1
- package/test/run-iso-strict.mjs +145 -14
- package/the-art-of-eyeprolog.md +1 -1
package/package.json
CHANGED
package/src/iso-arithmetic.js
CHANGED
|
@@ -5,6 +5,19 @@ import {
|
|
|
5
5
|
} from './term.js';
|
|
6
6
|
import { PrologError } from './errors.js';
|
|
7
7
|
|
|
8
|
+
function integerResource(operation) {
|
|
9
|
+
try {
|
|
10
|
+
return operation();
|
|
11
|
+
} catch (error) {
|
|
12
|
+
// With bounded=false, integer operations have no language-level numeric
|
|
13
|
+
// representation bound. A finite host can nevertheless exhaust storage
|
|
14
|
+
// (V8 reports oversized BigInt powers/shifts as RangeError). Keep that
|
|
15
|
+
// implementation resource boundary inside the ISO error model.
|
|
16
|
+
if (error?.name === 'RangeError') throw new PrologError('resource_error(memory)');
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
8
21
|
function evaluate(term, env, options = {}) {
|
|
9
22
|
term = deref(term, env);
|
|
10
23
|
if (term.type === VAR) throw new PrologError('instantiation_error');
|
|
@@ -91,9 +104,17 @@ function evaluateOperation(term, args, options = {}) {
|
|
|
91
104
|
if (options.isoStrict === true && ['truncate', 'round', 'ceiling', 'floor'].includes(name) && args[0].integer) {
|
|
92
105
|
throw new PrologError('type_error(float)', numericTerm(args[0]));
|
|
93
106
|
}
|
|
94
|
-
const a = Number(args[0].value);
|
|
95
107
|
if (name === 'abs' && args[0].integer) return { integer: true, value: args[0].value < 0n ? -args[0].value : args[0].value };
|
|
96
108
|
if (name === 'sign' && args[0].integer) return { integer: true, value: args[0].value < 0n ? -1n : args[0].value > 0n ? 1n : 0n };
|
|
109
|
+
const a = Number(args[0].value);
|
|
110
|
+
// The int-exp -> float templates first apply the processor's I->F
|
|
111
|
+
// conversion. With unbounded integers that conversion can overflow even
|
|
112
|
+
// though the integer itself is representable. Do not let a host math
|
|
113
|
+
// function observe +/-Infinity and turn the required float-overflow into
|
|
114
|
+
// a different result (for example atan(Huge) = pi/2).
|
|
115
|
+
if (options.isoStrict === true && args[0].integer && !Number.isFinite(a)) {
|
|
116
|
+
throw new PrologError('evaluation_error(float_overflow)');
|
|
117
|
+
}
|
|
97
118
|
if (name === 'truncate' || name === 'round' || name === 'ceiling' || name === 'floor') {
|
|
98
119
|
const fn = name === 'truncate' ? Math.trunc : name === 'round' ? Math.round : name === 'ceiling' ? Math.ceil : Math.floor;
|
|
99
120
|
return { integer: true, value: BigInt(fn(a)) };
|
|
@@ -107,6 +128,13 @@ function evaluateOperation(term, args, options = {}) {
|
|
|
107
128
|
const value = fn(a);
|
|
108
129
|
if (Number.isNaN(value) || (name === 'log' && a === 0)) throw new PrologError('evaluation_error(undefined)');
|
|
109
130
|
if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
|
|
131
|
+
// Unlike the implementation-defined resultF choice for ordinary floating
|
|
132
|
+
// arithmetic, exp/1 has an explicit exceptional condition when its
|
|
133
|
+
// mathematical non-zero result is too small to represent. Math.exp()
|
|
134
|
+
// signals that boundary by rounding the positive result to zero.
|
|
135
|
+
if (options.isoStrict === true && name === 'exp' && value === 0) {
|
|
136
|
+
throw new PrologError('evaluation_error(underflow)');
|
|
137
|
+
}
|
|
110
138
|
return { integer: false, value };
|
|
111
139
|
}
|
|
112
140
|
if (arity !== 2) throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
|
|
@@ -117,7 +145,7 @@ function evaluateOperation(term, args, options = {}) {
|
|
|
117
145
|
throw new PrologError('type_error(integer)', numericTerm(invalid));
|
|
118
146
|
}
|
|
119
147
|
if (bothInteger && name === '^') {
|
|
120
|
-
if (b >= 0n) return { integer: true, value: a ** b };
|
|
148
|
+
if (b >= 0n) return { integer: true, value: integerResource(() => a ** b) };
|
|
121
149
|
if (a === 0n) throw new PrologError('evaluation_error(undefined)');
|
|
122
150
|
if (a === 1n) return { integer: true, value: 1n };
|
|
123
151
|
if (a === -1n) return { integer: true, value: (-b) % 2n === 0n ? 1n : -1n };
|
|
@@ -126,23 +154,47 @@ function evaluateOperation(term, args, options = {}) {
|
|
|
126
154
|
}
|
|
127
155
|
if (bothInteger && ['+', '-', '*', '//', 'div', 'mod', 'rem', '/\\', '\\/', 'xor', '<<', '>>'].includes(name)) {
|
|
128
156
|
if ((name === '//' || name === 'div' || name === 'mod' || name === 'rem') && b === 0n) throw new PrologError('evaluation_error(zero_divisor)');
|
|
129
|
-
if (name === '+') return { integer: true, value: a + b };
|
|
130
|
-
if (name === '-') return { integer: true, value: a - b };
|
|
131
|
-
if (name === '*') return { integer: true, value: a * b };
|
|
132
|
-
if (name === '//') return { integer: true, value: a / b };
|
|
157
|
+
if (name === '+') return { integer: true, value: integerResource(() => a + b) };
|
|
158
|
+
if (name === '-') return { integer: true, value: integerResource(() => a - b) };
|
|
159
|
+
if (name === '*') return { integer: true, value: integerResource(() => a * b) };
|
|
160
|
+
if (name === '//') return { integer: true, value: integerResource(() => a / b) };
|
|
133
161
|
if (name === 'div') {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
162
|
+
return { integer: true, value: integerResource(() => {
|
|
163
|
+
const quotient = a / b;
|
|
164
|
+
const remainder = a % b;
|
|
165
|
+
return remainder !== 0n && ((a < 0n) !== (b < 0n)) ? quotient - 1n : quotient;
|
|
166
|
+
}) };
|
|
137
167
|
}
|
|
138
|
-
if (name === 'rem') return { integer: true, value: a % b };
|
|
139
|
-
if (name === 'mod') return { integer: true, value: ((a % b) + b) % b };
|
|
140
|
-
if (name === '/\\') return { integer: true, value: a & b };
|
|
141
|
-
if (name === '\\/') return { integer: true, value: a | b };
|
|
142
|
-
if (name === 'xor') return { integer: true, value: a ^ b };
|
|
143
|
-
if (name === '<<') return { integer: true, value: a << b };
|
|
144
|
-
if (name === '>>') return { integer: true, value: a >> b };
|
|
168
|
+
if (name === 'rem') return { integer: true, value: integerResource(() => a % b) };
|
|
169
|
+
if (name === 'mod') return { integer: true, value: integerResource(() => ((a % b) + b) % b) };
|
|
170
|
+
if (name === '/\\') return { integer: true, value: integerResource(() => a & b) };
|
|
171
|
+
if (name === '\\/') return { integer: true, value: integerResource(() => a | b) };
|
|
172
|
+
if (name === 'xor') return { integer: true, value: integerResource(() => a ^ b) };
|
|
173
|
+
if (name === '<<') return { integer: true, value: integerResource(() => a << b) };
|
|
174
|
+
if (name === '>>') return { integer: true, value: integerResource(() => a >> b) };
|
|
175
|
+
}
|
|
176
|
+
// Power has prescribed exceptional conditions that depend on the evaluated
|
|
177
|
+
// operand values/types and therefore precede any I->F conversion needed by
|
|
178
|
+
// the selected floating template. Checking these first matters with
|
|
179
|
+
// unbounded integers: conversion overflow must not hide an already-satisfied
|
|
180
|
+
// undefined-power condition.
|
|
181
|
+
const aNegative = args[0].integer ? a < 0n : a < 0;
|
|
182
|
+
const aZero = args[0].integer ? a === 0n : a === 0;
|
|
183
|
+
const bNegative = args[1].integer ? b < 0n : b < 0;
|
|
184
|
+
if (options.isoStrict === true && name === '**' && aNegative && !args[1].integer) {
|
|
185
|
+
// Corrigendum 3 corrects the operand name in 9.3.1.3(c): **/2 requires
|
|
186
|
+
// an integer-typed exponent for a negative base. A float such as 2.0 is
|
|
187
|
+
// still not an integer value of the required type here.
|
|
188
|
+
throw new PrologError('evaluation_error(undefined)');
|
|
145
189
|
}
|
|
190
|
+
if (options.isoStrict === true && name === '^' && aNegative && !args[1].integer && !Number.isInteger(b)) {
|
|
191
|
+
// Corrigendum 2 ^/2 additionally accepts a float with an integer value.
|
|
192
|
+
throw new PrologError('evaluation_error(undefined)');
|
|
193
|
+
}
|
|
194
|
+
if ((name === '**' || name === '^') && aZero && bNegative) {
|
|
195
|
+
throw new PrologError('evaluation_error(undefined)');
|
|
196
|
+
}
|
|
197
|
+
|
|
146
198
|
const x = Number(a), y = Number(b);
|
|
147
199
|
if ((!Number.isFinite(x) || !Number.isFinite(y)) && name !== 'max' && name !== 'min') {
|
|
148
200
|
throw new PrologError('evaluation_error(float_overflow)');
|
|
@@ -158,11 +210,6 @@ function evaluateOperation(term, args, options = {}) {
|
|
|
158
210
|
if (x === 0 && y === 0) throw new PrologError('evaluation_error(undefined)');
|
|
159
211
|
value = Math.atan2(x, y);
|
|
160
212
|
}
|
|
161
|
-
else if ((name === '**' || name === '^') && x === 0 && y < 0) {
|
|
162
|
-
// 9.3.1.3(d) and Cor.2 9.3.10.3(d): zero to a negative power is
|
|
163
|
-
// undefined, not floating-point overflow.
|
|
164
|
-
throw new PrologError('evaluation_error(undefined)');
|
|
165
|
-
}
|
|
166
213
|
else if (name === '+') value = x + y;
|
|
167
214
|
else if (name === '-') value = x - y;
|
|
168
215
|
else if (name === '*') value = x * y;
|
|
@@ -171,6 +218,14 @@ function evaluateOperation(term, args, options = {}) {
|
|
|
171
218
|
else throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
|
|
172
219
|
if (Number.isNaN(value)) throw new PrologError('evaluation_error(undefined)');
|
|
173
220
|
if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
|
|
221
|
+
// Part 1 **/2 and Corrigendum 2 ^/2 prescribe underflow when a
|
|
222
|
+
// mathematically non-zero floating power is too small. A non-zero finite
|
|
223
|
+
// base cannot have an exact zero power result, so a host result of +/-0 is
|
|
224
|
+
// the observable underflow boundary. Zero raised to a positive power remains
|
|
225
|
+
// the ordinary exact zero case.
|
|
226
|
+
if (options.isoStrict === true && (name === '**' || name === '^') && value === 0 && x !== 0) {
|
|
227
|
+
throw new PrologError('evaluation_error(underflow)');
|
|
228
|
+
}
|
|
174
229
|
return { integer: false, value };
|
|
175
230
|
}
|
|
176
231
|
export function arithmeticValueTerm(value) {
|
package/src/iso-limits.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// ISO/IEC 13211-1 implementation-defined processor limits.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
export const ISO_MAX_ARITY = 65535;
|
|
2
|
+
//
|
|
3
|
+
// max_arity describes the maximum arity of *compound terms*, not a separate
|
|
4
|
+
// implementation limit on procedures. EyeProlog does not impose a fixed
|
|
5
|
+
// compound-term arity ceiling: practical exhaustion is a resource issue.
|
|
6
|
+
// Keep null as the internal sentinel for the standard flag value `unbounded`.
|
|
7
|
+
export const ISO_MAX_ARITY = null;
|
package/src/iso.js
CHANGED
|
@@ -404,14 +404,30 @@ function* functorBuiltin({ goal, env }) {
|
|
|
404
404
|
}
|
|
405
405
|
const arity = BigInt(arityTerm.name);
|
|
406
406
|
if (arity > 0n && name.type !== ATOM) throw new PrologError('type_error(atom)', name);
|
|
407
|
-
if (arity > BigInt(ISO_MAX_ARITY))
|
|
407
|
+
if (ISO_MAX_ARITY != null && arity > BigInt(ISO_MAX_ARITY)) {
|
|
408
|
+
throw new PrologError('representation_error(max_arity)');
|
|
409
|
+
}
|
|
408
410
|
if (arity < 0n) throw new PrologError('domain_error(not_less_than_zero)', arityTerm);
|
|
409
411
|
if (arity === 0n) {
|
|
410
412
|
if (unify(goal.args[0], name, next)) yield next;
|
|
411
413
|
return;
|
|
412
414
|
}
|
|
415
|
+
// `max_arity = unbounded` means there is no ISO-visible representation
|
|
416
|
+
// ceiling on compound terms. The JavaScript host still has finite storage
|
|
417
|
+
// resources, however. In particular an Array length is limited to 2^32-1
|
|
418
|
+
// and allocation can fail before that. Report host exhaustion as the ISO
|
|
419
|
+
// resource error family rather than leaking a JavaScript RangeError or
|
|
420
|
+
// reintroducing an artificial max_arity limit.
|
|
421
|
+
if (arity > 0xffffffffn) throw new PrologError('resource_error(memory)');
|
|
413
422
|
const id = ++isoFresh;
|
|
414
|
-
|
|
423
|
+
let args;
|
|
424
|
+
try {
|
|
425
|
+
args = Array.from({ length: Number(arity) }, (_, i) => variable(`__functor${id}_${i}`));
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (error?.name === 'RangeError') throw new PrologError('resource_error(memory)');
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
430
|
+
if (unify(goal.args[0], compound(name.name, args), next)) yield next;
|
|
415
431
|
}
|
|
416
432
|
|
|
417
433
|
function* argBuiltin({ goal, env }) {
|
|
@@ -460,7 +476,9 @@ function* univBuiltin({ goal, env }) {
|
|
|
460
476
|
return;
|
|
461
477
|
}
|
|
462
478
|
const name = requireAtom(items[0], env);
|
|
463
|
-
if (items.length - 1 > ISO_MAX_ARITY)
|
|
479
|
+
if (ISO_MAX_ARITY != null && items.length - 1 > ISO_MAX_ARITY) {
|
|
480
|
+
throw new PrologError('representation_error(max_arity)');
|
|
481
|
+
}
|
|
464
482
|
if (unify(goal.args[0], compound(name.name, items.slice(1)), next)) yield next;
|
|
465
483
|
}
|
|
466
484
|
|
|
@@ -591,12 +609,17 @@ function isGrammarRuleProcedure(solver, head) {
|
|
|
591
609
|
|
|
592
610
|
function isProcessorStaticProcedure(solver, head) {
|
|
593
611
|
// Conjunction is an ISO control construct (7.5/Table 9) but is executed
|
|
594
|
-
// directly by the solver rather than through the builtin registry.
|
|
595
|
-
//
|
|
596
|
-
//
|
|
612
|
+
// directly by the solver rather than through the builtin registry. The
|
|
613
|
+
// source syntax functors (:-)/1-2 likewise have processor-defined roles and
|
|
614
|
+
// STC #56 calls for protecting them from database modification. Keep these
|
|
615
|
+
// in the same static/private family for strict runtime database operations.
|
|
616
|
+
const strictSyntaxProcedure = solver.isoStrict && (
|
|
617
|
+
(head.name === ',' && head.arity === 2) ||
|
|
618
|
+
(head.name === ':-' && (head.arity === 1 || head.arity === 2))
|
|
619
|
+
);
|
|
597
620
|
return Boolean(solver.registry.get(head.name, head.arity)) ||
|
|
598
621
|
isGrammarRuleProcedure(solver, head) ||
|
|
599
|
-
|
|
622
|
+
strictSyntaxProcedure;
|
|
600
623
|
}
|
|
601
624
|
|
|
602
625
|
function assertModifiable(solver, head, module = 'user') {
|
|
@@ -677,8 +700,14 @@ function predicateIndicatorParts(term, env) {
|
|
|
677
700
|
if (name.type !== ATOM) throw new PrologError('type_error(atom)', name);
|
|
678
701
|
const integer = BigInt(arity.name);
|
|
679
702
|
if (integer < 0n) throw new PrologError('domain_error(not_less_than_zero)', arity);
|
|
680
|
-
if (integer > BigInt(ISO_MAX_ARITY))
|
|
681
|
-
|
|
703
|
+
if (ISO_MAX_ARITY != null && integer > BigInt(ISO_MAX_ARITY)) {
|
|
704
|
+
throw new PrologError('representation_error(max_arity)');
|
|
705
|
+
}
|
|
706
|
+
// Actual clause arities are ordinary JavaScript array lengths. For a very
|
|
707
|
+
// large predicate indicator that names no existing procedure, retain the
|
|
708
|
+
// exact integer in the map key instead of rounding it through Number.
|
|
709
|
+
const runtimeArity = integer <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(integer) : integer;
|
|
710
|
+
return { name: name.name, arity: runtimeArity, indicator };
|
|
682
711
|
}
|
|
683
712
|
|
|
684
713
|
function* abolishBuiltin({ solver, goal, env }) {
|
|
@@ -2279,7 +2308,7 @@ function* callClosureBuiltin({ solver, goal, env }) {
|
|
|
2279
2308
|
const closure = callable(goal.args[0], env);
|
|
2280
2309
|
const existing = closure.type === COMPOUND ? closure.args : [];
|
|
2281
2310
|
const extra = goal.args.slice(1);
|
|
2282
|
-
if (existing.length + extra.length > ISO_MAX_ARITY) {
|
|
2311
|
+
if (ISO_MAX_ARITY != null && existing.length + extra.length > ISO_MAX_ARITY) {
|
|
2283
2312
|
throw new PrologError('representation_error(max_arity)');
|
|
2284
2313
|
}
|
|
2285
2314
|
const invoked = compound(closure.name, [...existing, ...extra]);
|
package/src/parser.js
CHANGED
|
@@ -724,7 +724,9 @@ class Parser {
|
|
|
724
724
|
}
|
|
725
725
|
while (true) {
|
|
726
726
|
args.push(this.parseTerm(ARG_MIN_PRECEDENCE, false, false, true));
|
|
727
|
-
if (args.length > ISO_MAX_ARITY)
|
|
727
|
+
if (ISO_MAX_ARITY != null && args.length > ISO_MAX_ARITY) {
|
|
728
|
+
throw new NumberRepresentationError('representation_error(max_arity)');
|
|
729
|
+
}
|
|
728
730
|
if (this.token.type !== TOK.COMMA) break;
|
|
729
731
|
this.advance();
|
|
730
732
|
}
|
package/src/program.js
CHANGED
|
@@ -1317,8 +1317,13 @@ function assertPredicateIsDefinable(name, arity, strictIso = false) {
|
|
|
1317
1317
|
// be redefined in either profile. Strict core mode extends the same ISO
|
|
1318
1318
|
// rule to every Part-1 built-in/control construct; the normal EyeProlog
|
|
1319
1319
|
// profile keeps its historical source-compatibility behavior.
|
|
1320
|
+
const strictSyntaxProcedure = strictIso && (
|
|
1321
|
+
(name === ',' && arity === 2) ||
|
|
1322
|
+
(name === ':-' && (arity === 1 || arity === 2))
|
|
1323
|
+
);
|
|
1320
1324
|
if ((name === 'false' && arity === 0) ||
|
|
1321
|
-
(strictIso &&
|
|
1325
|
+
(strictIso && getStrictIsoRegistry().get(name, arity)) ||
|
|
1326
|
+
strictSyntaxProcedure) {
|
|
1322
1327
|
throw staticProcedureModificationError(name, arity);
|
|
1323
1328
|
}
|
|
1324
1329
|
}
|
package/src/solver.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Depth-first EyeProlog solver with builtin dispatch, memoization, and guarded recursion handling.
|
|
2
2
|
// Most semantic decisions still flow through unification; optimizations only select candidates earlier.
|
|
3
3
|
import {
|
|
4
|
-
COMPOUND, NUMBER, VAR, Env, compactListLength, compactVariableList, compound, cons, copyResolved, deref, emptyList,
|
|
4
|
+
ATOM, COMPOUND, NUMBER, VAR, Env, compactListLength, compactVariableList, compound, cons, copyResolved, deref, emptyList,
|
|
5
5
|
flattenConjunction, freshTerm, isCons, isDecimalInteger, isEmptyList,
|
|
6
6
|
numberTerm, numberTextFromDouble, termIsGround, termToString, unify, variable, variantTerms,
|
|
7
7
|
} from './term.js';
|
|
@@ -962,7 +962,9 @@ function defaultPrologFlags(unknown = 'error', strictIso = false) {
|
|
|
962
962
|
['debug', { value: compound('off', []), allowed: ['on', 'off'], changeable: true }],
|
|
963
963
|
['max_integer', { value: null, allowed: [], changeable: false }],
|
|
964
964
|
['min_integer', { value: null, allowed: [], changeable: false }],
|
|
965
|
-
['max_arity',
|
|
965
|
+
['max_arity', ISO_MAX_ARITY == null
|
|
966
|
+
? { value: compound('unbounded', []), allowed: ['unbounded'], valueType: ATOM, changeable: false }
|
|
967
|
+
: { value: numberTerm(ISO_MAX_ARITY), allowed: [String(ISO_MAX_ARITY)], valueType: NUMBER, changeable: false }],
|
|
966
968
|
['unknown', { value: compound(unknown, []), allowed: ['error', 'fail', 'warning'], changeable: true }],
|
|
967
969
|
['double_quotes', { value: compound('chars', []), allowed: ['chars', 'codes', 'atom'], changeable: true }],
|
|
968
970
|
['occurs_check', { value: compound('true', []), allowed: ['true', 'error'], changeable: true }],
|
|
@@ -31,17 +31,17 @@ error-ordering alternative to an individual executable assertion.
|
|
|
31
31
|
| Standard area | Status | Current evidence |
|
|
32
32
|
| --- | --- | --- |
|
|
33
33
|
| Clause 6 — tokens, terms, lists, operators, quoted text | audit | Complete vendored WG17 syntax matrix, `lexical_and_curly_terms`, `scryer_lexical_terms`, operator suites, syntax-error cases, quoted-layout/escape error cases, writer/read-back regressions, and strict ASCII PCS/collation boundary tests. The implementation-defined 6.5/6.6 character-model decisions are now closed; wider shall-by-shall lexical mapping remains open. |
|
|
34
|
-
| 7.1-7.3 — term types, term order, unification | audit | Standard-order, identity, finite-tree and occurs-check suites, Corrigendum 2 term predicates, plus strict checks for the required `variable < float < integer < atom < compound` type order, PCS-based atom collation, and the
|
|
34
|
+
| 7.1-7.3 — term types, term order, unification | audit | Standard-order, identity, finite-tree and occurs-check suites, Corrigendum 2 term predicates, plus strict checks for the required `variable < float < integer < atom < compound` type order, PCS-based atom collation, and the `max_arity=unbounded` compound-term model. |
|
|
35
35
|
| 7.4 — Prolog text and directives | audit | All Part 1 directive indicators are parsed; include/ensure-loaded/operator/flag/character-conversion behavior has executable coverage. Preparation-time `char_conversion/2` affects later unquoted source text and respects `char_conversion=off`. Strict preparation now enforces declaration-before-clause ordering for `dynamic/1`, `multifile/1`, and `discontiguous/1`, cross-text `multifile/1`, discontiguous clause grouping, empty declared procedures, include textual-replacement behavior, and one-time initialization per prepared program. The remaining shall-by-shall mapping is still open. |
|
|
36
|
-
| 7.5-7.6 — database and term/clause conversion | audit | Dynamic database and logical-update-view suites. Strict mode restores Part 1 private-static/public-dynamic `clause/2` access; focused strict checks now cover `current_predicate/1`, `clause/2`, `asserta/1`, `assertz/1`, `retract/1`, Corrigendum 2 `retractall/1`, and `abolish/1` errors plus empty-procedure lifetime
|
|
36
|
+
| 7.5-7.6 — database and term/clause conversion | audit | Dynamic database and logical-update-view suites. Strict mode restores Part 1 private-static/public-dynamic `clause/2` access; focused strict checks now cover `current_predicate/1`, `clause/2`, `asserta/1`, `assertz/1`, `retract/1`, Corrigendum 2 `retractall/1`, and `abolish/1` errors plus empty-procedure lifetime. Runtime protection includes the solver-native conjunction control construct `','/2` and, following STC #56's accepted-direction action item, the source syntax functors `(:-)/1-2` for database modification/private access while leaving their call behavior distinct. The remaining clause-conversion shall-by-shall mapping is still open. |
|
|
37
37
|
| 7.7 — execution and backtracking | audit | Control/search suites. Strict mode disables EyeProlog automatic tabling, cycle guards, and recursive numeric shortcuts so core execution uses ordinary clause selection/backtracking. |
|
|
38
38
|
| 7.8 — control constructs and exceptions | audit | call, cut, conjunction, disjunction (including failed branches after callee-local cuts), if-then, catch/throw, renamed-copy tests. |
|
|
39
|
-
| 7.9 — expression evaluation | audit | Arithmetic/evaluation/error suites, including Corrigenda. Strict mode now
|
|
39
|
+
| 7.9 — expression evaluation | audit | Arithmetic/evaluation/error suites, including Corrigenda. Strict mode now pins direct-variable precedence, arithmetic operand type errors, float-only rounding conversions, the Part 1 mixed integer/float comparison conversion rule, I->F overflow before floating evaluable functors (STC #42), explicit `exp/1`/power underflow, and prescribed negative/zero power errors before later conversion overflow. Unbounded integer powers/shifts no longer leak host `RangeError`; finite-host exhaustion is normalized to `resource_error(memory)` in line with the Part 1 resource-error note and STC #21. The remaining exceptional-value/error-precedence rows are still being exhaustively enumerated. |
|
|
40
40
|
| 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, and Corrigendum 3 writer cases. The audit now also covers write-mode creation/truncation, append creation, repositioned overwrite, flushing, EOF actions, close/current-stream handling, strict stream-position terms, stream-property validation, stream-term requirements, text-vs-binary permission errors, and the prescribed `alias(A)` culprit for `open/4` alias collisions. The full option/mode cross-product is still being mapped. |
|
|
41
41
|
| 7.11 — flags | covered | The complete Part 1 flag set, selected defaults, standard value domains, changeability, `current_prolog_flag/2`, and `set_prolog_flag/2` error behavior have dedicated strict tests. EyeProlog selects `bounded=false` and `integer_rounding_function=toward_zero`; valid alternative values of those fixed flags reach `permission_error(modify,flag,...)`, while `max_integer` and `min_integer` have no current value. Strict mode excludes the EyeProlog `occurs_check` extension. |
|
|
42
|
-
| 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax families and focused error cases. Exact ISO 8.14.1-8.14.4 error precedence is covered for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`; the continuing audit has corrected additional 8.11-8.13 stream/character/byte errors (including the complete `alias(A)` culprit for `open/4` alias collisions), Corrigendum 2 `keysort/2` errors, atomic-conversion list-shape precedence, `arg/3`, `atom_concat/3`, `sub_atom/5`, `char_conversion/2`, runtime control-construct
|
|
43
|
-
| 8.2-8.17 — built-in predicates | audit | Predicate-family coverage is mapped in ISO-MATRIX.md; Corrigendum 2 additions (`subsumes_term/2`, `term_variables/2`, `call/2..8`, `false/0`) are in the strict registry. The audit now includes corrected `keysort/2` variable/non-pair errors, stricter 8.11-8.13 stream and character/byte modes/errors, the 8.14 option/error-order subfamily, closed 8.17 flags,
|
|
44
|
-
| Clause 9 — evaluable functors | audit | Integer/float/rounding/transcendental/bitwise suites and corrigendum cases. Strict mode excludes the EyeProlog-only evaluable atom `e`, retains Corrigendum 2 arithmetic additions, reports unknown zero-arity evaluables with the required `F/0` culprit shape, enforces the Part 1 numeric/integer operand errors and float-only rounding modes, and uses the Part 1 integer-to-float
|
|
42
|
+
| 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax/resource families and focused error cases. Exact ISO 8.14.1-8.14.4 error precedence is covered for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`; the continuing audit has corrected additional 8.11-8.13 stream/character/byte errors (including the complete `alias(A)` culprit for `open/4` alias collisions), Corrigendum 2 `keysort/2` errors, atomic-conversion list-shape precedence, `arg/3`, `atom_concat/3`, `sub_atom/5`, `char_conversion/2`, runtime control-construct/database protection, arithmetic culprit/precedence reporting, and host-resource normalization for unbounded term/integer operations. A complete one-row-per-prescribed-error ordering map remains open. |
|
|
43
|
+
| 8.2-8.17 — built-in predicates | audit | Predicate-family coverage is mapped in ISO-MATRIX.md; Corrigendum 2 additions (`subsumes_term/2`, `term_variables/2`, `call/2..8`, `false/0`) are in the strict registry. The audit now includes corrected `keysort/2` variable/non-pair errors, stricter 8.11-8.13 stream and character/byte modes/errors, the 8.14 option/error-order subfamily, closed 8.17 flags, additional prescribed precedence for `arg/3`, `atom_concat/3`, `sub_atom/5`, number/atom list conversions, and `char_conversion/2`, plus finite-host resource handling for unbounded `functor/3` construction. The remaining mode/error matrix is not yet one-row-per-standard-row. |
|
|
44
|
+
| Clause 9 — evaluable functors | audit | Integer/float/rounding/transcendental/bitwise suites and corrigendum cases. Strict mode excludes the EyeProlog-only evaluable atom `e`, retains Corrigendum 2 arithmetic additions, reports unknown zero-arity evaluables with the required `F/0` culprit shape, enforces the Part 1 numeric/integer operand errors and float-only rounding modes, performs I->F conversion before floating functors (including overflow), reports the explicit `exp/1`, `**/2`, and Corrigendum 2 `^/2` underflow conditions, preserves the power-specific undefined conditions ahead of conversion overflow, and uses the Part 1 integer-to-float rule for mixed arithmetic comparisons. Normal mode retains EyeProlog's exact mixed-type comparison and round-to-zero arithmetic behavior as extensions. Unbounded BigInt resource exhaustion is translated into the Prolog error model rather than leaking host exceptions. Host floating-point representation choices remain documented implementation-defined behavior; the remaining exceptional-value rows are still under audit. |
|
|
45
45
|
| Corrigendum 1 | covered | Double-quoted atom/operator-priority corrections have dedicated cases. |
|
|
46
46
|
| Corrigendum 2 | covered | Added predicates/functors, catch corrections, bar/operator and uninstantiation corrections have dedicated cases. |
|
|
47
47
|
| Corrigendum 3 | covered | Writer options, `variable_names/1`, canonical list output and negative-power corrections have dedicated cases. |
|
|
@@ -70,14 +70,17 @@ The complete WG17 syntax matrix remains green under this narrower strict
|
|
|
70
70
|
boundary.
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
The subsequent audit
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
The subsequent arity audit originally selected a finite `max_arity=65535`, but
|
|
74
|
+
issue #66 and the post-Corrigendum STC review exposed that as the wrong
|
|
75
|
+
abstraction: Part 1 `max_arity` is the maximum arity of **compound terms**, not
|
|
76
|
+
a procedure-arity limit. EyeProlog now again selects `max_arity=unbounded` and
|
|
77
|
+
removes the artificial 65535 checks from source parsing, `functor/3`, `=../2`,
|
|
78
|
+
predicate indicators, and Corrigendum 2 `call/N` closure expansion. Practical
|
|
79
|
+
host exhaustion remains a resource condition rather than a declared term-arity
|
|
80
|
+
boundary. Focused strict tests also lock the corrected 8.5 term-construction
|
|
81
|
+
errors, 8.8 clause-access precedence, 8.10 all-solutions goal/list precedence,
|
|
82
|
+
database update errors, and Corrigendum 3 variable metadata traversal/write
|
|
83
|
+
naming.
|
|
81
84
|
|
|
82
85
|
The public WG17 `number_chars/2` comparison used in preparation of Corrigendum
|
|
83
86
|
2 has additionally been checked against the current strict profile. That
|
|
@@ -110,14 +113,13 @@ EyeProlog-only evaluable atom `e`, while the Corrigendum arithmetic additions
|
|
|
110
113
|
remain available. These corrections narrow the remaining `audit` rows but do
|
|
111
114
|
not by themselves close the full shall-by-shall/error-order exit criteria.
|
|
112
115
|
|
|
113
|
-
The next Corrigendum 2 pass
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
`
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
both behaviors.
|
|
116
|
+
The next Corrigendum 2 pass closed the prescribed `call/2..8` max-arity
|
|
117
|
+
branch for processors with a finite `max_arity`, and corrected reverse
|
|
118
|
+
`atom_chars/2` / `atom_codes/2` improper-list culprits. After issue #66 restored
|
|
119
|
+
EyeProlog's selected `max_arity=unbounded`, the conditional `call/N`
|
|
120
|
+
`representation_error(max_arity)` branch is intentionally unreachable unless a
|
|
121
|
+
future processor profile selects a finite compound-term limit; closure
|
|
122
|
+
expansion itself remains covered by strict regressions.
|
|
121
123
|
|
|
122
124
|
|
|
123
125
|
The following Part 1 pass tightens more of the still-open prescribed-error and
|
|
@@ -135,6 +137,43 @@ its exact cross-type comparison as an extension. Dedicated strict regressions
|
|
|
135
137
|
cover these distinctions without changing the normal-profile arithmetic error
|
|
136
138
|
contract.
|
|
137
139
|
|
|
140
|
+
A further Clause 9 pass closes the floating conversion and exceptional-value
|
|
141
|
+
boundary exposed by unbounded integers. Strict floating evaluable functors now
|
|
142
|
+
perform the required integer-to-float conversion before invoking the host math
|
|
143
|
+
operation, so an integer outside the selected finite binary64 range reports
|
|
144
|
+
`evaluation_error(float_overflow)` rather than producing a secondary host-math
|
|
145
|
+
result. The explicit underflow clauses for `exp/1`, Part 1 `**/2`, and
|
|
146
|
+
Corrigendum 2 `^/2` are enforced even though EyeProlog retains the
|
|
147
|
+
implementation-defined `round(x)` choice for generic `resultF` arithmetic. The
|
|
148
|
+
power audit also preserves the specified negative-base and zero/negative
|
|
149
|
+
exception conditions before any later I->F overflow, including Corrigendum 3's
|
|
150
|
+
correction to the `**/2` operand wording and Corrigendum 2's distinct rule that
|
|
151
|
+
`^/2` may accept an integer-valued float exponent.
|
|
152
|
+
|
|
153
|
+
## Post-Corrigendum STC cross-check
|
|
154
|
+
|
|
155
|
+
The public WG17 STC draft is tracked as useful defect-discovery material, but it
|
|
156
|
+
is not silently treated as a fourth published Corrigendum. The current audit
|
|
157
|
+
confirms EyeProlog already has the behavior implicated by the substantive STC
|
|
158
|
+
items on negative-number layout/operator syntax, arithmetic instantiation
|
|
159
|
+
precedence, EOF actions/end tokens, character/code/byte output error overlap,
|
|
160
|
+
clause head/body variable identity, sequential `op/3` and `set_prolog_flag/2`
|
|
161
|
+
preparation effects, integer-to-float transcendental evaluation, `char_code/2`
|
|
162
|
+
type precedence, `read_term/3` EOF handling, mixed arithmetic comparison,
|
|
163
|
+
`integer_rounding_function=toward_zero`, `set_prolog_flag/2` variable errors,
|
|
164
|
+
and `bagof/3` answer order. STC #21 is also used to keep finite-host exhaustion
|
|
165
|
+
of unbounded integer operations inside the Prolog resource-error model.
|
|
166
|
+
|
|
167
|
+
Issue #66 corresponds to the newer STC arity discussion: `max_arity` describes
|
|
168
|
+
compound terms. EyeProlog therefore selects `unbounded` and does not invent a
|
|
169
|
+
finite predicate/procedure ceiling merely to preserve the former 65535 value.
|
|
170
|
+
STC #56, which became a WG17 action item, is implemented narrowly for database
|
|
171
|
+
protection: `(:-)/1-2` are static/private for modification and `clause/2`
|
|
172
|
+
access, while ordinary calls retain their separate existence behavior. Other
|
|
173
|
+
draft/editorial or deliberately controversial proposals (for example deleting
|
|
174
|
+
standalone if-then) remain outside the Part 1 + Corrigenda 1-3 strict baseline
|
|
175
|
+
until standardized or adopted as an explicit compatibility extension.
|
|
176
|
+
|
|
138
177
|
## Strict-core boundary
|
|
139
178
|
|
|
140
179
|
`isoStrict: true` is intentionally a **Part 1 + Corrigenda 1-3** mode. It does
|
|
@@ -65,7 +65,7 @@ Status values are:
|
|
|
65
65
|
| 9.1.3.1 | Integer division rounding function `rndI` | Truncation toward zero, matching the `integer_rounding_function=toward_zero` flag. | **defined** — BigInt division for `//` in `src/iso-arithmetic.js`. |
|
|
66
66
|
| 7.11.2.1 | Whether preparation-time `Convc` affects execution-time `Convc` | Yes: mappings created while Prolog text is prepared are retained and initialize the solver's execution-time conversion map. | **defined** — `src/parser.js`, `src/program.js`, `src/solver.js`. |
|
|
67
67
|
| 7.11.2.2 | Effect when `debug=on` | The flag is accepted and stored; it does not change goal semantics or enable a debugger. | **defined** — `src/solver.js`; no semantic branch depends on `debug`. |
|
|
68
|
-
| 7.11.2.3 | Default `max_arity` | `
|
|
68
|
+
| 7.11.2.3 | Default `max_arity` | `unbounded`: EyeProlog imposes no fixed semantic ceiling on compound-term arity. Practical host allocation exhaustion is a resource condition. This flag is distinct from any potential implementation-specific procedure-arity limit; EyeProlog currently declares no separate finite procedure limit. | **defined** — `src/iso-limits.js`, `src/solver.js`, `src/parser.js`, `src/iso.js`; issue #66 regression coverage. |
|
|
69
69
|
| 7.11.2.5 | Default `double_quotes` | `chars`. | **defined** — `src/solver.js`, parser flag state. |
|
|
70
70
|
| 7.12.1 | Second argument of `error/2` | The default context term is the atom `eyeprolog`. A few implementation-specific diagnostics may deliberately supply a more specific context term. | **defined** — `formalErrorTerm()` in `src/iso.js`. |
|
|
71
71
|
| 7.12.2(f) | Implementation-defined representation limits | Strict character and character-code operations are limited to the selected ASCII PCS/collating integers 0..127. Normal mode extends characters to Unicode scalar values. Arity/integer values are modeled as unbounded but may hit host/resource limits. Float input overflow uses the implementation-specific `max_float`/`min_float` representation names documented by the STC-oriented tests. | **defined** — parser/ISO numeric and character guards. |
|
|
@@ -73,7 +73,7 @@ Status values are:
|
|
|
73
73
|
| 8.17.3 | Other effects of `halt/0` | Terminates EyeProlog execution and returns host/process status `0`; it produces no Prolog solution. | **defined** — `HaltSignal`, `haltBuiltin()`, CLI/runner handling. |
|
|
74
74
|
| 8.17.4 | Meaning/effects of `halt(Status)` | Integer `Status` is converted to the host process/runner halt code; it produces no Prolog solution. | **defined** — `haltBuiltin()`, `src/execute.js`, `src/cli.js`. |
|
|
75
75
|
| 9.1.4.1 | Floating-point rounding function `rndF` | Floating values and operations use ECMAScript `Number` (IEEE-754 binary64) and the host's specified binary64 arithmetic/conversions. | **defined** — `src/iso-arithmetic.js`, `src/number-value.js`. |
|
|
76
|
-
| 9.1.4.2 | Floating-point result function
|
|
76
|
+
| 9.1.4.2 | Floating-point result function for operations whose result is governed only by `resultF` | EyeProlog chooses `round(x)` rather than the optional exceptional value `underflow`. ECMAScript binary64 arithmetic therefore preserves a representable subnormal result and rounds a still-smaller generic arithmetic result to `0.0`, consistently with float-token and `number_chars/2` input. This implementation-defined choice does not suppress the explicit mandatory underflow conditions stated separately for Part 1 `**/2`, `exp/1`, and Corrigendum 2 `^/2`. | **defined** — arithmetic evaluation in `src/iso-arithmetic.js` and parser/number conversion; strict exceptional-value regressions. |
|
|
77
77
|
| 9.1.4.3 | Approximate-addition function | ECMAScript binary64 addition is used; subtraction is implemented through the corresponding host operation and all finite results remain binary64 values. | **defined** — arithmetic evaluation in `src/iso-arithmetic.js`. |
|
|
78
78
|
| 9.4 | Representation of negative integers for bitwise operations | BigInt's unbounded signed binary semantics are used, equivalent to an infinite two's-complement sign extension for bitwise operations. | **defined** — `src/iso-arithmetic.js` BigInt bitwise operators. |
|
|
79
79
|
| 9.4.1 | Right shift of negative integers and unusual shift counts | `>>` is arithmetic/sign-propagating. A negative count reverses direction according to JavaScript BigInt shift semantics; there is no finite integer bit-size ceiling in the Prolog model. | **defined** — `a >> b` in `src/iso-arithmetic.js`. |
|
|
@@ -86,15 +86,20 @@ Status values are:
|
|
|
86
86
|
### Issue #56: one underflow policy
|
|
87
87
|
|
|
88
88
|
Clause 9.1.4.2 permits the processor to choose `round(x)` or the exceptional
|
|
89
|
-
value `underflow` for a tiny non-zero
|
|
90
|
-
`round(x)`. Because its
|
|
91
|
-
|
|
89
|
+
value `underflow` for a tiny non-zero result when an operation is governed by
|
|
90
|
+
the generic floating result function. EyeProlog chooses `round(x)`. Because its
|
|
91
|
+
floating-point profile is ECMAScript IEEE-754 binary64, representable
|
|
92
|
+
subnormal values remain non-zero and still-smaller generic results round to
|
|
92
93
|
`0.0`. Float-token and `number_chars/2` input use the same finite-double
|
|
93
|
-
rounding policy
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
rounding policy.
|
|
95
|
+
|
|
96
|
+
That generic implementation-defined choice is distinct from evaluable functors
|
|
97
|
+
whose own error clauses prescribe underflow. In strict mode, `exp/1`, Part 1
|
|
98
|
+
`**/2`, and Corrigendum 2 `^/2` therefore raise
|
|
99
|
+
`evaluation_error(underflow)` when their mathematical non-zero result falls
|
|
100
|
+
below the representable range and the host would otherwise round it to zero.
|
|
101
|
+
The regression suite pins both sides of this boundary, while
|
|
102
|
+
`stc/float_underflow_input` covers the related input conversion policy.
|
|
98
103
|
|
|
99
104
|
## Implementation-specific features required to be documented by 5.4
|
|
100
105
|
|
|
@@ -11,14 +11,14 @@ compliance audit and the remaining work before a full conformance claim.
|
|
|
11
11
|
| Clause 6 lexical and term syntax | tokenizer, operator parser, lists, curly terms, quotes, numeric syntax, comments, strict ASCII PCS/collation | `scryer_lexical_terms`, `lexical_and_curly_terms`, `double_quoted_lists`, `corrigendum1_double_quote_operator`, `wg17_syntax_high_risk`, `wg17_invalid_octal_escape`, `wg17_unterminated_quoted_token`, `wg17_literal_newline_in_quote`, `wg17_non_iso_escape`, strict PCS/collation tests in `run-iso-strict.mjs`, syntax error cases |
|
|
12
12
|
| Clause 7 term order and unification | finite-tree unification, identity, standard order, errors | `unification_control_information`, `swipl_occurs_check`, `term_modes_and_ordering`, `logtalk_compare_standard_order` |
|
|
13
13
|
| Clause 7 control and exceptions | call, cut, conjunction, disjunction, if-then-else, catch and throw | `cut_control`, `control_and_terms`, `exceptions_and_flags`, `corrigenda_catch_callability`, `throw_copies_ball` |
|
|
14
|
-
| 8.2-8.5 term predicates | unification, Corrigendum 2 tests, comparison, sorting, creation and decomposition | `corrigenda_term_predicates`, `corrigenda_sort_keysort`, `logtalk_arg_unification`, `logtalk_univ`, associated error cases, Corrigendum 2 `keysort/2` variable/non-pair errors, and strict `arg/3`, `functor/3` / `=../2` error-order plus
|
|
14
|
+
| 8.2-8.5 term predicates | unification, Corrigendum 2 tests, comparison, sorting, creation and decomposition | `corrigenda_term_predicates`, `corrigenda_sort_keysort`, `logtalk_arg_unification`, `logtalk_univ`, associated error cases, Corrigendum 2 `keysort/2` variable/non-pair errors, and strict `arg/3`, `functor/3` / `=../2` error-order plus unbounded-arity/resource checks |
|
|
15
15
|
| 8.6-8.7 arithmetic predicates | `is/2` and all six arithmetic comparisons | `arithmetic`, comparison cases, isolated evaluation errors |
|
|
16
|
-
| 8.8-8.10 database and solutions | logical update view, dynamic mutation, all-solutions grouping | `dynamic_database`, `trealla_logical_update_view`, `corrigenda_retractall`, `grouped_solutions_and_clauses`, plus strict clause/database error and all-solutions precedence checks, including runtime static/private protection for the solver-native conjunction control construct |
|
|
16
|
+
| 8.8-8.10 database and solutions | logical update view, dynamic mutation, all-solutions grouping | `dynamic_database`, `trealla_logical_update_view`, `corrigenda_retractall`, `grouped_solutions_and_clauses`, plus strict clause/database error and all-solutions precedence checks, including runtime static/private protection for the solver-native conjunction control construct and STC #56 `(:-)/1-2` database protection |
|
|
17
17
|
| 8.11-8.14 streams and term I/O | text/binary streams, properties, units, read/write options and operators | `streams_and_term_io`, `operators`, Corrigendum 3 option cases, stream error cases, strict 8.14.1-8.14.4 option/error-order tests, Corrigendum 3 variable metadata/write naming checks, and issue #65 checks for stream-term/property validation, text-vs-binary permissions, EOF/flush/position, open/close semantics, and the exact `alias(A)` collision culprit |
|
|
18
18
|
| 8.15 logic and control | negation, once, repeat, `call/2` through `call/8`, `false/0` | `logtalk_once`, `corrigenda_call_closure`, `false_builtin`, plus strict Corrigendum 2 resulting-goal `max_arity` error coverage |
|
|
19
19
|
| 8.16 atomic processing | atoms, characters, codes and number conversion with prescribed errors | `atomic_term_processing`, focused forward/reverse cases, parenthesized-number rejection, Logtalk-derived error cases, strict Corrigendum 2 partial/improper-list precedence for atom/number conversions, and prescribed `atom_concat/3` / `sub_atom/5` ordering checks |
|
|
20
20
|
| 8.17 flags and hooks | complete required Part 1 flag set, selected defaults, standard value domains/changeability, halt and character conversion | `exceptions_and_flags`, `remaining_builtins_and_directives`, flag error cases, strict `char_conversion/2` precedence, and the complete strict flag audit in `run-iso-strict.mjs` |
|
|
21
|
-
| Clause 9 evaluable functors | integer, float, rounding, transcendental and bitwise operations | `arithmetic`, `corrigenda_arithmetic`, `corrigenda_atan2_zero`, `corrigenda_integer_negative_power`, strict exclusion of the normal-mode `e` evaluable extension, zero-arity unknown-evaluable checks, prescribed operand/type and
|
|
21
|
+
| Clause 9 evaluable functors | integer, float, rounding, transcendental and bitwise operations | `arithmetic`, `corrigenda_arithmetic`, `corrigenda_atan2_zero`, `corrigenda_integer_negative_power`, strict exclusion of the normal-mode `e` evaluable extension, zero-arity unknown-evaluable checks, prescribed operand/type and power-error precedence, float-only rounding modes, I->F overflow before floating functors, explicit `exp/1`/`**/2`/`^/2` underflow, Part 1 mixed integer/float comparison conversion, and resource-error normalization for finite-host exhaustion of unbounded integer operations |
|
|
22
22
|
| Module compatibility profile (related to ISO/IEC 13211-2 and later WG17 amendment work) | module declarations, exports, imports, qualification, meta-predicate context | `modules/qualified_call`, `modules/selective_library_import`, `dcg_module_nonterminal_indicator` |
|
|
23
23
|
| Part 3-oriented DCG compatibility profile | `-->`, terminal and partial sequences, grammar control constructs, semicontexts, nonterminal indicators, modules, `phrase/2-3`, steadfastness and errors | `dcg_terminals_and_remainder`, `dcg_control_constructs`, `dcg_partial_sequences`, `dcg_phrase_steadfastness`, `dcg_dynamic_nonterminal_indicator`, `logtalk_dcg_phrase_identity`, `logtalk_dcg_semicontexts`, DCG error and precedence cases |
|
|
24
24
|
|
|
@@ -33,7 +33,8 @@ clause-by-clause in [ISO-IMPLEMENTATION-DEFINED.md](ISO-IMPLEMENTATION-DEFINED.m
|
|
|
33
33
|
with *The Art of EyeProlog* remaining the implementation reference. In
|
|
34
34
|
particular, the index records the unbounded integer model, `double_quotes=chars`,
|
|
35
35
|
`//` rounding toward zero, the ECMAScript binary64 float policy (including the
|
|
36
|
-
9.1.4.2 choice to round tiny
|
|
36
|
+
9.1.4.2 choice to round generic tiny results while retaining explicit
|
|
37
|
+
functor-specific underflow errors),
|
|
37
38
|
stream/character decisions, and the normal-profile extension boundary.
|
|
38
39
|
|
|
39
40
|
This is an executable conformance/compatibility matrix, not a certification issued by an
|
|
@@ -9,7 +9,7 @@ wording.
|
|
|
9
9
|
|
|
10
10
|
The purpose of this ledger is practical. Where an STC item can be expressed as
|
|
11
11
|
an executable Prolog observation, EyeProlog keeps a standards-facing case under
|
|
12
|
-
`test/conformance/stc/` (or points to an existing strict/WG17 case) so discussion
|
|
12
|
+
`test/conformance/cases/stc/` (or points to an existing strict/WG17 case) so discussion
|
|
13
13
|
of the draft can expose implementation problems before they become release
|
|
14
14
|
regressions.
|
|
15
15
|
|
|
@@ -17,14 +17,22 @@ regressions.
|
|
|
17
17
|
|
|
18
18
|
| STC item | Topic | EyeProlog evidence / finding |
|
|
19
19
|
| --- | --- | --- |
|
|
20
|
+
| [#17](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#17) | expression-error overlap | The strict suite pins both `X is 1/0+_` and `X is _+1/0` to `instantiation_error`, so the direct-variable rule is not masked by implementation evaluation order or a zero-divisor on the other operand. |
|
|
21
|
+
| [#18](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#18) | `eof_action(error)` | Stream regressions verify the first EOF observation yields the EOF value and a subsequent read with `eof_action(error)` raises `permission_error(input,past_end_of_stream,...)`; `eof_code` and `reset` remain distinct. |
|
|
22
|
+
| [#21](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#21) | unbounded-integer resource exhaustion | The strict suite now prevents host `RangeError` leakage from oversized integer shifts/powers and reports `resource_error(memory)`, consistent with the Part 1 resource-error note and the STC correction proposal. The same principle is used when unbounded `functor/3` construction exhausts a finite host resource. |
|
|
23
|
+
| [#27](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#27)-[#29](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#29) | overlapping character/code/byte stream errors | Existing stream tests plus strict probes preserve argument validation before later stream-state errors where the published error clauses require it; the STC examples with an open alias produce the stated instantiation/type errors. |
|
|
24
|
+
| [#32](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#32) | `number_chars/2` declarative/procedural wording | The strict suite follows the published procedural number-syntax path: `number_chars(N,['0','1'])` succeeds with `N = 1`. This records the executable behavior without treating the draft wording proposal as a new normative clause. |
|
|
20
25
|
| [#37](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#37) | `clause/2` variable identity | `stc/clause_variable_identity` verifies sharing between head and body is preserved. |
|
|
21
26
|
| [#39](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#39) | negative-number syntax | Covered extensively by the upstream-first WG17 syntax matrix; unary-minus read-back discrepancies found by that matrix were fixed in v1.3.27. |
|
|
22
27
|
| [#40](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#40) | inexact float representation | EyeProlog uses the host IEEE-754 binary64 value and requires written floats to read back to that same value. Input underflow is covered by `stc/float_underflow_input`. |
|
|
23
|
-
| [#
|
|
24
|
-
| [#
|
|
28
|
+
| [#41](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#41) | preparation-time `op/3` ordering | A strict parser regression verifies an `op/3` directive affects following text and cannot retroactively make preceding text parse with the new operator. The same preparation machinery is used for `set_prolog_flag/2`. |
|
|
29
|
+
| [#42](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#42) | integer-to-float conversion for float functions | `stc/integer_to_float_evaluable` verifies an integer expression is accepted by `sin/1` and produces a float. The strict suite additionally verifies that an unbounded integer outside the finite binary64 range raises `evaluation_error(float_overflow)` during I->F conversion before `sin/1`, `cos/1`, `atan/1`, the Corrigendum 2 inverse/trigonometric additions, `exp/1`, `log/1`, `sqrt/1`, or `float/1` can observe a host infinity. |
|
|
30
|
+
| [#44](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#44) | `char_code/2` error classification | Strict coverage distinguishes `type_error(integer,Code)` for a non-integer code from `representation_error(character_code)` for an integer outside the character-code set. |
|
|
31
|
+
| [#48](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#48) | `read_term/3` option wording with `variables([])` | The strict suite verifies `read_term(T,[variables([])])` on input `T.` has no solution because the actual variable list cannot unify with `[]`; this keeps the operational option semantics clear despite the draft's wording concern. |
|
|
25
32
|
| [#49](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#49) | `read_term/3` and EOF | Covered by strict reader/conformance tests and the interactive-read regressions. |
|
|
26
|
-
| [#50](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#50) | mixed integer/float arithmetic comparison |
|
|
33
|
+
| [#50](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#50) | mixed integer/float arithmetic comparison | The STC-facing normal-profile case requires exact cross-type ordering and `max/2`/`min/2` use the same comparison. `--iso-strict` deliberately retains the published Part 1 integer-to-float comparison rule; the draft case is not silently substituted for that normative baseline. |
|
|
27
34
|
| [#55](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#55) | integer rounding function | Existing flag conformance verifies `integer_rounding_function = toward_zero`. |
|
|
35
|
+
| [#56](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#56) | protect `(:-)/1` and `(:-)/2` from database modification | **Found a gap during the #65 audit.** Strict database operations now treat both functors as static/private for `assert*`, `retract*`, `abolish/1`, declarations, and `clause/2`, while ordinary calls still follow the separate procedure-existence behavior described by the STC item. |
|
|
28
36
|
| [#58](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#58) | `set_prolog_flag/2` instantiation error | Existing strict/error coverage requires an instantiation error when a required flag value is a variable. |
|
|
29
37
|
| [#67](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#67) | `bagof/3` answer-order example | `stc/bagof_answer_order` verifies the proposed clarifying example produces `[2,1]`. |
|
|
30
38
|
| [#73](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/stc#73) | float-reading limits discussed from issue #54 | Positive/negative literal and `number_chars/2` overflow are kept as explicit draft cases; underflow is tested separately. See the note below. |
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
answer(7, red, caught(eyeprolog), first, caught(eyeprolog)).
|
|
2
2
|
answer(7, red, caught(eyeprolog), second, caught(eyeprolog)).
|
|
3
|
-
flags(off, on, [pair(bounded, false), pair(integer_rounding_function, toward_zero), pair(char_conversion, on), pair(debug, off), pair(max_arity,
|
|
3
|
+
flags(off, on, [pair(bounded, false), pair(integer_rounding_function, toward_zero), pair(char_conversion, on), pair(debug, off), pair(max_arity, unbounded), pair(unknown, fail), pair(double_quotes, chars), pair(occurs_check, true)]).
|
package/test/run-iso-strict.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Env,
|
|
9
9
|
createStrictIsoRegistry,
|
|
10
10
|
parseGoalText,
|
|
11
|
+
parseProgramText,
|
|
11
12
|
run,
|
|
12
13
|
} from '../src/index.js';
|
|
13
14
|
import { TestReporter, isMainModule, runStandalone } from './test-style.mjs';
|
|
@@ -55,7 +56,7 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
55
56
|
'current_prolog_flag(integer_rounding_function,toward_zero)',
|
|
56
57
|
'current_prolog_flag(char_conversion,on)',
|
|
57
58
|
'current_prolog_flag(debug,off)',
|
|
58
|
-
'current_prolog_flag(max_arity,
|
|
59
|
+
'current_prolog_flag(max_arity,unbounded)',
|
|
59
60
|
'current_prolog_flag(unknown,error)',
|
|
60
61
|
'current_prolog_flag(double_quotes,chars)',
|
|
61
62
|
]) equal(answers(goalText), 1, goalText);
|
|
@@ -71,7 +72,7 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
71
72
|
'permission_error(modify, flag)', 'bounded valid-but-fixed value');
|
|
72
73
|
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(integer_rounding_function,down)' })).formal,
|
|
73
74
|
'permission_error(modify, flag)', 'rounding valid-but-fixed value');
|
|
74
|
-
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_arity,
|
|
75
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_arity,unbounded)' })).formal,
|
|
75
76
|
'permission_error(modify, flag)', 'max_arity valid-but-fixed value');
|
|
76
77
|
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_integer,unbounded)' })).formal,
|
|
77
78
|
'domain_error(flag_value)', 'max_integer has no value when unbounded');
|
|
@@ -81,13 +82,21 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
81
82
|
'domain_error(prolog_flag)', 'unsupported flag');
|
|
82
83
|
});
|
|
83
84
|
|
|
84
|
-
reporter.test('keeps max_arity
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
equal(
|
|
90
|
-
|
|
85
|
+
reporter.test('keeps max_arity unbounded for compound terms', () => {
|
|
86
|
+
const program = Program.parse('', { isoStrict: true });
|
|
87
|
+
const solver = new Solver(program, { isoStrict: true });
|
|
88
|
+
const answers = (text) => [...solver.solve([parseGoalText(text, { isoStrict: true })], new Env(), 0)].length;
|
|
89
|
+
equal(answers('current_prolog_flag(max_arity,unbounded)'), 1, 'unbounded flag value');
|
|
90
|
+
equal(answers('functor(T,foo,65536),arg(65536,T,z)'), 1, 'functor beyond former 65535 ceiling');
|
|
91
|
+
|
|
92
|
+
const source = `wide(${Array.from({ length: 65536 }, () => 'a').join(',')}).`;
|
|
93
|
+
const clauses = parseProgramText(source, { isoStrict: true, sourceMetadata: true });
|
|
94
|
+
equal(clauses[0]?.head?.arity, 65536, 'source term beyond former 65535 ceiling');
|
|
95
|
+
|
|
96
|
+
// A very large predicate indicator that names no procedure must not be
|
|
97
|
+
// rounded through JavaScript Number or misreported as max_arity.
|
|
98
|
+
equal(run('', { isoStrict: true, goal: 'abolish(foo/9007199254740993)' }).stats.completed_goal_lists,
|
|
99
|
+
1, 'large nonexistent predicate indicator');
|
|
91
100
|
});
|
|
92
101
|
|
|
93
102
|
reporter.test('preparation-time char_conversion affects later unquoted source only', () => {
|
|
@@ -291,6 +300,50 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
291
300
|
}
|
|
292
301
|
});
|
|
293
302
|
|
|
303
|
+
reporter.test('applies integer-to-float conversion before floating evaluable functors', () => {
|
|
304
|
+
const huge = `1${'0'.repeat(400)}`;
|
|
305
|
+
for (const functor of ['float', 'sin', 'cos', 'atan', 'asin', 'acos', 'tan', 'exp', 'log', 'sqrt']) {
|
|
306
|
+
equal(capture(() => run('', { isoStrict: true, goal: `X is ${functor}(${huge})` })).formal,
|
|
307
|
+
'evaluation_error(float_overflow)', `${functor}/1 integer-to-float overflow`);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
reporter.test('reports explicit transcendental and power underflow in strict mode', () => {
|
|
312
|
+
for (const [goal, label] of [
|
|
313
|
+
['X is exp(-1000.0)', 'exp/1 underflow'],
|
|
314
|
+
['X is 2.0 ** -1075.0', '**/2 underflow'],
|
|
315
|
+
['X is 2.0 ^ -1075.0', 'Corrigendum 2 ^/2 underflow'],
|
|
316
|
+
]) {
|
|
317
|
+
equal(capture(() => run('', { isoStrict: true, goal })).formal,
|
|
318
|
+
'evaluation_error(underflow)', label);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
equal(run('', { isoStrict: true, goal: 'X is 0.0 ** 2.0' }).stats.completed_goal_lists,
|
|
322
|
+
1, 'exact zero power remains zero');
|
|
323
|
+
equal(run('', { isoStrict: true, goal: 'X is 4.9406564584124654e-324 * 0.5' }).stats.completed_goal_lists,
|
|
324
|
+
1, 'generic resultF retains the selected round-to-zero policy');
|
|
325
|
+
|
|
326
|
+
// Keep these stricter exceptional conditions confined to --iso-strict.
|
|
327
|
+
equal(run('', { goal: 'X is exp(-1000.0)' }).stats.completed_goal_lists, 1, 'normal exp behavior');
|
|
328
|
+
equal(run('', { goal: 'X is 2.0 ** -1075.0' }).stats.completed_goal_lists, 1, 'normal power behavior');
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
reporter.test('preserves prescribed power errors before integer-to-float conversion', () => {
|
|
332
|
+
const hugeNegative = `-1${'0'.repeat(400)}`;
|
|
333
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'X is (-2) ** 2.0' })).formal,
|
|
334
|
+
'evaluation_error(undefined)', '**/2 negative base requires integer-typed exponent');
|
|
335
|
+
equal(run('', { isoStrict: true, goal: 'X is (-2.0) ** 2' }).stats.completed_goal_lists,
|
|
336
|
+
1, '**/2 negative base with integer exponent');
|
|
337
|
+
equal(run('', { isoStrict: true, goal: 'X is (-2) ^ 2.0' }).stats.completed_goal_lists,
|
|
338
|
+
1, 'Corrigendum 2 ^/2 accepts integer-valued float exponent');
|
|
339
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'X is (-2) ^ 0.5' })).formal,
|
|
340
|
+
'evaluation_error(undefined)', 'Corrigendum 2 ^/2 fractional exponent');
|
|
341
|
+
equal(capture(() => run('', { isoStrict: true, goal: `X is ${hugeNegative} ** 0.5` })).formal,
|
|
342
|
+
'evaluation_error(undefined)', 'undefined power precedes base conversion overflow');
|
|
343
|
+
equal(capture(() => run('', { isoStrict: true, goal: `X is 0.0 ** (${hugeNegative})` })).formal,
|
|
344
|
+
'evaluation_error(undefined)', 'zero-negative power precedes exponent conversion overflow');
|
|
345
|
+
});
|
|
346
|
+
|
|
294
347
|
reporter.test('uses the Part 1 mixed arithmetic comparison operations', () => {
|
|
295
348
|
// 8.7 converts the integer operand to float in mixed comparisons.
|
|
296
349
|
equal(run('', { isoStrict: true, goal: '18014398509481985 =:= 18014398509481984.0' }).stats.completed_goal_lists,
|
|
@@ -315,11 +368,12 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
315
368
|
'type_error(list)', '=../2 fixed non-list');
|
|
316
369
|
});
|
|
317
370
|
|
|
318
|
-
reporter.test('
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
371
|
+
reporter.test('keeps Corrigendum 2 call/N compatible with unbounded max_arity', () => {
|
|
372
|
+
// Corrigendum 2 prescribes representation_error(max_arity) only when the
|
|
373
|
+
// resulting closure exceeds a finite max_arity. With the selected
|
|
374
|
+
// `unbounded` value that conditional branch does not apply.
|
|
375
|
+
equal(run('', { isoStrict: true, goal: "call(=(x),x)" }).stdout,
|
|
376
|
+
'call(=(x), x).\n', 'call/2 closure expansion');
|
|
323
377
|
});
|
|
324
378
|
|
|
325
379
|
reporter.test('reports the complete List culprit for Corrigendum 2 atomic conversions', () => {
|
|
@@ -351,6 +405,48 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
351
405
|
equal(program.operators.has('xfx\u0000?-'), false, 'xfx ?-');
|
|
352
406
|
});
|
|
353
407
|
|
|
408
|
+
reporter.test('pins applicable post-Corrigendum STC clarifications', () => {
|
|
409
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'X is 1/0+_' })).formal,
|
|
410
|
+
'instantiation_error', 'STC 17 direct variable precedes zero divisor on the other branch');
|
|
411
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'X is _+1/0' })).formal,
|
|
412
|
+
'instantiation_error', 'STC 17 is independent of operand order');
|
|
413
|
+
|
|
414
|
+
equal(run('', { isoStrict: true, goal: 'integer(- /**/ 1)' }).stats.completed_goal_lists,
|
|
415
|
+
1, 'layout between minus and integer token');
|
|
416
|
+
equal(run('', { isoStrict: true, goal: "number_chars(N,['0','1']),N=1" }).stats.completed_goal_lists,
|
|
417
|
+
1, 'STC 32 number_chars/2 follows the procedural number syntax for leading zero');
|
|
418
|
+
|
|
419
|
+
const shared = run(
|
|
420
|
+
':- dynamic(a/1).\na(X) :- b(X).\n',
|
|
421
|
+
{ isoStrict: true, goal: 'clause(a(A),b(B)),A==B,A=ok' },
|
|
422
|
+
);
|
|
423
|
+
equal(shared.stats.completed_goal_lists, 1, 'clause/2 preserves head/body variable identity');
|
|
424
|
+
|
|
425
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'char_code(_,c)' })).formal,
|
|
426
|
+
'type_error(integer)', 'char_code/2 non-integer code');
|
|
427
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(unknown,_)' })).formal,
|
|
428
|
+
'instantiation_error', 'set_prolog_flag/2 variable value');
|
|
429
|
+
equal(run('', {
|
|
430
|
+
isoStrict: true,
|
|
431
|
+
goal: 'read(T),T=end_of_file',
|
|
432
|
+
ioOptions: { input: '' },
|
|
433
|
+
}).stats.completed_goal_lists, 1, 'read/1 end_of_file');
|
|
434
|
+
includes(run('', {
|
|
435
|
+
isoStrict: true,
|
|
436
|
+
goal: 'bagof(X,(X=2;X=1),S),S=[2,1],X=ok',
|
|
437
|
+
}).stdout, '[2, 1]', 'bagof/3 preserves answer order');
|
|
438
|
+
|
|
439
|
+
Program.parse(':- op(500,xfx,foo).\na foo b.\n', { isoStrict: true });
|
|
440
|
+
const beforeDefinition = capture(() => Program.parse('a foo b.\n:- op(500,xfx,foo).\n', { isoStrict: true }));
|
|
441
|
+
includes(beforeDefinition.message, 'expected .', 'STC 41 op/3 directive applies only to following text');
|
|
442
|
+
|
|
443
|
+
equal(run('', {
|
|
444
|
+
isoStrict: true,
|
|
445
|
+
goal: 'read_term(T,[variables([])])',
|
|
446
|
+
ioOptions: { input: 'T.' },
|
|
447
|
+
}).stats.completed_goal_lists, 0, 'STC 48 variables([]) does not match an input variable');
|
|
448
|
+
});
|
|
449
|
+
|
|
354
450
|
reporter.test('rejects EyeProlog module directives', () => {
|
|
355
451
|
const error = capture(() => Program.parse(':- use_module(library(lists)).\n', { isoStrict: true }));
|
|
356
452
|
includes(error.message, 'implementation-specific directive use_module/1', 'message');
|
|
@@ -400,6 +496,41 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
400
496
|
'permission_error(access, private_procedure)', 'clause/2 conjunction access');
|
|
401
497
|
});
|
|
402
498
|
|
|
499
|
+
reporter.test('protects directive and rule functors from database modification per STC 56', () => {
|
|
500
|
+
const protectedGoals = [
|
|
501
|
+
"asserta(((':-'(a,b)):-true))",
|
|
502
|
+
"asserta(((':-'(b)):-true))",
|
|
503
|
+
"assertz(((':-'(a,b)):-true))",
|
|
504
|
+
"retract((':-'(a,b):-true))",
|
|
505
|
+
"retractall((':-'(a,b):-true))",
|
|
506
|
+
"abolish('/'(':-',2))",
|
|
507
|
+
"abolish('/'(':-',1))",
|
|
508
|
+
];
|
|
509
|
+
for (const goal of protectedGoals) {
|
|
510
|
+
equal(capture(() => run('', { isoStrict: true, goal })).formal,
|
|
511
|
+
'permission_error(modify, static_procedure)', goal);
|
|
512
|
+
}
|
|
513
|
+
equal(capture(() => run('', { isoStrict: true, goal: "clause(':-'(a,b),_)" })).formal,
|
|
514
|
+
'permission_error(access, private_procedure)', 'clause/2 (:-)/2 access');
|
|
515
|
+
equal(capture(() => run('', { isoStrict: true, goal: "clause(':-'(b),_)" })).formal,
|
|
516
|
+
'permission_error(access, private_procedure)', 'clause/2 (:-)/1 access');
|
|
517
|
+
equal(capture(() => run('', { isoStrict: true, goal: "call(':-'(a,b))" })).formal,
|
|
518
|
+
'existence_error(procedure)', 'STC 56 keeps calls distinct from modification');
|
|
519
|
+
equal(capture(() => Program.parse(":- dynamic('/'(':-',2)).\n", { isoStrict: true })).formal,
|
|
520
|
+
'permission_error(modify, static_procedure)', 'preparation-time (:-)/2 declaration protection');
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
reporter.test('reports finite host exhaustion without imposing ISO representation bounds', () => {
|
|
524
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'functor(T,f,4294967296)' })).formal,
|
|
525
|
+
'resource_error(memory)', 'unbounded max_arity host array exhaustion');
|
|
526
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'X is 1 << 4294967296' })).formal,
|
|
527
|
+
'resource_error(memory)', 'unbounded integer shift host exhaustion');
|
|
528
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'X is 2 ^ 4294967296' })).formal,
|
|
529
|
+
'resource_error(memory)', 'unbounded integer power host exhaustion');
|
|
530
|
+
equal(run('', { isoStrict: true, goal: 'current_prolog_flag(max_arity,unbounded)' }).stats.completed_goal_lists,
|
|
531
|
+
1, 'resource handling does not reintroduce a finite max_arity');
|
|
532
|
+
});
|
|
533
|
+
|
|
403
534
|
reporter.test('covers ISO database predicate errors and empty-procedure lifetime', () => {
|
|
404
535
|
const errors = [
|
|
405
536
|
['current_predicate(4)', '', 'type_error(predicate_indicator)'],
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -6004,7 +6004,7 @@ silently changing a static program.
|
|
|
6004
6004
|
| `debug` | `off` | `on`, `off` | yes |
|
|
6005
6005
|
| `max_integer` | no current value because `bounded=false` | not applicable | no |
|
|
6006
6006
|
| `min_integer` | no current value because `bounded=false` | not applicable | no |
|
|
6007
|
-
| `max_arity` | `
|
|
6007
|
+
| `max_arity` | `unbounded` | `unbounded` | no |
|
|
6008
6008
|
| `unknown` | `error` | `error`, `fail`, `warning` | yes |
|
|
6009
6009
|
| `double_quotes` | `chars` | `chars`, `codes`, `atom` | yes |
|
|
6010
6010
|
| `occurs_check` | `true` | `true`, `error` | yes |
|