eyeprolog 1.5.55 → 1.5.57

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.
Files changed (37) hide show
  1. package/examples/combinatorics-findall-sort.pl +4 -4
  2. package/examples/d3-group.pl +37 -37
  3. package/examples/eulerian-path.pl +4 -4
  4. package/examples/graph-reachability.pl +4 -4
  5. package/examples/monkey-bananas.pl +4 -4
  6. package/examples/proof/graph-reachability.pl +8 -8
  7. package/package.json +1 -1
  8. package/src/cli.js +13 -0
  9. package/src/parser.js +74 -1
  10. package/src/program.js +50 -0
  11. package/test/conformance/ISO-PROLOG-TEXT-EXECUTION-MATRIX.md +1 -1
  12. package/test/conformance/expected-errors/syntax/extra_missing_head_rejected.txt +1 -1
  13. package/test/regression/cases-api.mjs +1677 -0
  14. package/test/regression/cases-documentation-sync.mjs +717 -0
  15. package/test/regression/cases-regression.mjs +5286 -0
  16. package/test/regression/cases-white-box.mjs +938 -0
  17. package/test/regression/support.mjs +770 -0
  18. package/test/run-all.mjs +0 -0
  19. package/test/run-architecture.mjs +0 -0
  20. package/test/run-book-examples.mjs +0 -0
  21. package/test/run-cleanup.mjs +0 -0
  22. package/test/run-conformance-all.mjs +0 -0
  23. package/test/run-conformance-report.mjs +0 -0
  24. package/test/run-conformance.mjs +0 -0
  25. package/test/run-examples.mjs +0 -0
  26. package/test/run-http-json.mjs +0 -0
  27. package/test/run-interop.mjs +0 -0
  28. package/test/run-iso-part2-amendment.mjs +0 -0
  29. package/test/run-iso-strict.mjs +0 -0
  30. package/test/run-neumerkel-tests.mjs +0 -0
  31. package/test/run-neumerkel.mjs +0 -0
  32. package/test/run-openrulebench.mjs +0 -0
  33. package/test/run-playground.mjs +0 -0
  34. package/test/run-regression.mjs +28 -9240
  35. package/test/run-wg17.mjs +0 -0
  36. package/tools/extract-book-examples.mjs +0 -0
  37. package/tools/report-wg17-syntax-coverage.mjs +0 -0
@@ -14,16 +14,16 @@
14
14
 
15
15
  % select/3 nondeterministically removes one item from a list; because it is an
16
16
  % ordinary rule, the example also demonstrates user-level list recursion.
17
- select(Item, [Item | Rest], Rest).
17
+ select_item(Item, [Item | Rest], Rest).
18
18
  % The recursive clause keeps the non-selected head and searches the tail.
19
- select(Item, [Head | Tail], [Head | Rest]) :-
20
- select(Item, Tail, Rest).
19
+ select_item(Item, [Head | Tail], [Head | Rest]) :-
20
+ select_item(Item, Tail, Rest).
21
21
 
22
22
  % combination/3 builds an unordered K-combination by repeated selection.
23
23
  combination(0, _items, []).
24
24
  combination(I, Items, Combination) :-
25
25
  (I > 0),
26
- select(Item, Items, Remaining),
26
+ select_item(Item, Items, Remaining),
27
27
  (J is I - 1),
28
28
  combination(J, Remaining, Partial),
29
29
  sort([Item | Partial], Combination).
@@ -21,42 +21,42 @@ symmetry(reflection_b).
21
21
  symmetry(reflection_c).
22
22
 
23
23
  % Cayley table for D3 composition.
24
- compose(identity, identity, identity).
25
- compose(identity, rotation_120, rotation_120).
26
- compose(identity, rotation_240, rotation_240).
27
- compose(identity, reflection_a, reflection_a).
28
- compose(identity, reflection_b, reflection_b).
29
- compose(identity, reflection_c, reflection_c).
30
- compose(rotation_120, identity, rotation_120).
31
- compose(rotation_120, rotation_120, rotation_240).
32
- compose(rotation_120, rotation_240, identity).
33
- compose(rotation_120, reflection_a, reflection_b).
34
- compose(rotation_120, reflection_b, reflection_c).
35
- compose(rotation_120, reflection_c, reflection_a).
36
- compose(rotation_240, identity, rotation_240).
37
- compose(rotation_240, rotation_120, identity).
38
- compose(rotation_240, rotation_240, rotation_120).
39
- compose(rotation_240, reflection_a, reflection_c).
40
- compose(rotation_240, reflection_b, reflection_a).
41
- compose(rotation_240, reflection_c, reflection_b).
42
- compose(reflection_a, identity, reflection_a).
43
- compose(reflection_a, rotation_120, reflection_c).
44
- compose(reflection_a, rotation_240, reflection_b).
45
- compose(reflection_a, reflection_a, identity).
46
- compose(reflection_a, reflection_b, rotation_240).
47
- compose(reflection_a, reflection_c, rotation_120).
48
- compose(reflection_b, identity, reflection_b).
49
- compose(reflection_b, rotation_120, reflection_a).
50
- compose(reflection_b, rotation_240, reflection_c).
51
- compose(reflection_b, reflection_a, rotation_120).
52
- compose(reflection_b, reflection_b, identity).
53
- compose(reflection_b, reflection_c, rotation_240).
54
- compose(reflection_c, identity, reflection_c).
55
- compose(reflection_c, rotation_120, reflection_b).
56
- compose(reflection_c, rotation_240, reflection_a).
57
- compose(reflection_c, reflection_a, rotation_240).
58
- compose(reflection_c, reflection_b, rotation_120).
59
- compose(reflection_c, reflection_c, identity).
24
+ compose_op(identity, identity, identity).
25
+ compose_op(identity, rotation_120, rotation_120).
26
+ compose_op(identity, rotation_240, rotation_240).
27
+ compose_op(identity, reflection_a, reflection_a).
28
+ compose_op(identity, reflection_b, reflection_b).
29
+ compose_op(identity, reflection_c, reflection_c).
30
+ compose_op(rotation_120, identity, rotation_120).
31
+ compose_op(rotation_120, rotation_120, rotation_240).
32
+ compose_op(rotation_120, rotation_240, identity).
33
+ compose_op(rotation_120, reflection_a, reflection_b).
34
+ compose_op(rotation_120, reflection_b, reflection_c).
35
+ compose_op(rotation_120, reflection_c, reflection_a).
36
+ compose_op(rotation_240, identity, rotation_240).
37
+ compose_op(rotation_240, rotation_120, identity).
38
+ compose_op(rotation_240, rotation_240, rotation_120).
39
+ compose_op(rotation_240, reflection_a, reflection_c).
40
+ compose_op(rotation_240, reflection_b, reflection_a).
41
+ compose_op(rotation_240, reflection_c, reflection_b).
42
+ compose_op(reflection_a, identity, reflection_a).
43
+ compose_op(reflection_a, rotation_120, reflection_c).
44
+ compose_op(reflection_a, rotation_240, reflection_b).
45
+ compose_op(reflection_a, reflection_a, identity).
46
+ compose_op(reflection_a, reflection_b, rotation_240).
47
+ compose_op(reflection_a, reflection_c, rotation_120).
48
+ compose_op(reflection_b, identity, reflection_b).
49
+ compose_op(reflection_b, rotation_120, reflection_a).
50
+ compose_op(reflection_b, rotation_240, reflection_c).
51
+ compose_op(reflection_b, reflection_a, rotation_120).
52
+ compose_op(reflection_b, reflection_b, identity).
53
+ compose_op(reflection_b, reflection_c, rotation_240).
54
+ compose_op(reflection_c, identity, reflection_c).
55
+ compose_op(reflection_c, rotation_120, reflection_b).
56
+ compose_op(reflection_c, rotation_240, reflection_a).
57
+ compose_op(reflection_c, reflection_a, rotation_240).
58
+ compose_op(reflection_c, reflection_b, rotation_120).
59
+ compose_op(reflection_c, reflection_c, identity).
60
60
  % Each candidate subgroup must also contain inverses.
61
61
  inverse(identity, identity).
62
62
  inverse(rotation_120, rotation_240).
@@ -78,7 +78,7 @@ all_symmetries(Symmetries) :-
78
78
 
79
79
  % A valid subgroup is closed under both composition and inverse.
80
80
  closed_under_composition(Group) :-
81
- \+ (member(X, Group), member(Y, Group), compose(X, Y, Z), \+ member(Z, Group)).
81
+ \+ (member(X, Group), member(Y, Group), compose_op(X, Y, Z), \+ member(Z, Group)).
82
82
 
83
83
  closed_under_inverse(Group) :-
84
84
  \+ (member(X, Group), inverse(X, Y), \+ member(Y, Group)).
@@ -39,9 +39,9 @@ incident(V, E) :- edge(E, _u, V).
39
39
  adjacent_by_edge(V, U, E) :- edge(E, V, U).
40
40
  adjacent_by_edge(V, U, E) :- edge(E, U, V).
41
41
 
42
- select(Item, [Item | Rest], Rest).
43
- select(Item, [Head | Tail], [Head | Rest]) :-
44
- select(Item, Tail, Rest).
42
+ select_item(Item, [Item | Rest], Rest).
43
+ select_item(Item, [Head | Tail], [Head | Rest]) :-
44
+ select_item(Item, Tail, Rest).
45
45
 
46
46
  % Eulerian paths start at an odd-degree vertex when exactly two exist.
47
47
  odd_degree(V) :-
@@ -77,7 +77,7 @@ eulerian_path(Path) :-
77
77
  dfs_euler(_current, Path, [], Path).
78
78
  dfs_euler(Current, Visited, Remaining, Path) :-
79
79
  adjacent_by_edge(Current, Next, Edge),
80
- select(Edge, Remaining, Newremaining),
80
+ select_item(Edge, Remaining, Newremaining),
81
81
  dfs_euler(Next, [Next | Visited], Newremaining, Path).
82
82
 
83
83
  oddVertices(eulerian_path_case, Odds) :-
@@ -18,14 +18,14 @@ edge(d, f).
18
18
  edge(e, f).
19
19
  edge(f, g).
20
20
 
21
- reachable(Node, Node, _visited).
22
- reachable(Start, Goal, Visited) :-
21
+ reachable_via(Node, Node, _visited).
22
+ reachable_via(Start, Goal, Visited) :-
23
23
  edge(Start, Next),
24
24
  \+ member(Next, Visited),
25
- reachable(Next, Goal, [Next|Visited]).
25
+ reachable_via(Next, Goal, [Next|Visited]).
26
26
 
27
27
  is_reachable(Start, Goal) :-
28
- reachable(Start, Goal, [Start]).
28
+ reachable_via(Start, Goal, [Start]).
29
29
 
30
30
  reachable(reachability_case, path(a, f)) :-
31
31
  is_reachable(a, f).
@@ -20,16 +20,16 @@ plan(Moves) :-
20
20
  candidate_plan(Moves),
21
21
  initial_state(I),
22
22
  goal_state(G),
23
- reachable(I, Moves, G).
23
+ reachable_state(I, Moves, G).
24
24
 
25
25
  candidate_plan([_, _, _]).
26
26
  candidate_plan([_, _, _, _]).
27
27
  candidate_plan([_, _, _, _, _]).
28
28
 
29
- reachable(S, [], S).
30
- reachable(S1, [M|L], S3) :-
29
+ reachable_state(S, [], S).
30
+ reachable_state(S1, [M|L], S3) :-
31
31
  legal_move(S1, M, S2),
32
- reachable(S2, L, S3).
32
+ reachable_state(S2, L, S3).
33
33
 
34
34
  initial_state([loc1, loc2, loc3, n, n]).
35
35
  goal_state([_, _, _, _, y]).
@@ -11,7 +11,7 @@ why(
11
11
  bindings([binding("Start", a), binding("Goal", f)]),
12
12
  uses([
13
13
  proof(
14
- goal(reachable(a, f, "a")),
14
+ goal(reachable_via(a, f, "a")),
15
15
  by(rule("graph-reachability.pl", clause(9))),
16
16
  bindings([binding("Start", a), binding("Goal", f), binding("Visited", "a"), binding("Next", b)]),
17
17
  uses([
@@ -24,7 +24,7 @@ why(
24
24
  by(builtin('\\+', 1))
25
25
  ),
26
26
  proof(
27
- goal(reachable(b, f, "ba")),
27
+ goal(reachable_via(b, f, "ba")),
28
28
  by(rule("graph-reachability.pl", clause(9))),
29
29
  bindings([binding("Start", b), binding("Goal", f), binding("Visited", "ba"), binding("Next", d)]),
30
30
  uses([
@@ -37,7 +37,7 @@ why(
37
37
  by(builtin('\\+', 1))
38
38
  ),
39
39
  proof(
40
- goal(reachable(d, f, "dba")),
40
+ goal(reachable_via(d, f, "dba")),
41
41
  by(rule("graph-reachability.pl", clause(9))),
42
42
  bindings([binding("Start", d), binding("Goal", f), binding("Visited", "dba"), binding("Next", f)]),
43
43
  uses([
@@ -50,7 +50,7 @@ why(
50
50
  by(builtin('\\+', 1))
51
51
  ),
52
52
  proof(
53
- goal(reachable(f, f, "fdba")),
53
+ goal(reachable_via(f, f, "fdba")),
54
54
  by(fact("graph-reachability.pl", clause(8))),
55
55
  bindings([binding("Node", f), binding("_visited", "fdba")])
56
56
  )
@@ -79,7 +79,7 @@ why(
79
79
  bindings([binding("Start", c), binding("Goal", g)]),
80
80
  uses([
81
81
  proof(
82
- goal(reachable(c, g, "c")),
82
+ goal(reachable_via(c, g, "c")),
83
83
  by(rule("graph-reachability.pl", clause(9))),
84
84
  bindings([binding("Start", c), binding("Goal", g), binding("Visited", "c"), binding("Next", e)]),
85
85
  uses([
@@ -92,7 +92,7 @@ why(
92
92
  by(builtin('\\+', 1))
93
93
  ),
94
94
  proof(
95
- goal(reachable(e, g, "ec")),
95
+ goal(reachable_via(e, g, "ec")),
96
96
  by(rule("graph-reachability.pl", clause(9))),
97
97
  bindings([binding("Start", e), binding("Goal", g), binding("Visited", "ec"), binding("Next", f)]),
98
98
  uses([
@@ -105,7 +105,7 @@ why(
105
105
  by(builtin('\\+', 1))
106
106
  ),
107
107
  proof(
108
- goal(reachable(f, g, "fec")),
108
+ goal(reachable_via(f, g, "fec")),
109
109
  by(rule("graph-reachability.pl", clause(9))),
110
110
  bindings([binding("Start", f), binding("Goal", g), binding("Visited", "fec"), binding("Next", g)]),
111
111
  uses([
@@ -118,7 +118,7 @@ why(
118
118
  by(builtin('\\+', 1))
119
119
  ),
120
120
  proof(
121
- goal(reachable(g, g, "gfec")),
121
+ goal(reachable_via(g, g, "gfec")),
122
122
  by(fact("graph-reachability.pl", clause(8))),
123
123
  bindings([binding("Node", g), binding("_visited", "gfec")])
124
124
  )
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.55",
6
+ "version": "1.5.57",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/cli.js CHANGED
@@ -196,6 +196,10 @@ export async function main(argv) {
196
196
  });
197
197
 
198
198
  const portabilityFailures = program.interopPortabilityWarnings ?? [];
199
+ // Shadowing is reported unconditionally: silently replacing a library
200
+ // predicate for the whole program is the failure mode this diagnostic exists
201
+ // to surface, so it must not depend on opting in to --warnings.
202
+ printLibraryShadowingWarnings(program);
199
203
  if (options.warnings || (options.portable && portabilityFailures.length > 0)) printWarnings(program);
200
204
  if (options.portable && portabilityFailures.length > 0) {
201
205
  process.exitCode = 1;
@@ -331,6 +335,7 @@ Options:
331
335
  --proof-detail mode Use abstract or expanded proof detail (implies --proof).
332
336
  --verify-proof file Verify why/2 proof certificates against the input program.
333
337
  -q, --quads Run embedded quad tests and fail if any do not hold.
338
+ Note: -q is quads, not quiet; --quiet has no short form.
334
339
  --quiet Suppress answer terms while preserving Prolog output.
335
340
  -s, --stats Print solver and memory statistics to stderr after execution.
336
341
  --iso-strict Use ISO/IEC 13211-1 core + Corrigenda 1-3 only;
@@ -364,6 +369,14 @@ function printSourceWarning(warning) {
364
369
  `);
365
370
  }
366
371
 
372
+ function printLibraryShadowingWarnings(program) {
373
+ for (const warning of program.libraryShadowingWarnings ?? []) {
374
+ process.stderr.write('eyeprolog warning: user definition shadows a bundled library predicate\n');
375
+ process.stderr.write(` ${warning.indicator} replaces library(${warning.library}) ${warning.indicator} for all user code in this program\n`);
376
+ process.stderr.write(` import it explicitly with :- use_module(library(${warning.library}), [${warning.indicator}]). to make this an error\n`);
377
+ }
378
+ }
379
+
367
380
  function printWarnings(program) {
368
381
  for (const warning of program.interopPortabilityWarnings ?? []) {
369
382
  if (warning.kind === 'library') {
package/src/parser.js CHANGED
@@ -1138,7 +1138,7 @@ class Parser {
1138
1138
  }
1139
1139
  const operator = this.applyOperatorDirective(directive, line);
1140
1140
  if (!coreDirective && !extensionDirective && !operator) {
1141
- throw new Error(`parse line ${line}: bad term`);
1141
+ throw new Error(`parse line ${line}: ${unsupportedDirectiveMessage(directive, this.strictIso)}`);
1142
1142
  }
1143
1143
  this.expect(TOK.DOT, '.');
1144
1144
  this.applyParserFlagDirective(directive, line);
@@ -1911,3 +1911,76 @@ export function parseGoalText(text, options = {}) {
1911
1911
  }
1912
1912
  return head.args[0];
1913
1913
  }
1914
+
1915
+ // Directives are accepted from a fixed list, so an unrecognized one is a
1916
+ // different mistake from unparseable input and deserves a different message.
1917
+ // The three cases below cover what people actually write: a bare goal that
1918
+ // belongs in initialization/1, a misspelt directive name, and a term that is
1919
+ // not a directive at all.
1920
+ const CORE_DIRECTIVE_INDICATORS = [
1921
+ 'dynamic/1', 'multifile/1', 'discontiguous/1', 'initialization/1', 'include/1',
1922
+ 'ensure_loaded/1', 'char_conversion/2', 'set_prolog_flag/2', 'op/3',
1923
+ ];
1924
+ const EXTENSION_DIRECTIVE_INDICATORS = [
1925
+ 'use_module/1', 'use_module/2', 'meta_predicate/1', 'attribute/1', 'table/1',
1926
+ 'public/1', 'module/2',
1927
+ ];
1928
+
1929
+ function directiveNameDistance(a, b) {
1930
+ // Small Levenshtein distance, used only to suggest a near-miss spelling.
1931
+ const rows = a.length + 1;
1932
+ const cols = b.length + 1;
1933
+ let previous = Array.from({ length: cols }, (_, i) => i);
1934
+ for (let i = 1; i < rows; i++) {
1935
+ const current = [i];
1936
+ for (let j = 1; j < cols; j++) {
1937
+ current[j] = Math.min(
1938
+ previous[j] + 1,
1939
+ current[j - 1] + 1,
1940
+ previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1),
1941
+ );
1942
+ }
1943
+ previous = current;
1944
+ }
1945
+ return previous[cols - 1];
1946
+ }
1947
+
1948
+ function nearestDirectiveName(name, available) {
1949
+ let best = null;
1950
+ let bestDistance = Infinity;
1951
+ for (const indicator of available) {
1952
+ const candidate = indicator.slice(0, indicator.lastIndexOf('/'));
1953
+ const distance = directiveNameDistance(name, candidate);
1954
+ if (distance < bestDistance) {
1955
+ bestDistance = distance;
1956
+ best = candidate;
1957
+ }
1958
+ }
1959
+ // Only suggest a genuinely close spelling, not the alphabetically nearest.
1960
+ return bestDistance <= Math.max(1, Math.floor(name.length / 3)) ? best : null;
1961
+ }
1962
+
1963
+ function unsupportedDirectiveMessage(directive, strictIso) {
1964
+ const available = strictIso
1965
+ ? CORE_DIRECTIVE_INDICATORS
1966
+ : [...CORE_DIRECTIVE_INDICATORS, ...EXTENSION_DIRECTIVE_INDICATORS];
1967
+ if (directive.type !== COMPOUND && directive.type !== ATOM) {
1968
+ return 'a directive must be a callable term';
1969
+ }
1970
+ const name = directive.name;
1971
+ const arity = directive.type === COMPOUND ? directive.arity : 0;
1972
+ const indicator = `${name}/${arity}`;
1973
+ const known = available.filter((entry) => entry.slice(0, entry.lastIndexOf('/')) === name);
1974
+ if (known.length > 0) {
1975
+ return `directive ${indicator} has the wrong arity; expected ${known.join(' or ')}`;
1976
+ }
1977
+ const suggestion = nearestDirectiveName(name, available);
1978
+ if (suggestion != null) {
1979
+ return `unknown directive ${indicator}; did you mean ${suggestion}?`;
1980
+ }
1981
+ // A goal such as `:- write(hello), nl.` is well-formed but is not one of the
1982
+ // recognized declarations, so point at initialization/1 rather than reporting
1983
+ // a syntax error.
1984
+ return `unknown directive ${indicator}; to run a goal at load time use ` +
1985
+ ':- initialization(Goal).';
1986
+ }
package/src/program.js CHANGED
@@ -153,6 +153,7 @@ export class Program {
153
153
  this.autoloadedPredicates = [];
154
154
  this.libraryImports = [];
155
155
  this.interopPortabilityWarnings = [];
156
+ this.libraryShadowingWarnings = [];
156
157
  this.dynamicPredicates = new Set();
157
158
  // ISO 7.5.3 notes that a public/1 directive declaring user-defined
158
159
  // procedures to be public would be an extension. Such a procedure stays
@@ -1428,6 +1429,55 @@ function analyzeInteropPortability(program, extraGoals = []) {
1428
1429
  });
1429
1430
  }
1430
1431
  program.interopPortabilityWarnings = [...warnings.values()];
1432
+ analyzeLibraryShadowing(program);
1433
+ }
1434
+
1435
+ // A user-defined procedure replaces the bundled-library procedure of the same
1436
+ // name and arity: the library clauses are never autoloaded, so other parts of
1437
+ // the same program that expected the library behaviour just see the user's
1438
+ // clauses. Bundled library modules keep resolving their own internal calls, so
1439
+ // this only affects user code, but within user code it acts at a distance
1440
+ // across included files.
1441
+ //
1442
+ // How strictly this is treated depends on how explicit the program was. The
1443
+ // first two tiers match what Trealla and Scryer do for the same program
1444
+ // (`:- use_module(library(lists)).` followed by clauses for append/3): both
1445
+ // warn and continue, Trealla with `overwriting user:'append'/3` and Scryer with
1446
+ // `overwriting append/3 because the clauses are discontiguous`. SWI-Prolog
1447
+ // treats imported predicates as weak symbols and warns likewise. No system
1448
+ // raises an error there, so neither does EyeProlog:
1449
+ //
1450
+ // implicit autoload -> warning
1451
+ // use_module(library(L)) -> warning
1452
+ // use_module(library(L), [Name/Arity]) -> permission_error
1453
+ //
1454
+ // Only the last case is an outright contradiction: the program asked for that
1455
+ // exact predicate to be imported and then supplied clauses for it. The first
1456
+ // two stay warnings so that flat, module-free programs keep loading here
1457
+ // exactly as they do on Scryer and Trealla, which was checked directly rather
1458
+ // than inferred. Note that ISO reserves permission_error(modify, static_procedure)
1459
+ // for built-in predicates (7.5.3); library predicates are not built-ins, so
1460
+ // this error is raised only where the program's own import list demands it.
1461
+ function analyzeLibraryShadowing(program) {
1462
+ const explicitlyImported = new Map();
1463
+ for (const entry of program.libraryImports) {
1464
+ if (entry.imports == null) continue;
1465
+ for (const indicator of entry.imports) {
1466
+ explicitlyImported.set(`${entry.targetModule}:${indicator.key}`, entry.library);
1467
+ }
1468
+ }
1469
+
1470
+ const shadowed = new Map();
1471
+ for (const group of program.groups.values()) {
1472
+ if (group.module !== 'user') continue;
1473
+ const key = `${group.name}/${group.arity}`;
1474
+ const explicitLibrary = explicitlyImported.get(`${group.module}:${key}`);
1475
+ if (explicitLibrary != null) throw staticProcedureModificationError(group.name, group.arity);
1476
+ const library = eyePrologLibraryAutoload[key];
1477
+ if (library == null || shadowed.has(key)) continue;
1478
+ shadowed.set(key, { kind: 'shadowed-library-predicate', indicator: key, library });
1479
+ }
1480
+ program.libraryShadowingWarnings = [...shadowed.values()];
1431
1481
  }
1432
1482
 
1433
1483
  function sourceOptionsFor(source, options) {
@@ -23,7 +23,7 @@ chosen behavior is also indexed by `ISO-IMPLEMENTATION-DEFINED.md`.
23
23
  | 7.4.2.7 `include/1` | covered | The included text is prepared at the directive position and shares operator/character/flag preparation state with its parent. |
24
24
  | 7.4.2.8 `ensure_loaded/1` | covered | A source is prepared at most once in the current load graph, including repeated references and self/top-level references. |
25
25
  | 7.4.2.9 `set_prolog_flag/2` | covered | Preparation-time flag changes affect subsequent text and are replayed into execution state; strict flag names/values/changeability remain governed by the closed 7.11 review. |
26
- | 7.4.3 source clauses | covered | Source heads/bodies are validated like program clauses, standardized static/control procedures are protected, declarations can create empty procedures, and body conversion follows 7.6.2 while preserving head/body variable identity. The built-in restriction applies in every execution mode, so consulting a clause for a built-in predicate or control construct reports the same `permission_error(modify, static_procedure)` as `assert`ing one; EyeProlog's own library and extension predicates are not standard built-ins and stay redefinable. Corpus: `error/iso/consult_redefines_builtin`. |
26
+ | 7.4.3 source clauses | covered | Source heads/bodies are validated like program clauses, standardized static/control procedures are protected, declarations can create empty procedures, and body conversion follows 7.6.2 while preserving head/body variable identity. The built-in restriction applies in every execution mode, so consulting a clause for a built-in predicate or control construct reports the same `permission_error(modify, static_procedure)` as `assert`ing one; EyeProlog's own library and extension predicates are not standard built-ins, so ISO does not protect them. They stay redefinable when they reach the program implicitly, through autoloading or a whole-module `use_module(library(L))`, but the redefinition is reported as a shadowing warning because it replaces the library predicate for all user code in the program. Naming a predicate in an explicit import list, `use_module(library(L), [Name/Arity])`, and then supplying clauses for it is a contradiction and reports `permission_error(modify, static_procedure)`. Bundled library modules keep resolving their own internal calls, so a user redefinition never changes library behaviour. Trealla and Scryer also warn and continue for the whole-module case rather than raising an error, so the warning tiers match those systems. Corpus: `error/iso/consult_redefines_builtin`. |
27
27
 
28
28
  The strict release test `closes ISO 7.4 Prolog-text preparation and directive
29
29
  rows` exercises the cross-text operator/character/flag state, include and
@@ -1 +1 @@
1
- parse line 1: bad term
1
+ parse line 1: unknown directive q/1; to run a goal at load time use :- initialization(Goal).