eyeprolog 1.3.43 → 1.3.44
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/README.md +7 -0
- package/package.json +1 -1
- package/src/iso.js +115 -56
- package/src/solver.js +4 -4
- package/test/conformance/ISO-COMPLIANCE.md +16 -4
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +4 -4
- package/test/conformance/ISO-MATRIX.md +2 -2
- package/test/conformance/README.md +3 -1
- package/test/conformance/expected-errors/iso/swipl_current_op_float_priority.txt +1 -1
- package/test/conformance/expected-errors/iso/swipl_current_op_priority_type.txt +1 -1
- package/test/conformance/expected-errors/iso/swipl_current_op_specifier_type.txt +1 -1
- package/test/run-iso-strict.mjs +106 -0
- package/the-art-of-eyeprolog.md +14 -5
package/README.md
CHANGED
|
@@ -218,6 +218,13 @@ raise representation errors in strict parsing and character operations. Normal
|
|
|
218
218
|
mode retains Unicode scalar character data as an implementation-specific
|
|
219
219
|
extension.
|
|
220
220
|
|
|
221
|
+
Strict term I/O likewise keeps the standardized option boundary: `read_term/2-3`
|
|
222
|
+
and `write_term/2-3` accept the Part 1 plus Corrigendum 3 options, while the
|
|
223
|
+
normal-profile `write_term(...,[double_quotes(...)])` option remains an
|
|
224
|
+
EyeProlog extension. The strict gate also checks the prescribed ISO error
|
|
225
|
+
precedence for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`, and
|
|
226
|
+
the complete Part 1 flag defaults/value domains/changeability rules.
|
|
227
|
+
|
|
221
228
|
The auditable processor-requirement checklist lives in
|
|
222
229
|
[`test/conformance/ISO-COMPLIANCE.md`](test/conformance/ISO-COMPLIANCE.md).
|
|
223
230
|
The ISO 5.4 implementation-defined/implementation-specific decision index is
|
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -690,44 +690,49 @@ function* setPrologFlagBuiltin({ solver, goal, env }) {
|
|
|
690
690
|
|
|
691
691
|
const operatorSpecifiers = new Set(['fx', 'fy', 'xf', 'yf', 'xfx', 'xfy', 'yfx']);
|
|
692
692
|
|
|
693
|
-
function
|
|
693
|
+
function opListPreflight(term, env) {
|
|
694
694
|
const value = deref(term, env);
|
|
695
695
|
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
696
|
-
if (value.type
|
|
697
|
-
const
|
|
698
|
-
if (
|
|
699
|
-
|
|
696
|
+
if (value.type === ATOM) return { value, items: [value], proper: true, atom: true };
|
|
697
|
+
const { items, tail } = listElements(value, env);
|
|
698
|
+
if (tail.type === VAR || items.some((item) => item.type === VAR)) {
|
|
699
|
+
throw new PrologError('instantiation_error');
|
|
700
|
+
}
|
|
701
|
+
const proper = tail.type === ATOM && tail.name === '[]';
|
|
702
|
+
return { value, items, proper, atom: false };
|
|
700
703
|
}
|
|
701
704
|
|
|
702
|
-
function
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
if (value.type !== ATOM) throw new PrologError('type_error(atom)', value);
|
|
706
|
-
if (!operatorSpecifiers.has(value.name)) throw new PrologError('domain_error(operator_specifier)', value);
|
|
707
|
-
return value.name;
|
|
708
|
-
}
|
|
705
|
+
function* opBuiltin({ solver, goal, env }) {
|
|
706
|
+
const priorityTerm = deref(goal.args[0], env);
|
|
707
|
+
const specifierTerm = deref(goal.args[1], env);
|
|
709
708
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
709
|
+
// ISO 8.14.3.3 prescribes the ordering below. In particular, all three
|
|
710
|
+
// instantiation cases precede type/domain errors, and Operator list-shape
|
|
711
|
+
// errors precede priority/specifier domain errors.
|
|
712
|
+
if (priorityTerm.type === VAR) throw new PrologError('instantiation_error');
|
|
713
|
+
if (specifierTerm.type === VAR) throw new PrologError('instantiation_error');
|
|
714
|
+
const operator = opListPreflight(goal.args[2], env);
|
|
715
|
+
|
|
716
|
+
if (priorityTerm.type !== NUMBER || !isDecimalInteger(priorityTerm.name)) {
|
|
717
|
+
throw new PrologError('type_error(integer)', priorityTerm);
|
|
718
718
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (
|
|
719
|
+
if (specifierTerm.type !== ATOM) throw new PrologError('type_error(atom)', specifierTerm);
|
|
720
|
+
if (!operator.atom && !operator.proper) throw new PrologError('type_error(list)', operator.value);
|
|
721
|
+
for (const item of operator.items) {
|
|
722
|
+
if (item.type !== ATOM) throw new PrologError('type_error(atom)', item);
|
|
723
723
|
}
|
|
724
|
-
return items.map((item) => deref(item, env));
|
|
725
|
-
}
|
|
726
724
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
725
|
+
const priorityBig = BigInt(priorityTerm.name);
|
|
726
|
+
if (priorityBig < 0n || priorityBig > 1200n) {
|
|
727
|
+
throw new PrologError('domain_error(operator_priority)', priorityTerm);
|
|
728
|
+
}
|
|
729
|
+
if (!operatorSpecifiers.has(specifierTerm.name)) {
|
|
730
|
+
throw new PrologError('domain_error(operator_specifier)', specifierTerm);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const priority = Number(priorityBig);
|
|
734
|
+
const specifier = specifierTerm.name;
|
|
735
|
+
for (const name of operator.items) {
|
|
731
736
|
if (name.name === ',') {
|
|
732
737
|
throw new PrologError('permission_error(modify, operator)', name);
|
|
733
738
|
}
|
|
@@ -755,19 +760,16 @@ function* currentOpBuiltin({ solver, goal, env }) {
|
|
|
755
760
|
const priority = deref(goal.args[0], env);
|
|
756
761
|
const specifier = deref(goal.args[1], env);
|
|
757
762
|
const name = deref(goal.args[2], env);
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
}
|
|
763
|
+
// ISO 8.14.4.3 defines the first two argument errors by domain, not by
|
|
764
|
+
// their underlying atom/integer representation type.
|
|
765
|
+
if (priority.type !== VAR &&
|
|
766
|
+
(priority.type !== NUMBER || !isDecimalInteger(priority.name) ||
|
|
767
|
+
BigInt(priority.name) < 0n || BigInt(priority.name) > 1200n)) {
|
|
768
|
+
throw new PrologError('domain_error(operator_priority)', priority);
|
|
765
769
|
}
|
|
766
|
-
if (specifier.type !== VAR
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
throw new PrologError('domain_error(operator_specifier)', specifier);
|
|
770
|
-
}
|
|
770
|
+
if (specifier.type !== VAR &&
|
|
771
|
+
(specifier.type !== ATOM || !operatorSpecifiers.has(specifier.name))) {
|
|
772
|
+
throw new PrologError('domain_error(operator_specifier)', specifier);
|
|
771
773
|
}
|
|
772
774
|
if (name.type !== VAR && name.type !== ATOM) throw new PrologError('type_error(atom)', name);
|
|
773
775
|
for (const definition of solver.program.operators.values()) {
|
|
@@ -849,6 +851,28 @@ function optionList(term, env) {
|
|
|
849
851
|
return items.map((item) => deref(item, env));
|
|
850
852
|
}
|
|
851
853
|
|
|
854
|
+
// read_term/3 and write_term/3 have prescribed error ordering in ISO 8.14.
|
|
855
|
+
// In particular, a partial option list (or a variable option element) is
|
|
856
|
+
// diagnosed before several stream errors, while the relative order of a
|
|
857
|
+
// non-list Options term and an invalid stream-or-alias differs between the
|
|
858
|
+
// read and write predicates. Split list-shape inspection from full option
|
|
859
|
+
// validation so those predicates can follow the standard order exactly.
|
|
860
|
+
function preflightOptionList(term, env) {
|
|
861
|
+
const value = deref(term, env);
|
|
862
|
+
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
863
|
+
const { items, tail } = listElements(value, env);
|
|
864
|
+
if (tail.type === VAR || items.some((item) => item.type === VAR)) {
|
|
865
|
+
throw new PrologError('instantiation_error');
|
|
866
|
+
}
|
|
867
|
+
const proper = tail.type === ATOM && tail.name === '[]';
|
|
868
|
+
return { value, items, proper };
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function requireProperOptionList(preflight) {
|
|
872
|
+
if (!preflight.proper) throw new PrologError('type_error(list)', preflight.value);
|
|
873
|
+
return preflight.items;
|
|
874
|
+
}
|
|
875
|
+
|
|
852
876
|
function optionAtom(option, name) {
|
|
853
877
|
if (option.type !== COMPOUND || option.name !== name || option.arity !== 1) return null;
|
|
854
878
|
return option.args[0];
|
|
@@ -1306,17 +1330,38 @@ function* readBuiltin({ solver, goal, env }) {
|
|
|
1306
1330
|
const { term } = readTermFromStream(stream, solver);
|
|
1307
1331
|
if (unify(goal.args[goal.arity - 1], term, next)) yield next;
|
|
1308
1332
|
}
|
|
1333
|
+
function validateReadOptions(options) {
|
|
1334
|
+
for (const option of options) {
|
|
1335
|
+
if (option.type !== COMPOUND || option.arity !== 1 ||
|
|
1336
|
+
!['variables', 'variable_names', 'singletons'].includes(option.name)) {
|
|
1337
|
+
throw new PrologError('domain_error(read_option)', option);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1309
1342
|
function* readTermBuiltin({ solver, goal, env }) {
|
|
1310
|
-
const
|
|
1343
|
+
const optionTerm = goal.args[goal.arity - 1];
|
|
1344
|
+
const streamTerm = goal.arity === 3 ? deref(goal.args[0], env) : null;
|
|
1345
|
+
|
|
1346
|
+
// ISO 8.14.1.3 error order:
|
|
1347
|
+
// a) variable stream; b) partial/options-variable; c) bad stream-or-alias;
|
|
1348
|
+
// d) non-list options; e) invalid read option; then stream existence/mode.
|
|
1349
|
+
if (streamTerm?.type === VAR) throw new PrologError('instantiation_error');
|
|
1350
|
+
const preflight = preflightOptionList(optionTerm, env);
|
|
1351
|
+
if (goal.arity === 3) streamReference(goal.args[0], env);
|
|
1352
|
+
const options = requireProperOptionList(preflight);
|
|
1353
|
+
validateReadOptions(options);
|
|
1354
|
+
|
|
1355
|
+
const stream = goal.arity === 2
|
|
1356
|
+
? solver.io.resolve(solver.io.currentInput)
|
|
1357
|
+
: requireStream(solver, goal.args[0], env, 'read');
|
|
1311
1358
|
if (stream.type !== 'text') throw new PrologError('permission_error(input, binary_stream)', streamHandle(stream.id));
|
|
1312
|
-
|
|
1359
|
+
|
|
1313
1360
|
const target = goal.args[goal.arity - 2];
|
|
1314
1361
|
const { term, variables } = readTermFromStream(stream, solver);
|
|
1315
1362
|
const next = env.clone();
|
|
1316
1363
|
if (!unify(target, term, next)) return;
|
|
1317
1364
|
for (const option of options) {
|
|
1318
|
-
if (option.type === VAR) throw new PrologError('instantiation_error');
|
|
1319
|
-
if (option.type !== COMPOUND || option.arity !== 1) throw new PrologError('domain_error(read_option)', option);
|
|
1320
1365
|
let value;
|
|
1321
1366
|
if (option.name === 'variables') {
|
|
1322
1367
|
value = listFromItems(variables.map((item) => item.term));
|
|
@@ -1324,12 +1369,10 @@ function* readTermBuiltin({ solver, goal, env }) {
|
|
|
1324
1369
|
value = listFromItems(variables
|
|
1325
1370
|
.filter((item) => !item.anonymous)
|
|
1326
1371
|
.map((item) => compound('=', [atom(item.sourceName), item.term])));
|
|
1327
|
-
} else
|
|
1372
|
+
} else {
|
|
1328
1373
|
value = listFromItems(variables
|
|
1329
1374
|
.filter((item) => !item.anonymous && item.count === 1)
|
|
1330
1375
|
.map((item) => compound('=', [atom(item.sourceName), item.term])));
|
|
1331
|
-
} else {
|
|
1332
|
-
throw new PrologError('domain_error(read_option)', option);
|
|
1333
1376
|
}
|
|
1334
1377
|
if (!unify(option.args[0], value, next)) return;
|
|
1335
1378
|
}
|
|
@@ -1378,23 +1421,26 @@ function writeVariableNames(value, env, option) {
|
|
|
1378
1421
|
return names;
|
|
1379
1422
|
}
|
|
1380
1423
|
|
|
1381
|
-
function
|
|
1424
|
+
function termWriteOptionsFromItems(options, env, mode = 'write_term', solver = null) {
|
|
1382
1425
|
const result = defaultTermWriteOptions(mode);
|
|
1383
|
-
for (const option of
|
|
1384
|
-
if (option.type === VAR) throw new PrologError('instantiation_error');
|
|
1426
|
+
for (const option of options) {
|
|
1385
1427
|
if (option.type !== COMPOUND || option.arity !== 1) {
|
|
1386
1428
|
throw new PrologError('domain_error(write_option)', option);
|
|
1387
1429
|
}
|
|
1388
1430
|
if (option.name === 'quoted') result.quoted = writeOptionBoolean(option.args[0], env, option);
|
|
1389
1431
|
else if (option.name === 'ignore_ops') result.ignoreOps = writeOptionBoolean(option.args[0], env, option);
|
|
1390
1432
|
else if (option.name === 'numbervars') result.numbervars = writeOptionBoolean(option.args[0], env, option);
|
|
1391
|
-
else if (option.name === 'double_quotes') result.doubleQuotes = writeOptionBoolean(option.args[0], env, option);
|
|
1392
1433
|
else if (option.name === 'variable_names') result.variableNames = writeVariableNames(option.args[0], env, option);
|
|
1393
|
-
else
|
|
1434
|
+
else if (option.name === 'double_quotes' && !solver?.isoStrict) {
|
|
1435
|
+
result.doubleQuotes = writeOptionBoolean(option.args[0], env, option);
|
|
1436
|
+
} else {
|
|
1437
|
+
throw new PrologError('domain_error(write_option)', option);
|
|
1438
|
+
}
|
|
1394
1439
|
}
|
|
1395
1440
|
return result;
|
|
1396
1441
|
}
|
|
1397
1442
|
|
|
1443
|
+
|
|
1398
1444
|
function writeBuiltin(mode) {
|
|
1399
1445
|
return function* ({ solver, goal, env }) {
|
|
1400
1446
|
const stream = outputStreamFor(solver, goal, env);
|
|
@@ -1410,9 +1456,22 @@ function writeBuiltin(mode) {
|
|
|
1410
1456
|
};
|
|
1411
1457
|
}
|
|
1412
1458
|
function* writeTermBuiltin({ solver, goal, env }) {
|
|
1413
|
-
const
|
|
1459
|
+
const optionTerm = goal.args[goal.arity - 1];
|
|
1460
|
+
const streamTerm = goal.arity === 3 ? deref(goal.args[0], env) : null;
|
|
1461
|
+
|
|
1462
|
+
// ISO 8.14.2.3 error order:
|
|
1463
|
+
// a) variable stream; b) partial/options-variable; c) non-list options;
|
|
1464
|
+
// d) bad stream-or-alias; e) invalid write option; then stream errors.
|
|
1465
|
+
if (streamTerm?.type === VAR) throw new PrologError('instantiation_error');
|
|
1466
|
+
const preflight = preflightOptionList(optionTerm, env);
|
|
1467
|
+
const optionItems = requireProperOptionList(preflight);
|
|
1468
|
+
if (goal.arity === 3) streamReference(goal.args[0], env);
|
|
1469
|
+
const options = termWriteOptionsFromItems(optionItems, env, 'write_term', solver);
|
|
1470
|
+
|
|
1471
|
+
const stream = goal.arity === 2
|
|
1472
|
+
? solver.io.resolve(solver.io.currentOutput)
|
|
1473
|
+
: requireStream(solver, goal.args[0], env, 'write');
|
|
1414
1474
|
if (stream.type !== 'text') throw new PrologError('permission_error(output, binary_stream)', streamHandle(stream.id));
|
|
1415
|
-
const options = termWriteOptions(goal.args[goal.arity - 1], env);
|
|
1416
1475
|
if (options.doubleQuotes === true) {
|
|
1417
1476
|
options.doubleQuotes = solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars';
|
|
1418
1477
|
} else {
|
package/src/solver.js
CHANGED
|
@@ -951,12 +951,12 @@ const ISO_CORE_FLAG_NAMES = new Set([
|
|
|
951
951
|
|
|
952
952
|
function defaultPrologFlags(unknown = 'error', strictIso = false) {
|
|
953
953
|
const flags = new Map([
|
|
954
|
-
['bounded', { value: compound('false', []), allowed: ['false'], changeable: false }],
|
|
955
|
-
['integer_rounding_function', { value: compound('toward_zero', []), allowed: ['toward_zero'], changeable: false }],
|
|
954
|
+
['bounded', { value: compound('false', []), allowed: ['true', 'false'], changeable: false }],
|
|
955
|
+
['integer_rounding_function', { value: compound('toward_zero', []), allowed: ['down', 'toward_zero'], changeable: false }],
|
|
956
956
|
['char_conversion', { value: compound('on', []), allowed: ['on', 'off'], changeable: true }],
|
|
957
957
|
['debug', { value: compound('off', []), allowed: ['on', 'off'], changeable: true }],
|
|
958
|
-
['max_integer', { value: null, allowed: [
|
|
959
|
-
['min_integer', { value: null, allowed: [
|
|
958
|
+
['max_integer', { value: null, allowed: [], changeable: false }],
|
|
959
|
+
['min_integer', { value: null, allowed: [], changeable: false }],
|
|
960
960
|
['max_arity', { value: compound('unbounded', []), allowed: ['unbounded'], changeable: false }],
|
|
961
961
|
['unknown', { value: compound(unknown, []), allowed: ['error', 'fail', 'warning'], changeable: true }],
|
|
962
962
|
['double_quotes', { value: compound('chars', []), allowed: ['chars', 'codes', 'atom'], changeable: true }],
|
|
@@ -37,10 +37,10 @@ error-ordering alternative to an individual executable assertion.
|
|
|
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
39
|
| 7.9 — expression evaluation | audit | Arithmetic/evaluation/error suites, including Corrigenda. Exceptional-value/error-precedence matrix remains to be exhaustively enumerated. |
|
|
40
|
-
| 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, Corrigendum 3 writer cases.
|
|
41
|
-
| 7.11 — flags |
|
|
42
|
-
| 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax families and focused error cases.
|
|
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.
|
|
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 Part 1/Corrigendum 3 `read_term/2-3` and `write_term/2-3` option surfaces and ISO 8.14.1-8.14.2 prescribed error ordering now have dedicated strict tests; the wider stream/open/close option cross-product remains open. |
|
|
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 now covered for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`; prescribed ordering for the remaining built-in families is still being mapped. |
|
|
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 8.14 term-I/O/operator subfamily now has dedicated strict option-surface and prescribed-error-order assertions, and 8.17 flag behavior is closed; the remaining mode/error matrix is not yet one-row-per-standard-row. |
|
|
44
44
|
| Clause 9 — evaluable functors | audit | Integer/float/rounding/transcendental/bitwise suites and corrigendum cases. Host floating-point representation choices remain documented implementation-defined behavior. |
|
|
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. |
|
|
@@ -69,6 +69,18 @@ normal profile retains Unicode scalar character data as an explicit extension.
|
|
|
69
69
|
The complete WG17 syntax matrix remains green under this narrower strict
|
|
70
70
|
boundary.
|
|
71
71
|
|
|
72
|
+
A further issue #65 audit closes the Part 1 flag family and tightens the 8.14
|
|
73
|
+
term-I/O/operator error rules. The strict flag registry now distinguishes a
|
|
74
|
+
standard value that is valid but not selectable from a value outside the
|
|
75
|
+
standard domain: attempts to change fixed `bounded` or
|
|
76
|
+
`integer_rounding_function` to another standard value therefore reach the
|
|
77
|
+
required permission error. `read_term/3`, `write_term/3`, `op/3`, and
|
|
78
|
+
`current_op/3` now follow their prescribed error precedence, including cases
|
|
79
|
+
where more than one argument is erroneous. Strict `write_term/2-3` accepts only
|
|
80
|
+
the Part 1 plus Corrigendum 3 option surface; the normal-profile
|
|
81
|
+
`double_quotes/1` write option remains an explicitly documented EyeProlog
|
|
82
|
+
extension.
|
|
83
|
+
|
|
72
84
|
## Strict-core boundary
|
|
73
85
|
|
|
74
86
|
`isoStrict: true` is intentionally a **Part 1 + Corrigenda 1-3** mode. It does
|
|
@@ -58,10 +58,10 @@ Status values are:
|
|
|
58
58
|
| 7.10.2.11 | Default `eof_action` | Newly opened file streams default to `error`. The predefined standard streams use `reset`. | **defined** — `src/io.js`. |
|
|
59
59
|
| 7.10.2.13 | `eof_action(Action)` property when the stream uses its default action | Reports the same selected action: `error` for ordinary opened files and `reset` for the predefined standard streams. | **defined** — `streamProperties()` in `src/iso.js`, defaults in `src/io.js`. |
|
|
60
60
|
| 7.10.2.11 | Whether `reposition(false)` streams may nevertheless be repositioned | No. `set_stream_position/2` raises a permission error unless the stream was created with `reposition(true)`. | **defined** — `src/iso.js`. |
|
|
61
|
-
| 7.11.1.1 | Default `bounded` flag | `false`: EyeProlog's Prolog integer model uses arbitrary-precision `BigInt`, subject to host resources. | **defined** — `defaultPrologFlags()` in `src/solver.js
|
|
61
|
+
| 7.11.1.1 | Default `bounded` flag | `false`: EyeProlog's Prolog integer model uses arbitrary-precision `BigInt`, subject to host resources. The standard values remain `true` and `false`; because this flag is fixed, selecting either value through `set_prolog_flag/2` reaches the standard non-changeable-flag rule rather than narrowing the value domain to the current choice. | **defined** — `defaultPrologFlags()` in `src/solver.js`; strict flag tests. |
|
|
62
62
|
| 7.11.1.2 | Default `max_integer` when `bounded=true` | Not applicable because EyeProlog selects `bounded=false`; consequently `current_prolog_flag(max_integer, _)` fails. | **not applicable** — `src/solver.js`, `current_prolog_flag/2`; strict-core regression coverage. |
|
|
63
63
|
| 7.11.1.3 | Default `min_integer` when `bounded=true` | Not applicable because EyeProlog selects `bounded=false`; consequently `current_prolog_flag(min_integer, _)` fails. | **not applicable** — `src/solver.js`, `current_prolog_flag/2`; strict-core regression coverage. |
|
|
64
|
-
| 7.11.1.4 | Default `integer_rounding_function` flag | `toward_zero`; `//` uses truncation toward zero. `div` is provided separately with downward/floor division semantics. | **defined** — `src/solver.js`, `src/iso-arithmetic.js
|
|
64
|
+
| 7.11.1.4 | Default `integer_rounding_function` flag | `toward_zero`; `//` uses truncation toward zero. The standard flag values are `down` and `toward_zero`, but the selected flag is non-changeable; an attempt to select the other valid value is therefore a permission error. `div` is provided separately with downward/floor division semantics. | **defined** — `src/solver.js`, `src/iso-arithmetic.js`; strict flag tests. |
|
|
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`. |
|
|
@@ -69,7 +69,7 @@ Status values are:
|
|
|
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. |
|
|
72
|
-
| 8.17.1 | Implementation-defined flag value ranges | Strict mode exposes only Part 1 core flags and their standard value sets. Normal mode additionally exposes EyeProlog's `occurs_check` flag. With `bounded=false`, `max_integer` and `min_integer` have no current value and their `current_prolog_flag/2` queries fail. | **defined** — strict registry/flag filtering in `src/solver.js
|
|
72
|
+
| 8.17.1 | Implementation-defined flag value ranges | Strict mode exposes only Part 1 core flags and their standard value sets. Normal mode additionally exposes EyeProlog's `occurs_check` flag. With `bounded=false`, `max_integer` and `min_integer` have no current or selectable value and their `current_prolog_flag/2` queries fail. Valid alternative values of fixed standard flags are distinguished from invalid values so `set_prolog_flag/2` reports permission versus domain errors as prescribed. | **defined** — strict registry/flag filtering in `src/solver.js`; strict flag tests. |
|
|
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`. |
|
|
@@ -116,7 +116,7 @@ families; `--iso-strict` is intended to remove their Part 1 interpretation.
|
|
|
116
116
|
| 5.5.9 Built-in predicates | EyeProlog libraries, CLP(Z), statistics, Part 3 `phrase/2-3`, and interop autoloaded predicates | Strict registry contains only the Part 1 + Corrigenda core registry. |
|
|
117
117
|
| 5.5.10 Evaluable functors | Library/runtime extensions when registered outside the strict core | Strict arithmetic registry is the Part 1 + Corrigenda set. |
|
|
118
118
|
| 5.5.11 Reserved atoms | None | None. |
|
|
119
|
-
| Cor.3 5.5.12 Options | Extra library/host options may exist outside core option lists |
|
|
119
|
+
| Cor.3 5.5.12 Options | Extra library/host options may exist outside core option lists | Normal mode additionally accepts the EyeProlog `write_term/2-3` option `double_quotes(true|false)`. Strict core excludes that extension and accepts only the Part 1 plus Corrigendum 3 write-option surface; unknown extension options raise `domain_error(write_option,...)`. |
|
|
120
120
|
|
|
121
121
|
Normal mode provides documented module and DCG compatibility profiles whose
|
|
122
122
|
features overlap standardized Part 2 and Part 3 facilities. They are extensions
|
|
@@ -14,10 +14,10 @@ compliance audit and the remaining work before a full conformance claim.
|
|
|
14
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 |
|
|
15
15
|
| 8.6-8.7 arithmetic predicates | `is/2` and all six arithmetic comparisons | `arithmetic`, comparison cases, isolated evaluation errors |
|
|
16
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` |
|
|
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 |
|
|
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, plus strict 8.14.1-8.14.4 option-surface/error-precedence tests for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3` |
|
|
18
18
|
| 8.15 logic and control | negation, once, repeat, `call/2` through `call/8`, `false/0` | `logtalk_once`, `corrigenda_call_closure`, `false_builtin` |
|
|
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 |
|
|
20
|
-
| 8.17 flags and hooks | required
|
|
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, and the strict complete-flag audit in `run-iso-strict.mjs` |
|
|
21
21
|
| Clause 9 evaluable functors | integer, float, rounding, transcendental and bitwise operations | `arithmetic`, `corrigenda_arithmetic`, `corrigenda_atan2_zero`, `corrigenda_integer_negative_power` |
|
|
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 |
|
|
@@ -21,7 +21,9 @@ predicate indicators listed in Appendix B of the book across the Part 1 strict-c
|
|
|
21
21
|
target and the normal-mode module/DCG compatibility families. [ISO-COMPLIANCE.md](ISO-COMPLIANCE.md) is the explicit
|
|
22
22
|
processor-requirement ledger for the Part 1 strict-core audit. This suite is
|
|
23
23
|
still not an independent certification of every processor requirement, lexical
|
|
24
|
-
edge, option combination
|
|
24
|
+
edge, or option combination. Selected prescribed-error sequences are now
|
|
25
|
+
checked exactly (including ISO 8.14.1-8.14.4), while the remaining built-in
|
|
26
|
+
error-order matrix is tracked in the compliance ledger. Cases under `iso/`
|
|
25
27
|
identify standards-derived behavior; other directories cover EyeProlog host
|
|
26
28
|
contracts and extensions. EyeProlog-only execution features such as automatic
|
|
27
29
|
tabling and `tnot/1` well-founded negation are outside the Part 1 strict-core
|
|
@@ -1 +1 @@
|
|
|
1
|
-
error(
|
|
1
|
+
error(domain_error(operator_priority), 1.2)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
error(
|
|
1
|
+
error(domain_error(operator_priority), x)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
error(
|
|
1
|
+
error(domain_error(operator_specifier), 1)
|
package/test/run-iso-strict.mjs
CHANGED
|
@@ -46,6 +46,41 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
reporter.test('covers the Part 1 flag defaults, value domains, and changeability', () => {
|
|
50
|
+
const program = Program.parse('', { isoStrict: true });
|
|
51
|
+
const solver = new Solver(program, { isoStrict: true });
|
|
52
|
+
const answers = (text) => [...solver.solve([parseGoalText(text, { isoStrict: true })], new Env(), 0)].length;
|
|
53
|
+
for (const goalText of [
|
|
54
|
+
'current_prolog_flag(bounded,false)',
|
|
55
|
+
'current_prolog_flag(integer_rounding_function,toward_zero)',
|
|
56
|
+
'current_prolog_flag(char_conversion,on)',
|
|
57
|
+
'current_prolog_flag(debug,off)',
|
|
58
|
+
'current_prolog_flag(max_arity,unbounded)',
|
|
59
|
+
'current_prolog_flag(unknown,error)',
|
|
60
|
+
'current_prolog_flag(double_quotes,chars)',
|
|
61
|
+
]) equal(answers(goalText), 1, goalText);
|
|
62
|
+
|
|
63
|
+
for (const goalText of [
|
|
64
|
+
'set_prolog_flag(char_conversion,off)',
|
|
65
|
+
'set_prolog_flag(debug,on)',
|
|
66
|
+
'set_prolog_flag(unknown,fail)',
|
|
67
|
+
'set_prolog_flag(double_quotes,codes)',
|
|
68
|
+
]) run('', { isoStrict: true, goal: goalText });
|
|
69
|
+
|
|
70
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(bounded,true)' })).formal,
|
|
71
|
+
'permission_error(modify, flag)', 'bounded valid-but-fixed value');
|
|
72
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(integer_rounding_function,down)' })).formal,
|
|
73
|
+
'permission_error(modify, flag)', 'rounding valid-but-fixed value');
|
|
74
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_arity,unbounded)' })).formal,
|
|
75
|
+
'permission_error(modify, flag)', 'max_arity fixed value');
|
|
76
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_integer,unbounded)' })).formal,
|
|
77
|
+
'domain_error(flag_value)', 'max_integer has no value when unbounded');
|
|
78
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(unknown,maybe)' })).formal,
|
|
79
|
+
'domain_error(flag_value)', 'unknown value domain');
|
|
80
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(not_a_flag,on)' })).formal,
|
|
81
|
+
'domain_error(prolog_flag)', 'unsupported flag');
|
|
82
|
+
});
|
|
83
|
+
|
|
49
84
|
reporter.test('preparation-time char_conversion affects later unquoted source only', () => {
|
|
50
85
|
const program = Program.parse(
|
|
51
86
|
":- char_conversion(x,y).\np(x).\nquoted('x').\n:- set_prolog_flag(char_conversion,off).\nraw(x).\n",
|
|
@@ -107,6 +142,77 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
107
142
|
equal(result.stdout, "char_code('é', 233).\n", 'normal Unicode char_code/2');
|
|
108
143
|
});
|
|
109
144
|
|
|
145
|
+
reporter.test('keeps the strict write-option surface to Part 1 plus Corrigendum 3', () => {
|
|
146
|
+
const strictError = capture(() => run('', {
|
|
147
|
+
isoStrict: true,
|
|
148
|
+
goal: 'write_term(a,[double_quotes(true)])',
|
|
149
|
+
}));
|
|
150
|
+
equal(strictError.formal, 'domain_error(write_option)', 'strict extension rejection');
|
|
151
|
+
|
|
152
|
+
const normal = run('', { goal: 'write_term("ab",[double_quotes(true)])' });
|
|
153
|
+
includes(normal.stdout, '"ab"', 'normal-profile extension remains available');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
reporter.test('follows the ISO 8.14.1.3 read_term/3 error order', () => {
|
|
157
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'read_term(f(a),_,[X])' })).formal,
|
|
158
|
+
'instantiation_error', 'partial/variable option before stream domain');
|
|
159
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'read_term(f(a),_,foo)' })).formal,
|
|
160
|
+
'domain_error(stream_or_alias)', 'stream domain before non-list options');
|
|
161
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'read_term(user_output,_,foo)' })).formal,
|
|
162
|
+
'type_error(list)', 'non-list options before stream permission');
|
|
163
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'read_term(user_output,_,[bogus])' })).formal,
|
|
164
|
+
'domain_error(read_option)', 'invalid option before stream permission');
|
|
165
|
+
equal(capture(() => run('', { isoStrict: true, goal: "read_term('$stream'(999),_,[bogus])" })).formal,
|
|
166
|
+
'domain_error(read_option)', 'invalid option before stream existence');
|
|
167
|
+
|
|
168
|
+
const program = Program.parse('', { isoStrict: true });
|
|
169
|
+
const solver = new Solver(program, { isoStrict: true, ioOptions: { input: 'a.' } });
|
|
170
|
+
const error = capture(() => [...solver.solve([
|
|
171
|
+
parseGoalText('read_term(user_input,_,[bogus])', { isoStrict: true }),
|
|
172
|
+
], new Env(), 0)]);
|
|
173
|
+
equal(error.formal, 'domain_error(read_option)', 'invalid read option');
|
|
174
|
+
equal(solver.io.resolve('user_input').position, 0, 'invalid options are rejected before input');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
reporter.test('follows the ISO 8.14.2.3 write_term/3 error order', () => {
|
|
178
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'write_term(f(a),a,[X])' })).formal,
|
|
179
|
+
'instantiation_error', 'partial/variable option before stream domain');
|
|
180
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'write_term(f(a),a,foo)' })).formal,
|
|
181
|
+
'type_error(list)', 'non-list options before stream domain');
|
|
182
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'write_term(f(a),a,[bogus])' })).formal,
|
|
183
|
+
'domain_error(stream_or_alias)', 'stream domain before invalid option');
|
|
184
|
+
equal(capture(() => run('', { isoStrict: true, goal: "write_term('$stream'(999),a,[bogus])" })).formal,
|
|
185
|
+
'domain_error(write_option)', 'invalid option before stream existence');
|
|
186
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'write_term(user_input,a,[bogus])' })).formal,
|
|
187
|
+
'domain_error(write_option)', 'invalid option before stream permission');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
reporter.test('follows the ISO 8.14.3.3 op/3 error order', () => {
|
|
191
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'op(a,X,0)' })).formal,
|
|
192
|
+
'instantiation_error', 'specifier variable before priority type');
|
|
193
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'op(a,xfy,[X])' })).formal,
|
|
194
|
+
'instantiation_error', 'operator variable element before priority type');
|
|
195
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'op(a,1,0)' })).formal,
|
|
196
|
+
'type_error(integer)', 'priority type before specifier type');
|
|
197
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'op(1,1,0)' })).formal,
|
|
198
|
+
'type_error(atom)', 'specifier type before operator-list type');
|
|
199
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'op(1300,xfy,[1])' })).formal,
|
|
200
|
+
'type_error(atom)', 'operator element type before priority domain');
|
|
201
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'op(1300,foo,a)' })).formal,
|
|
202
|
+
'domain_error(operator_priority)', 'priority domain before specifier domain');
|
|
203
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'op(100,foo,a)' })).formal,
|
|
204
|
+
'domain_error(operator_specifier)', 'specifier domain');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
reporter.test('uses ISO 8.14.4.3 domain errors for current_op/3 filters', () => {
|
|
208
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'current_op(a,_,_)' })).formal,
|
|
209
|
+
'domain_error(operator_priority)', 'priority domain');
|
|
210
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'current_op(1,1,_)' })).formal,
|
|
211
|
+
'domain_error(operator_specifier)', 'specifier domain');
|
|
212
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'current_op(1,fx,1)' })).formal,
|
|
213
|
+
'type_error(atom)', 'operator type');
|
|
214
|
+
});
|
|
215
|
+
|
|
110
216
|
reporter.test('uses the Part 1 predefined operator table', () => {
|
|
111
217
|
const program = Program.parse('', { isoStrict: true });
|
|
112
218
|
equal(program.operators.has('fx\u0000?-'), true, 'fx ?-');
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5425,7 +5425,10 @@ emitted compactly when no lexical ambiguity would arise. For example,
|
|
|
5425
5425
|
still retained where adjacent graphic tokens would otherwise merge, as in
|
|
5426
5426
|
`a+ -b`.
|
|
5427
5427
|
`write_term/2-3` supports `quoted/1`, `ignore_ops/1`, `numbervars/1`, and
|
|
5428
|
-
`variable_names/1`.
|
|
5428
|
+
`variable_names/1`. Normal mode additionally accepts the EyeProlog extension
|
|
5429
|
+
`double_quotes(true|false)`: `true` lets eligible character/code lists use the
|
|
5430
|
+
current `double_quotes` representation, while strict ISO mode rejects this
|
|
5431
|
+
non-standard write option.
|
|
5429
5432
|
|
|
5430
5433
|
Character operations are `get_char`, `peek_char`, `put_char`, `get_code`,
|
|
5431
5434
|
`peek_code`, and `put_code`; byte streams use the corresponding byte
|
|
@@ -7646,8 +7649,11 @@ conformance audit; it does **not** yet claim that every processor requirement
|
|
|
7646
7649
|
has been independently certified. `test/conformance/ISO-COMPLIANCE.md` tracks
|
|
7647
7650
|
the remaining normative audit work. The executable examples are mostly
|
|
7648
7651
|
EyeProlog-profile programs using host-supplied goals, character lists, explicit
|
|
7649
|
-
integrity relations, automatic tabling, and the EyeProlog library. The
|
|
7650
|
-
|
|
7652
|
+
integrity relations, automatic tabling, and the EyeProlog library. The strict audit also exercises the complete Part 1 flag
|
|
7653
|
+
set and the prescribed 8.14 error precedence for `read_term/3`, `write_term/3`,
|
|
7654
|
+
`op/3`, and `current_op/3`; these checks distinguish invalid values from
|
|
7655
|
+
valid-but-fixed flag values and reject normal-profile write options at the
|
|
7656
|
+
strict boundary. The remaining qualifications are:
|
|
7651
7657
|
|
|
7652
7658
|
- zero-arity compound syntax such as `ready()` is represented by the atom
|
|
7653
7659
|
`ready`;
|
|
@@ -7655,8 +7661,11 @@ remaining qualifications are:
|
|
|
7655
7661
|
- variables cannot occupy functor or predicate position;
|
|
7656
7662
|
- double-quoted text follows `double_quotes` exactly; the default `chars` value
|
|
7657
7663
|
matches Trealla and Scryer and may be changed to `codes` or `atom`;
|
|
7658
|
-
- `write_term/2-3` implements
|
|
7659
|
-
|
|
7664
|
+
- `write_term/2-3` implements the Part 1 plus Corrigendum 3 `quoted/1`,
|
|
7665
|
+
`ignore_ops/1`, `numbervars/1`, and `variable_names/1` option surface,
|
|
7666
|
+
including option validation and traversal rules; normal mode also offers
|
|
7667
|
+
`double_quotes(true|false)` as an explicitly non-standard extension, which
|
|
7668
|
+
strict mode rejects;
|
|
7660
7669
|
- unification consistently performs an occurs check, rejecting rational-tree
|
|
7661
7670
|
bindings accepted as extensions by some systems.
|
|
7662
7671
|
|