eyeprolog 1.3.48 → 1.3.49

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.48",
6
+ "version": "1.3.49",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/iso.js CHANGED
@@ -545,7 +545,7 @@ function* clauseBuiltin({ solver, goal, env }) {
545
545
  const indicator = compound('/', [atom(head.name), numberTerm(head.arity)]);
546
546
  const group = solver.program.findGroup(head.name, head.arity, head.module ?? goal.module ?? 'user');
547
547
  // ISO 8.8.1.3 places private-procedure access before Body callability.
548
- if (solver.registry.get(head.name, head.arity) || isGrammarRuleProcedure(solver, head) ||
548
+ if (isProcessorStaticProcedure(solver, head) ||
549
549
  (solver.isoStrict && group && !group.dynamic)) {
550
550
  throw new PrologError('permission_error(access, private_procedure)', indicator);
551
551
  }
@@ -589,9 +589,19 @@ function isGrammarRuleProcedure(solver, head) {
589
589
  return !solver.isoStrict && head.name === '-->' && head.arity === 2;
590
590
  }
591
591
 
592
+ function isProcessorStaticProcedure(solver, head) {
593
+ // Conjunction is an ISO control construct (7.5/Table 9) but is executed
594
+ // directly by the solver rather than through the builtin registry. Keep it
595
+ // in the same protected static/private family as the registered Part 1
596
+ // control constructs for runtime database operations.
597
+ return Boolean(solver.registry.get(head.name, head.arity)) ||
598
+ isGrammarRuleProcedure(solver, head) ||
599
+ (solver.isoStrict && head.name === ',' && head.arity === 2);
600
+ }
601
+
592
602
  function assertModifiable(solver, head, module = 'user') {
593
603
  const group = solver.program.findGroup(head.name, head.arity, head.module ?? module);
594
- if (solver.registry.get(head.name, head.arity) || isGrammarRuleProcedure(solver, head) || (group && !group.dynamic)) {
604
+ if (isProcessorStaticProcedure(solver, head) || (group && !group.dynamic)) {
595
605
  throw new PrologError('permission_error(modify, static_procedure)', procedureIndicator(head));
596
606
  }
597
607
  }
@@ -618,7 +628,7 @@ function* retractBuiltin({ solver, goal, env }) {
618
628
  const parts = clauseParts(goal.args[0], env);
619
629
  requireClauseHead(parts.head);
620
630
  const group = solver.program.findGroup(parts.head.name, parts.head.arity, parts.head.module ?? goal.module ?? 'user');
621
- if (solver.registry.get(parts.head.name, parts.head.arity) || isGrammarRuleProcedure(solver, parts.head) || (group && !group.dynamic)) {
631
+ if (isProcessorStaticProcedure(solver, parts.head) || (group && !group.dynamic)) {
622
632
  throw new PrologError('permission_error(modify, static_procedure)', procedureIndicator(parts.head));
623
633
  }
624
634
  if (!group) return;
@@ -641,7 +651,7 @@ function* retractAllBuiltin({ solver, goal, env }) {
641
651
  const head = deref(goal.args[0], env);
642
652
  requireClauseHead(head);
643
653
  const group = solver.program.findGroup(head.name, head.arity, head.module ?? goal.module ?? 'user');
644
- if (solver.registry.get(head.name, head.arity) || isGrammarRuleProcedure(solver, head) || (group && !group.dynamic)) {
654
+ if (isProcessorStaticProcedure(solver, head) || (group && !group.dynamic)) {
645
655
  throw new PrologError('permission_error(modify, static_procedure)', procedureIndicator(head));
646
656
  }
647
657
  if (group) {
@@ -675,7 +685,7 @@ function* abolishBuiltin({ solver, goal, env }) {
675
685
  const target = predicateIndicatorParts(goal.args[0], env);
676
686
  const module = goal.module ?? 'user';
677
687
  const group = solver.program.findGroup(target.name, target.arity, module);
678
- if (solver.registry.get(target.name, target.arity) || isGrammarRuleProcedure(solver, target) || (group && !group.dynamic)) {
688
+ if (isProcessorStaticProcedure(solver, target) || (group && !group.dynamic)) {
679
689
  throw new PrologError('permission_error(modify, static_procedure)', target.indicator);
680
690
  }
681
691
  solver.program.abolishDynamicGroup(target.name, target.arity, module);
@@ -984,7 +994,7 @@ function* openBuiltin({ solver, goal, env }) {
984
994
  if (!['read', 'write', 'append'].includes(mode.name)) throw new PrologError('domain_error(io_mode)', mode);
985
995
  const options = goal.arity === 3 ? {} : openOptions(optionTerm, env);
986
996
  if (options.alias && solver.io.resolve(options.alias)) {
987
- throw new PrologError('permission_error(open, source_sink)', atom(options.alias));
997
+ throw new PrologError('permission_error(open, source_sink)', compound('alias', [atom(options.alias)]));
988
998
  }
989
999
  let stream;
990
1000
  try {
@@ -33,13 +33,13 @@ error-ordering alternative to an individual executable assertion.
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
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; 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. |
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, including runtime protection of the solver-native conjunction control construct `','/2`. 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. Strict mode now also pins direct-variable precedence, arithmetic operand type errors, float-only rounding conversions, zero-to-negative-power undefined errors, and the Part 1 mixed integer/float comparison conversion rule. Exceptional-value/error-precedence mapping remains to be exhaustively enumerated. |
40
- | 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, and Corrigendum 3 writer cases. The audit now also covers write-mode creation/truncation, append creation, repositioned overwrite, flushing, EOF actions, close/current-stream handling, strict stream-position terms, stream-property validation, stream-term requirements, and text-vs-binary permission errors. The full option/mode cross-product is still being mapped. |
40
+ | 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, and Corrigendum 3 writer cases. The audit now also covers write-mode creation/truncation, append creation, repositioned overwrite, flushing, EOF actions, close/current-stream handling, strict stream-position terms, stream-property validation, stream-term requirements, text-vs-binary permission errors, and the prescribed `alias(A)` culprit for `open/4` alias collisions. The full option/mode cross-product is still being mapped. |
41
41
  | 7.11 — flags | covered | The complete Part 1 flag set, selected defaults, standard value domains, changeability, `current_prolog_flag/2`, and `set_prolog_flag/2` error behavior have dedicated strict tests. EyeProlog selects `bounded=false` and `integer_rounding_function=toward_zero`; valid alternative values of those fixed flags reach `permission_error(modify,flag,...)`, while `max_integer` and `min_integer` have no current value. Strict mode excludes the EyeProlog `occurs_check` extension. |
42
- | 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax families and focused error cases. Exact ISO 8.14.1-8.14.4 error precedence is covered for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`; the continuing audit has corrected additional 8.11-8.13 stream/character/byte errors, Corrigendum 2 `keysort/2` errors, atomic-conversion list-shape precedence, `arg/3`, `atom_concat/3`, `sub_atom/5`, `char_conversion/2`, and arithmetic culprit/precedence reporting. A complete one-row-per-prescribed-error ordering map remains open. |
42
+ | 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax families and focused error cases. Exact ISO 8.14.1-8.14.4 error precedence is covered for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`; the continuing audit has corrected additional 8.11-8.13 stream/character/byte errors (including the complete `alias(A)` culprit for `open/4` alias collisions), Corrigendum 2 `keysort/2` errors, atomic-conversion list-shape precedence, `arg/3`, `atom_concat/3`, `sub_atom/5`, `char_conversion/2`, runtime control-construct database protection, and arithmetic culprit/precedence reporting. A complete one-row-per-prescribed-error ordering map remains open. |
43
43
  | 8.2-8.17 — built-in predicates | audit | Predicate-family coverage is mapped in ISO-MATRIX.md; Corrigendum 2 additions (`subsumes_term/2`, `term_variables/2`, `call/2..8`, `false/0`) are in the strict registry. The audit now includes corrected `keysort/2` variable/non-pair errors, stricter 8.11-8.13 stream and character/byte modes/errors, the 8.14 option/error-order subfamily, closed 8.17 flags, and additional prescribed precedence for `arg/3`, `atom_concat/3`, `sub_atom/5`, number/atom list conversions, and `char_conversion/2`. The remaining mode/error matrix is not yet one-row-per-standard-row. |
44
44
  | Clause 9 — evaluable functors | audit | Integer/float/rounding/transcendental/bitwise suites and corrigendum cases. Strict mode excludes the EyeProlog-only evaluable atom `e`, retains Corrigendum 2 arithmetic additions, reports unknown zero-arity evaluables with the required `F/0` culprit shape, enforces the Part 1 numeric/integer operand errors and float-only rounding modes, and uses the Part 1 integer-to-float conversion rule for mixed arithmetic comparisons. Normal mode retains EyeProlog's exact mixed-type comparison extension. Host floating-point representation choices remain documented implementation-defined behavior; exhaustive exceptional-value precedence remains open. |
45
45
  | Corrigendum 1 | covered | Double-quoted atom/operator-priority corrections have dedicated cases. |
@@ -13,8 +13,8 @@ compliance audit and the remaining work before a full conformance claim.
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
14
  | 8.2-8.5 term predicates | unification, Corrigendum 2 tests, comparison, sorting, creation and decomposition | `corrigenda_term_predicates`, `corrigenda_sort_keysort`, `logtalk_arg_unification`, `logtalk_univ`, associated error cases, Corrigendum 2 `keysort/2` variable/non-pair errors, and strict `arg/3`, `functor/3` / `=../2` error-order plus 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`, 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 |
16
+ | 8.8-8.10 database and solutions | logical update view, dynamic mutation, all-solutions grouping | `dynamic_database`, `trealla_logical_update_view`, `corrigenda_retractall`, `grouped_solutions_and_clauses`, plus strict clause/database error and all-solutions precedence checks, including runtime static/private protection for the solver-native conjunction control construct |
17
+ | 8.11-8.14 streams and term I/O | text/binary streams, properties, units, read/write options and operators | `streams_and_term_io`, `operators`, Corrigendum 3 option cases, stream error cases, strict 8.14.1-8.14.4 option/error-order tests, Corrigendum 3 variable metadata/write naming checks, and issue #65 checks for stream-term/property validation, text-vs-binary permissions, EOF/flush/position, open/close semantics, and the exact `alias(A)` collision culprit |
18
18
  | 8.15 logic and control | negation, once, repeat, `call/2` through `call/8`, `false/0` | `logtalk_once`, `corrigenda_call_closure`, `false_builtin`, plus strict Corrigendum 2 resulting-goal `max_arity` error coverage |
19
19
  | 8.16 atomic processing | atoms, characters, codes and number conversion with prescribed errors | `atomic_term_processing`, focused forward/reverse cases, parenthesized-number rejection, Logtalk-derived error cases, strict Corrigendum 2 partial/improper-list precedence for atom/number conversions, and prescribed `atom_concat/3` / `sub_atom/5` ordering checks |
20
20
  | 8.17 flags and hooks | complete required Part 1 flag set, selected defaults, standard value domains/changeability, halt and character conversion | `exceptions_and_flags`, `remaining_builtins_and_directives`, flag error cases, strict `char_conversion/2` precedence, and the complete strict flag audit in `run-iso-strict.mjs` |
@@ -183,6 +183,15 @@ export function runIsoStrict(reporter = new TestReporter()) {
183
183
  includes(ignoredNonVariable.stdout, 'x', 'non-variable right side is permitted and ignored');
184
184
  });
185
185
 
186
+ reporter.test('reports the complete alias option in open/4 alias collisions', () => {
187
+ const error = capture(() => run('', {
188
+ isoStrict: true,
189
+ goal: 'open(dummy,write,_,[alias(user_input)])',
190
+ }));
191
+ equal(error.formal, 'permission_error(open, source_sink)', 'formal error');
192
+ includes(error.message, 'alias(user_input)', 'alias option culprit');
193
+ });
194
+
186
195
  reporter.test('follows the ISO 8.14.1.3 read_term/3 error order', () => {
187
196
  equal(capture(() => run('', { isoStrict: true, goal: 'read_term(f(a),_,[X])' })).formal,
188
197
  'instantiation_error', 'partial/variable option before stream domain');
@@ -376,6 +385,21 @@ export function runIsoStrict(reporter = new TestReporter()) {
376
385
  equal(answers.length, 1, 'answer count');
377
386
  });
378
387
 
388
+ reporter.test('protects conjunction as an ISO static/private control construct at runtime', () => {
389
+ for (const goal of [
390
+ "asserta(','(a,b))",
391
+ "assertz(','(a,b))",
392
+ "retract(','(a,b))",
393
+ "retractall(','(a,b))",
394
+ "abolish('/'(',',2))",
395
+ ]) {
396
+ equal(capture(() => run('', { isoStrict: true, goal })).formal,
397
+ 'permission_error(modify, static_procedure)', goal);
398
+ }
399
+ equal(capture(() => run('', { isoStrict: true, goal: "clause(','(a,b),_)" })).formal,
400
+ 'permission_error(access, private_procedure)', 'clause/2 conjunction access');
401
+ });
402
+
379
403
  reporter.test('covers ISO database predicate errors and empty-procedure lifetime', () => {
380
404
  const errors = [
381
405
  ['current_predicate(4)', '', 'type_error(predicate_indicator)'],