eyeling 1.5.28 → 1.5.30

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.
@@ -1989,6 +1989,156 @@
1989
1989
 
1990
1990
  :test-greaterThan-1 :status :pass .
1991
1991
 
1992
+ # ----------------------------------------------------------------------
1993
+ # Proof for derived triple:
1994
+ # :test-integerQuotient-1 a :MathBuiltinTest .
1995
+ # It holds because the following instance of the rule body is provable:
1996
+ # (10 3) math:integerQuotient 3 .
1997
+ # 3 math:equalTo 3 .
1998
+ # via the schematic forward rule:
1999
+ # {
2000
+ # (10 3) math:integerQuotient ?q .
2001
+ # ?q math:equalTo 3 .
2002
+ # } => {
2003
+ # :test-integerQuotient-1 a :MathBuiltinTest .
2004
+ # :test-integerQuotient-1 :builtin math:integerQuotient .
2005
+ # :test-integerQuotient-1 :input (10 3) .
2006
+ # :test-integerQuotient-1 :expected 3 .
2007
+ # :test-integerQuotient-1 :actual ?q .
2008
+ # :test-integerQuotient-1 :status :pass .
2009
+ # } .
2010
+ # with substitution (on rule variables):
2011
+ # ?q = 3
2012
+ # Therefore the derived triple above is entailed by the rules and facts.
2013
+ # ----------------------------------------------------------------------
2014
+
2015
+ :test-integerQuotient-1 a :MathBuiltinTest .
2016
+
2017
+ # ----------------------------------------------------------------------
2018
+ # Proof for derived triple:
2019
+ # :test-integerQuotient-1 :builtin math:integerQuotient .
2020
+ # It holds because the following instance of the rule body is provable:
2021
+ # (10 3) math:integerQuotient 3 .
2022
+ # 3 math:equalTo 3 .
2023
+ # via the schematic forward rule:
2024
+ # {
2025
+ # (10 3) math:integerQuotient ?q .
2026
+ # ?q math:equalTo 3 .
2027
+ # } => {
2028
+ # :test-integerQuotient-1 a :MathBuiltinTest .
2029
+ # :test-integerQuotient-1 :builtin math:integerQuotient .
2030
+ # :test-integerQuotient-1 :input (10 3) .
2031
+ # :test-integerQuotient-1 :expected 3 .
2032
+ # :test-integerQuotient-1 :actual ?q .
2033
+ # :test-integerQuotient-1 :status :pass .
2034
+ # } .
2035
+ # with substitution (on rule variables):
2036
+ # ?q = 3
2037
+ # Therefore the derived triple above is entailed by the rules and facts.
2038
+ # ----------------------------------------------------------------------
2039
+
2040
+ :test-integerQuotient-1 :builtin math:integerQuotient .
2041
+
2042
+ # ----------------------------------------------------------------------
2043
+ # Proof for derived triple:
2044
+ # :test-integerQuotient-1 :input (10 3) .
2045
+ # It holds because the following instance of the rule body is provable:
2046
+ # (10 3) math:integerQuotient 3 .
2047
+ # 3 math:equalTo 3 .
2048
+ # via the schematic forward rule:
2049
+ # {
2050
+ # (10 3) math:integerQuotient ?q .
2051
+ # ?q math:equalTo 3 .
2052
+ # } => {
2053
+ # :test-integerQuotient-1 a :MathBuiltinTest .
2054
+ # :test-integerQuotient-1 :builtin math:integerQuotient .
2055
+ # :test-integerQuotient-1 :input (10 3) .
2056
+ # :test-integerQuotient-1 :expected 3 .
2057
+ # :test-integerQuotient-1 :actual ?q .
2058
+ # :test-integerQuotient-1 :status :pass .
2059
+ # } .
2060
+ # with substitution (on rule variables):
2061
+ # ?q = 3
2062
+ # Therefore the derived triple above is entailed by the rules and facts.
2063
+ # ----------------------------------------------------------------------
2064
+
2065
+ :test-integerQuotient-1 :input (10 3) .
2066
+
2067
+ # ----------------------------------------------------------------------
2068
+ # Proof for derived triple:
2069
+ # :test-integerQuotient-1 :expected 3 .
2070
+ # It holds because the following instance of the rule body is provable:
2071
+ # (10 3) math:integerQuotient 3 .
2072
+ # 3 math:equalTo 3 .
2073
+ # via the schematic forward rule:
2074
+ # {
2075
+ # (10 3) math:integerQuotient ?q .
2076
+ # ?q math:equalTo 3 .
2077
+ # } => {
2078
+ # :test-integerQuotient-1 a :MathBuiltinTest .
2079
+ # :test-integerQuotient-1 :builtin math:integerQuotient .
2080
+ # :test-integerQuotient-1 :input (10 3) .
2081
+ # :test-integerQuotient-1 :expected 3 .
2082
+ # :test-integerQuotient-1 :actual ?q .
2083
+ # :test-integerQuotient-1 :status :pass .
2084
+ # } .
2085
+ # with substitution (on rule variables):
2086
+ # ?q = 3
2087
+ # Therefore the derived triple above is entailed by the rules and facts.
2088
+ # ----------------------------------------------------------------------
2089
+
2090
+ :test-integerQuotient-1 :expected 3 .
2091
+
2092
+ # ----------------------------------------------------------------------
2093
+ # Proof for derived triple:
2094
+ # :test-integerQuotient-1 :actual 3 .
2095
+ # It holds because the following instance of the rule body is provable:
2096
+ # (10 3) math:integerQuotient 3 .
2097
+ # 3 math:equalTo 3 .
2098
+ # via the schematic forward rule:
2099
+ # {
2100
+ # (10 3) math:integerQuotient ?q .
2101
+ # ?q math:equalTo 3 .
2102
+ # } => {
2103
+ # :test-integerQuotient-1 a :MathBuiltinTest .
2104
+ # :test-integerQuotient-1 :builtin math:integerQuotient .
2105
+ # :test-integerQuotient-1 :input (10 3) .
2106
+ # :test-integerQuotient-1 :expected 3 .
2107
+ # :test-integerQuotient-1 :actual ?q .
2108
+ # :test-integerQuotient-1 :status :pass .
2109
+ # } .
2110
+ # with substitution (on rule variables):
2111
+ # ?q = 3
2112
+ # Therefore the derived triple above is entailed by the rules and facts.
2113
+ # ----------------------------------------------------------------------
2114
+
2115
+ :test-integerQuotient-1 :actual 3 .
2116
+
2117
+ # ----------------------------------------------------------------------
2118
+ # Proof for derived triple:
2119
+ # :test-integerQuotient-1 :status :pass .
2120
+ # It holds because the following instance of the rule body is provable:
2121
+ # (10 3) math:integerQuotient 3 .
2122
+ # 3 math:equalTo 3 .
2123
+ # via the schematic forward rule:
2124
+ # {
2125
+ # (10 3) math:integerQuotient ?q .
2126
+ # ?q math:equalTo 3 .
2127
+ # } => {
2128
+ # :test-integerQuotient-1 a :MathBuiltinTest .
2129
+ # :test-integerQuotient-1 :builtin math:integerQuotient .
2130
+ # :test-integerQuotient-1 :input (10 3) .
2131
+ # :test-integerQuotient-1 :expected 3 .
2132
+ # :test-integerQuotient-1 :actual ?q .
2133
+ # :test-integerQuotient-1 :status :pass .
2134
+ # } .
2135
+ # with substitution (on rule variables):
2136
+ # ?q = 3
2137
+ # Therefore the derived triple above is entailed by the rules and facts.
2138
+ # ----------------------------------------------------------------------
2139
+
2140
+ :test-integerQuotient-1 :status :pass .
2141
+
1992
2142
  # ----------------------------------------------------------------------
1993
2143
  # Proof for derived triple:
1994
2144
  # :test-lessThan-1 a :MathBuiltinTest .
package/eyeling.js CHANGED
@@ -257,15 +257,21 @@ function lex(inputText) {
257
257
  continue;
258
258
  }
259
259
 
260
- // 4) Datatype operator ^^
260
+ // 4) Path + datatype operators: !, ^, ^^
261
+ if (c === "!") {
262
+ tokens.push(new Token("OpPathFwd"));
263
+ i += 1;
264
+ continue;
265
+ }
261
266
  if (c === "^") {
262
267
  if (peek(1) === "^") {
263
268
  tokens.push(new Token("HatHat"));
264
269
  i += 2;
265
270
  continue;
266
- } else {
267
- throw new Error("Unexpected '^' (did you mean ^^?)");
268
271
  }
272
+ tokens.push(new Token("OpPathRev"));
273
+ i += 1;
274
+ continue;
269
275
  }
270
276
 
271
277
  // 5) Single-character punctuation
@@ -734,6 +740,28 @@ class Parser {
734
740
  }
735
741
 
736
742
  parseTerm() {
743
+ let t = this.parsePathItem();
744
+
745
+ while (this.peek().typ === "OpPathFwd" || this.peek().typ === "OpPathRev") {
746
+ const dir = this.next().typ; // OpPathFwd | OpPathRev
747
+ const pred = this.parsePathItem();
748
+
749
+ this.blankCounter += 1;
750
+ const bn = new Blank(`_:b${this.blankCounter}`);
751
+
752
+ this.pendingTriples.push(
753
+ dir === "OpPathFwd"
754
+ ? new Triple(t, pred, bn)
755
+ : new Triple(bn, pred, t)
756
+ );
757
+
758
+ t = bn;
759
+ }
760
+
761
+ return t;
762
+ }
763
+
764
+ parsePathItem() {
737
765
  const tok = this.next();
738
766
  const typ = tok.typ;
739
767
  const val = tok.value;
@@ -916,20 +944,37 @@ class Parser {
916
944
 
917
945
  parsePredicateObjectList(subject) {
918
946
  const out = [];
947
+
948
+ // If the SUBJECT was a path, emit its helper triples first
949
+ if (this.pendingTriples.length > 0) {
950
+ out.push(...this.pendingTriples);
951
+ this.pendingTriples = [];
952
+ }
953
+
919
954
  while (true) {
920
955
  let verb;
921
956
  let invert = false;
957
+
922
958
  if (this.peek().typ === "Ident" && (this.peek().value || "") === "a") {
923
959
  this.next();
924
960
  verb = new Iri(RDF_NS + "type");
925
961
  } else if (this.peek().typ === "OpPredInvert") {
926
- this.next(); // consume "<-"
927
- verb = this.parseTerm(); // predicate expression
962
+ this.next(); // "<-"
963
+ verb = this.parseTerm();
928
964
  invert = true;
929
965
  } else {
930
966
  verb = this.parseTerm();
931
967
  }
968
+
932
969
  const objects = this.parseObjectList();
970
+
971
+ // If VERB or OBJECTS contained paths, their helper triples must come
972
+ // before the triples that consume the path results (Easter depends on this).
973
+ if (this.pendingTriples.length > 0) {
974
+ out.push(...this.pendingTriples);
975
+ this.pendingTriples = [];
976
+ }
977
+
933
978
  for (const o of objects) {
934
979
  out.push(new Triple(invert ? o : subject, verb, invert ? subject : o));
935
980
  }
@@ -942,11 +987,6 @@ class Parser {
942
987
  break;
943
988
  }
944
989
 
945
- if (this.pendingTriples.length > 0) {
946
- out.push(...this.pendingTriples);
947
- this.pendingTriples = [];
948
- }
949
-
950
990
  return out;
951
991
  }
952
992
 
@@ -2328,6 +2368,47 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
2328
2368
  }
2329
2369
  }
2330
2370
 
2371
+ // math:integerQuotient
2372
+ // Schema: ( $a $b ) math:integerQuotient $q
2373
+ // Cwm: divide first integer by second integer, ignoring remainder. :contentReference[oaicite:1]{index=1}
2374
+ if (g.p instanceof Iri && g.p.value === MATH_NS + "integerQuotient") {
2375
+ if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
2376
+ const [a0, b0] = g.s.elems;
2377
+
2378
+ // Prefer exact integer division using BigInt when possible
2379
+ const ai = parseIntLiteral(a0);
2380
+ const bi = parseIntLiteral(b0);
2381
+ if (ai !== null && bi !== null) {
2382
+ if (bi === 0n) return [];
2383
+ const q = ai / bi; // BigInt division truncates toward zero
2384
+ const lit = new Literal(q.toString());
2385
+ if (g.o instanceof Var) {
2386
+ const s2 = { ...subst };
2387
+ s2[g.o.name] = lit;
2388
+ return [s2];
2389
+ }
2390
+ const s2 = unifyTerm(g.o, lit, subst);
2391
+ return s2 !== null ? [s2] : [];
2392
+ }
2393
+
2394
+ // Fallback: allow Number literals that still represent integers
2395
+ const a = parseNum(a0);
2396
+ const b = parseNum(b0);
2397
+ if (a === null || b === null) return [];
2398
+ if (!Number.isFinite(a) || !Number.isFinite(b) || b === 0) return [];
2399
+ if (!Number.isInteger(a) || !Number.isInteger(b)) return [];
2400
+
2401
+ const q = Math.trunc(a / b);
2402
+ const lit = new Literal(String(q));
2403
+ if (g.o instanceof Var) {
2404
+ const s2 = { ...subst };
2405
+ s2[g.o.name] = lit;
2406
+ return [s2];
2407
+ }
2408
+ const s2 = unifyTerm(g.o, lit, subst);
2409
+ return s2 !== null ? [s2] : [];
2410
+ }
2411
+
2331
2412
  // math:exponentiation
2332
2413
  if (g.p instanceof Iri && g.p.value === MATH_NS + "exponentiation") {
2333
2414
  if (g.s instanceof ListTerm && g.s.elems.length === 2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.5.28",
3
+ "version": "1.5.30",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/test/api.test.js CHANGED
@@ -650,6 +650,28 @@ ${U('a')} <-${U('p')} ${U('b')}.`,
650
650
  { ${U('o')} ${U('r')} ?x } => { ?x ${U('q')} ${U('k')} }.`,
651
651
  expect: [new RegExp(`_:b1\\s+<${EX}q>\\s+<${EX}k>\\s*\\.`)],
652
652
  },
653
+
654
+ { name: '46 syntax: N3 resource paths (! / ^) expand to blank-node triples (forward chain)',
655
+ opt: { proofComments: false },
656
+ input: ` ${U('joe')}!${U('hasAddress')}!${U('hasCity')} ${U('name')} "Metropolis".
657
+ { ${U('joe')} ${U('hasAddress')} ?a } => { ?a ${U('q')} "addr" }.
658
+ { ?a ${U('hasCity')} ?c } => { ?c ${U('q')} "city" }.
659
+ `,
660
+ expect: [
661
+ new RegExp(`_:b1\\s+<${EX}q>\\s+"addr"\\s*\\.`),
662
+ new RegExp(`_:b2\\s+<${EX}q>\\s+"city"\\s*\\.`),
663
+ ],
664
+ },
665
+
666
+ { name: '47 syntax: N3 resource paths support reverse steps (^) in the chain',
667
+ opt: { proofComments: false },
668
+ input: ` ${U('joe')}!${U('hasMother')}^${U('hasMother')} ${U('knows')} ${U('someone')}.
669
+ { ?sib ${U('hasMother')} ?mom. ${U('joe')} ${U('hasMother')} ?mom } => { ?sib ${U('q')} ${U('joe')} }.
670
+ `,
671
+ expect: [
672
+ new RegExp(`_:b2\\s+<${EX}q>\\s+<${EX}joe>\\s*\\.`),
673
+ ],
674
+ },
653
675
  ];
654
676
 
655
677
  let passed = 0;