eyeling 1.5.31 → 1.5.33

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.
@@ -1,5 +1,6 @@
1
1
  @prefix : <http://example.org/math-tests#> .
2
2
  @prefix math: <http://www.w3.org/2000/10/swap/math#> .
3
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
3
4
 
4
5
  # ----------------------------------------------------------------------
5
6
  # Proof for derived triple:
@@ -3431,16 +3432,16 @@
3431
3432
  # Proof for derived triple:
3432
3433
  # :test-rounded-1 a :MathBuiltinTest .
3433
3434
  # It holds because the following instance of the rule body is provable:
3434
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded 3 .
3435
+ # "3.3"^^xsd:double math:rounded 3 .
3435
3436
  # 3 math:equalTo 3 .
3436
3437
  # via the schematic forward rule:
3437
3438
  # {
3438
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded ?r .
3439
+ # "3.3"^^xsd:double math:rounded ?r .
3439
3440
  # ?r math:equalTo 3 .
3440
3441
  # } => {
3441
3442
  # :test-rounded-1 a :MathBuiltinTest .
3442
3443
  # :test-rounded-1 :builtin math:rounded .
3443
- # :test-rounded-1 :input "3.3"^^<http://www.w3.org/2001/XMLSchema#double> .
3444
+ # :test-rounded-1 :input "3.3"^^xsd:double .
3444
3445
  # :test-rounded-1 :expected 3 .
3445
3446
  # :test-rounded-1 :actual ?r .
3446
3447
  # :test-rounded-1 :status :pass .
@@ -3456,16 +3457,16 @@
3456
3457
  # Proof for derived triple:
3457
3458
  # :test-rounded-1 :builtin math:rounded .
3458
3459
  # It holds because the following instance of the rule body is provable:
3459
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded 3 .
3460
+ # "3.3"^^xsd:double math:rounded 3 .
3460
3461
  # 3 math:equalTo 3 .
3461
3462
  # via the schematic forward rule:
3462
3463
  # {
3463
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded ?r .
3464
+ # "3.3"^^xsd:double math:rounded ?r .
3464
3465
  # ?r math:equalTo 3 .
3465
3466
  # } => {
3466
3467
  # :test-rounded-1 a :MathBuiltinTest .
3467
3468
  # :test-rounded-1 :builtin math:rounded .
3468
- # :test-rounded-1 :input "3.3"^^<http://www.w3.org/2001/XMLSchema#double> .
3469
+ # :test-rounded-1 :input "3.3"^^xsd:double .
3469
3470
  # :test-rounded-1 :expected 3 .
3470
3471
  # :test-rounded-1 :actual ?r .
3471
3472
  # :test-rounded-1 :status :pass .
@@ -3479,18 +3480,18 @@
3479
3480
 
3480
3481
  # ----------------------------------------------------------------------
3481
3482
  # Proof for derived triple:
3482
- # :test-rounded-1 :input "3.3"^^<http://www.w3.org/2001/XMLSchema#double> .
3483
+ # :test-rounded-1 :input "3.3"^^xsd:double .
3483
3484
  # It holds because the following instance of the rule body is provable:
3484
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded 3 .
3485
+ # "3.3"^^xsd:double math:rounded 3 .
3485
3486
  # 3 math:equalTo 3 .
3486
3487
  # via the schematic forward rule:
3487
3488
  # {
3488
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded ?r .
3489
+ # "3.3"^^xsd:double math:rounded ?r .
3489
3490
  # ?r math:equalTo 3 .
3490
3491
  # } => {
3491
3492
  # :test-rounded-1 a :MathBuiltinTest .
3492
3493
  # :test-rounded-1 :builtin math:rounded .
3493
- # :test-rounded-1 :input "3.3"^^<http://www.w3.org/2001/XMLSchema#double> .
3494
+ # :test-rounded-1 :input "3.3"^^xsd:double .
3494
3495
  # :test-rounded-1 :expected 3 .
3495
3496
  # :test-rounded-1 :actual ?r .
3496
3497
  # :test-rounded-1 :status :pass .
@@ -3500,22 +3501,22 @@
3500
3501
  # Therefore the derived triple above is entailed by the rules and facts.
3501
3502
  # ----------------------------------------------------------------------
3502
3503
 
3503
- :test-rounded-1 :input "3.3"^^<http://www.w3.org/2001/XMLSchema#double> .
3504
+ :test-rounded-1 :input "3.3"^^xsd:double .
3504
3505
 
3505
3506
  # ----------------------------------------------------------------------
3506
3507
  # Proof for derived triple:
3507
3508
  # :test-rounded-1 :expected 3 .
3508
3509
  # It holds because the following instance of the rule body is provable:
3509
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded 3 .
3510
+ # "3.3"^^xsd:double math:rounded 3 .
3510
3511
  # 3 math:equalTo 3 .
3511
3512
  # via the schematic forward rule:
3512
3513
  # {
3513
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded ?r .
3514
+ # "3.3"^^xsd:double math:rounded ?r .
3514
3515
  # ?r math:equalTo 3 .
3515
3516
  # } => {
3516
3517
  # :test-rounded-1 a :MathBuiltinTest .
3517
3518
  # :test-rounded-1 :builtin math:rounded .
3518
- # :test-rounded-1 :input "3.3"^^<http://www.w3.org/2001/XMLSchema#double> .
3519
+ # :test-rounded-1 :input "3.3"^^xsd:double .
3519
3520
  # :test-rounded-1 :expected 3 .
3520
3521
  # :test-rounded-1 :actual ?r .
3521
3522
  # :test-rounded-1 :status :pass .
@@ -3531,16 +3532,16 @@
3531
3532
  # Proof for derived triple:
3532
3533
  # :test-rounded-1 :actual 3 .
3533
3534
  # It holds because the following instance of the rule body is provable:
3534
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded 3 .
3535
+ # "3.3"^^xsd:double math:rounded 3 .
3535
3536
  # 3 math:equalTo 3 .
3536
3537
  # via the schematic forward rule:
3537
3538
  # {
3538
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded ?r .
3539
+ # "3.3"^^xsd:double math:rounded ?r .
3539
3540
  # ?r math:equalTo 3 .
3540
3541
  # } => {
3541
3542
  # :test-rounded-1 a :MathBuiltinTest .
3542
3543
  # :test-rounded-1 :builtin math:rounded .
3543
- # :test-rounded-1 :input "3.3"^^<http://www.w3.org/2001/XMLSchema#double> .
3544
+ # :test-rounded-1 :input "3.3"^^xsd:double .
3544
3545
  # :test-rounded-1 :expected 3 .
3545
3546
  # :test-rounded-1 :actual ?r .
3546
3547
  # :test-rounded-1 :status :pass .
@@ -3556,16 +3557,16 @@
3556
3557
  # Proof for derived triple:
3557
3558
  # :test-rounded-1 :status :pass .
3558
3559
  # It holds because the following instance of the rule body is provable:
3559
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded 3 .
3560
+ # "3.3"^^xsd:double math:rounded 3 .
3560
3561
  # 3 math:equalTo 3 .
3561
3562
  # via the schematic forward rule:
3562
3563
  # {
3563
- # "3.3"^^<http://www.w3.org/2001/XMLSchema#double> math:rounded ?r .
3564
+ # "3.3"^^xsd:double math:rounded ?r .
3564
3565
  # ?r math:equalTo 3 .
3565
3566
  # } => {
3566
3567
  # :test-rounded-1 a :MathBuiltinTest .
3567
3568
  # :test-rounded-1 :builtin math:rounded .
3568
- # :test-rounded-1 :input "3.3"^^<http://www.w3.org/2001/XMLSchema#double> .
3569
+ # :test-rounded-1 :input "3.3"^^xsd:double .
3569
3570
  # :test-rounded-1 :expected 3 .
3570
3571
  # :test-rounded-1 :actual ?r .
3571
3572
  # :test-rounded-1 :status :pass .
package/eyeling.js CHANGED
@@ -34,11 +34,36 @@ const LIST_NS = "http://www.w3.org/2000/10/swap/list#";
34
34
  const LOG_NS = "http://www.w3.org/2000/10/swap/log#";
35
35
  const STRING_NS = "http://www.w3.org/2000/10/swap/string#";
36
36
  const SKOLEM_NS = "https://eyereasoner.github.io/.well-known/genid/";
37
+ const RDF_JSON_DT = RDF_NS + "JSON";
38
+
39
+ function isRdfJsonDatatype(dt) {
40
+ // dt comes from literalParts() and may be expanded or prefixed depending on parsing/printing.
41
+ return dt === null || dt === RDF_JSON_DT || dt === "rdf:JSON";
42
+ }
43
+
44
+ function termToJsonText(t) {
45
+ if (!(t instanceof Literal)) return null;
46
+ const [lex, dt] = literalParts(t.value);
47
+ if (!isRdfJsonDatatype(dt)) return null;
48
+ // decode escapes for short literals; long literals are taken verbatim
49
+ return termToJsStringDecoded(t);
50
+ }
51
+
52
+ function makeRdfJsonLiteral(jsonText) {
53
+ // Prefer a readable long literal when safe; fall back to short if needed.
54
+ if (!jsonText.includes('"""')) {
55
+ return new Literal('"""' + jsonText + '"""^^<' + RDF_JSON_DT + '>');
56
+ }
57
+ return new Literal(JSON.stringify(jsonText) + '^^<' + RDF_JSON_DT + '>');
58
+ }
37
59
 
38
60
  // For a single reasoning run, this maps a canonical representation
39
61
  // of the subject term in log:skolem to a Skolem IRI.
40
62
  const skolemCache = new Map();
41
63
 
64
+ // Cache for string:jsonPointer: jsonText -> { parsed: any|null, ptrCache: Map<string, Term|null> }
65
+ const jsonPointerCache = new Map();
66
+
42
67
  // Controls whether human-readable proof comments are printed.
43
68
  let proofCommentsEnabled = true;
44
69
 
@@ -549,6 +574,9 @@ function collectIrisInTerm(t) {
549
574
  const out = [];
550
575
  if (t instanceof Iri) {
551
576
  out.push(t.value);
577
+ } else if (t instanceof Literal) {
578
+ const [_lex, dt] = literalParts(t.value);
579
+ if (dt) out.push(dt); // so rdf/xsd prefixes are emitted when only used in ^^...
552
580
  } else if (t instanceof ListTerm) {
553
581
  for (const x of t.elems) out.push(...collectIrisInTerm(x));
554
582
  } else if (t instanceof OpenListTerm) {
@@ -1819,6 +1847,192 @@ function makeStringLiteral(str) {
1819
1847
  return new Literal(JSON.stringify(str));
1820
1848
  }
1821
1849
 
1850
+ function termToJsStringDecoded(t) {
1851
+ // Like termToJsString, but for short literals it *also* interprets escapes
1852
+ // (\" \n \uXXXX …) by attempting JSON.parse on the quoted lexical form.
1853
+ if (!(t instanceof Literal)) return null;
1854
+ const [lex, _dt] = literalParts(t.value);
1855
+
1856
+ // Long strings: """ ... """ are taken verbatim.
1857
+ if (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""')) {
1858
+ return lex.slice(3, -3);
1859
+ }
1860
+
1861
+ // Short strings: try to decode escapes (this makes "{\"a\":1}" usable too).
1862
+ if (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') {
1863
+ try { return JSON.parse(lex); } catch (e) { /* fall through */ }
1864
+ return stripQuotes(lex);
1865
+ }
1866
+
1867
+ return stripQuotes(lex);
1868
+ }
1869
+
1870
+ function _jsonPointerUnescape(seg) {
1871
+ // RFC6901: ~1 -> '/', ~0 -> '~'
1872
+ // Any other '~' escape is invalid.
1873
+ let out = "";
1874
+ for (let i = 0; i < seg.length; i++) {
1875
+ const c = seg[i];
1876
+ if (c !== "~") { out += c; continue; }
1877
+ if (i + 1 >= seg.length) return null;
1878
+ const n = seg[i + 1];
1879
+ if (n === "0") out += "~";
1880
+ else if (n === "1") out += "/";
1881
+ else return null;
1882
+ i++;
1883
+ }
1884
+ return out;
1885
+ }
1886
+
1887
+ function _jsonToTerm(v) {
1888
+ if (v === null) return makeStringLiteral("null");
1889
+ if (typeof v === "string") return makeStringLiteral(v);
1890
+ if (typeof v === "number") return new Literal(String(v));
1891
+ if (typeof v === "boolean") return new Literal(v ? "true" : "false");
1892
+ if (Array.isArray(v)) return new ListTerm(v.map(_jsonToTerm));
1893
+ if (typeof v === "object") return makeStringLiteral(JSON.stringify(v));
1894
+ return null;
1895
+ }
1896
+
1897
+ function _jsonPointerLookup(jsonText, pointer) {
1898
+ // Support URI fragment form "#/a/b" (percent-decoded) as well.
1899
+ let ptr = pointer;
1900
+ if (ptr.startsWith("#")) {
1901
+ try { ptr = decodeURIComponent(ptr.slice(1)); } catch (e) { return null; }
1902
+ }
1903
+
1904
+ // Cache per JSON document
1905
+ let entry = jsonPointerCache.get(jsonText);
1906
+ if (!entry) {
1907
+ let parsed = null;
1908
+ try { parsed = JSON.parse(jsonText); } catch (e) { parsed = null; }
1909
+ entry = { parsed, ptrCache: new Map() };
1910
+ jsonPointerCache.set(jsonText, entry);
1911
+ }
1912
+ if (entry.parsed === null) return null;
1913
+
1914
+ // Cache per pointer within this doc
1915
+ if (entry.ptrCache.has(ptr)) return entry.ptrCache.get(ptr);
1916
+
1917
+ let cur = entry.parsed;
1918
+
1919
+ if (ptr === "") {
1920
+ const t = _jsonToTerm(cur);
1921
+ entry.ptrCache.set(ptr, t);
1922
+ return t;
1923
+ }
1924
+ if (!ptr.startsWith("/")) { entry.ptrCache.set(ptr, null); return null; }
1925
+
1926
+ const parts = ptr.split("/").slice(1);
1927
+ for (const raw of parts) {
1928
+ const seg = _jsonPointerUnescape(raw);
1929
+ if (seg === null) { entry.ptrCache.set(ptr, null); return null; }
1930
+
1931
+ if (Array.isArray(cur)) {
1932
+ // JSON Pointer uses array indices as decimal strings
1933
+ if (!/^(0|[1-9]\d*)$/.test(seg)) { entry.ptrCache.set(ptr, null); return null; }
1934
+ const idx = Number(seg);
1935
+ if (!Number.isFinite(idx) || idx < 0 || idx >= cur.length) { entry.ptrCache.set(ptr, null); return null; }
1936
+ cur = cur[idx];
1937
+ continue;
1938
+ }
1939
+
1940
+ if (cur !== null && typeof cur === "object") {
1941
+ if (!Object.prototype.hasOwnProperty.call(cur, seg)) { entry.ptrCache.set(ptr, null); return null; }
1942
+ cur = cur[seg];
1943
+ continue;
1944
+ }
1945
+
1946
+ entry.ptrCache.set(ptr, null);
1947
+ return null;
1948
+ }
1949
+
1950
+ const out = _jsonToTerm(cur);
1951
+ entry.ptrCache.set(ptr, out);
1952
+ return out;
1953
+ }
1954
+
1955
+ function jsonPointerUnescape(seg) {
1956
+ // RFC6901: ~1 -> '/', ~0 -> '~'
1957
+ let out = "";
1958
+ for (let i = 0; i < seg.length; i++) {
1959
+ const c = seg[i];
1960
+ if (c !== "~") { out += c; continue; }
1961
+ if (i + 1 >= seg.length) return null;
1962
+ const n = seg[i + 1];
1963
+ if (n === "0") out += "~";
1964
+ else if (n === "1") out += "/";
1965
+ else return null;
1966
+ i++;
1967
+ }
1968
+ return out;
1969
+ }
1970
+
1971
+ function jsonToTerm(v) {
1972
+ if (v === null) return makeStringLiteral("null");
1973
+ if (typeof v === "string") return makeStringLiteral(v);
1974
+ if (typeof v === "number") return new Literal(String(v));
1975
+ if (typeof v === "boolean") return new Literal(v ? "true" : "false");
1976
+ if (Array.isArray(v)) return new ListTerm(v.map(jsonToTerm));
1977
+
1978
+ if (v && typeof v === "object") {
1979
+ return makeRdfJsonLiteral(JSON.stringify(v));
1980
+ }
1981
+ return null;
1982
+ }
1983
+
1984
+ function jsonPointerLookup(jsonText, pointer) {
1985
+ let ptr = pointer;
1986
+
1987
+ // Support URI fragment form "#/a/b"
1988
+ if (ptr.startsWith("#")) {
1989
+ try { ptr = decodeURIComponent(ptr.slice(1)); } catch { return null; }
1990
+ }
1991
+
1992
+ let entry = jsonPointerCache.get(jsonText);
1993
+ if (!entry) {
1994
+ let parsed = null;
1995
+ try { parsed = JSON.parse(jsonText); } catch { parsed = null; }
1996
+ entry = { parsed, ptrCache: new Map() };
1997
+ jsonPointerCache.set(jsonText, entry);
1998
+ }
1999
+ if (entry.parsed === null) return null;
2000
+
2001
+ if (entry.ptrCache.has(ptr)) return entry.ptrCache.get(ptr);
2002
+
2003
+ let cur = entry.parsed;
2004
+
2005
+ if (ptr === "") {
2006
+ const t = jsonToTerm(cur);
2007
+ entry.ptrCache.set(ptr, t);
2008
+ return t;
2009
+ }
2010
+ if (!ptr.startsWith("/")) { entry.ptrCache.set(ptr, null); return null; }
2011
+
2012
+ const parts = ptr.split("/").slice(1);
2013
+ for (const raw of parts) {
2014
+ const seg = jsonPointerUnescape(raw);
2015
+ if (seg === null) { entry.ptrCache.set(ptr, null); return null; }
2016
+
2017
+ if (Array.isArray(cur)) {
2018
+ if (!/^(0|[1-9]\d*)$/.test(seg)) { entry.ptrCache.set(ptr, null); return null; }
2019
+ const idx = Number(seg);
2020
+ if (idx < 0 || idx >= cur.length) { entry.ptrCache.set(ptr, null); return null; }
2021
+ cur = cur[idx];
2022
+ } else if (cur !== null && typeof cur === "object") {
2023
+ if (!Object.prototype.hasOwnProperty.call(cur, seg)) { entry.ptrCache.set(ptr, null); return null; }
2024
+ cur = cur[seg];
2025
+ } else {
2026
+ entry.ptrCache.set(ptr, null);
2027
+ return null;
2028
+ }
2029
+ }
2030
+
2031
+ const out = jsonToTerm(cur);
2032
+ entry.ptrCache.set(ptr, out);
2033
+ return out;
2034
+ }
2035
+
1822
2036
  // Tiny subset of sprintf: supports only %s and %%.
1823
2037
  // Good enough for most N3 string:format use cases that just splice strings.
1824
2038
  function simpleStringFormat(fmt, args) {
@@ -3370,6 +3584,22 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
3370
3584
  return s2 !== null ? [s2] : [];
3371
3585
  }
3372
3586
 
3587
+ // string:jsonPointer
3588
+ // Schema: ( $jsonText $pointer ) string:jsonPointer $value
3589
+ if (g.p instanceof Iri && g.p.value === STRING_NS + "jsonPointer") {
3590
+ if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
3591
+
3592
+ const jsonText = termToJsonText(g.s.elems[0]); // <-- changed
3593
+ const ptr = termToJsStringDecoded(g.s.elems[1]);
3594
+ if (jsonText === null || ptr === null) return [];
3595
+
3596
+ const valTerm = jsonPointerLookup(jsonText, ptr);
3597
+ if (valTerm === null) return [];
3598
+
3599
+ const s2 = unifyTerm(g.o, valTerm, subst);
3600
+ return s2 !== null ? [s2] : [];
3601
+ }
3602
+
3373
3603
  // string:greaterThan
3374
3604
  if (g.p instanceof Iri && g.p.value === STRING_NS + "greaterThan") {
3375
3605
  const sStr = termToJsString(g.s);
@@ -3967,7 +4197,13 @@ function termToN3(t, pref) {
3967
4197
  if (i.startsWith("_:")) return i;
3968
4198
  return `<${i}>`;
3969
4199
  }
3970
- if (t instanceof Literal) return t.value;
4200
+ if (t instanceof Literal) {
4201
+ const [lex, dt] = literalParts(t.value);
4202
+ if (!dt) return t.value; // keep numbers, booleans, lang-tagged strings, etc.
4203
+ const qdt = pref.shrinkIri(dt);
4204
+ if (qdt !== null) return `${lex}^^${qdt}`; // e.g. ^^rdf:JSON
4205
+ return `${lex}^^<${dt}>`; // fallback
4206
+ }
3971
4207
  if (t instanceof Var) return `?${t.name}`;
3972
4208
  if (t instanceof Blank) return t.label;
3973
4209
  if (t instanceof ListTerm) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.5.31",
3
+ "version": "1.5.33",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [