eyeprolog 1.5.53 → 1.5.55
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 +0 -1
- package/conformance-report.md +21 -0
- package/package.json +1 -2
- package/src/datalog.js +1 -1
- package/src/expansion-builtins.js +1 -1
- package/src/iso-character.js +0 -10
- package/src/iso.js +10 -14
- package/src/program-analysis.js +7 -7
- package/src/program-indexing.js +0 -8
- package/src/program.js +0 -4
- package/test/conformance/ISO-COMPLIANCE.md +3 -1
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +3 -4
- package/test/conformance/ISO-PART2.md +117 -0
- package/test/conformance/ISO-PART3.md +99 -0
- package/test/conformance/README.md +5 -4
- package/test/fixtures/README.md +6 -4
- package/test/run-conformance-report.mjs +29 -0
- package/test/run-iso-strict.mjs +32 -0
- package/test/run-regression.mjs +8 -5
- package/the-art-of-eyeprolog.md +12 -3
- package/ACKNOWLEDGEMENTS.md +0 -36
- package/test/conformance/ISO-PART2-AMENDMENT-2013.md +0 -36
- package/test/conformance/ISO-PART2-PART3-SCOPE.md +0 -71
package/README.md
CHANGED
|
@@ -73,7 +73,6 @@ printf 'human(socrates).\nmortal(X) :- human(X).\n' |
|
|
|
73
73
|
- [ISO conformance review](test/conformance/ISO-COMPLIANCE.md) — supported Part 1 profile
|
|
74
74
|
- [Latest Neumerkel conformity](test/conformance/NEUMERKEL-LATEST.md) — tracked result from the current live upstream inventory
|
|
75
75
|
- [Conformance report](conformance-report.md) — generated executable conformance status, local corpus summary, and known deviations
|
|
76
|
-
- [Acknowledgements](ACKNOWLEDGEMENTS.md) — funding, standards consulted, and credits for vendored conformance corpora
|
|
77
76
|
- [OpenRuleBench](openrulebench/README.md) — portable benchmark profile
|
|
78
77
|
## RDF, Prolog, and symbiotic knowledge graphs
|
|
79
78
|
|
package/conformance-report.md
CHANGED
|
@@ -70,3 +70,24 @@ runs that corpus live through the Neumerkel gate; the offline regression
|
|
|
70
70
|
suite runs all 58 vendored quads. Neither expectations nor error matching
|
|
71
71
|
are relaxed for quads 41-44.
|
|
72
72
|
|
|
73
|
+
|
|
74
|
+
## Integer flag choice under `bounded=false`
|
|
75
|
+
|
|
76
|
+
EyeProlog reports `bounded=false` and uses arbitrary-precision integers.
|
|
77
|
+
It therefore associates no current value with `max_integer` or
|
|
78
|
+
`min_integer`, so `current_prolog_flag/2` does not enumerate them and
|
|
79
|
+
fails when either is named. Both flags stay registered, so
|
|
80
|
+
`set_prolog_flag/2` still reaches the normal non-changeable-flag errors.
|
|
81
|
+
|
|
82
|
+
This is an implementation choice, not a requirement of Part 1. Clause
|
|
83
|
+
7.11.1.1 defines the `bounded` flag and does not govern
|
|
84
|
+
`current_prolog_flag/2` outcomes, while 7.11.1.2 and 7.11.1.3 give both
|
|
85
|
+
flags an implementation-defined default value unconditionally; the
|
|
86
|
+
`bounded` condition constrains what that value *means*, not whether the
|
|
87
|
+
flag exists. Two alternative readings are equally defensible: expose
|
|
88
|
+
implementation-defined values so the flags enumerate, or treat them as
|
|
89
|
+
unsupported and raise `domain_error(prolog_flag, Flag)` per 8.17.2.3 b.
|
|
90
|
+
EyeProlog prefers silence over inventing a largest integer that its
|
|
91
|
+
arithmetic does not have. The vendored Prologue corpus records the
|
|
92
|
+
resulting single divergence rather than patching the upstream fixture.
|
|
93
|
+
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.5.
|
|
6
|
+
"version": "1.5.55",
|
|
7
7
|
"description": "EyeProlog turns facts and rules into answers and proofs.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./index.js",
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"LICENSE.md",
|
|
30
30
|
"README.md",
|
|
31
|
-
"ACKNOWLEDGEMENTS.md",
|
|
32
31
|
"index.js",
|
|
33
32
|
"index.d.ts",
|
|
34
33
|
"playground.html",
|
package/src/datalog.js
CHANGED
|
@@ -66,7 +66,7 @@ function sourceTermList(term, env) {
|
|
|
66
66
|
return null;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
function systemExpandTerm(term, module = 'user') {
|
|
70
70
|
const resolved = deref(term, new Env());
|
|
71
71
|
if (resolved.type === COMPOUND && resolved.name === '-->' && resolved.arity === 2) {
|
|
72
72
|
const expanded = expandDcgRuleClause({ head: resolved, body: [] }, module);
|
package/src/iso-character.js
CHANGED
|
@@ -23,13 +23,3 @@ export function isStrictIsoPcsCharacter(character) {
|
|
|
23
23
|
if (typeof character !== 'string' || Array.from(character).length !== 1) return false;
|
|
24
24
|
return isStrictIsoPcsCodePoint(character.codePointAt(0));
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
// EyeProlog chooses the Unicode scalar value as the collating-sequence integer.
|
|
28
|
-
export function strictIsoCollatingInteger(character) {
|
|
29
|
-
return isStrictIsoPcsCharacter(character) ? character.codePointAt(0) : null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function assertStrictIsoPcsCharacter(character, formal = 'representation_error(character)') {
|
|
33
|
-
if (!isStrictIsoPcsCharacter(character)) throw new CharacterRepresentationError(formal);
|
|
34
|
-
return character;
|
|
35
|
-
}
|
package/src/iso.js
CHANGED
|
@@ -35,7 +35,7 @@ class ThrownTerm extends Error {
|
|
|
35
35
|
const succeed = function* ({ env }) { yield env; };
|
|
36
36
|
const fail = function* () {};
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
const isoBuiltins = {
|
|
39
39
|
register(registry) {
|
|
40
40
|
registry.add('true', 0, succeed, { deterministic: true });
|
|
41
41
|
registry.add('fail', 0, fail, { deterministic: true });
|
|
@@ -956,10 +956,15 @@ function* currentPrologFlagSolutions({ solver, goal, env }, state) {
|
|
|
956
956
|
.filter(([name, definition]) => definition.value != null && (flag.type === VAR || flag.name === name));
|
|
957
957
|
for (let index = 0; index < definitions.length; index++) {
|
|
958
958
|
const [name, definition] = definitions[index];
|
|
959
|
-
//
|
|
960
|
-
// current value and
|
|
961
|
-
//
|
|
962
|
-
//
|
|
959
|
+
// Implementation choice: with bounded=false there is no largest integer to
|
|
960
|
+
// report, so max_integer and min_integer carry no current value and are not
|
|
961
|
+
// enumerated. ISO 7.11.1.1 defines the bounded flag and does not govern
|
|
962
|
+
// current_prolog_flag/2; 7.11.1.2 and 7.11.1.3 give both flags an
|
|
963
|
+
// implementation-defined default unconditionally, so the alternative
|
|
964
|
+
// reading (expose values, or raise domain_error(prolog_flag)) is equally
|
|
965
|
+
// available. See conformance-report.md. The definitions remain registered
|
|
966
|
+
// so attempts to change these non-changeable flags still receive the
|
|
967
|
+
// normal flag error handling.
|
|
963
968
|
const next = env.clone();
|
|
964
969
|
if (unify(goal.args[0], atom(name), next) && unify(goal.args[1], definition.value, next)) {
|
|
965
970
|
state.pending = index + 1 < definitions.length;
|
|
@@ -1802,15 +1807,6 @@ function parseReadTermText(text, solver) {
|
|
|
1802
1807
|
});
|
|
1803
1808
|
}
|
|
1804
1809
|
|
|
1805
|
-
export function isCompleteReadTermText(text, solver) {
|
|
1806
|
-
try {
|
|
1807
|
-
parseReadTermText(text, solver);
|
|
1808
|
-
return true;
|
|
1809
|
-
} catch (_) {
|
|
1810
|
-
return false;
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
1810
|
function readTermFromStream(stream, solver) {
|
|
1815
1811
|
if (stream.pastEnd && stream.eofAction === 'error') {
|
|
1816
1812
|
throw new PrologError('permission_error(input, past_end_of_stream)', streamHandle(stream.id));
|
package/src/program-analysis.js
CHANGED
|
@@ -16,13 +16,13 @@ export function componentHasNegativeEdge(start, deps, negativeEdges) {
|
|
|
16
16
|
return negativeEdges.some(([from, to]) => component.has(from) && component.has(to));
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
function compactClauseIsDirectRecursive(clause, group) {
|
|
20
20
|
return isCompactBinaryClause(clause) && clause.bodyName === group.name && group.arity === 2;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
function clauseIsDirectRecursive(clause, group) {
|
|
26
26
|
if (isCompactBinaryClause(clause)) return compactClauseIsDirectRecursive(clause, group);
|
|
27
27
|
return clause.body.some((goal) =>
|
|
28
28
|
goal.type === COMPOUND && goal.name === group.name && goal.arity === group.arity
|
|
@@ -86,7 +86,7 @@ function reachableIndexesTransposed(target, deps, candidates) {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
function isFiniteDatalogArgument(term) {
|
|
90
90
|
return term?.type === VAR || term?.type === ATOM || term?.type === 'string' || term?.type === 'number';
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -289,7 +289,7 @@ export function isFiniteWfsDatalogGroup(program, group, cache = new Map(), visit
|
|
|
289
289
|
return finite;
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
|
|
292
|
+
function collectVariables(term, output) {
|
|
293
293
|
if (!term) return;
|
|
294
294
|
if (term.type === VAR) {
|
|
295
295
|
output.add(term.name);
|
|
@@ -420,12 +420,12 @@ export function isPortableBetweenGenerator(group) {
|
|
|
420
420
|
group.clauses.every((clause) => clause.eyePrologLibraryPortable === true);
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
-
|
|
423
|
+
function termContainsVariable(term, name) {
|
|
424
424
|
if (term.type === 'var') return term.name === name;
|
|
425
425
|
return term.args.some((arg) => termContainsVariable(arg, name));
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
-
|
|
428
|
+
function sameClauseTerm(left, right) {
|
|
429
429
|
if (left.type !== right.type || left.name !== right.name || left.args.length !== right.args.length) return false;
|
|
430
430
|
return left.args.every((arg, index) => sameClauseTerm(arg, right.args[index]));
|
|
431
431
|
}
|
|
@@ -446,7 +446,7 @@ export function directGoalDependencyKey(goal) {
|
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
// ISO 13211-1, 7.1.6.3: `V1^V2^...^Goal` has the iterated goal Goal.
|
|
449
|
-
|
|
449
|
+
function iteratedGoal(goal) {
|
|
450
450
|
let current = goal;
|
|
451
451
|
while (current?.type === COMPOUND && current.name === '^' && current.arity === 2) {
|
|
452
452
|
current = current.args[1];
|
package/src/program-indexing.js
CHANGED
|
@@ -68,14 +68,6 @@ export function compactHeadArgName(clause, index) {
|
|
|
68
68
|
return index === 0 ? clause.head0Name : clause.head1Name;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export function compactBodyArgType(clause, index) {
|
|
72
|
-
return index === 0 ? clause.body0Type : clause.body1Type;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function compactBodyArgName(clause, index) {
|
|
76
|
-
return index === 0 ? clause.body0Name : clause.body1Name;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
71
|
export function clauseBodyLength(clause) {
|
|
80
72
|
return isCompactBinaryClause(clause) ? (clause.bodyName == null ? 0 : 1) : clause.body.length;
|
|
81
73
|
}
|
package/src/program.js
CHANGED
|
@@ -1940,7 +1940,3 @@ function predicateIndicator(name, arity) {
|
|
|
1940
1940
|
export function makeProgram(source, options = {}) {
|
|
1941
1941
|
return Program.parse(source, options);
|
|
1942
1942
|
}
|
|
1943
|
-
|
|
1944
|
-
export function parseSourceClauses(source, options = {}) {
|
|
1945
|
-
return parseClauses(source, options);
|
|
1946
|
-
}
|
|
@@ -58,7 +58,9 @@ concrete mismatches that are now part of the closed Part 1 review:
|
|
|
58
58
|
|
|
59
59
|
- `bounded=false` no longer exposes implementation-specific `unbounded` values
|
|
60
60
|
for `max_integer` or `min_integer`; the corresponding
|
|
61
|
-
`current_prolog_flag/2` queries fail
|
|
61
|
+
`current_prolog_flag/2` queries fail. Part 1 does not require that outcome
|
|
62
|
+
(7.11.1.1 defines the `bounded` flag and does not govern
|
|
63
|
+
`current_prolog_flag/2`), so it is recorded as an implementation choice;
|
|
62
64
|
- preparation-time `char_conversion/2` now converts later unquoted source text
|
|
63
65
|
when the `char_conversion` flag is `on`, leaves quoted characters unchanged,
|
|
64
66
|
and feeds the same mapping into execution-time term input.
|
|
@@ -131,10 +131,9 @@ Normal mode provides documented module and DCG compatibility profiles whose
|
|
|
131
131
|
features overlap standardized Part 2 and Part 3 facilities. They are extensions
|
|
132
132
|
relative to the Part 1 strict-core boundary and are tested separately; this
|
|
133
133
|
ledger does not assert complete Part 2 or Part 3 conformance. The concrete
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
`ISO-PART2-AMENDMENT-2013.md` ledger.
|
|
134
|
+
per-directive and per-predicate boundary is recorded in `ISO-PART2.md` and
|
|
135
|
+
`ISO-PART3.md`, including the 2013 module-amendment evidence and the
|
|
136
|
+
normal-profile `phrase/2-3` terminal-sequence error choice.
|
|
138
137
|
|
|
139
138
|
## Important implementation-dependent behavior (not the 5.4 mandatory table)
|
|
140
139
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# ISO/IEC 13211-2:2000 (Modules) — implementation status
|
|
2
|
+
|
|
3
|
+
This document records, predicate by predicate and directive by directive, what
|
|
4
|
+
EyeProlog implements of Prolog Part 2 (Modules). It is a status ledger, not a
|
|
5
|
+
conformance claim.
|
|
6
|
+
|
|
7
|
+
**EyeProlog does not claim Part 2 conformance.** What normal mode provides is a
|
|
8
|
+
procedure-oriented module *compatibility layer* built around the widespread
|
|
9
|
+
Quintus-style syntax. The ISO interface/body directive surface is not
|
|
10
|
+
implemented. `--iso-strict` targets Part 1 + Corrigenda 1-3 only and excludes
|
|
11
|
+
module directives and the `:` operator entirely.
|
|
12
|
+
|
|
13
|
+
## Interface and body directives (ISO form)
|
|
14
|
+
|
|
15
|
+
Part 2 separates a module *interface* from one or more module *bodies*. None of
|
|
16
|
+
this surface is implemented; EyeProlog accepts only the compatibility syntax in
|
|
17
|
+
the next section.
|
|
18
|
+
|
|
19
|
+
| Directive | Part 2 role | Status |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| `module/1` | begin the interface for a named module | **not implemented** |
|
|
22
|
+
| `export/1` | export locally defined procedures | **not implemented** |
|
|
23
|
+
| `reexport/2` | selectively import from another module and export again | **not implemented** |
|
|
24
|
+
| `reexport/1` | re-export the exports of named modules | **not implemented** |
|
|
25
|
+
| `metapredicate/1` | declare and export context-sensitive procedures (ISO spelling, no underscore) | **not implemented** |
|
|
26
|
+
| `op/3` in an interface | initial operators for this module's bodies only | **not implemented** — operator state is global |
|
|
27
|
+
| `char_conversion/2` in an interface | initial character conversions for this module's bodies only | **not implemented** — conversion state is global |
|
|
28
|
+
| `set_prolog_flag/2` in an interface | initial flag values for this module's bodies only | **not implemented** — flag state is global |
|
|
29
|
+
| `end_module/1` | end the interface | **not implemented** |
|
|
30
|
+
| `body/1` | begin a body of an already interfaced module | **not implemented** |
|
|
31
|
+
| `import/2` | selectively import exported procedures | **not implemented** |
|
|
32
|
+
| `import/1` | import all exports of named modules | **not implemented** |
|
|
33
|
+
| `end_body/1` | end the module body | **not implemented** |
|
|
34
|
+
|
|
35
|
+
## Compatibility directives (what EyeProlog actually accepts)
|
|
36
|
+
|
|
37
|
+
| Directive | Status | Notes |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `module/2` | **implemented** | `:- module(Name, Exports).` Records the named module and its export map. Non-terminal indicators `A//N` are accepted in the export list. |
|
|
40
|
+
| `use_module/1` | **implemented** | Imports the full export map of the source module. |
|
|
41
|
+
| `use_module/2` | **implemented** | Selective import, validated against the source module's exports. |
|
|
42
|
+
| `meta_predicate/1` | **implemented** | Both `:- meta_predicate p(:).` and the parenthesized spelling. Numeric closure modes are a further EyeProlog extension. |
|
|
43
|
+
| `ensure_loaded/1` | **implemented** | Imports a module's public predicates while keeping source loading idempotent. |
|
|
44
|
+
|
|
45
|
+
Only one module body is supported per Prolog text: a second `module/2` in the
|
|
46
|
+
same text is rejected rather than switching modules mid-text. Part 2 allows a
|
|
47
|
+
module to have several non-contiguous bodies.
|
|
48
|
+
|
|
49
|
+
## Built-in predicates
|
|
50
|
+
|
|
51
|
+
| Predicate | Part 2 requirement | Status |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `current_module/1` | enumerate existing modules and test a supplied name; `user` must exist | **not implemented** — raises `existence_error(procedure, current_module/1)` |
|
|
54
|
+
| `predicate_property/2` | module-aware predicate-property model | **not implemented** — raises `existence_error(procedure, predicate_property/2)` |
|
|
55
|
+
| `clause/2` | module-aware redefinition | **partial** — qualification is resolved, but against the compatibility model rather than Part 2 lookup/defining-module rules |
|
|
56
|
+
| `current_predicate/1` | module-aware redefinition | **partial** — as above |
|
|
57
|
+
| `asserta/1` | module-aware redefinition | **partial** — as above |
|
|
58
|
+
| `assertz/1` | module-aware redefinition | **partial** — as above |
|
|
59
|
+
| `retract/1` | module-aware redefinition | **partial** — as above |
|
|
60
|
+
| `abolish/1` | module-aware redefinition | **partial** — as above |
|
|
61
|
+
|
|
62
|
+
The six *partial* rows share one cause: EyeProlog resolves `Module:Goal` and
|
|
63
|
+
tracks a single module per goal, whereas Part 2 distinguishes defining module,
|
|
64
|
+
lookup module, qualifying module, and calling context. Ordinary calls make
|
|
65
|
+
these coincide, so the difference shows up in meta-calls and nested
|
|
66
|
+
qualification rather than in everyday use.
|
|
67
|
+
|
|
68
|
+
## Operators and flags
|
|
69
|
+
|
|
70
|
+
| Item | Part 2 requirement | Status |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| `:` operator | added to the initial operator table as `op(600, xfy, :)` | **implemented** in normal mode (`current_op(600, xfy, ':')`); deliberately absent from the Part 1 strict initial table |
|
|
73
|
+
| `colon_sets_calling_context` | a Boolean flag governing whether explicit qualification establishes calling context | **not implemented** — `current_prolog_flag/2` raises `domain_error(prolog_flag, colon_sets_calling_context)` |
|
|
74
|
+
|
|
75
|
+
## 2013 amendment (WG17 N251) coverage
|
|
76
|
+
|
|
77
|
+
The requirements clarified by the 2013 amendment draft have executable evidence
|
|
78
|
+
in `test/run-iso-part2-amendment.mjs`, which is part of `npm test` and the
|
|
79
|
+
conformance aggregate. This is the one part of the Part 2 surface that is
|
|
80
|
+
release-gated.
|
|
81
|
+
|
|
82
|
+
| Amendment clause | Required behaviour | EyeProlog behaviour / evidence |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| 6.2.4.1 | A `module(Name, Exports)` directive identifies a named module and its public predicate indicators. | `Program.defineModule()` records the named module and export map. The focused suite checks that public predicates import and private predicates stay module-local. |
|
|
85
|
+
| 6.2.5 | A module body is a Prolog text beginning with its `module/2` directive and extending to the end of that text. | Files named by `use_module/1-2` are accepted as module sources only when their first read term is `module/2`. A second `module/2` in the same text is rejected; a distinct text may begin a distinct module body. |
|
|
86
|
+
| 6.2.5.5 | `use_module(F, L)` selectively imports the predicates in `L` from the exports of the module defined by `F`. | `Program.importModule()` validates requested indicators against the source module's export map and installs only those imports. |
|
|
87
|
+
| 6.2.5.6 | `use_module(F)` imports all exported predicates; the amendment also aligns the public-predicate effect of `ensure_loaded(F)` for a module source. | `use_module/1` imports the full export map. `ensure_loaded/1` imports a module's public predicates while retaining idempotent source loading, including when the module was already loaded for another caller. |
|
|
88
|
+
| 6.2.5.7 | `meta_predicate/1` marks context-sensitive arguments; `:` arguments carry the current source/calling module. | Normal parsing accepts `:- meta_predicate p(:).`. Colon-mode arguments are represented as an observable `Module:Goal` term, including when the argument is a variable. |
|
|
89
|
+
| 6.4.4.3 | Imported metapredicates preserve the caller's module context for their meta-arguments. | The focused suite verifies both visible `Module:Goal` decomposition and execution of a caller-private predicate through a variable meta-argument. Explicit `Module:Goal` calls set their stated module context. |
|
|
90
|
+
|
|
91
|
+
## Known gaps
|
|
92
|
+
|
|
93
|
+
Beyond the *not implemented* rows above, four structural gaps stand between the
|
|
94
|
+
current compatibility layer and a Part 2 claim.
|
|
95
|
+
|
|
96
|
+
1. **Module-local reader/writer state.** Part 2 makes a calling context include
|
|
97
|
+
the operator table, character-conversion mapping, and relevant flag values,
|
|
98
|
+
not just a set of visible procedures. EyeProlog keeps operators, conversions,
|
|
99
|
+
and flags in one global program state, so interface-local environment
|
|
100
|
+
settings cannot be expressed. This is the largest single item.
|
|
101
|
+
2. **Four separate module notions.** Defining, lookup, qualifying, and calling
|
|
102
|
+
context are collapsed into one module field per goal.
|
|
103
|
+
3. **Re-export and visibility conflicts.** There is no re-export model, and no
|
|
104
|
+
preparation-time rejection of a second import that would make a *different*
|
|
105
|
+
procedure visible under the same unqualified indicator.
|
|
106
|
+
4. **Introspection.** `current_module/1` and `predicate_property/2` are absent,
|
|
107
|
+
so the module structure is not observable from within a program.
|
|
108
|
+
|
|
109
|
+
Interoperability with Scryer, Trealla, or Logtalk is not treated as evidence of
|
|
110
|
+
Part 2 coverage.
|
|
111
|
+
|
|
112
|
+
## See also
|
|
113
|
+
|
|
114
|
+
- `ISO-PART3.md` — Part 3 (definite clause grammar rules) status
|
|
115
|
+
- `ISO-COMPLIANCE.md` — the Part 1 review that carries the release-facing claim
|
|
116
|
+
- `ISO-IMPLEMENTATION-DEFINED.md` — implementation-defined choices, including
|
|
117
|
+
the module-qualification error choice
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# ISO/IEC 13211-3 (Definite clause grammar rules) — implementation status
|
|
2
|
+
|
|
3
|
+
This document records what EyeProlog implements of Prolog Part 3, non-terminal
|
|
4
|
+
by non-terminal and predicate by predicate. It is a status ledger, not a
|
|
5
|
+
conformance claim.
|
|
6
|
+
|
|
7
|
+
Normal mode implements the whole grammar surface below. `--iso-strict` leaves
|
|
8
|
+
`-->/2` as ordinary Part 1 operator syntax and excludes grammar expansion and
|
|
9
|
+
`phrase/2-3` from the strict registry, so `phrase/2` there raises
|
|
10
|
+
`existence_error(procedure, phrase/2)`.
|
|
11
|
+
|
|
12
|
+
## Grammar control constructs (clause 7.14)
|
|
13
|
+
|
|
14
|
+
| Clause | Construct | Status |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| 7.14.1 | `[]//0` — empty terminal-sequence | **implemented** |
|
|
17
|
+
| 7.14.2 | `('.')//2` — terminal sequence | **implemented** |
|
|
18
|
+
| 7.14.3 | `(',')//2` — concatenation | **implemented** |
|
|
19
|
+
| 7.14.4 | `(;)//2` — alternative | **implemented** |
|
|
20
|
+
| 7.14.5 | `(;)//2` with `(->)//2` — if-then-else | **implemented** |
|
|
21
|
+
| 7.14.6 | `('\|')//2` — second form of alternative | **implemented** |
|
|
22
|
+
| 7.14.7 | `{}//1` — grammar-body-goal | **implemented** |
|
|
23
|
+
| 7.14.8 | `call//1` | **implemented** |
|
|
24
|
+
| 7.14.9 | `phrase//1` | **implemented** |
|
|
25
|
+
| 7.14.10 | `!//0` — grammar-body-cut | **implemented** |
|
|
26
|
+
| 7.14.11 | `(\+)//1` — grammar-body-not | **implemented** |
|
|
27
|
+
| 7.14.12 | `(->)//2` — if-then | **implemented** |
|
|
28
|
+
|
|
29
|
+
## Built-in predicates (clause 8.18)
|
|
30
|
+
|
|
31
|
+
| Clause | Predicate | Status |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| 8.18.1 | `phrase/3` | **implemented** |
|
|
34
|
+
| 8.18.1.3 | `phrase/2` (bootstrapped as `phrase(GRBody, S0, [])`) | **implemented** |
|
|
35
|
+
|
|
36
|
+
### Prescribed errors (8.18.1.4)
|
|
37
|
+
|
|
38
|
+
| Error | Requirement | EyeProlog |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| a) `GRBody` is a variable | `instantiation_error` | **matches** |
|
|
41
|
+
| b) `GRBody` is neither a variable nor callable | `type_error(callable, GRBody)` | **matches** |
|
|
42
|
+
| c) `S0` is not a terminal-sequence | `type_error(terminal_sequence, S0)` — implementation-defined for `phrase/3`, **required for `phrase/2`** | **diverges**: EyeProlog raises `type_error(list, S0)` |
|
|
43
|
+
| d) `S` is not a terminal-sequence | `type_error(terminal_sequence, S)` — implementation-defined | **diverges**: EyeProlog raises `type_error(list, S)` |
|
|
44
|
+
|
|
45
|
+
**Open divergence.** EyeProlog performs both optional checks — which the draft
|
|
46
|
+
permits, and which for `phrase/2` clause c it requires — but reports
|
|
47
|
+
`type_error(list, Culprit)` where the 2023-08-14 draft specifies
|
|
48
|
+
`type_error(terminal_sequence, Culprit)`. The draft is explicit that if a
|
|
49
|
+
processor offers these errors, "their form and consequence must be the
|
|
50
|
+
following", so the *form* is not left open even though performing the check is.
|
|
51
|
+
|
|
52
|
+
This appears to track an earlier edition, in which these clauses were numbered
|
|
53
|
+
8.18.1.3 g and h and specified `list`. The behaviour is deliberate and covered
|
|
54
|
+
by a dedicated regression matrix, so changing the error term is a decision to
|
|
55
|
+
take explicitly rather than a bug to patch silently. Until it is taken, this
|
|
56
|
+
row is the one known Part 3 deviation.
|
|
57
|
+
|
|
58
|
+
What the regression matrix does pin, independently of the error term: the same
|
|
59
|
+
diagnostic across both arities, both sequence positions, atomic and compound
|
|
60
|
+
non-lists, and improper lists at several depths; the culprit is the whole
|
|
61
|
+
invalid argument, including an improper list, not just its tail; variables,
|
|
62
|
+
proper lists, and partial lists are accepted; and for otherwise valid grammar
|
|
63
|
+
bodies, validation precedes execution even when the grammar would fail or
|
|
64
|
+
produce a side effect. The upstream phrase quads permit both checking and
|
|
65
|
+
non-checking outcomes and so do not establish this consistency on their own.
|
|
66
|
+
|
|
67
|
+
## Language concepts
|
|
68
|
+
|
|
69
|
+
| Clause | Concept | Status |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| 7.4.4 | grammar rules in Prolog text | **implemented** |
|
|
72
|
+
| 7.5.1 | grammar-rule expansion during preparation | **implemented** |
|
|
73
|
+
| 7.13.1 | terminals and non-terminals | **implemented** |
|
|
74
|
+
| 7.13.2 | format of grammar rules | **implemented** |
|
|
75
|
+
| 7.13.3 | semicontext (pushback) | **implemented** — `head, [Pushback] --> Body` |
|
|
76
|
+
| 7.13.4 | non-terminal indicator `A//N` | **implemented** — accepted in module export lists |
|
|
77
|
+
| 7.15 | executing clauses expanded from grammar rules | **implemented** |
|
|
78
|
+
| 5.5.2 | predefined operators | **implemented** — `-->` is predeclared in both profiles; it is grammar syntax only in normal mode |
|
|
79
|
+
|
|
80
|
+
`predicate_property/2` does not appear in the 2023-08-14 draft, so no Part 3
|
|
81
|
+
predicate-property extension is claimed here. See `ISO-PART2.md` for the Part 2
|
|
82
|
+
status of that predicate.
|
|
83
|
+
|
|
84
|
+
## Executable evidence
|
|
85
|
+
|
|
86
|
+
Grammar expansion, sequence validation, grammar-body callability, variable-body
|
|
87
|
+
instantiation errors, and `phrase/3` steadfastness are under focused tests in
|
|
88
|
+
`test/run-regression.mjs` and the conformance corpus. The live Neumerkel gate
|
|
89
|
+
covers the upstream phrase quads.
|
|
90
|
+
|
|
91
|
+
Changes intended to advance a formal Part 3 claim should update this ledger and
|
|
92
|
+
the corresponding error cases rather than silently changing the compatibility
|
|
93
|
+
profile.
|
|
94
|
+
|
|
95
|
+
## See also
|
|
96
|
+
|
|
97
|
+
- `ISO-PART2.md` — Part 2 (modules) status
|
|
98
|
+
- `ISO-COMPLIANCE.md` — the Part 1 review that carries the release-facing claim
|
|
99
|
+
- `NEUMERKEL-LIVE.md` — the live upstream gate
|
|
@@ -16,10 +16,11 @@ closes 7.9/Clause 9, and [ISO-PROCESSOR-REQUIREMENTS.md](ISO-PROCESSOR-REQUIREME
|
|
|
16
16
|
decomposes the Clause 5 processor obligations.
|
|
17
17
|
[ISO-CORRIGENDA-MATRIX.md](ISO-CORRIGENDA-MATRIX.md) gives every published
|
|
18
18
|
Corrigenda amendment cluster an executable, editorial, or superseded
|
|
19
|
-
disposition. [ISO-PART2
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
disposition. [ISO-PART2.md](ISO-PART2.md) and [ISO-PART3.md](ISO-PART3.md) record,
|
|
20
|
+
directive by directive and predicate by predicate, what the normal profile
|
|
21
|
+
implements of Part 2 (modules) and Part 3 (definite clause grammar rules),
|
|
22
|
+
including the 2013 module-amendment evidence and the known gaps and deviations.
|
|
23
|
+
Neither part carries a conformance claim. Built-in rows may group closely related conditions only when the
|
|
23
24
|
row names every grouped condition and its executable evidence.
|
|
24
25
|
The exit checklist is embedded in [ISO-COMPLIANCE.md](ISO-COMPLIANCE.md). [WG17-SYNTAX-STATUS.md](WG17-SYNTAX-STATUS.md) records the
|
|
25
26
|
complete one-to-one trace for the vendored active upstream WG17 syntax cases.
|
package/test/fixtures/README.md
CHANGED
|
@@ -32,7 +32,9 @@ included before its examples are run.
|
|
|
32
32
|
|
|
33
33
|
The upstream Prologue snapshot contains one `max_integer` quad that accepts an
|
|
34
34
|
implementation-specific `Max = unbounded` result. EyeProlog deliberately does
|
|
35
|
-
not patch that vendored fixture: with
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
not patch that vendored fixture: with `bounded=false` it reports no value for
|
|
36
|
+
`max_integer`, so `current_prolog_flag(max_integer, _)` fails. Part 1 does not
|
|
37
|
+
mandate that outcome -- 7.11.1.1 defines the `bounded` flag and says nothing
|
|
38
|
+
about `current_prolog_flag/2` -- so this is an implementation choice, not a
|
|
39
|
+
standards requirement. The regression gate records the single divergence
|
|
40
|
+
explicitly while requiring the other 32 quads to pass.
|
|
@@ -120,6 +120,7 @@ export function formatConformanceReport(report = buildConformanceReport()) {
|
|
|
120
120
|
lines.push(`| **Total** | **${report.total.positive}** | **${report.total.errors}** | **${report.total.warnings}** | **${report.total.proofs}** | **${report.total.total}** |`);
|
|
121
121
|
|
|
122
122
|
lines.push(...dcgConformanceSection());
|
|
123
|
+
lines.push(...integerFlagChoiceSection());
|
|
123
124
|
|
|
124
125
|
if (report.corpusIssues.length > 0) {
|
|
125
126
|
lines.push('', '## Corpus issues', '');
|
|
@@ -163,6 +164,34 @@ function dcgConformanceSection() {
|
|
|
163
164
|
];
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
// Record the unbounded-integer flag choice as an explicit implementation
|
|
168
|
+
// decision rather than leaving it implicit in the source comments.
|
|
169
|
+
function integerFlagChoiceSection() {
|
|
170
|
+
return [
|
|
171
|
+
'',
|
|
172
|
+
'## Integer flag choice under `bounded=false`',
|
|
173
|
+
'',
|
|
174
|
+
'EyeProlog reports `bounded=false` and uses arbitrary-precision integers.',
|
|
175
|
+
'It therefore associates no current value with `max_integer` or',
|
|
176
|
+
'`min_integer`, so `current_prolog_flag/2` does not enumerate them and',
|
|
177
|
+
'fails when either is named. Both flags stay registered, so',
|
|
178
|
+
'`set_prolog_flag/2` still reaches the normal non-changeable-flag errors.',
|
|
179
|
+
'',
|
|
180
|
+
'This is an implementation choice, not a requirement of Part 1. Clause',
|
|
181
|
+
'7.11.1.1 defines the `bounded` flag and does not govern',
|
|
182
|
+
'`current_prolog_flag/2` outcomes, while 7.11.1.2 and 7.11.1.3 give both',
|
|
183
|
+
'flags an implementation-defined default value unconditionally; the',
|
|
184
|
+
'`bounded` condition constrains what that value *means*, not whether the',
|
|
185
|
+
'flag exists. Two alternative readings are equally defensible: expose',
|
|
186
|
+
'implementation-defined values so the flags enumerate, or treat them as',
|
|
187
|
+
'unsupported and raise `domain_error(prolog_flag, Flag)` per 8.17.2.3 b.',
|
|
188
|
+
'EyeProlog prefers silence over inventing a largest integer that its',
|
|
189
|
+
'arithmetic does not have. The vendored Prologue corpus records the',
|
|
190
|
+
'resulting single divergence rather than patching the upstream fixture.',
|
|
191
|
+
'',
|
|
192
|
+
];
|
|
193
|
+
}
|
|
194
|
+
|
|
166
195
|
function executeGate(name, runSuite) {
|
|
167
196
|
const failures = [];
|
|
168
197
|
const reporter = {
|
package/test/run-iso-strict.mjs
CHANGED
|
@@ -95,6 +95,38 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
+
// ISO 7.10.5 and 8.14.2: write_term/2 with quoted(true) shall emit text that
|
|
99
|
+
// reads back as the same term under the current operator table. Individual
|
|
100
|
+
// spellings are pinned elsewhere; this is the general property, over a corpus
|
|
101
|
+
// chosen to stress operator priority, prefix minus, and quoted atoms.
|
|
102
|
+
reporter.test('writeq output reads back as the identical term', () => {
|
|
103
|
+
const corpus = [
|
|
104
|
+
'-(1)', '- (-(1))', '-(a)', '- - 1', '1 - -1', 'f(-1)', '-(1)^2', '2^ -1',
|
|
105
|
+
'1*(2+3)', '(1*2)+3', '2** -3', 'a* -1', 'a- (-1)', '1 rem 2', 'a mod b',
|
|
106
|
+
'[a|b]', '[[]]', '[]', '[-]', '[-,+]', '[a,b,c]', '{}', '{a,b}',
|
|
107
|
+
"f(',')", "','(a,b)", 'f(;)', 'f(!)', 'f(-)', 'f(+)', 'f(*)',
|
|
108
|
+
'f(a,(b,c))', 'f((a,b))', 'f(g(h(1)))', '(a:-b,c)', '(a;b;c)', '(a->b;c)',
|
|
109
|
+
'*(1,2)', '+(1)', 'a=..b', "'ABC'", 'abc', "''", "' '", "'/*'", "'%'",
|
|
110
|
+
'1.0', '-1.0', '0.0', '1.0e-10', '1.5e300',
|
|
111
|
+
String.raw`'\n'`, String.raw`'\t'`, String.raw`'don''t'`,
|
|
112
|
+
String.raw`\+ a`, String.raw`f('|')`,
|
|
113
|
+
];
|
|
114
|
+
for (const text of corpus) {
|
|
115
|
+
let written = '';
|
|
116
|
+
run('', {
|
|
117
|
+
isoStrict: true,
|
|
118
|
+
goal: `writeq(${text})`,
|
|
119
|
+
ioOptions: { write: (chunk) => { written += chunk; } },
|
|
120
|
+
});
|
|
121
|
+
const reread = run('', {
|
|
122
|
+
isoStrict: true,
|
|
123
|
+
goal: `read_term(X,[]), X == (${text})`,
|
|
124
|
+
ioOptions: { input: `${written}.` },
|
|
125
|
+
});
|
|
126
|
+
equal(reread.stats.completed_goal_lists, 1, `writeq round-trip ${text} -> ${written}`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
98
130
|
reporter.test('keeps Corrigendum 2 core predicates and excludes Part 3 phrase', () => {
|
|
99
131
|
const registry = createStrictIsoRegistry();
|
|
100
132
|
equal(Boolean(registry.get('subsumes_term', 2)), true, 'subsumes_term/2');
|
package/test/run-regression.mjs
CHANGED
|
@@ -2325,10 +2325,11 @@ c4 ?- call((!;1)).
|
|
|
2325
2325
|
program.quads = maxIntegerQuads;
|
|
2326
2326
|
const result = publicApi.runQuads(program);
|
|
2327
2327
|
// The upstream working-draft quad accepts either integer overflow or
|
|
2328
|
-
// Max=unbounded.
|
|
2329
|
-
// bounded=false, current_prolog_flag(max_integer, N) fails.
|
|
2330
|
-
//
|
|
2331
|
-
// standards
|
|
2328
|
+
// Max=unbounded. EyeProlog reports no value for max_integer when
|
|
2329
|
+
// bounded=false, so current_prolog_flag(max_integer, N) fails. Part 1
|
|
2330
|
+
// does not mandate that outcome, so this is an implementation choice
|
|
2331
|
+
// rather than a standards requirement. Preserve the upstream fixture
|
|
2332
|
+
// unchanged and make the one deliberate divergence explicit here.
|
|
2332
2333
|
assertEqual(result.total, 1, 'quad total');
|
|
2333
2334
|
assertEqual(result.passed, 0, 'quad passed');
|
|
2334
2335
|
assertEqual(result.failed, 1, 'quad failed');
|
|
@@ -9070,7 +9071,9 @@ function declaredDefaultExportNames() {
|
|
|
9070
9071
|
function missingDocumentedPackageScripts() {
|
|
9071
9072
|
const docs = documentationFiles();
|
|
9072
9073
|
const missing = [];
|
|
9073
|
-
|
|
9074
|
+
// Native npm subcommands are not package scripts, so documenting them must
|
|
9075
|
+
// not require a matching entry in package.json.
|
|
9076
|
+
const nativeCommands = new Set(['exec', 'install', 'link', 'pack', 'publish', 'version', 'ci']);
|
|
9074
9077
|
for (const file of docs) {
|
|
9075
9078
|
const text = fs.readFileSync(file, 'utf8');
|
|
9076
9079
|
for (const line of text.split('\n')) {
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5883,7 +5883,9 @@ Variables, proper lists, and partial lists pass these checks. Validation
|
|
|
5883
5883
|
precedes grammar execution; an otherwise valid failing grammar does not
|
|
5884
5884
|
suppress the diagnostic. Dedicated regressions enforce this policy separately
|
|
5885
5885
|
from the portable quads, which accept both checking and non-checking outcomes. See
|
|
5886
|
-
[`ISO-
|
|
5886
|
+
[`ISO-PART3.md`](test/conformance/ISO-PART3.md), which also records that the
|
|
5887
|
+
2023-08-14 working draft specifies `type_error(terminal_sequence, Culprit)` for
|
|
5888
|
+
this condition.
|
|
5887
5889
|
|
|
5888
5890
|
#### A bidirectional expression grammar
|
|
5889
5891
|
|
|
@@ -6192,8 +6194,15 @@ silently changing a static program.
|
|
|
6192
6194
|
- **`occurs_check`** — **Default:** `true`; **Allowed:** `true`, `error`; **Mutable:** yes.
|
|
6193
6195
|
|
|
6194
6196
|
Because `bounded=false`, `current_prolog_flag(max_integer, _)` and
|
|
6195
|
-
`current_prolog_flag(min_integer, _)` fail
|
|
6196
|
-
|
|
6197
|
+
`current_prolog_flag(min_integer, _)` fail, and EyeProlog does not expose an
|
|
6198
|
+
`unbounded` sentinel as either flag value. This is a deliberate implementation
|
|
6199
|
+
choice rather than a requirement: ISO 7.11.1.1 defines the `bounded` flag and
|
|
6200
|
+
does not govern `current_prolog_flag/2` outcomes, and 7.11.1.2 and 7.11.1.3
|
|
6201
|
+
give `max_integer` and `min_integer` an implementation-defined default value
|
|
6202
|
+
unconditionally, making the `bounded` condition a constraint on what the value
|
|
6203
|
+
*means* rather than on whether the flag exists. A processor with unbounded
|
|
6204
|
+
integers has no largest integer to report, so EyeProlog declines to invent one.
|
|
6205
|
+
See `conformance-report.md` for the alternative reading.
|
|
6197
6206
|
Preparation-time `char_conversion/2` mappings affect later unquoted source text
|
|
6198
6207
|
and also initialize the execution-time conversion mapping; setting the
|
|
6199
6208
|
`char_conversion` flag to `off` disables conversion for following source text.
|
package/ACKNOWLEDGEMENTS.md
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# Acknowledgements
|
|
2
|
-
|
|
3
|
-
This work was carried out at [IDLab](https://idlab.ugent.be/), Ghent University
|
|
4
|
-
– imec, within the [KNoWS](https://knows.idlab.ugent.be/) (Knowledge on Web
|
|
5
|
-
Scale) research team, as part of the **Koreografeye** project.
|
|
6
|
-
|
|
7
|
-
## Standards
|
|
8
|
-
|
|
9
|
-
Conformance work on this repository targets the following standards.
|
|
10
|
-
Licensed standards documents are not redistributed here:
|
|
11
|
-
|
|
12
|
-
- ISO/IEC 13211-1:1995, *Information technology — Programming languages —
|
|
13
|
-
Prolog — Part 1: General core*
|
|
14
|
-
- ISO/IEC 13211-1:1995/Cor.1:2007
|
|
15
|
-
- ISO/IEC 13211-1:1995/Cor.2:2012
|
|
16
|
-
- ISO/IEC 13211-1:1995/Cor.3:2017
|
|
17
|
-
- ISO/IEC 13211-2:2000, *Part 2: Modules*, and its 2013 amendment
|
|
18
|
-
- ISO/IEC TS 13211-3:2025, *Part 3: Definite clause grammar rules*
|
|
19
|
-
|
|
20
|
-
## External conformance corpora
|
|
21
|
-
|
|
22
|
-
The test suite executes third-party material that is vendored unmodified and
|
|
23
|
-
credited in place:
|
|
24
|
-
|
|
25
|
-
- Ulrich Neumerkel's ISO Prolog conformity corpora (TU Wien), including the
|
|
26
|
-
`phrase_quad.pl` and `variable_names_quad.pl` snapshots under
|
|
27
|
-
`test/fixtures/`.
|
|
28
|
-
- The WG17 syntax conformity-testing matrix.
|
|
29
|
-
- Test cases adapted from the Logtalk ISO conformance suite, marked as such in
|
|
30
|
-
the corpus files that use them.
|
|
31
|
-
|
|
32
|
-
## Citation
|
|
33
|
-
|
|
34
|
-
When referring to this work in a publication, please acknowledge the project as
|
|
35
|
-
described above. A suggested sentence is available in the paper template used by
|
|
36
|
-
the KNoWS team.
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# ISO/IEC 13211-2 module amendment (2013) coverage
|
|
2
|
-
|
|
3
|
-
This ledger maps EyeProlog's normal module profile to the requirements clarified
|
|
4
|
-
by the attached 2013 ISO/IEC 13211-2 amendment draft (WG17 N251). It is narrowly
|
|
5
|
-
scoped to that amendment. It does **not** turn the broader ISO/IEC 13211-2:2000
|
|
6
|
-
base document into a release-facing certification claim; unchanged Part 2
|
|
7
|
-
facilities outside the amendment still need their own clause-by-clause review.
|
|
8
|
-
|
|
9
|
-
Executable evidence lives in `test/run-iso-part2-amendment.mjs` and is included
|
|
10
|
-
in both `npm test` and the conformance aggregate.
|
|
11
|
-
|
|
12
|
-
| Amendment clause | Required behavior | EyeProlog behavior / evidence |
|
|
13
|
-
| --- | --- | --- |
|
|
14
|
-
| 6.2.4.1 | A `module(Name, Exports)` directive identifies a named module and its public predicate indicators. | `Program.defineModule()` records the named module and export map. The focused suite checks that public predicates import and private predicates remain module-local. |
|
|
15
|
-
| 6.2.5 | A module body is a Prolog text beginning with its `module/2` directive and extending to the end of that text. | Files designated by `use_module/1-2` are accepted as module sources only when their first read term is `module/2`. A second `module/2` later in the same Prolog text is rejected rather than switching modules mid-text, while a distinct Prolog text may begin a distinct module body. |
|
|
16
|
-
| 6.2.5.5 | `use_module(F, L)` selectively imports the predicates in `L` from the exports of the module defined by `F`. | `Program.importModule()` validates requested indicators against the source module's export map and installs only those imports. The focused suite verifies selective import. |
|
|
17
|
-
| 6.2.5.6 | `use_module(F)` imports all exported predicates; the amendment also aligns the public-predicate effect of `ensure_loaded(F)` for a module source. | `use_module/1` imports the full export map. `ensure_loaded/1` now imports a module's public predicates while retaining idempotent source loading, including when the same module was already loaded for another caller. |
|
|
18
|
-
| 6.2.5.7 | `meta_predicate/1` marks context-sensitive arguments; `:` arguments carry the current source/calling module. | Normal parsing accepts the amendment spelling `:- meta_predicate p(:).`. Colon-mode arguments are represented as an observable `Module:Goal` term, including when the argument is a variable. Numeric closure modes remain a separate compatibility extension. |
|
|
19
|
-
| 6.4.4.3 | Imported metapredicates preserve the caller's module context for their meta-arguments. | The focused suite verifies both visible `Module:Goal` decomposition and execution of a caller-private predicate through a variable meta-argument. Explicit `Module:Goal` calls continue to set their stated module context. |
|
|
20
|
-
|
|
21
|
-
## Operator boundary
|
|
22
|
-
|
|
23
|
-
ISO/IEC 13211-2 adds `:` as the module-qualification operator relative to the
|
|
24
|
-
Part 1 initial operator table. EyeProlog therefore predeclares `:` only in its
|
|
25
|
-
normal module profile; `--iso-strict` no longer includes it in the Part 1
|
|
26
|
-
initial table. Normal mode also predeclares `meta_predicate` as a directive
|
|
27
|
-
operator so the amendment's source spelling is accepted, while the parenthesized
|
|
28
|
-
`meta_predicate(...)` compatibility spelling remains valid.
|
|
29
|
-
|
|
30
|
-
## Remaining Part 2 scope
|
|
31
|
-
|
|
32
|
-
The amendment coverage above is executable and release-gated. EyeProlog still
|
|
33
|
-
describes the overall Part 2 surface as a compatibility profile until the
|
|
34
|
-
unchanged portions of ISO/IEC 13211-2:2000 - including its broader module
|
|
35
|
-
interface and re-export model - have a complete processor/semantics ledger.
|
|
36
|
-
See `ISO-PART2-PART3-SCOPE.md` for that release boundary.
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# ISO Part 2 and Part 3 compatibility scope
|
|
2
|
-
|
|
3
|
-
EyeProlog's release-facing `--iso-strict` claim is deliberately limited to
|
|
4
|
-
ISO/IEC 13211-1:1995 together with Technical Corrigenda 1:2007, 2:2012, and
|
|
5
|
-
3:2017. This note records the separate status of the normal-profile facilities
|
|
6
|
-
that overlap ISO/IEC 13211-2:2000 (modules) and ISO/IEC TS 13211-3:2025 (definite
|
|
7
|
-
clause grammar rules). It is a scope ledger, not an independent certification.
|
|
8
|
-
|
|
9
|
-
## Part 2 modules
|
|
10
|
-
|
|
11
|
-
Normal mode implements a procedure-oriented module compatibility layer:
|
|
12
|
-
`module/2`, `use_module/1-2`, `meta_predicate/1`, explicit `Module:Goal`
|
|
13
|
-
qualification, exports/imports, nonterminal indicators, and module-aware meta
|
|
14
|
-
calls are covered by the regression and conformance corpora.
|
|
15
|
-
|
|
16
|
-
The requirements clarified by the 2013 ISO/IEC 13211-2 module amendment draft
|
|
17
|
-
(WG17 N251) now have a dedicated executable ledger in
|
|
18
|
-
`ISO-PART2-AMENDMENT-2013.md`: module/2 exports, selective and full imports, the
|
|
19
|
-
module-source behavior of ensure_loaded/1, the amendment's meta_predicate
|
|
20
|
-
directive spelling, and visible caller-module qualification of `:`
|
|
21
|
-
meta-arguments. The focused runner is part of the release gate.
|
|
22
|
-
|
|
23
|
-
This amendment coverage is intentionally narrower than a complete Part 2
|
|
24
|
-
conformance claim. In particular, the Part 1 strict registry does not enable
|
|
25
|
-
module directives or the Part 2 `:` operator, and the project does not infer
|
|
26
|
-
full Part 2 coverage from interoperability with Scryer, Trealla, or Logtalk. A
|
|
27
|
-
future complete Part 2 claim would require a clause-by-clause review of the
|
|
28
|
-
unchanged ISO/IEC 13211-2:2000 module-interface and re-export facilities as well
|
|
29
|
-
as the amendment.
|
|
30
|
-
|
|
31
|
-
## Part 3 definite clause grammars
|
|
32
|
-
|
|
33
|
-
Normal mode expands grammar rules to ordinary predicates and supports terminal
|
|
34
|
-
sequences, sequencing, alternatives, semicontexts, embedded goals, cut,
|
|
35
|
-
`call//1`, `phrase//1`, `phrase/2-3`, module-qualified nonterminals, and the
|
|
36
|
-
implementation-dependent negation/if-then choices documented in the reference.
|
|
37
|
-
`--iso-strict` leaves `-->/2` as ordinary Part 1 operator syntax and excludes
|
|
38
|
-
Part 3 grammar expansion and `phrase/2-3` from the strict registry.
|
|
39
|
-
|
|
40
|
-
EyeProlog elects to perform both implementation-defined terminal-sequence
|
|
41
|
-
checks in ISO/IEC TS 13211-3:2025, 8.18.1.3 g and h. The checks are optional;
|
|
42
|
-
when performed, their specified error type is `list`. EyeProlog consistently
|
|
43
|
-
raises `type_error(list, Culprit)` for a non-list input to `phrase/2` or
|
|
44
|
-
`phrase/3`, and for a non-list remainder to `phrase/3`. The culprit is the
|
|
45
|
-
whole invalid argument, including an improper list, not just its tail.
|
|
46
|
-
Variables, proper lists, and partial lists are accepted by these checks.
|
|
47
|
-
For otherwise valid grammar bodies, validation precedes execution, even when
|
|
48
|
-
the grammar would fail or produce a side effect.
|
|
49
|
-
|
|
50
|
-
The upstream phrase quads permit both checking and non-checking outcomes and
|
|
51
|
-
therefore do not establish this consistency. A dedicated regression matrix in
|
|
52
|
-
`test/run-regression.mjs` requires the exact diagnostic across both arities,
|
|
53
|
-
both sequence positions, atomic and compound non-lists, improper lists at
|
|
54
|
-
several depths, and several grammar bodies. Positive cases protect variables,
|
|
55
|
-
proper lists, and partial lists. These are policy checks, not a relaxation of
|
|
56
|
-
the unmodified upstream corpus.
|
|
57
|
-
|
|
58
|
-
The Part 3 implementation otherwise keeps sequence validation, grammar-body
|
|
59
|
-
callability, variable-body instantiation errors, `phrase/3` steadfastness, and
|
|
60
|
-
grammar expansion under focused executable tests. Changes intended to advance a
|
|
61
|
-
formal Part 3 claim should update this ledger and the corresponding error cases
|
|
62
|
-
rather than silently changing the compatibility profile.
|
|
63
|
-
|
|
64
|
-
## Release boundary
|
|
65
|
-
|
|
66
|
-
- Part 1 + Corrigenda 1-3: release-facing strict-core conformance target.
|
|
67
|
-
- Part 2: 2013 amendment requirements release-gated; broader Part 2 remains a normal-mode compatibility profile, not a complete certification.
|
|
68
|
-
- Part 3: normal-mode compatibility profile, tested but not certified complete.
|
|
69
|
-
|
|
70
|
-
This separation keeps Part 1 conformance evidence independent of useful module
|
|
71
|
-
and DCG extensions while making known Part 2/Part 3 scope limits visible.
|