eyeling 1.6.17 → 1.6.18

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 (2) hide show
  1. package/eyeling.js +218 -214
  2. package/package.json +1 -1
package/eyeling.js CHANGED
@@ -19,9 +19,9 @@
19
19
  const { version } = require('./package.json');
20
20
  const nodeCrypto = require('crypto');
21
21
 
22
- // ============================================================================
22
+ // ===========================================================================
23
23
  // Namespace constants
24
- // ============================================================================
24
+ // ===========================================================================
25
25
 
26
26
  const RDF_NS = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
27
27
  const RDFS_NS = 'http://www.w3.org/2000/01/rdf-schema#';
@@ -97,6 +97,47 @@ function normalizeDateTimeLex(s) {
97
97
  return t.trim();
98
98
  }
99
99
 
100
+ // ===========================================================================
101
+ // Run-level time helpers
102
+ // ===========================================================================
103
+
104
+ function localIsoDateTimeString(d) {
105
+ function pad(n, width = 2) {
106
+ return String(n).padStart(width, '0');
107
+ }
108
+ const year = d.getFullYear();
109
+ const month = d.getMonth() + 1;
110
+ const day = d.getDate();
111
+ const hour = d.getHours();
112
+ const min = d.getMinutes();
113
+ const sec = d.getSeconds();
114
+ const ms = d.getMilliseconds();
115
+ const offsetMin = -d.getTimezoneOffset(); // minutes east of UTC
116
+ const sign = offsetMin >= 0 ? '+' : '-';
117
+ const abs = Math.abs(offsetMin);
118
+ const oh = Math.floor(abs / 60);
119
+ const om = abs % 60;
120
+ const msPart = ms ? '.' + String(ms).padStart(3, '0') : '';
121
+ return (
122
+ pad(year, 4) +
123
+ '-' +
124
+ pad(month) +
125
+ '-' +
126
+ pad(day) +
127
+ 'T' +
128
+ pad(hour) +
129
+ ':' +
130
+ pad(min) +
131
+ ':' +
132
+ pad(sec) +
133
+ msPart +
134
+ sign +
135
+ pad(oh) +
136
+ ':' +
137
+ pad(om)
138
+ );
139
+ }
140
+
100
141
  function utcIsoDateTimeStringFromEpochSeconds(sec) {
101
142
  const ms = sec * 1000;
102
143
  const d = new Date(ms);
@@ -154,9 +195,9 @@ function deterministicSkolemIdFromKey(key) {
154
195
 
155
196
  let runLocalTimeCache = null;
156
197
 
157
- // ============================================================================
198
+ // ===========================================================================
158
199
  // AST (Abstract Syntax Tree)
159
- // ============================================================================
200
+ // ===========================================================================
160
201
 
161
202
  class Term {}
162
203
 
@@ -238,9 +279,9 @@ class DerivedFact {
238
279
  }
239
280
  }
240
281
 
241
- // ============================================================================
282
+ // ===========================================================================
242
283
  // LEXER
243
- // ============================================================================
284
+ // ===========================================================================
244
285
 
245
286
  class Token {
246
287
  constructor(typ, value = null) {
@@ -631,9 +672,9 @@ function lex(inputText) {
631
672
  return tokens;
632
673
  }
633
674
 
634
- // ============================================================================
675
+ // ===========================================================================
635
676
  // PREFIX ENVIRONMENT
636
- // ============================================================================
677
+ // ===========================================================================
637
678
 
638
679
  class PrefixEnv {
639
680
  constructor(map) {
@@ -789,9 +830,9 @@ function collectBlankLabelsInTriples(triples) {
789
830
  return acc;
790
831
  }
791
832
 
792
- // ============================================================================
833
+ // ===========================================================================
793
834
  // PARSER
794
- // ============================================================================
835
+ // ===========================================================================
795
836
 
796
837
  class Parser {
797
838
  constructor(tokens) {
@@ -1231,9 +1272,9 @@ class Parser {
1231
1272
  }
1232
1273
  }
1233
1274
 
1234
- // ============================================================================
1275
+ // ===========================================================================
1235
1276
  // Blank-node lifting and Skolemization
1236
- // ============================================================================
1277
+ // ===========================================================================
1237
1278
 
1238
1279
  function liftBlankRuleVars(premise, conclusion) {
1239
1280
  function convertTerm(t, mapping, counter) {
@@ -1340,9 +1381,9 @@ function skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter, f
1340
1381
  );
1341
1382
  }
1342
1383
 
1343
- // ============================================================================
1384
+ // ===========================================================================
1344
1385
  // Alpha equivalence helpers
1345
- // ============================================================================
1386
+ // ===========================================================================
1346
1387
 
1347
1388
  function termsEqual(a, b) {
1348
1389
  if (a === b) return true;
@@ -1614,9 +1655,9 @@ function hasAlphaEquiv(triples, tr) {
1614
1655
  return triples.some((t) => alphaEqTriple(t, tr));
1615
1656
  }
1616
1657
 
1617
- // ============================================================================
1658
+ // ===========================================================================
1618
1659
  // Indexes (facts + backward rules)
1619
- // ============================================================================
1660
+ // ===========================================================================
1620
1661
  //
1621
1662
  // Facts:
1622
1663
  // - __byPred: Map<predicateIRI, Triple[]>
@@ -1784,9 +1825,9 @@ function indexBackRule(backRules, r) {
1784
1825
  }
1785
1826
  }
1786
1827
 
1787
- // ============================================================================
1828
+ // ===========================================================================
1788
1829
  // Special predicate helpers
1789
- // ============================================================================
1830
+ // ===========================================================================
1790
1831
 
1791
1832
  function isRdfTypePred(p) {
1792
1833
  return p instanceof Iri && p.value === RDF_NS + 'type';
@@ -1804,9 +1845,9 @@ function isLogImpliedBy(p) {
1804
1845
  return p instanceof Iri && p.value === LOG_NS + 'impliedBy';
1805
1846
  }
1806
1847
 
1807
- // ============================================================================
1848
+ // ===========================================================================
1808
1849
  // Constraint / "test" builtins
1809
- // ============================================================================
1850
+ // ===========================================================================
1810
1851
 
1811
1852
  function isConstraintBuiltin(tr) {
1812
1853
  if (!(tr.p instanceof Iri)) return false;
@@ -1874,9 +1915,9 @@ function reorderPremiseForConstraints(premise) {
1874
1915
  return normal.concat(delayed);
1875
1916
  }
1876
1917
 
1877
- // ============================================================================
1918
+ // ===========================================================================
1878
1919
  // Unification + substitution
1879
- // ============================================================================
1920
+ // ===========================================================================
1880
1921
 
1881
1922
  function containsVarTerm(t, v) {
1882
1923
  if (t instanceof Var) return t.name === v;
@@ -2188,9 +2229,9 @@ function composeSubst(outer, delta) {
2188
2229
  return out;
2189
2230
  }
2190
2231
 
2191
- // ============================================================================
2232
+ // ===========================================================================
2192
2233
  // BUILTINS
2193
- // ============================================================================
2234
+ // ===========================================================================
2194
2235
 
2195
2236
  function literalParts(lit) {
2196
2237
  // Split a literal into lexical form and datatype IRI (if any).
@@ -2528,9 +2569,9 @@ function parseXsdFloatSpecialLex(s) {
2528
2569
  return null;
2529
2570
  }
2530
2571
 
2531
- // ============================================================================
2572
+ // ===========================================================================
2532
2573
  // Math builtin helpers
2533
- // ============================================================================
2574
+ // ===========================================================================
2534
2575
 
2535
2576
  function formatXsdFloatSpecialLex(n) {
2536
2577
  if (n === Infinity) return 'INF';
@@ -2686,9 +2727,9 @@ function pow10n(k) {
2686
2727
  return 10n ** BigInt(k);
2687
2728
  }
2688
2729
 
2689
- // ============================================================================
2730
+ // ===========================================================================
2690
2731
  // Time & duration builtin helpers
2691
- // ============================================================================
2732
+ // ===========================================================================
2692
2733
 
2693
2734
  function parseXsdDateTerm(t) {
2694
2735
  if (!(t instanceof Literal)) return null;
@@ -3017,9 +3058,9 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
3017
3058
  return [];
3018
3059
  }
3019
3060
 
3020
- // ============================================================================
3061
+ // ===========================================================================
3021
3062
  // List builtin helpers
3022
- // ============================================================================
3063
+ // ===========================================================================
3023
3064
 
3024
3065
  function listAppendSplit(parts, resElems, subst) {
3025
3066
  if (!parts.length) {
@@ -3070,9 +3111,142 @@ function evalListRestLikeBuiltin(sTerm, oTerm, subst) {
3070
3111
  return [];
3071
3112
  }
3072
3113
 
3073
- // ============================================================================
3114
+ // ===========================================================================
3115
+ // RDF list materialization
3116
+ // ===========================================================================
3117
+
3118
+ // Turn RDF Collections described with rdf:first/rdf:rest (+ rdf:nil) into ListTerm terms.
3119
+ // This mutates triples/rules in-place so list:* builtins work on RDF-serialized lists too.
3120
+ function materializeRdfLists(triples, forwardRules, backwardRules) {
3121
+ const RDF_FIRST = RDF_NS + 'first';
3122
+ const RDF_REST = RDF_NS + 'rest';
3123
+ const RDF_NIL = RDF_NS + 'nil';
3124
+
3125
+ function nodeKey(t) {
3126
+ if (t instanceof Blank) return 'B:' + t.label;
3127
+ if (t instanceof Iri) return 'I:' + t.value;
3128
+ return null;
3129
+ }
3130
+
3131
+ // Collect first/rest arcs from *input triples*
3132
+ const firstMap = new Map(); // key(subject) -> Term (object)
3133
+ const restMap = new Map(); // key(subject) -> Term (object)
3134
+ for (const tr of triples) {
3135
+ if (!(tr.p instanceof Iri)) continue;
3136
+ const k = nodeKey(tr.s);
3137
+ if (!k) continue;
3138
+ if (tr.p.value === RDF_FIRST) firstMap.set(k, tr.o);
3139
+ else if (tr.p.value === RDF_REST) restMap.set(k, tr.o);
3140
+ }
3141
+ if (!firstMap.size && !restMap.size) return;
3142
+
3143
+ const cache = new Map(); // key(node) -> ListTerm
3144
+ const visiting = new Set(); // cycle guard
3145
+
3146
+ function buildListForKey(k) {
3147
+ if (cache.has(k)) return cache.get(k);
3148
+ if (visiting.has(k)) return null; // cycle => not a well-formed list
3149
+ visiting.add(k);
3150
+
3151
+ // rdf:nil => ()
3152
+ if (k === 'I:' + RDF_NIL) {
3153
+ const empty = new ListTerm([]);
3154
+ cache.set(k, empty);
3155
+ visiting.delete(k);
3156
+ return empty;
3157
+ }
3158
+
3159
+ const head = firstMap.get(k);
3160
+ const tail = restMap.get(k);
3161
+ if (head === undefined || tail === undefined) {
3162
+ visiting.delete(k);
3163
+ return null; // not a full cons cell
3164
+ }
3165
+
3166
+ const headTerm = rewriteTerm(head);
3167
+
3168
+ let tailListElems = null;
3169
+ if (tail instanceof Iri && tail.value === RDF_NIL) {
3170
+ tailListElems = [];
3171
+ } else {
3172
+ const tk = nodeKey(tail);
3173
+ if (!tk) {
3174
+ visiting.delete(k);
3175
+ return null;
3176
+ }
3177
+ const tailList = buildListForKey(tk);
3178
+ if (!tailList) {
3179
+ visiting.delete(k);
3180
+ return null;
3181
+ }
3182
+ tailListElems = tailList.elems;
3183
+ }
3184
+
3185
+ const out = new ListTerm([headTerm, ...tailListElems]);
3186
+ cache.set(k, out);
3187
+ visiting.delete(k);
3188
+ return out;
3189
+ }
3190
+
3191
+ function rewriteTerm(t) {
3192
+ // Replace list nodes (Blank/Iri) by their constructed ListTerm when possible
3193
+ const k = nodeKey(t);
3194
+ if (k) {
3195
+ const built = buildListForKey(k);
3196
+ if (built) return built;
3197
+ // Also rewrite rdf:nil even if not otherwise referenced
3198
+ if (t instanceof Iri && t.value === RDF_NIL) return new ListTerm([]);
3199
+ return t;
3200
+ }
3201
+ if (t instanceof ListTerm) {
3202
+ let changed = false;
3203
+ const elems = t.elems.map((e) => {
3204
+ const r = rewriteTerm(e);
3205
+ if (r !== e) changed = true;
3206
+ return r;
3207
+ });
3208
+ return changed ? new ListTerm(elems) : t;
3209
+ }
3210
+ if (t instanceof OpenListTerm) {
3211
+ let changed = false;
3212
+ const prefix = t.prefix.map((e) => {
3213
+ const r = rewriteTerm(e);
3214
+ if (r !== e) changed = true;
3215
+ return r;
3216
+ });
3217
+ return changed ? new OpenListTerm(prefix, t.tailVar) : t;
3218
+ }
3219
+ if (t instanceof FormulaTerm) {
3220
+ for (const tr of t.triples) rewriteTriple(tr);
3221
+ return t;
3222
+ }
3223
+ return t;
3224
+ }
3225
+
3226
+ function rewriteTriple(tr) {
3227
+ tr.s = rewriteTerm(tr.s);
3228
+ tr.p = rewriteTerm(tr.p);
3229
+ tr.o = rewriteTerm(tr.o);
3230
+ }
3231
+
3232
+ // Pre-build all reachable list heads
3233
+ for (const k of firstMap.keys()) buildListForKey(k);
3234
+
3235
+ // Rewrite input triples + rules in-place
3236
+ for (const tr of triples) rewriteTriple(tr);
3237
+ for (const r of forwardRules) {
3238
+ for (const tr of r.premise) rewriteTriple(tr);
3239
+ for (const tr of r.conclusion) rewriteTriple(tr);
3240
+ }
3241
+ for (const r of backwardRules) {
3242
+ for (const tr of r.premise) rewriteTriple(tr);
3243
+ for (const tr of r.conclusion) rewriteTriple(tr);
3244
+ }
3245
+ }
3246
+
3247
+ // ===========================================================================
3074
3248
  // Crypto builtin helpers
3075
- // ============================================================================
3249
+ // ===========================================================================
3076
3250
 
3077
3251
  function hashLiteralTerm(t, algo) {
3078
3252
  if (!(t instanceof Literal)) return null;
@@ -3098,9 +3272,9 @@ function evalCryptoHashBuiltin(g, subst, algo) {
3098
3272
  return s2 !== null ? [s2] : [];
3099
3273
  }
3100
3274
 
3101
- // ============================================================================
3275
+ // ===========================================================================
3102
3276
  // Builtin evaluation
3103
- // ============================================================================
3277
+ // ===========================================================================
3104
3278
  // Backward proof & builtins mutual recursion — declarations first
3105
3279
 
3106
3280
  function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
@@ -4560,9 +4734,9 @@ function isBuiltinPred(p) {
4560
4734
  );
4561
4735
  }
4562
4736
 
4563
- // ============================================================================
4737
+ // ===========================================================================
4564
4738
  // Backward proof (SLD-style)
4565
- // ============================================================================
4739
+ // ===========================================================================
4566
4740
 
4567
4741
  function standardizeRule(rule, gen) {
4568
4742
  function renameTerm(t, vmap, genArr) {
@@ -4609,9 +4783,9 @@ function listHasTriple(list, tr) {
4609
4783
  return list.some((t) => triplesEqual(t, tr));
4610
4784
  }
4611
4785
 
4612
- // ============================================================================
4786
+ // ===========================================================================
4613
4787
  // Substitution compaction (to avoid O(depth^2) in deep backward chains)
4614
- // ============================================================================
4788
+ // ===========================================================================
4615
4789
  //
4616
4790
  // Why: backward chaining with standardizeRule introduces fresh variables at
4617
4791
  // each step. composeSubst frequently copies a growing substitution object.
@@ -4846,9 +5020,9 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
4846
5020
  return results;
4847
5021
  }
4848
5022
 
4849
- // ============================================================================
5023
+ // ===========================================================================
4850
5024
  // Forward chaining to fixpoint
4851
- // ============================================================================
5025
+ // ===========================================================================
4852
5026
 
4853
5027
  function forwardChain(facts, forwardRules, backRules) {
4854
5028
  ensureFactIndexes(facts);
@@ -5032,9 +5206,9 @@ function forwardChain(facts, forwardRules, backRules) {
5032
5206
  return derivedForward;
5033
5207
  }
5034
5208
 
5035
- // ============================================================================
5209
+ // ===========================================================================
5036
5210
  // Pretty printing as N3/Turtle
5037
- // ============================================================================
5211
+ // ===========================================================================
5038
5212
 
5039
5213
  function termToN3(t, pref) {
5040
5214
  if (t instanceof Iri) {
@@ -5201,179 +5375,9 @@ function printExplanation(df, prefixes) {
5201
5375
  console.log('# ----------------------------------------------------------------------\n');
5202
5376
  }
5203
5377
 
5204
- // ============================================================================
5205
- // Misc helpers
5206
- // ============================================================================
5207
-
5208
- // Turn RDF Collections described with rdf:first/rdf:rest (+ rdf:nil) into ListTerm terms.
5209
- // This mutates triples/rules in-place so list:* builtins work on RDF-serialized lists too.
5210
- function materializeRdfLists(triples, forwardRules, backwardRules) {
5211
- const RDF_FIRST = RDF_NS + 'first';
5212
- const RDF_REST = RDF_NS + 'rest';
5213
- const RDF_NIL = RDF_NS + 'nil';
5214
-
5215
- function nodeKey(t) {
5216
- if (t instanceof Blank) return 'B:' + t.label;
5217
- if (t instanceof Iri) return 'I:' + t.value;
5218
- return null;
5219
- }
5220
-
5221
- // Collect first/rest arcs from *input triples*
5222
- const firstMap = new Map(); // key(subject) -> Term (object)
5223
- const restMap = new Map(); // key(subject) -> Term (object)
5224
- for (const tr of triples) {
5225
- if (!(tr.p instanceof Iri)) continue;
5226
- const k = nodeKey(tr.s);
5227
- if (!k) continue;
5228
- if (tr.p.value === RDF_FIRST) firstMap.set(k, tr.o);
5229
- else if (tr.p.value === RDF_REST) restMap.set(k, tr.o);
5230
- }
5231
- if (!firstMap.size && !restMap.size) return;
5232
-
5233
- const cache = new Map(); // key(node) -> ListTerm
5234
- const visiting = new Set(); // cycle guard
5235
-
5236
- function buildListForKey(k) {
5237
- if (cache.has(k)) return cache.get(k);
5238
- if (visiting.has(k)) return null; // cycle => not a well-formed list
5239
- visiting.add(k);
5240
-
5241
- // rdf:nil => ()
5242
- if (k === 'I:' + RDF_NIL) {
5243
- const empty = new ListTerm([]);
5244
- cache.set(k, empty);
5245
- visiting.delete(k);
5246
- return empty;
5247
- }
5248
-
5249
- const head = firstMap.get(k);
5250
- const tail = restMap.get(k);
5251
- if (head === undefined || tail === undefined) {
5252
- visiting.delete(k);
5253
- return null; // not a full cons cell
5254
- }
5255
-
5256
- const headTerm = rewriteTerm(head);
5257
-
5258
- let tailListElems = null;
5259
- if (tail instanceof Iri && tail.value === RDF_NIL) {
5260
- tailListElems = [];
5261
- } else {
5262
- const tk = nodeKey(tail);
5263
- if (!tk) {
5264
- visiting.delete(k);
5265
- return null;
5266
- }
5267
- const tailList = buildListForKey(tk);
5268
- if (!tailList) {
5269
- visiting.delete(k);
5270
- return null;
5271
- }
5272
- tailListElems = tailList.elems;
5273
- }
5274
-
5275
- const out = new ListTerm([headTerm, ...tailListElems]);
5276
- cache.set(k, out);
5277
- visiting.delete(k);
5278
- return out;
5279
- }
5280
-
5281
- function rewriteTerm(t) {
5282
- // Replace list nodes (Blank/Iri) by their constructed ListTerm when possible
5283
- const k = nodeKey(t);
5284
- if (k) {
5285
- const built = buildListForKey(k);
5286
- if (built) return built;
5287
- // Also rewrite rdf:nil even if not otherwise referenced
5288
- if (t instanceof Iri && t.value === RDF_NIL) return new ListTerm([]);
5289
- return t;
5290
- }
5291
- if (t instanceof ListTerm) {
5292
- let changed = false;
5293
- const elems = t.elems.map((e) => {
5294
- const r = rewriteTerm(e);
5295
- if (r !== e) changed = true;
5296
- return r;
5297
- });
5298
- return changed ? new ListTerm(elems) : t;
5299
- }
5300
- if (t instanceof OpenListTerm) {
5301
- let changed = false;
5302
- const prefix = t.prefix.map((e) => {
5303
- const r = rewriteTerm(e);
5304
- if (r !== e) changed = true;
5305
- return r;
5306
- });
5307
- return changed ? new OpenListTerm(prefix, t.tailVar) : t;
5308
- }
5309
- if (t instanceof FormulaTerm) {
5310
- for (const tr of t.triples) rewriteTriple(tr);
5311
- return t;
5312
- }
5313
- return t;
5314
- }
5315
-
5316
- function rewriteTriple(tr) {
5317
- tr.s = rewriteTerm(tr.s);
5318
- tr.p = rewriteTerm(tr.p);
5319
- tr.o = rewriteTerm(tr.o);
5320
- }
5321
-
5322
- // Pre-build all reachable list heads
5323
- for (const k of firstMap.keys()) buildListForKey(k);
5324
-
5325
- // Rewrite input triples + rules in-place
5326
- for (const tr of triples) rewriteTriple(tr);
5327
- for (const r of forwardRules) {
5328
- for (const tr of r.premise) rewriteTriple(tr);
5329
- for (const tr of r.conclusion) rewriteTriple(tr);
5330
- }
5331
- for (const r of backwardRules) {
5332
- for (const tr of r.premise) rewriteTriple(tr);
5333
- for (const tr of r.conclusion) rewriteTriple(tr);
5334
- }
5335
- }
5336
-
5337
- function localIsoDateTimeString(d) {
5338
- function pad(n, width = 2) {
5339
- return String(n).padStart(width, '0');
5340
- }
5341
- const year = d.getFullYear();
5342
- const month = d.getMonth() + 1;
5343
- const day = d.getDate();
5344
- const hour = d.getHours();
5345
- const min = d.getMinutes();
5346
- const sec = d.getSeconds();
5347
- const ms = d.getMilliseconds();
5348
- const offsetMin = -d.getTimezoneOffset(); // minutes east of UTC
5349
- const sign = offsetMin >= 0 ? '+' : '-';
5350
- const abs = Math.abs(offsetMin);
5351
- const oh = Math.floor(abs / 60);
5352
- const om = abs % 60;
5353
- const msPart = ms ? '.' + String(ms).padStart(3, '0') : '';
5354
- return (
5355
- pad(year, 4) +
5356
- '-' +
5357
- pad(month) +
5358
- '-' +
5359
- pad(day) +
5360
- 'T' +
5361
- pad(hour) +
5362
- ':' +
5363
- pad(min) +
5364
- ':' +
5365
- pad(sec) +
5366
- msPart +
5367
- sign +
5368
- pad(oh) +
5369
- ':' +
5370
- pad(om)
5371
- );
5372
- }
5373
-
5374
- // ============================================================================
5378
+ // ===========================================================================
5375
5379
  // CLI entry point
5376
- // ============================================================================
5380
+ // ===========================================================================
5377
5381
  function main() {
5378
5382
  // Drop "node" and script name; keep only user-provided args
5379
5383
  const argv = process.argv.slice(2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.6.17",
3
+ "version": "1.6.18",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [