eyeprolog 1.3.45 → 1.3.46
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-limits.js +8 -0
- package/src/iso.js +35 -21
- package/src/parser.js +2 -0
- package/src/solver.js +2 -1
- package/test/conformance/ISO-COMPLIANCE.md +17 -2
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +1 -1
- package/test/conformance/ISO-MATRIX.md +3 -3
- package/test/conformance/README.md +3 -2
- package/test/conformance/expected/iso/exceptions_and_flags.pl +1 -1
- package/test/conformance/expected-errors/iso/logtalk_functor_compound_name.txt +1 -1
- package/test/run-iso-strict.mjs +97 -3
- package/the-art-of-eyeprolog.md +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// ISO/IEC 13211-1 implementation-defined processor limits.
|
|
2
|
+
// Keep these values in one place so Prolog flags and all predicates that
|
|
3
|
+
// expose representation limits cannot drift apart.
|
|
4
|
+
|
|
5
|
+
// A finite arity limit keeps the Part 1 max_arity flag truthful. 65,535 is
|
|
6
|
+
// comfortably representable by the JavaScript term model while giving source
|
|
7
|
+
// parsing and term construction one stable ISO-visible boundary.
|
|
8
|
+
export const ISO_MAX_ARITY = 65535;
|
package/src/iso.js
CHANGED
|
@@ -14,6 +14,7 @@ import { formatTermForWrite } from './write.js';
|
|
|
14
14
|
import { emptyTerminalSequence, expandDcgBody, isListOrPartialList, validateDcgEmbeddedGoals } from './dcg.js';
|
|
15
15
|
import { INVALID_UTF8_SENTINEL } from './io.js';
|
|
16
16
|
import { CharacterRepresentationError, isStrictIsoPcsCharacter, isStrictIsoPcsCodePoint } from './iso-character.js';
|
|
17
|
+
import { ISO_MAX_ARITY } from './iso-limits.js';
|
|
17
18
|
import {
|
|
18
19
|
characterCodeConstantEnd, continuesGraphicToken, isTerminatingFullStop, quotedEscapeEnd,
|
|
19
20
|
} from './syntax-scan.js';
|
|
@@ -389,18 +390,26 @@ function* functorBuiltin({ goal, env }) {
|
|
|
389
390
|
if (unify(goal.args[1], name, next) && unify(goal.args[2], numberTerm(term.arity), next)) yield next;
|
|
390
391
|
return;
|
|
391
392
|
}
|
|
393
|
+
|
|
392
394
|
const name = deref(goal.args[1], env);
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
395
|
+
const arityTerm = deref(goal.args[2], env);
|
|
396
|
+
// ISO 8.5.1.3 orders construction-mode diagnostics. A compound Name is an
|
|
397
|
+
// atomic type error (before an Arity type error); only an atomic non-atom
|
|
398
|
+
// used for positive arity receives type_error(atom,...).
|
|
399
|
+
if (name.type === VAR) throw new PrologError('instantiation_error');
|
|
400
|
+
if (arityTerm.type === VAR) throw new PrologError('instantiation_error');
|
|
401
|
+
if (name.type === COMPOUND) throw new PrologError('type_error(atomic)', name);
|
|
402
|
+
if (arityTerm.type !== NUMBER || !isDecimalInteger(arityTerm.name)) {
|
|
403
|
+
throw new PrologError('type_error(integer)', arityTerm);
|
|
404
|
+
}
|
|
405
|
+
const arity = BigInt(arityTerm.name);
|
|
406
|
+
if (arity > 0n && name.type !== ATOM) throw new PrologError('type_error(atom)', name);
|
|
407
|
+
if (arity > BigInt(ISO_MAX_ARITY)) throw new PrologError('representation_error(max_arity)');
|
|
408
|
+
if (arity < 0n) throw new PrologError('domain_error(not_less_than_zero)', arityTerm);
|
|
396
409
|
if (arity === 0n) {
|
|
397
|
-
if (name.type === VAR) throw new PrologError('instantiation_error');
|
|
398
|
-
if (name.type === COMPOUND) throw new PrologError('type_error(atomic)', name);
|
|
399
410
|
if (unify(goal.args[0], name, next)) yield next;
|
|
400
411
|
return;
|
|
401
412
|
}
|
|
402
|
-
if (name.type === VAR) throw new PrologError('instantiation_error');
|
|
403
|
-
if (name.type !== ATOM) throw new PrologError('type_error(atom)', name);
|
|
404
413
|
const id = ++isoFresh;
|
|
405
414
|
if (unify(goal.args[0], compound(name.name, Array.from({ length: Number(arity) }, (_, i) => variable(`__functor${id}_${i}`))), next)) yield next;
|
|
406
415
|
}
|
|
@@ -420,6 +429,11 @@ function* univBuiltin({ goal, env }) {
|
|
|
420
429
|
const term = deref(goal.args[0], env);
|
|
421
430
|
const next = env.clone();
|
|
422
431
|
if (term.type !== VAR) {
|
|
432
|
+
// 8.5.3.3(b) also applies in decomposition mode: a fixed second argument
|
|
433
|
+
// that is neither a list nor a partial list is an error, not failure.
|
|
434
|
+
if (listKind(goal.args[1], env) === 'nonlist') {
|
|
435
|
+
throw new PrologError('type_error(list)', deref(goal.args[1], env));
|
|
436
|
+
}
|
|
423
437
|
const items = term.type === COMPOUND ? [atom(term.name), ...term.args] : [term];
|
|
424
438
|
if (unify(goal.args[1], listFromItems(items), next)) yield next;
|
|
425
439
|
return;
|
|
@@ -438,6 +452,7 @@ function* univBuiltin({ goal, env }) {
|
|
|
438
452
|
return;
|
|
439
453
|
}
|
|
440
454
|
const name = requireAtom(items[0], env);
|
|
455
|
+
if (items.length - 1 > ISO_MAX_ARITY) throw new PrologError('representation_error(max_arity)');
|
|
441
456
|
if (unify(goal.args[0], compound(name.name, items.slice(1)), next)) yield next;
|
|
442
457
|
}
|
|
443
458
|
|
|
@@ -519,20 +534,15 @@ function* clauseBuiltin({ solver, goal, env }) {
|
|
|
519
534
|
const head = deref(goal.args[0], env);
|
|
520
535
|
if (head.type === VAR) throw new PrologError('instantiation_error');
|
|
521
536
|
if (head.type !== ATOM && head.type !== COMPOUND) throw new PrologError('type_error(callable)', head);
|
|
522
|
-
callableOrVariable(goal.args[1], env);
|
|
523
537
|
const indicator = compound('/', [atom(head.name), numberTerm(head.arity)]);
|
|
524
|
-
if (solver.registry.get(head.name, head.arity) || isGrammarRuleProcedure(solver, head)) {
|
|
525
|
-
throw new PrologError('permission_error(access, private_procedure)', indicator);
|
|
526
|
-
}
|
|
527
538
|
const group = solver.program.findGroup(head.name, head.arity, head.module ?? goal.module ?? 'user');
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
// clauses inspectable for proof tooling; strict core mode restores the ISO
|
|
532
|
-
// access rule used by clause/2.
|
|
533
|
-
if (solver.isoStrict && !group.dynamic) {
|
|
539
|
+
// ISO 8.8.1.3 places private-procedure access before Body callability.
|
|
540
|
+
if (solver.registry.get(head.name, head.arity) || isGrammarRuleProcedure(solver, head) ||
|
|
541
|
+
(solver.isoStrict && group && !group.dynamic)) {
|
|
534
542
|
throw new PrologError('permission_error(access, private_procedure)', indicator);
|
|
535
543
|
}
|
|
544
|
+
callableOrVariable(goal.args[1], env);
|
|
545
|
+
if (!group) return;
|
|
536
546
|
for (const clause of group.clauses) {
|
|
537
547
|
const pair = compound('$clause', [clause.head, clauseBodyTerm(clause.body)]);
|
|
538
548
|
const copied = freshCopy(pair, new Env());
|
|
@@ -649,7 +659,7 @@ function predicateIndicatorParts(term, env) {
|
|
|
649
659
|
if (name.type !== ATOM) throw new PrologError('type_error(atom)', name);
|
|
650
660
|
const integer = BigInt(arity.name);
|
|
651
661
|
if (integer < 0n) throw new PrologError('domain_error(not_less_than_zero)', arity);
|
|
652
|
-
if (integer > BigInt(
|
|
662
|
+
if (integer > BigInt(ISO_MAX_ARITY)) throw new PrologError('representation_error(max_arity)');
|
|
653
663
|
return { name: name.name, arity: Number(integer), indicator };
|
|
654
664
|
}
|
|
655
665
|
|
|
@@ -688,7 +698,8 @@ function* setPrologFlagBuiltin({ solver, goal, env }) {
|
|
|
688
698
|
if (flag.type !== ATOM) throw new PrologError('type_error(atom)', flag);
|
|
689
699
|
const definition = solver.prologFlags.get(flag.name);
|
|
690
700
|
if (!definition) throw new PrologError('domain_error(prolog_flag)', flag);
|
|
691
|
-
|
|
701
|
+
const expectedType = definition.valueType ?? ATOM;
|
|
702
|
+
if (value.type !== expectedType || !definition.allowed.includes(value.name)) {
|
|
692
703
|
throw new PrologError('domain_error(flag_value)', compound('+', [flag, value]));
|
|
693
704
|
}
|
|
694
705
|
if (!definition.changeable) throw new PrologError('permission_error(modify, flag)', flag);
|
|
@@ -2072,11 +2083,13 @@ function flatFindallTemplate(template) {
|
|
|
2072
2083
|
|
|
2073
2084
|
function* findallBuiltin({ solver, goal, env }) {
|
|
2074
2085
|
const [template, innerGoal, bag] = goal.args;
|
|
2086
|
+
// ISO 8.10.1.3 diagnoses Goal before Instances when both are erroneous.
|
|
2087
|
+
const invoked = callable(innerGoal, env);
|
|
2075
2088
|
assertListOrPartial(bag, env);
|
|
2076
2089
|
const collector = solver.cloneForInnerGoal(10000000);
|
|
2077
2090
|
let compact = flatFindallTemplate(template);
|
|
2078
2091
|
let collected = compact == null ? [] : null;
|
|
2079
|
-
for (const answerEnv of collector.solve([
|
|
2092
|
+
for (const answerEnv of collector.solve([invoked], env.clone(), 0)) {
|
|
2080
2093
|
if (compact != null && compact.push(template, answerEnv)) continue;
|
|
2081
2094
|
if (compact != null) {
|
|
2082
2095
|
collected = compact.materialize();
|
|
@@ -2135,8 +2148,9 @@ function sortedUnique(items) {
|
|
|
2135
2148
|
|
|
2136
2149
|
function allSolutionsBuiltin(asSet) {
|
|
2137
2150
|
return function* ({ solver, goal, env }) {
|
|
2138
|
-
|
|
2151
|
+
// ISO 8.10.2.3/8.10.3.3 diagnose the iterated goal before Instances.
|
|
2139
2152
|
const { iterated, quantified } = bagGoalParts(goal.args[1], env);
|
|
2153
|
+
assertListOrPartial(goal.args[2], env);
|
|
2140
2154
|
const free = freeVariables(iterated, goal.args[0], quantified, env);
|
|
2141
2155
|
const collector = solver.cloneForInnerGoal(10000000);
|
|
2142
2156
|
const groups = [];
|
package/src/parser.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { ATOM, COMPOUND, atom, compound, cons, emptyList, numberTerm, variable } from './term.js';
|
|
4
4
|
import { continuesGraphicToken, isTerminatingFullStop } from './syntax-scan.js';
|
|
5
5
|
import { CharacterRepresentationError, isStrictIsoPcsCodePoint } from './iso-character.js';
|
|
6
|
+
import { ISO_MAX_ARITY } from './iso-limits.js';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
export class NumberRepresentationError extends Error {
|
|
@@ -723,6 +724,7 @@ class Parser {
|
|
|
723
724
|
}
|
|
724
725
|
while (true) {
|
|
725
726
|
args.push(this.parseTerm(ARG_MIN_PRECEDENCE, false, false, true));
|
|
727
|
+
if (args.length > ISO_MAX_ARITY) throw new NumberRepresentationError('representation_error(max_arity)');
|
|
726
728
|
if (this.token.type !== TOK.COMMA) break;
|
|
727
729
|
this.advance();
|
|
728
730
|
}
|
package/src/solver.js
CHANGED
|
@@ -13,6 +13,7 @@ import { StreamManager } from './io.js';
|
|
|
13
13
|
import { clpzStateConsistent } from './clpz.js';
|
|
14
14
|
import { hardHeapLimit, softHeapLimit, usedHeapSize } from './platform.js';
|
|
15
15
|
import { evaluateWfs, relationForGroup, truthOfGroundGoal } from './wfs.js';
|
|
16
|
+
import { ISO_MAX_ARITY } from './iso-limits.js';
|
|
16
17
|
import { evaluatePositiveDatalog, relationForDatalogGroup, datalogCandidateIndexes } from './datalog.js';
|
|
17
18
|
|
|
18
19
|
let freshCounter = 0;
|
|
@@ -961,7 +962,7 @@ function defaultPrologFlags(unknown = 'error', strictIso = false) {
|
|
|
961
962
|
['debug', { value: compound('off', []), allowed: ['on', 'off'], changeable: true }],
|
|
962
963
|
['max_integer', { value: null, allowed: [], changeable: false }],
|
|
963
964
|
['min_integer', { value: null, allowed: [], changeable: false }],
|
|
964
|
-
['max_arity', { value:
|
|
965
|
+
['max_arity', { value: numberTerm(ISO_MAX_ARITY), allowed: [String(ISO_MAX_ARITY)], valueType: NUMBER, changeable: false }],
|
|
965
966
|
['unknown', { value: compound(unknown, []), allowed: ['error', 'fail', 'warning'], changeable: true }],
|
|
966
967
|
['double_quotes', { value: compound('chars', []), allowed: ['chars', 'codes', 'atom'], changeable: true }],
|
|
967
968
|
['occurs_check', { value: compound('true', []), allowed: ['true', 'error'], changeable: true }],
|
|
@@ -31,9 +31,9 @@ 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
|
|
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 finite `max_arity` representation boundary used by term construction. |
|
|
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
|
|
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. 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
39
|
| 7.9 — expression evaluation | audit | Arithmetic/evaluation/error suites, including Corrigenda. Exceptional-value/error-precedence matrix remains to be exhaustively enumerated. |
|
|
@@ -69,6 +69,21 @@ 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
|
+
|
|
73
|
+
The subsequent audit also reconciles the processor's arity limit: EyeProlog no
|
|
74
|
+
longer advertises `max_arity=unbounded` while enforcing a hidden host ceiling.
|
|
75
|
+
The implementation-defined Part 1 value is now `65535`, and the same boundary
|
|
76
|
+
is used by parsing and the standard predicates whose error tables prescribe
|
|
77
|
+
`representation_error(max_arity)`. Focused strict tests also lock the corrected
|
|
78
|
+
8.5 term-construction errors, 8.8 clause-access precedence, 8.10 all-solutions
|
|
79
|
+
goal/list precedence, database update errors, and Corrigendum 3 variable
|
|
80
|
+
metadata traversal/write naming.
|
|
81
|
+
|
|
82
|
+
The public WG17 `number_chars/2` comparison used in preparation of Corrigendum
|
|
83
|
+
2 has additionally been checked against the current strict profile. That
|
|
84
|
+
external comparison is useful independent evidence, but it is not yet vendored
|
|
85
|
+
as an offline release gate; the exit criterion below therefore remains open.
|
|
86
|
+
|
|
72
87
|
A further issue #65 audit closes the Part 1 flag family and tightens the 8.14
|
|
73
88
|
term-I/O/operator error rules. The strict flag registry now distinguishes a
|
|
74
89
|
standard value that is valid but not selectable from a value outside the
|
|
@@ -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` | `65535`. The same finite limit is used by strict parsing and the standard predicates that prescribe `representation_error(max_arity)`. | **defined** — `src/iso-limits.js`, `src/solver.js`, `src/parser.js`, `src/iso.js`. |
|
|
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. |
|
|
@@ -11,10 +11,10 @@ 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,
|
|
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 `functor/3` / `=../2` error-order plus max-arity 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
|
|
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, and issue #65 checks for stream-term/property validation, text-vs-binary permissions, EOF/flush/position, and open/close semantics |
|
|
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 |
|
|
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, and open/close semantics |
|
|
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
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` |
|
|
@@ -22,8 +22,9 @@ target and the normal-mode module/DCG compatibility families. [ISO-COMPLIANCE.md
|
|
|
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
24
|
edge, or option combination. Selected prescribed-error sequences are now
|
|
25
|
-
checked exactly (including
|
|
26
|
-
|
|
25
|
+
checked exactly (including term construction, database/all-solutions predicates,
|
|
26
|
+
and ISO 8.14.1-8.14.4), while the remaining built-in error-order matrix is
|
|
27
|
+
tracked in the compliance ledger. Cases under `iso/`
|
|
27
28
|
identify standards-derived behavior; other directories cover EyeProlog host
|
|
28
29
|
contracts and extensions. EyeProlog-only execution features such as automatic
|
|
29
30
|
tabling and `tnot/1` well-founded negation are outside the Part 1 strict-core
|
|
@@ -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, 65535), pair(unknown, fail), pair(double_quotes, chars), pair(occurs_check, true)]).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
error(type_error(
|
|
1
|
+
error(type_error(atomic), foo(a))
|
package/test/run-iso-strict.mjs
CHANGED
|
@@ -55,7 +55,7 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
55
55
|
'current_prolog_flag(integer_rounding_function,toward_zero)',
|
|
56
56
|
'current_prolog_flag(char_conversion,on)',
|
|
57
57
|
'current_prolog_flag(debug,off)',
|
|
58
|
-
'current_prolog_flag(max_arity,
|
|
58
|
+
'current_prolog_flag(max_arity,65535)',
|
|
59
59
|
'current_prolog_flag(unknown,error)',
|
|
60
60
|
'current_prolog_flag(double_quotes,chars)',
|
|
61
61
|
]) equal(answers(goalText), 1, goalText);
|
|
@@ -71,8 +71,8 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
71
71
|
'permission_error(modify, flag)', 'bounded valid-but-fixed value');
|
|
72
72
|
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(integer_rounding_function,down)' })).formal,
|
|
73
73
|
'permission_error(modify, flag)', 'rounding valid-but-fixed value');
|
|
74
|
-
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_arity,
|
|
75
|
-
'permission_error(modify, flag)', 'max_arity fixed value');
|
|
74
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_arity,65535)' })).formal,
|
|
75
|
+
'permission_error(modify, flag)', 'max_arity valid-but-fixed value');
|
|
76
76
|
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_integer,unbounded)' })).formal,
|
|
77
77
|
'domain_error(flag_value)', 'max_integer has no value when unbounded');
|
|
78
78
|
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(unknown,maybe)' })).formal,
|
|
@@ -81,6 +81,15 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
81
81
|
'domain_error(prolog_flag)', 'unsupported flag');
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
+
reporter.test('keeps max_arity consistent with term and predicate-indicator limits', () => {
|
|
85
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'functor(_,foo,65536)' })).formal,
|
|
86
|
+
'representation_error(max_arity)', 'functor/3 arity boundary');
|
|
87
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'abolish(foo/65536)' })).formal,
|
|
88
|
+
'representation_error(max_arity)', 'abolish/1 arity boundary');
|
|
89
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'set_prolog_flag(max_arity,unbounded)' })).formal,
|
|
90
|
+
'domain_error(flag_value)', 'unbounded is not the selected max_arity value');
|
|
91
|
+
});
|
|
92
|
+
|
|
84
93
|
reporter.test('preparation-time char_conversion affects later unquoted source only', () => {
|
|
85
94
|
const program = Program.parse(
|
|
86
95
|
":- char_conversion(x,y).\np(x).\nquoted('x').\n:- set_prolog_flag(char_conversion,off).\nraw(x).\n",
|
|
@@ -153,6 +162,27 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
153
162
|
includes(normal.stdout, '"ab"', 'normal-profile extension remains available');
|
|
154
163
|
});
|
|
155
164
|
|
|
165
|
+
reporter.test('follows Corrigendum 3 variable metadata traversal and write naming', () => {
|
|
166
|
+
const readMetadata = run('', {
|
|
167
|
+
isoStrict: true,
|
|
168
|
+
goal: "read_term(f(B,A,B,C,D,E),[variables([B,A,C,D,E]),variable_names(['B'=B,'A'=A,'C'=C,'_D'=D]),singletons(['A'=A,'C'=C,'_D'=D])])",
|
|
169
|
+
ioOptions: { input: 'f(B,A,B,C,_D,_).' },
|
|
170
|
+
});
|
|
171
|
+
equal(readMetadata.stats.completed_goal_lists, 1, 'read metadata order');
|
|
172
|
+
|
|
173
|
+
const named = run('', {
|
|
174
|
+
isoStrict: true,
|
|
175
|
+
goal: "write_term(f(X,Y,X),[quoted(true),variable_names([z=X,a=X,y=Y])])",
|
|
176
|
+
});
|
|
177
|
+
includes(named.stdout, 'f(z,y,z)', 'leftmost variable name wins');
|
|
178
|
+
|
|
179
|
+
const ignoredNonVariable = run('', {
|
|
180
|
+
isoStrict: true,
|
|
181
|
+
goal: "write_term(X,[variable_names([ignored=42,x=X])])",
|
|
182
|
+
});
|
|
183
|
+
includes(ignoredNonVariable.stdout, 'x', 'non-variable right side is permitted and ignored');
|
|
184
|
+
});
|
|
185
|
+
|
|
156
186
|
reporter.test('follows the ISO 8.14.1.3 read_term/3 error order', () => {
|
|
157
187
|
equal(capture(() => run('', { isoStrict: true, goal: 'read_term(f(a),_,[X])' })).formal,
|
|
158
188
|
'instantiation_error', 'partial/variable option before stream domain');
|
|
@@ -213,6 +243,29 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
213
243
|
'type_error(atom)', 'operator type');
|
|
214
244
|
});
|
|
215
245
|
|
|
246
|
+
reporter.test('follows ISO term-construction error types and precedence', () => {
|
|
247
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'functor(_,foo(a),1)' })).formal,
|
|
248
|
+
'type_error(atomic)', 'functor compound name');
|
|
249
|
+
equal(capture(() => run('', { isoStrict: true, goal: 'functor(_,foo(a),bad)' })).formal,
|
|
250
|
+
'type_error(atomic)', 'functor name error before arity type');
|
|
251
|
+
equal(capture(() => run('', { isoStrict: true, goal: "'=..'(foo,bar)" })).formal,
|
|
252
|
+
'type_error(list)', '=../2 fixed non-list');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
reporter.test('follows ISO clause/2 private-procedure error precedence', () => {
|
|
256
|
+
equal(capture(() => run('p.\n', { isoStrict: true, goal: 'clause(atom(_),4)' })).formal,
|
|
257
|
+
'permission_error(access, private_procedure)', 'private procedure before body callability');
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
reporter.test('validates all-solutions goals before their output lists', () => {
|
|
261
|
+
for (const predicate of ['findall', 'bagof', 'setof']) {
|
|
262
|
+
equal(capture(() => run('', { isoStrict: true, goal: `${predicate}(X,Y,foo)` })).formal,
|
|
263
|
+
'instantiation_error', `${predicate}/3 variable goal`);
|
|
264
|
+
equal(capture(() => run('', { isoStrict: true, goal: `${predicate}(X,4,foo)` })).formal,
|
|
265
|
+
'type_error(callable)', `${predicate}/3 non-callable goal`);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
216
269
|
reporter.test('uses the Part 1 predefined operator table', () => {
|
|
217
270
|
const program = Program.parse('', { isoStrict: true });
|
|
218
271
|
equal(program.operators.has('fx\u0000?-'), true, 'fx ?-');
|
|
@@ -253,6 +306,47 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
253
306
|
equal(answers.length, 1, 'answer count');
|
|
254
307
|
});
|
|
255
308
|
|
|
309
|
+
reporter.test('covers ISO database predicate errors and empty-procedure lifetime', () => {
|
|
310
|
+
const errors = [
|
|
311
|
+
['current_predicate(4)', '', 'type_error(predicate_indicator)'],
|
|
312
|
+
['asserta(_)', '', 'instantiation_error'],
|
|
313
|
+
['asserta(4)', '', 'type_error(callable)'],
|
|
314
|
+
['asserta((atom(_):-true))', '', 'permission_error(modify, static_procedure)'],
|
|
315
|
+
['assertz(_)', '', 'instantiation_error'],
|
|
316
|
+
['assertz(4)', '', 'type_error(callable)'],
|
|
317
|
+
['retract(_)', '', 'instantiation_error'],
|
|
318
|
+
['retract(4)', '', 'type_error(callable)'],
|
|
319
|
+
['retract(atom(_))', '', 'permission_error(modify, static_procedure)'],
|
|
320
|
+
['retractall(_)', '', 'instantiation_error'],
|
|
321
|
+
['retractall(3)', '', 'type_error(callable)'],
|
|
322
|
+
['retractall(retractall(_))', '', 'permission_error(modify, static_procedure)'],
|
|
323
|
+
['abolish(foo/_)', '', 'instantiation_error'],
|
|
324
|
+
['abolish(foo)', '', 'type_error(predicate_indicator)'],
|
|
325
|
+
['abolish(foo/a)', '', 'type_error(integer)'],
|
|
326
|
+
['abolish(4/1)', '', 'type_error(atom)'],
|
|
327
|
+
['abolish(foo/(-1))', '', 'domain_error(not_less_than_zero)'],
|
|
328
|
+
['abolish(atom/1)', '', 'permission_error(modify, static_procedure)'],
|
|
329
|
+
];
|
|
330
|
+
for (const [goal, source, formal] of errors) {
|
|
331
|
+
equal(capture(() => run(source, { isoStrict: true, goal })).formal, formal, goal);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const retained = run(':- dynamic(p/1).\np(a).\n', {
|
|
335
|
+
isoStrict: true,
|
|
336
|
+
goal: 'retractall(p(_)), current_predicate(p/1), \\+ clause(p(_),_)',
|
|
337
|
+
});
|
|
338
|
+
equal(retained.stats.completed_goal_lists, 1, 'retractall keeps the dynamic procedure');
|
|
339
|
+
|
|
340
|
+
const empty = run(':- dynamic(empty/1).\n', { isoStrict: true, goal: 'current_predicate(empty/1)' });
|
|
341
|
+
equal(empty.stats.completed_goal_lists, 1, 'declared empty procedure exists');
|
|
342
|
+
|
|
343
|
+
const abolished = run(':- dynamic(q/1).\nq(a).\n', {
|
|
344
|
+
isoStrict: true,
|
|
345
|
+
goal: 'abolish(q/1), \\+ current_predicate(q/1)',
|
|
346
|
+
});
|
|
347
|
+
equal(abolished.stats.completed_goal_lists, 1, 'abolish removes the procedure');
|
|
348
|
+
});
|
|
349
|
+
|
|
256
350
|
reporter.test('disables automatic tabling and recursion guards', () => {
|
|
257
351
|
const group = Program.parse('p :- p.\n', { isoStrict: true }).findGroup('p', 0);
|
|
258
352
|
equal(group?.recursive, false, 'recursive planner');
|
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` | `65535` | `65535` | 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 |
|