eyeprolog 1.5.104 → 1.5.106

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.5.104",
6
+ "version": "1.5.106",
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
@@ -674,21 +674,29 @@ function* termVariablesBuiltin({ goal, env }) {
674
674
  }
675
675
 
676
676
  function validPredicateIndicator(term) {
677
- if (term.type !== COMPOUND || term.name !== '/' || term.arity !== 2) return false;
678
- const [name, arity] = term.args;
679
- // Name/Arity denotes a predicate indicator only when some instance of it
680
- // could be one: Name an atom, Arity a non-negative integer. The same
681
- // shared variable in both positions (current_predicate(X/X), issue #115)
682
- // looks valid argument-by-argument -- each position individually accepts
683
- // an unbound variable -- but no instantiation can satisfy both at once,
684
- // since binding that one variable to an atom makes Arity an atom too, not
685
- // an integer. stc#79 treats this as no valid instance existing at all,
686
- // the same as a bare atom argument, so it must raise
687
- // type_error(predicate_indicator, _) rather than just finding no matches.
688
- if (name.type === VAR && arity.type === VAR && name.name === arity.name) return false;
689
- return (name.type === VAR || name.type === ATOM) &&
690
- (arity.type === VAR ||
691
- (arity.type === NUMBER && isDecimalInteger(arity.name) && BigInt(arity.name) >= 0n));
677
+ // ISO 13211-1 8.8.2.3's sole error condition is "PI is neither a variable
678
+ // nor a predicate indicator -> type_error(predicate_indicator, PI)", and
679
+ // 8.8.2.4's own worked example -- current_predicate(Name/1) succeeds,
680
+ // backtracking through every matching Name -- shows that a compound whose
681
+ // Name is merely a variable already counts as "a predicate indicator" for
682
+ // this check, not just one whose Name and Arity are already bound to an
683
+ // atom and a non-negative integer as 7.1.6.6 defines the type. So each
684
+ // position is checked independently here, not "could some single
685
+ // instantiation satisfy both at once": current_predicate(X/X) (issue #115)
686
+ // passes this check exactly like current_predicate(X/Y) does, and is left
687
+ // to the ordinary group search below (8.8.2.1 a-c), which unifies
688
+ // goal.args[0] (still X/X, the two positions aliased) against each
689
+ // candidate Name/Arity in turn. Name and Arity are never the same type, so
690
+ // that unification always fails for every candidate and the goal simply
691
+ // finds no solutions -- 8.8.2.1's own procedure gives no other outcome for
692
+ // an empty candidate set than "the goal fails" (step c), with no separate
693
+ // provision for an argument that turned out unable to ever unify. A
694
+ // predicate_indicator type error remains for a term that cannot look like
695
+ // a Name/Arity pair at all (a bare atom, a negative arity, ...).
696
+ return term.type === COMPOUND && term.name === '/' && term.arity === 2 &&
697
+ (term.args[0].type === VAR || term.args[0].type === ATOM) &&
698
+ (term.args[1].type === VAR ||
699
+ (term.args[1].type === NUMBER && isDecimalInteger(term.args[1].name) && BigInt(term.args[1].name) >= 0n));
692
700
  }
693
701
 
694
702
  const currentPredicateBuiltin = pendingBuiltin(currentPredicateSolutions);
@@ -1541,12 +1541,6 @@ export function runIsoStrict(reporter = new TestReporter()) {
1541
1541
  ['clause(X,_)', 'instantiation_error', 'clause/2 variable head'],
1542
1542
  ['clause(4,_)', 'type_error(callable)', 'clause/2 head type'],
1543
1543
  ['current_predicate(4)', 'type_error(predicate_indicator)', 'current_predicate/1 indicator type'],
1544
- // The same variable filling both Name and Arity (issue #115, stc#79)
1545
- // looks valid position-by-position -- each alone accepts an unbound
1546
- // variable -- but no instantiation can satisfy both at once, since
1547
- // binding it to an atom makes Arity an atom too, not an integer.
1548
- ['current_predicate(X/X)', 'type_error(predicate_indicator)', 'current_predicate/1 aliased name/arity variable'],
1549
- ['X = Y, current_predicate(X/Y)', 'type_error(predicate_indicator)', 'current_predicate/1 previously unified name/arity variables'],
1550
1544
  ['asserta(_)', 'instantiation_error', 'asserta/1 variable head'],
1551
1545
  ['asserta(4)', 'type_error(callable)', 'asserta/1 head type'],
1552
1546
  ['asserta((p:-4))', 'type_error(callable)', 'asserta/1 body conversion'],
@@ -1597,6 +1591,37 @@ export function runIsoStrict(reporter = new TestReporter()) {
1597
1591
 
1598
1592
  const current = run('p(a).\nq.\n', { isoStrict: true, goal: 'current_predicate(p/1)' });
1599
1593
  equal(current.stats.completed_goal_lists, 1, 'current_predicate/1 finds a user procedure');
1594
+
1595
+ // ISO 13211-1 8.8.2.4's own worked example: current_predicate(Name/1)
1596
+ // succeeds with an unbound Name, backtracking through every arity-1
1597
+ // predicate. A compound with an unbound component already counts as "a
1598
+ // predicate indicator" for 8.8.2.3's error check, not only one whose
1599
+ // Name and Arity are already bound to an atom and a non-negative
1600
+ // integer as 7.1.6.6 defines the type.
1601
+ equal(
1602
+ run('elk(a).\ninsect(b).\n', { isoStrict: true, goal: 'current_predicate(Name/1)' }).stats.completed_goal_lists,
1603
+ 2,
1604
+ 'current_predicate/1 enumerates every matching Name with Arity fixed and Name unbound',
1605
+ );
1606
+
1607
+ // The same variable filling both Name and Arity (issue #115) looks
1608
+ // valid position-by-position by that same per-component check -- each
1609
+ // position alone accepts an unbound variable, and current_predicate/1
1610
+ // must support both fully unbound (current_predicate(X/Y), a generator,
1611
+ // just demonstrated above) -- but no instantiation can satisfy both
1612
+ // positions of one shared variable at once, since binding it to an atom
1613
+ // makes Arity an atom too, not an integer. 8.8.2.3 has only the one
1614
+ // error condition above, and 8.8.2.1's procedure gives no outcome for an
1615
+ // empty candidate set (step a-c) besides "the goal fails" (step c) --
1616
+ // there is no separate provision for an argument that turned out unable
1617
+ // to ever unify with anything. current_predicate/1 follows that: it
1618
+ // fails rather than raising an error.
1619
+ for (const [goal, label] of [
1620
+ ['current_predicate(X/X)', 'current_predicate/1 aliased name/arity variable fails'],
1621
+ ['X = Y, current_predicate(X/Y)', 'current_predicate/1 previously unified name/arity variables fails'],
1622
+ ]) {
1623
+ equal(run('', { isoStrict: true, goal }).stats.completed_goal_lists, 0, label);
1624
+ }
1600
1625
  });
1601
1626
 
1602
1627
  reporter.test('preserves 7.6.2 source-body conversion identity for clause/2 and retract/1', () => {