eyeling 1.5.38 → 1.5.40
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/examples/oslo-steps-library-scholarly.n3 +144 -183
- package/examples/oslo-steps-workflow-composition.n3 +192 -217
- package/examples/output/oslo-steps-library-scholarly.n3 +672 -392
- package/examples/output/oslo-steps-workflow-composition.n3 +96 -56
- package/eyeling.js +779 -804
- package/package.json +1 -1
package/eyeling.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
'use strict';
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
* eyeling.js — a minimal Notation3 (N3) reasoner in JavaScript
|
|
@@ -17,28 +17,28 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
const { version } = require('./package.json');
|
|
20
|
-
const nodeCrypto = require(
|
|
20
|
+
const nodeCrypto = require('crypto');
|
|
21
21
|
|
|
22
22
|
// ============================================================================
|
|
23
23
|
// Namespace constants
|
|
24
24
|
// ============================================================================
|
|
25
25
|
|
|
26
|
-
const RDF_NS =
|
|
27
|
-
const RDFS_NS =
|
|
28
|
-
const OWL_NS =
|
|
29
|
-
const XSD_NS =
|
|
30
|
-
const CRYPTO_NS =
|
|
31
|
-
const MATH_NS =
|
|
32
|
-
const TIME_NS =
|
|
33
|
-
const LIST_NS =
|
|
34
|
-
const LOG_NS =
|
|
35
|
-
const STRING_NS =
|
|
36
|
-
const SKOLEM_NS =
|
|
37
|
-
const RDF_JSON_DT = RDF_NS +
|
|
26
|
+
const RDF_NS = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
|
|
27
|
+
const RDFS_NS = 'http://www.w3.org/2000/01/rdf-schema#';
|
|
28
|
+
const OWL_NS = 'http://www.w3.org/2002/07/owl#';
|
|
29
|
+
const XSD_NS = 'http://www.w3.org/2001/XMLSchema#';
|
|
30
|
+
const CRYPTO_NS = 'http://www.w3.org/2000/10/swap/crypto#';
|
|
31
|
+
const MATH_NS = 'http://www.w3.org/2000/10/swap/math#';
|
|
32
|
+
const TIME_NS = 'http://www.w3.org/2000/10/swap/time#';
|
|
33
|
+
const LIST_NS = 'http://www.w3.org/2000/10/swap/list#';
|
|
34
|
+
const LOG_NS = 'http://www.w3.org/2000/10/swap/log#';
|
|
35
|
+
const STRING_NS = 'http://www.w3.org/2000/10/swap/string#';
|
|
36
|
+
const SKOLEM_NS = 'https://eyereasoner.github.io/.well-known/genid/';
|
|
37
|
+
const RDF_JSON_DT = RDF_NS + 'JSON';
|
|
38
38
|
|
|
39
39
|
function isRdfJsonDatatype(dt) {
|
|
40
40
|
// dt comes from literalParts() and may be expanded or prefixed depending on parsing/printing.
|
|
41
|
-
return dt === null || dt === RDF_JSON_DT || dt ===
|
|
41
|
+
return dt === null || dt === RDF_JSON_DT || dt === 'rdf:JSON';
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function termToJsonText(t) {
|
|
@@ -81,7 +81,7 @@ function normalizeDateTimeLex(s) {
|
|
|
81
81
|
// Accept: 2025-... , "2025-..." , "2025-..."^^xsd:dateTime , "..."^^<...>
|
|
82
82
|
if (s == null) return null;
|
|
83
83
|
let t = String(s).trim();
|
|
84
|
-
const caret = t.indexOf(
|
|
84
|
+
const caret = t.indexOf('^^');
|
|
85
85
|
if (caret >= 0) t = t.slice(0, caret).trim();
|
|
86
86
|
if (t.startsWith('"') && t.endsWith('"') && t.length >= 2) t = t.slice(1, -1);
|
|
87
87
|
return t.trim();
|
|
@@ -90,7 +90,9 @@ function normalizeDateTimeLex(s) {
|
|
|
90
90
|
function utcIsoDateTimeStringFromEpochSeconds(sec) {
|
|
91
91
|
const ms = sec * 1000;
|
|
92
92
|
const d = new Date(ms);
|
|
93
|
-
function pad(n, w = 2) {
|
|
93
|
+
function pad(n, w = 2) {
|
|
94
|
+
return String(n).padStart(w, '0');
|
|
95
|
+
}
|
|
94
96
|
const year = d.getUTCFullYear();
|
|
95
97
|
const month = d.getUTCMonth() + 1;
|
|
96
98
|
const day = d.getUTCDate();
|
|
@@ -98,12 +100,8 @@ function utcIsoDateTimeStringFromEpochSeconds(sec) {
|
|
|
98
100
|
const min = d.getUTCMinutes();
|
|
99
101
|
const s2 = d.getUTCSeconds();
|
|
100
102
|
const ms2 = d.getUTCMilliseconds();
|
|
101
|
-
const msPart = ms2 ?
|
|
102
|
-
return (
|
|
103
|
-
pad(year, 4) + "-" + pad(month) + "-" + pad(day) + "T" +
|
|
104
|
-
pad(hour) + ":" + pad(min) + ":" + pad(s2) + msPart +
|
|
105
|
-
"+00:00"
|
|
106
|
-
);
|
|
103
|
+
const msPart = ms2 ? '.' + String(ms2).padStart(3, '0') : '';
|
|
104
|
+
return pad(year, 4) + '-' + pad(month) + '-' + pad(day) + 'T' + pad(hour) + ':' + pad(min) + ':' + pad(s2) + msPart + '+00:00';
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
function getNowLex() {
|
|
@@ -138,18 +136,10 @@ function deterministicSkolemIdFromKey(key) {
|
|
|
138
136
|
h4 = (h4 * 0x01000193) >>> 0;
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
const hex = [h1, h2, h3, h4]
|
|
142
|
-
.map(h => h.toString(16).padStart(8, "0"))
|
|
143
|
-
.join(""); // 32 hex chars
|
|
139
|
+
const hex = [h1, h2, h3, h4].map((h) => h.toString(16).padStart(8, '0')).join(''); // 32 hex chars
|
|
144
140
|
|
|
145
141
|
// Format like a UUID: 8-4-4-4-12
|
|
146
|
-
return (
|
|
147
|
-
hex.slice(0, 8) + "-" +
|
|
148
|
-
hex.slice(8, 12) + "-" +
|
|
149
|
-
hex.slice(12, 16) + "-" +
|
|
150
|
-
hex.slice(16, 20) + "-" +
|
|
151
|
-
hex.slice(20)
|
|
152
|
-
);
|
|
142
|
+
return hex.slice(0, 8) + '-' + hex.slice(8, 12) + '-' + hex.slice(12, 16) + '-' + hex.slice(16, 20) + '-' + hex.slice(20);
|
|
153
143
|
}
|
|
154
144
|
|
|
155
145
|
let runLocalTimeCache = null;
|
|
@@ -220,10 +210,10 @@ class Triple {
|
|
|
220
210
|
|
|
221
211
|
class Rule {
|
|
222
212
|
constructor(premise, conclusion, isForward, isFuse, headBlankLabels) {
|
|
223
|
-
this.premise = premise;
|
|
213
|
+
this.premise = premise; // Triple[]
|
|
224
214
|
this.conclusion = conclusion; // Triple[]
|
|
225
|
-
this.isForward = isForward;
|
|
226
|
-
this.isFuse = isFuse;
|
|
215
|
+
this.isForward = isForward; // boolean
|
|
216
|
+
this.isFuse = isFuse; // boolean
|
|
227
217
|
// Set<string> of blank-node labels that occur explicitly in the rule head
|
|
228
218
|
this.headBlankLabels = headBlankLabels || new Set();
|
|
229
219
|
}
|
|
@@ -231,10 +221,10 @@ class Rule {
|
|
|
231
221
|
|
|
232
222
|
class DerivedFact {
|
|
233
223
|
constructor(fact, rule, premises, subst) {
|
|
234
|
-
this.fact = fact;
|
|
235
|
-
this.rule = rule;
|
|
236
|
-
this.premises = premises;
|
|
237
|
-
this.subst = subst;
|
|
224
|
+
this.fact = fact; // Triple
|
|
225
|
+
this.rule = rule; // Rule
|
|
226
|
+
this.premises = premises; // Triple[]
|
|
227
|
+
this.subst = subst; // { varName: Term }
|
|
238
228
|
}
|
|
239
229
|
}
|
|
240
230
|
|
|
@@ -283,82 +273,82 @@ function lex(inputText) {
|
|
|
283
273
|
}
|
|
284
274
|
|
|
285
275
|
// 2) Comments starting with '#'
|
|
286
|
-
if (c ===
|
|
287
|
-
while (i < n && chars[i] !==
|
|
276
|
+
if (c === '#') {
|
|
277
|
+
while (i < n && chars[i] !== '\n' && chars[i] !== '\r') i++;
|
|
288
278
|
continue;
|
|
289
279
|
}
|
|
290
280
|
|
|
291
281
|
// 3) Two-character operators: => and <=
|
|
292
|
-
if (c ===
|
|
293
|
-
if (peek(1) ===
|
|
294
|
-
tokens.push(new Token(
|
|
282
|
+
if (c === '=') {
|
|
283
|
+
if (peek(1) === '>') {
|
|
284
|
+
tokens.push(new Token('OpImplies'));
|
|
295
285
|
i += 2;
|
|
296
286
|
continue;
|
|
297
287
|
} else {
|
|
298
288
|
// N3 syntactic sugar: '=' means owl:sameAs
|
|
299
|
-
tokens.push(new Token(
|
|
289
|
+
tokens.push(new Token('Equals'));
|
|
300
290
|
i += 1;
|
|
301
291
|
continue;
|
|
302
292
|
}
|
|
303
293
|
}
|
|
304
294
|
|
|
305
|
-
if (c ===
|
|
306
|
-
if (peek(1) ===
|
|
307
|
-
tokens.push(new Token(
|
|
295
|
+
if (c === '<') {
|
|
296
|
+
if (peek(1) === '=') {
|
|
297
|
+
tokens.push(new Token('OpImpliedBy'));
|
|
308
298
|
i += 2;
|
|
309
299
|
continue;
|
|
310
300
|
}
|
|
311
301
|
// N3 predicate inversion: "<-" (swap subject/object for this predicate)
|
|
312
|
-
if (peek(1) ===
|
|
313
|
-
tokens.push(new Token(
|
|
302
|
+
if (peek(1) === '-') {
|
|
303
|
+
tokens.push(new Token('OpPredInvert'));
|
|
314
304
|
i += 2;
|
|
315
305
|
continue;
|
|
316
306
|
}
|
|
317
307
|
// Otherwise IRIREF <...>
|
|
318
308
|
i++; // skip '<'
|
|
319
309
|
const iriChars = [];
|
|
320
|
-
while (i < n && chars[i] !==
|
|
310
|
+
while (i < n && chars[i] !== '>') {
|
|
321
311
|
iriChars.push(chars[i]);
|
|
322
312
|
i++;
|
|
323
313
|
}
|
|
324
|
-
if (i >= n || chars[i] !==
|
|
325
|
-
throw new Error(
|
|
314
|
+
if (i >= n || chars[i] !== '>') {
|
|
315
|
+
throw new Error('Unterminated IRI <...>');
|
|
326
316
|
}
|
|
327
317
|
i++; // skip '>'
|
|
328
|
-
const iri = iriChars.join(
|
|
329
|
-
tokens.push(new Token(
|
|
318
|
+
const iri = iriChars.join('');
|
|
319
|
+
tokens.push(new Token('IriRef', iri));
|
|
330
320
|
continue;
|
|
331
321
|
}
|
|
332
322
|
|
|
333
323
|
// 4) Path + datatype operators: !, ^, ^^
|
|
334
|
-
if (c ===
|
|
335
|
-
tokens.push(new Token(
|
|
324
|
+
if (c === '!') {
|
|
325
|
+
tokens.push(new Token('OpPathFwd'));
|
|
336
326
|
i += 1;
|
|
337
327
|
continue;
|
|
338
328
|
}
|
|
339
|
-
if (c ===
|
|
340
|
-
if (peek(1) ===
|
|
341
|
-
tokens.push(new Token(
|
|
329
|
+
if (c === '^') {
|
|
330
|
+
if (peek(1) === '^') {
|
|
331
|
+
tokens.push(new Token('HatHat'));
|
|
342
332
|
i += 2;
|
|
343
333
|
continue;
|
|
344
334
|
}
|
|
345
|
-
tokens.push(new Token(
|
|
335
|
+
tokens.push(new Token('OpPathRev'));
|
|
346
336
|
i += 1;
|
|
347
337
|
continue;
|
|
348
338
|
}
|
|
349
339
|
|
|
350
340
|
// 5) Single-character punctuation
|
|
351
|
-
if (
|
|
341
|
+
if ('{}()[];,.'.includes(c)) {
|
|
352
342
|
const mapping = {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
343
|
+
'{': 'LBrace',
|
|
344
|
+
'}': 'RBrace',
|
|
345
|
+
'(': 'LParen',
|
|
346
|
+
')': 'RParen',
|
|
347
|
+
'[': 'LBracket',
|
|
348
|
+
']': 'RBracket',
|
|
349
|
+
';': 'Semicolon',
|
|
350
|
+
',': 'Comma',
|
|
351
|
+
'.': 'Dot',
|
|
362
352
|
};
|
|
363
353
|
tokens.push(new Token(mapping[c]));
|
|
364
354
|
i++;
|
|
@@ -381,12 +371,12 @@ function lex(inputText) {
|
|
|
381
371
|
}
|
|
382
372
|
let cc = chars[i];
|
|
383
373
|
i++;
|
|
384
|
-
if (cc ===
|
|
374
|
+
if (cc === '\\') {
|
|
385
375
|
// Preserve escapes verbatim (same behavior as short strings)
|
|
386
376
|
if (i < n) {
|
|
387
377
|
const esc = chars[i];
|
|
388
378
|
i++;
|
|
389
|
-
sChars.push(
|
|
379
|
+
sChars.push('\\');
|
|
390
380
|
sChars.push(esc);
|
|
391
381
|
}
|
|
392
382
|
continue;
|
|
@@ -394,8 +384,8 @@ function lex(inputText) {
|
|
|
394
384
|
sChars.push(cc);
|
|
395
385
|
}
|
|
396
386
|
if (!closed) throw new Error('Unterminated long string literal """..."""');
|
|
397
|
-
const s = '"""' + sChars.join(
|
|
398
|
-
tokens.push(new Token(
|
|
387
|
+
const s = '"""' + sChars.join('') + '"""';
|
|
388
|
+
tokens.push(new Token('Literal', s));
|
|
399
389
|
continue;
|
|
400
390
|
}
|
|
401
391
|
|
|
@@ -405,11 +395,11 @@ function lex(inputText) {
|
|
|
405
395
|
while (i < n) {
|
|
406
396
|
let cc = chars[i];
|
|
407
397
|
i++;
|
|
408
|
-
if (cc ===
|
|
398
|
+
if (cc === '\\') {
|
|
409
399
|
if (i < n) {
|
|
410
400
|
const esc = chars[i];
|
|
411
401
|
i++;
|
|
412
|
-
sChars.push(
|
|
402
|
+
sChars.push('\\');
|
|
413
403
|
sChars.push(esc);
|
|
414
404
|
}
|
|
415
405
|
continue;
|
|
@@ -417,13 +407,13 @@ function lex(inputText) {
|
|
|
417
407
|
if (cc === '"') break;
|
|
418
408
|
sChars.push(cc);
|
|
419
409
|
}
|
|
420
|
-
const s = '"' + sChars.join(
|
|
421
|
-
tokens.push(new Token(
|
|
410
|
+
const s = '"' + sChars.join('') + '"';
|
|
411
|
+
tokens.push(new Token('Literal', s));
|
|
422
412
|
continue;
|
|
423
413
|
}
|
|
424
414
|
|
|
425
415
|
// Variable ?name
|
|
426
|
-
if (c ===
|
|
416
|
+
if (c === '?') {
|
|
427
417
|
i++;
|
|
428
418
|
const nameChars = [];
|
|
429
419
|
let cc;
|
|
@@ -431,19 +421,16 @@ function lex(inputText) {
|
|
|
431
421
|
nameChars.push(cc);
|
|
432
422
|
i++;
|
|
433
423
|
}
|
|
434
|
-
const name = nameChars.join(
|
|
435
|
-
tokens.push(new Token(
|
|
424
|
+
const name = nameChars.join('');
|
|
425
|
+
tokens.push(new Token('Var', name));
|
|
436
426
|
continue;
|
|
437
427
|
}
|
|
438
428
|
|
|
439
429
|
// Directives: @prefix, @base (and language tags after string literals)
|
|
440
|
-
if (c ===
|
|
430
|
+
if (c === '@') {
|
|
441
431
|
const prevTok = tokens.length ? tokens[tokens.length - 1] : null;
|
|
442
432
|
const prevWasQuotedLiteral =
|
|
443
|
-
prevTok &&
|
|
444
|
-
prevTok.typ === "Literal" &&
|
|
445
|
-
typeof prevTok.value === "string" &&
|
|
446
|
-
prevTok.value.startsWith('"');
|
|
433
|
+
prevTok && prevTok.typ === 'Literal' && typeof prevTok.value === 'string' && prevTok.value.startsWith('"');
|
|
447
434
|
|
|
448
435
|
i++; // consume '@'
|
|
449
436
|
|
|
@@ -459,8 +446,8 @@ function lex(inputText) {
|
|
|
459
446
|
tagChars.push(cc);
|
|
460
447
|
i++;
|
|
461
448
|
}
|
|
462
|
-
while (peek() ===
|
|
463
|
-
tagChars.push(
|
|
449
|
+
while (peek() === '-') {
|
|
450
|
+
tagChars.push('-');
|
|
464
451
|
i++; // consume '-'
|
|
465
452
|
const segChars = [];
|
|
466
453
|
while ((cc = peek()) !== null && /[A-Za-z0-9]/.test(cc)) {
|
|
@@ -472,7 +459,7 @@ function lex(inputText) {
|
|
|
472
459
|
}
|
|
473
460
|
tagChars.push(...segChars);
|
|
474
461
|
}
|
|
475
|
-
tokens.push(new Token(
|
|
462
|
+
tokens.push(new Token('LangTag', tagChars.join('')));
|
|
476
463
|
continue;
|
|
477
464
|
}
|
|
478
465
|
|
|
@@ -483,15 +470,15 @@ function lex(inputText) {
|
|
|
483
470
|
wordChars.push(cc);
|
|
484
471
|
i++;
|
|
485
472
|
}
|
|
486
|
-
const word = wordChars.join(
|
|
487
|
-
if (word ===
|
|
488
|
-
else if (word ===
|
|
473
|
+
const word = wordChars.join('');
|
|
474
|
+
if (word === 'prefix') tokens.push(new Token('AtPrefix'));
|
|
475
|
+
else if (word === 'base') tokens.push(new Token('AtBase'));
|
|
489
476
|
else throw new Error(`Unknown directive @${word}`);
|
|
490
477
|
continue;
|
|
491
478
|
}
|
|
492
479
|
|
|
493
480
|
// 6) Numeric literal (integer or float)
|
|
494
|
-
if (/[0-9]/.test(c) || (c ===
|
|
481
|
+
if (/[0-9]/.test(c) || (c === '-' && peek(1) !== null && /[0-9]/.test(peek(1)))) {
|
|
495
482
|
const numChars = [c];
|
|
496
483
|
i++;
|
|
497
484
|
while (i < n) {
|
|
@@ -501,9 +488,9 @@ function lex(inputText) {
|
|
|
501
488
|
i++;
|
|
502
489
|
continue;
|
|
503
490
|
}
|
|
504
|
-
if (cc ===
|
|
491
|
+
if (cc === '.') {
|
|
505
492
|
if (i + 1 < n && /[0-9]/.test(chars[i + 1])) {
|
|
506
|
-
numChars.push(
|
|
493
|
+
numChars.push('.');
|
|
507
494
|
i++;
|
|
508
495
|
continue;
|
|
509
496
|
} else {
|
|
@@ -512,7 +499,7 @@ function lex(inputText) {
|
|
|
512
499
|
}
|
|
513
500
|
break;
|
|
514
501
|
}
|
|
515
|
-
tokens.push(new Token(
|
|
502
|
+
tokens.push(new Token('Literal', numChars.join('')));
|
|
516
503
|
continue;
|
|
517
504
|
}
|
|
518
505
|
|
|
@@ -526,17 +513,17 @@ function lex(inputText) {
|
|
|
526
513
|
if (!wordChars.length) {
|
|
527
514
|
throw new Error(`Unexpected char: ${JSON.stringify(c)}`);
|
|
528
515
|
}
|
|
529
|
-
const word = wordChars.join(
|
|
530
|
-
if (word ===
|
|
531
|
-
tokens.push(new Token(
|
|
532
|
-
} else if ([...word].every(ch => /[0-9.\-]/.test(ch))) {
|
|
533
|
-
tokens.push(new Token(
|
|
516
|
+
const word = wordChars.join('');
|
|
517
|
+
if (word === 'true' || word === 'false') {
|
|
518
|
+
tokens.push(new Token('Literal', word));
|
|
519
|
+
} else if ([...word].every((ch) => /[0-9.\-]/.test(ch))) {
|
|
520
|
+
tokens.push(new Token('Literal', word));
|
|
534
521
|
} else {
|
|
535
|
-
tokens.push(new Token(
|
|
522
|
+
tokens.push(new Token('Ident', word));
|
|
536
523
|
}
|
|
537
524
|
}
|
|
538
525
|
|
|
539
|
-
tokens.push(new Token(
|
|
526
|
+
tokens.push(new Token('EOF'));
|
|
540
527
|
return tokens;
|
|
541
528
|
}
|
|
542
529
|
|
|
@@ -551,16 +538,16 @@ class PrefixEnv {
|
|
|
551
538
|
|
|
552
539
|
static newDefault() {
|
|
553
540
|
const m = {};
|
|
554
|
-
m[
|
|
555
|
-
m[
|
|
556
|
-
m[
|
|
557
|
-
m[
|
|
558
|
-
m[
|
|
559
|
-
m[
|
|
560
|
-
m[
|
|
561
|
-
m[
|
|
562
|
-
m[
|
|
563
|
-
m[
|
|
541
|
+
m['rdf'] = RDF_NS;
|
|
542
|
+
m['rdfs'] = RDFS_NS;
|
|
543
|
+
m['xsd'] = XSD_NS;
|
|
544
|
+
m['log'] = LOG_NS;
|
|
545
|
+
m['math'] = MATH_NS;
|
|
546
|
+
m['string'] = STRING_NS;
|
|
547
|
+
m['list'] = LIST_NS;
|
|
548
|
+
m['time'] = TIME_NS;
|
|
549
|
+
m['genid'] = SKOLEM_NS;
|
|
550
|
+
m[''] = '';
|
|
564
551
|
return new PrefixEnv(m);
|
|
565
552
|
}
|
|
566
553
|
|
|
@@ -569,9 +556,9 @@ class PrefixEnv {
|
|
|
569
556
|
}
|
|
570
557
|
|
|
571
558
|
expandQName(q) {
|
|
572
|
-
if (q.includes(
|
|
573
|
-
const [p, local] = q.split(
|
|
574
|
-
const base = this.map[p] ||
|
|
559
|
+
if (q.includes(':')) {
|
|
560
|
+
const [p, local] = q.split(':', 2);
|
|
561
|
+
const base = this.map[p] || '';
|
|
575
562
|
if (base) return base + local;
|
|
576
563
|
return q;
|
|
577
564
|
}
|
|
@@ -591,7 +578,7 @@ class PrefixEnv {
|
|
|
591
578
|
}
|
|
592
579
|
if (best === null) return null;
|
|
593
580
|
const [p, local] = best;
|
|
594
|
-
if (p ===
|
|
581
|
+
if (p === '') return `:${local}`;
|
|
595
582
|
return `${p}:${local}`;
|
|
596
583
|
}
|
|
597
584
|
|
|
@@ -723,7 +710,7 @@ class Parser {
|
|
|
723
710
|
|
|
724
711
|
expectDot() {
|
|
725
712
|
const tok = this.next();
|
|
726
|
-
if (tok.typ !==
|
|
713
|
+
if (tok.typ !== 'Dot') {
|
|
727
714
|
throw new Error(`Expected '.', got ${tok.toString()}`);
|
|
728
715
|
}
|
|
729
716
|
}
|
|
@@ -733,21 +720,21 @@ class Parser {
|
|
|
733
720
|
const forwardRules = [];
|
|
734
721
|
const backwardRules = [];
|
|
735
722
|
|
|
736
|
-
while (this.peek().typ !==
|
|
737
|
-
if (this.peek().typ ===
|
|
723
|
+
while (this.peek().typ !== 'EOF') {
|
|
724
|
+
if (this.peek().typ === 'AtPrefix') {
|
|
738
725
|
this.next();
|
|
739
726
|
this.parsePrefixDirective();
|
|
740
|
-
} else if (this.peek().typ ===
|
|
727
|
+
} else if (this.peek().typ === 'AtBase') {
|
|
741
728
|
this.next();
|
|
742
729
|
this.parseBaseDirective();
|
|
743
730
|
} else {
|
|
744
731
|
const first = this.parseTerm();
|
|
745
|
-
if (this.peek().typ ===
|
|
732
|
+
if (this.peek().typ === 'OpImplies') {
|
|
746
733
|
this.next();
|
|
747
734
|
const second = this.parseTerm();
|
|
748
735
|
this.expectDot();
|
|
749
736
|
forwardRules.push(this.makeRule(first, second, true));
|
|
750
|
-
} else if (this.peek().typ ===
|
|
737
|
+
} else if (this.peek().typ === 'OpImpliedBy') {
|
|
751
738
|
this.next();
|
|
752
739
|
const second = this.parseTerm();
|
|
753
740
|
this.expectDot();
|
|
@@ -755,10 +742,10 @@ class Parser {
|
|
|
755
742
|
} else {
|
|
756
743
|
let more;
|
|
757
744
|
|
|
758
|
-
if (this.peek().typ ===
|
|
745
|
+
if (this.peek().typ === 'Dot') {
|
|
759
746
|
// Allow a bare blank-node property list statement, e.g. `[ a :Statement ].`
|
|
760
747
|
const lastTok = this.toks[this.pos - 1];
|
|
761
|
-
if (this.pendingTriples.length > 0 && lastTok && lastTok.typ ===
|
|
748
|
+
if (this.pendingTriples.length > 0 && lastTok && lastTok.typ === 'RBracket') {
|
|
762
749
|
more = this.pendingTriples;
|
|
763
750
|
this.pendingTriples = [];
|
|
764
751
|
this.next(); // consume '.'
|
|
@@ -789,26 +776,26 @@ class Parser {
|
|
|
789
776
|
|
|
790
777
|
parsePrefixDirective() {
|
|
791
778
|
const tok = this.next();
|
|
792
|
-
if (tok.typ !==
|
|
779
|
+
if (tok.typ !== 'Ident') {
|
|
793
780
|
throw new Error(`Expected prefix name, got ${tok.toString()}`);
|
|
794
781
|
}
|
|
795
|
-
const pref = tok.value ||
|
|
796
|
-
const prefName = pref.endsWith(
|
|
782
|
+
const pref = tok.value || '';
|
|
783
|
+
const prefName = pref.endsWith(':') ? pref.slice(0, -1) : pref;
|
|
797
784
|
|
|
798
|
-
if (this.peek().typ ===
|
|
785
|
+
if (this.peek().typ === 'Dot') {
|
|
799
786
|
this.next();
|
|
800
787
|
if (!this.prefixes.map.hasOwnProperty(prefName)) {
|
|
801
|
-
this.prefixes.set(prefName,
|
|
788
|
+
this.prefixes.set(prefName, '');
|
|
802
789
|
}
|
|
803
790
|
return;
|
|
804
791
|
}
|
|
805
792
|
|
|
806
793
|
const tok2 = this.next();
|
|
807
794
|
let iri;
|
|
808
|
-
if (tok2.typ ===
|
|
809
|
-
iri = tok2.value ||
|
|
810
|
-
} else if (tok2.typ ===
|
|
811
|
-
iri = this.prefixes.expandQName(tok2.value ||
|
|
795
|
+
if (tok2.typ === 'IriRef') {
|
|
796
|
+
iri = tok2.value || '';
|
|
797
|
+
} else if (tok2.typ === 'Ident') {
|
|
798
|
+
iri = this.prefixes.expandQName(tok2.value || '');
|
|
812
799
|
} else {
|
|
813
800
|
throw new Error(`Expected IRI after @prefix, got ${tok2.toString()}`);
|
|
814
801
|
}
|
|
@@ -819,32 +806,28 @@ class Parser {
|
|
|
819
806
|
parseBaseDirective() {
|
|
820
807
|
const tok = this.next();
|
|
821
808
|
let iri;
|
|
822
|
-
if (tok.typ ===
|
|
823
|
-
iri = tok.value ||
|
|
824
|
-
} else if (tok.typ ===
|
|
825
|
-
iri = tok.value ||
|
|
809
|
+
if (tok.typ === 'IriRef') {
|
|
810
|
+
iri = tok.value || '';
|
|
811
|
+
} else if (tok.typ === 'Ident') {
|
|
812
|
+
iri = tok.value || '';
|
|
826
813
|
} else {
|
|
827
814
|
throw new Error(`Expected IRI after @base, got ${tok.toString()}`);
|
|
828
815
|
}
|
|
829
816
|
this.expectDot();
|
|
830
|
-
this.prefixes.set(
|
|
817
|
+
this.prefixes.set('', iri);
|
|
831
818
|
}
|
|
832
819
|
|
|
833
820
|
parseTerm() {
|
|
834
821
|
let t = this.parsePathItem();
|
|
835
822
|
|
|
836
|
-
while (this.peek().typ ===
|
|
837
|
-
const dir = this.next().typ;
|
|
823
|
+
while (this.peek().typ === 'OpPathFwd' || this.peek().typ === 'OpPathRev') {
|
|
824
|
+
const dir = this.next().typ; // OpPathFwd | OpPathRev
|
|
838
825
|
const pred = this.parsePathItem();
|
|
839
826
|
|
|
840
827
|
this.blankCounter += 1;
|
|
841
828
|
const bn = new Blank(`_:b${this.blankCounter}`);
|
|
842
829
|
|
|
843
|
-
this.pendingTriples.push(
|
|
844
|
-
dir === "OpPathFwd"
|
|
845
|
-
? new Triple(t, pred, bn)
|
|
846
|
-
: new Triple(bn, pred, t)
|
|
847
|
-
);
|
|
830
|
+
this.pendingTriples.push(dir === 'OpPathFwd' ? new Triple(t, pred, bn) : new Triple(bn, pred, t));
|
|
848
831
|
|
|
849
832
|
t = bn;
|
|
850
833
|
}
|
|
@@ -857,55 +840,55 @@ class Parser {
|
|
|
857
840
|
const typ = tok.typ;
|
|
858
841
|
const val = tok.value;
|
|
859
842
|
|
|
860
|
-
if (typ ===
|
|
861
|
-
return new Iri(OWL_NS +
|
|
843
|
+
if (typ === 'Equals') {
|
|
844
|
+
return new Iri(OWL_NS + 'sameAs');
|
|
862
845
|
}
|
|
863
846
|
|
|
864
|
-
if (typ ===
|
|
865
|
-
return new Iri(val ||
|
|
847
|
+
if (typ === 'IriRef') {
|
|
848
|
+
return new Iri(val || '');
|
|
866
849
|
}
|
|
867
850
|
|
|
868
|
-
if (typ ===
|
|
869
|
-
const name = val ||
|
|
870
|
-
if (name ===
|
|
871
|
-
return new Iri(RDF_NS +
|
|
872
|
-
} else if (name.startsWith(
|
|
851
|
+
if (typ === 'Ident') {
|
|
852
|
+
const name = val || '';
|
|
853
|
+
if (name === 'a') {
|
|
854
|
+
return new Iri(RDF_NS + 'type');
|
|
855
|
+
} else if (name.startsWith('_:')) {
|
|
873
856
|
return new Blank(name);
|
|
874
|
-
} else if (name.includes(
|
|
857
|
+
} else if (name.includes(':')) {
|
|
875
858
|
return new Iri(this.prefixes.expandQName(name));
|
|
876
859
|
} else {
|
|
877
860
|
return new Iri(name);
|
|
878
861
|
}
|
|
879
862
|
}
|
|
880
863
|
|
|
881
|
-
if (typ ===
|
|
882
|
-
let s = val ||
|
|
864
|
+
if (typ === 'Literal') {
|
|
865
|
+
let s = val || '';
|
|
883
866
|
|
|
884
867
|
// Optional language tag: "..."@en, per N3 LANGTAG production.
|
|
885
|
-
if (this.peek().typ ===
|
|
868
|
+
if (this.peek().typ === 'LangTag') {
|
|
886
869
|
// Only quoted string literals can carry a language tag.
|
|
887
870
|
if (!(s.startsWith('"') && s.endsWith('"'))) {
|
|
888
|
-
throw new Error(
|
|
871
|
+
throw new Error('Language tag is only allowed on quoted string literals');
|
|
889
872
|
}
|
|
890
873
|
const langTok = this.next();
|
|
891
|
-
const lang = langTok.value ||
|
|
874
|
+
const lang = langTok.value || '';
|
|
892
875
|
s = `${s}@${lang}`;
|
|
893
876
|
|
|
894
877
|
// N3/Turtle: language tags and datatypes are mutually exclusive.
|
|
895
|
-
if (this.peek().typ ===
|
|
896
|
-
throw new Error(
|
|
878
|
+
if (this.peek().typ === 'HatHat') {
|
|
879
|
+
throw new Error('A literal cannot have both a language tag (@...) and a datatype (^^...)');
|
|
897
880
|
}
|
|
898
881
|
}
|
|
899
882
|
|
|
900
|
-
if (this.peek().typ ===
|
|
883
|
+
if (this.peek().typ === 'HatHat') {
|
|
901
884
|
this.next();
|
|
902
885
|
const dtTok = this.next();
|
|
903
886
|
let dtIri;
|
|
904
|
-
if (dtTok.typ ===
|
|
905
|
-
dtIri = dtTok.value ||
|
|
906
|
-
} else if (dtTok.typ ===
|
|
907
|
-
const qn = dtTok.value ||
|
|
908
|
-
if (qn.includes(
|
|
887
|
+
if (dtTok.typ === 'IriRef') {
|
|
888
|
+
dtIri = dtTok.value || '';
|
|
889
|
+
} else if (dtTok.typ === 'Ident') {
|
|
890
|
+
const qn = dtTok.value || '';
|
|
891
|
+
if (qn.includes(':')) dtIri = this.prefixes.expandQName(qn);
|
|
909
892
|
else dtIri = qn;
|
|
910
893
|
} else {
|
|
911
894
|
throw new Error(`Expected datatype after ^^, got ${dtTok.toString()}`);
|
|
@@ -915,17 +898,17 @@ class Parser {
|
|
|
915
898
|
return new Literal(s);
|
|
916
899
|
}
|
|
917
900
|
|
|
918
|
-
if (typ ===
|
|
919
|
-
if (typ ===
|
|
920
|
-
if (typ ===
|
|
921
|
-
if (typ ===
|
|
901
|
+
if (typ === 'Var') return new Var(val || '');
|
|
902
|
+
if (typ === 'LParen') return this.parseList();
|
|
903
|
+
if (typ === 'LBracket') return this.parseBlank();
|
|
904
|
+
if (typ === 'LBrace') return this.parseFormula();
|
|
922
905
|
|
|
923
906
|
throw new Error(`Unexpected term token: ${tok.toString()}`);
|
|
924
907
|
}
|
|
925
908
|
|
|
926
909
|
parseList() {
|
|
927
910
|
const elems = [];
|
|
928
|
-
while (this.peek().typ !==
|
|
911
|
+
while (this.peek().typ !== 'RParen') {
|
|
929
912
|
elems.push(this.parseTerm());
|
|
930
913
|
}
|
|
931
914
|
this.next(); // consume ')'
|
|
@@ -934,7 +917,7 @@ class Parser {
|
|
|
934
917
|
|
|
935
918
|
parseBlank() {
|
|
936
919
|
// [] or [ ... ] property list
|
|
937
|
-
if (this.peek().typ ===
|
|
920
|
+
if (this.peek().typ === 'RBracket') {
|
|
938
921
|
this.next();
|
|
939
922
|
this.blankCounter += 1;
|
|
940
923
|
return new Blank(`_:b${this.blankCounter}`);
|
|
@@ -949,10 +932,10 @@ class Parser {
|
|
|
949
932
|
// Verb (can also be 'a')
|
|
950
933
|
let pred;
|
|
951
934
|
let invert = false;
|
|
952
|
-
if (this.peek().typ ===
|
|
935
|
+
if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'a') {
|
|
953
936
|
this.next();
|
|
954
|
-
pred = new Iri(RDF_NS +
|
|
955
|
-
} else if (this.peek().typ ===
|
|
937
|
+
pred = new Iri(RDF_NS + 'type');
|
|
938
|
+
} else if (this.peek().typ === 'OpPredInvert') {
|
|
956
939
|
this.next(); // consume "<-"
|
|
957
940
|
pred = this.parseTerm();
|
|
958
941
|
invert = true;
|
|
@@ -962,32 +945,27 @@ class Parser {
|
|
|
962
945
|
|
|
963
946
|
// Object list: o1, o2, ...
|
|
964
947
|
const objs = [this.parseTerm()];
|
|
965
|
-
while (this.peek().typ ===
|
|
948
|
+
while (this.peek().typ === 'Comma') {
|
|
966
949
|
this.next();
|
|
967
950
|
objs.push(this.parseTerm());
|
|
968
951
|
}
|
|
969
952
|
|
|
970
953
|
for (const o of objs) {
|
|
971
|
-
this.pendingTriples.push(invert ? new Triple(o, pred, subj)
|
|
972
|
-
: new Triple(subj, pred, o));
|
|
954
|
+
this.pendingTriples.push(invert ? new Triple(o, pred, subj) : new Triple(subj, pred, o));
|
|
973
955
|
}
|
|
974
956
|
|
|
975
|
-
if (this.peek().typ ===
|
|
957
|
+
if (this.peek().typ === 'Semicolon') {
|
|
976
958
|
this.next();
|
|
977
|
-
if (this.peek().typ ===
|
|
959
|
+
if (this.peek().typ === 'RBracket') break;
|
|
978
960
|
continue;
|
|
979
961
|
}
|
|
980
962
|
break;
|
|
981
963
|
}
|
|
982
964
|
|
|
983
|
-
if (this.peek().typ ===
|
|
965
|
+
if (this.peek().typ === 'RBracket') {
|
|
984
966
|
this.next();
|
|
985
967
|
} else {
|
|
986
|
-
throw new Error(
|
|
987
|
-
`Expected ']' at end of blank node property list, got ${JSON.stringify(
|
|
988
|
-
this.peek()
|
|
989
|
-
)}`
|
|
990
|
-
);
|
|
968
|
+
throw new Error(`Expected ']' at end of blank node property list, got ${JSON.stringify(this.peek())}`);
|
|
991
969
|
}
|
|
992
970
|
|
|
993
971
|
return new Blank(id);
|
|
@@ -995,45 +973,45 @@ class Parser {
|
|
|
995
973
|
|
|
996
974
|
parseFormula() {
|
|
997
975
|
const triples = [];
|
|
998
|
-
while (this.peek().typ !==
|
|
976
|
+
while (this.peek().typ !== 'RBrace') {
|
|
999
977
|
const left = this.parseTerm();
|
|
1000
|
-
if (this.peek().typ ===
|
|
978
|
+
if (this.peek().typ === 'OpImplies') {
|
|
1001
979
|
this.next();
|
|
1002
980
|
const right = this.parseTerm();
|
|
1003
|
-
const pred = new Iri(LOG_NS +
|
|
981
|
+
const pred = new Iri(LOG_NS + 'implies');
|
|
1004
982
|
triples.push(new Triple(left, pred, right));
|
|
1005
|
-
if (this.peek().typ ===
|
|
1006
|
-
else if (this.peek().typ ===
|
|
983
|
+
if (this.peek().typ === 'Dot') this.next();
|
|
984
|
+
else if (this.peek().typ === 'RBrace') {
|
|
1007
985
|
// ok
|
|
1008
986
|
} else {
|
|
1009
987
|
throw new Error(`Expected '.' or '}', got ${this.peek().toString()}`);
|
|
1010
988
|
}
|
|
1011
|
-
} else if (this.peek().typ ===
|
|
989
|
+
} else if (this.peek().typ === 'OpImpliedBy') {
|
|
1012
990
|
this.next();
|
|
1013
991
|
const right = this.parseTerm();
|
|
1014
|
-
const pred = new Iri(LOG_NS +
|
|
992
|
+
const pred = new Iri(LOG_NS + 'impliedBy');
|
|
1015
993
|
triples.push(new Triple(left, pred, right));
|
|
1016
|
-
if (this.peek().typ ===
|
|
1017
|
-
else if (this.peek().typ ===
|
|
994
|
+
if (this.peek().typ === 'Dot') this.next();
|
|
995
|
+
else if (this.peek().typ === 'RBrace') {
|
|
1018
996
|
// ok
|
|
1019
997
|
} else {
|
|
1020
998
|
throw new Error(`Expected '.' or '}', got ${this.peek().toString()}`);
|
|
1021
999
|
}
|
|
1022
1000
|
} else {
|
|
1023
1001
|
// Allow a bare blank-node property list statement inside a formula, e.g. `{ [ a :X ]. }`
|
|
1024
|
-
if (this.peek().typ ===
|
|
1002
|
+
if (this.peek().typ === 'Dot' || this.peek().typ === 'RBrace') {
|
|
1025
1003
|
const lastTok = this.toks[this.pos - 1];
|
|
1026
|
-
if (this.pendingTriples.length > 0 && lastTok && lastTok.typ ===
|
|
1004
|
+
if (this.pendingTriples.length > 0 && lastTok && lastTok.typ === 'RBracket') {
|
|
1027
1005
|
triples.push(...this.pendingTriples);
|
|
1028
1006
|
this.pendingTriples = [];
|
|
1029
|
-
if (this.peek().typ ===
|
|
1007
|
+
if (this.peek().typ === 'Dot') this.next();
|
|
1030
1008
|
continue;
|
|
1031
1009
|
}
|
|
1032
1010
|
}
|
|
1033
1011
|
|
|
1034
1012
|
triples.push(...this.parsePredicateObjectList(left));
|
|
1035
|
-
if (this.peek().typ ===
|
|
1036
|
-
else if (this.peek().typ ===
|
|
1013
|
+
if (this.peek().typ === 'Dot') this.next();
|
|
1014
|
+
else if (this.peek().typ === 'RBrace') {
|
|
1037
1015
|
// ok
|
|
1038
1016
|
} else {
|
|
1039
1017
|
throw new Error(`Expected '.' or '}', got ${this.peek().toString()}`);
|
|
@@ -1057,10 +1035,10 @@ class Parser {
|
|
|
1057
1035
|
let verb;
|
|
1058
1036
|
let invert = false;
|
|
1059
1037
|
|
|
1060
|
-
if (this.peek().typ ===
|
|
1038
|
+
if (this.peek().typ === 'Ident' && (this.peek().value || '') === 'a') {
|
|
1061
1039
|
this.next();
|
|
1062
|
-
verb = new Iri(RDF_NS +
|
|
1063
|
-
} else if (this.peek().typ ===
|
|
1040
|
+
verb = new Iri(RDF_NS + 'type');
|
|
1041
|
+
} else if (this.peek().typ === 'OpPredInvert') {
|
|
1064
1042
|
this.next(); // "<-"
|
|
1065
1043
|
verb = this.parseTerm();
|
|
1066
1044
|
invert = true;
|
|
@@ -1081,9 +1059,9 @@ class Parser {
|
|
|
1081
1059
|
out.push(new Triple(invert ? o : subject, verb, invert ? subject : o));
|
|
1082
1060
|
}
|
|
1083
1061
|
|
|
1084
|
-
if (this.peek().typ ===
|
|
1062
|
+
if (this.peek().typ === 'Semicolon') {
|
|
1085
1063
|
this.next();
|
|
1086
|
-
if (this.peek().typ ===
|
|
1064
|
+
if (this.peek().typ === 'Dot') break;
|
|
1087
1065
|
continue;
|
|
1088
1066
|
}
|
|
1089
1067
|
break;
|
|
@@ -1094,7 +1072,7 @@ class Parser {
|
|
|
1094
1072
|
|
|
1095
1073
|
parseObjectList() {
|
|
1096
1074
|
const objs = [this.parseTerm()];
|
|
1097
|
-
while (this.peek().typ ===
|
|
1075
|
+
while (this.peek().typ === 'Comma') {
|
|
1098
1076
|
this.next();
|
|
1099
1077
|
objs.push(this.parseTerm());
|
|
1100
1078
|
}
|
|
@@ -1114,7 +1092,7 @@ class Parser {
|
|
|
1114
1092
|
|
|
1115
1093
|
let isFuse = false;
|
|
1116
1094
|
if (isForward) {
|
|
1117
|
-
if (conclTerm instanceof Literal && conclTerm.value ===
|
|
1095
|
+
if (conclTerm instanceof Literal && conclTerm.value === 'false') {
|
|
1118
1096
|
isFuse = true;
|
|
1119
1097
|
}
|
|
1120
1098
|
}
|
|
@@ -1122,7 +1100,7 @@ class Parser {
|
|
|
1122
1100
|
let rawPremise;
|
|
1123
1101
|
if (premiseTerm instanceof FormulaTerm) {
|
|
1124
1102
|
rawPremise = premiseTerm.triples;
|
|
1125
|
-
} else if (premiseTerm instanceof Literal && premiseTerm.value ===
|
|
1103
|
+
} else if (premiseTerm instanceof Literal && premiseTerm.value === 'true') {
|
|
1126
1104
|
rawPremise = [];
|
|
1127
1105
|
} else {
|
|
1128
1106
|
rawPremise = [];
|
|
@@ -1131,7 +1109,7 @@ class Parser {
|
|
|
1131
1109
|
let rawConclusion;
|
|
1132
1110
|
if (conclTerm instanceof FormulaTerm) {
|
|
1133
1111
|
rawConclusion = conclTerm.triples;
|
|
1134
|
-
} else if (conclTerm instanceof Literal && conclTerm.value ===
|
|
1112
|
+
} else if (conclTerm instanceof Literal && conclTerm.value === 'false') {
|
|
1135
1113
|
rawConclusion = [];
|
|
1136
1114
|
} else {
|
|
1137
1115
|
rawConclusion = [];
|
|
@@ -1164,21 +1142,17 @@ function liftBlankRuleVars(premise, conclusion) {
|
|
|
1164
1142
|
return new Var(mapping[label]);
|
|
1165
1143
|
}
|
|
1166
1144
|
if (t instanceof ListTerm) {
|
|
1167
|
-
return new ListTerm(t.elems.map(e => convertTerm(e, mapping, counter)));
|
|
1145
|
+
return new ListTerm(t.elems.map((e) => convertTerm(e, mapping, counter)));
|
|
1168
1146
|
}
|
|
1169
1147
|
if (t instanceof OpenListTerm) {
|
|
1170
1148
|
return new OpenListTerm(
|
|
1171
|
-
t.prefix.map(e => convertTerm(e, mapping, counter)),
|
|
1172
|
-
t.tailVar
|
|
1149
|
+
t.prefix.map((e) => convertTerm(e, mapping, counter)),
|
|
1150
|
+
t.tailVar,
|
|
1173
1151
|
);
|
|
1174
1152
|
}
|
|
1175
1153
|
if (t instanceof FormulaTerm) {
|
|
1176
|
-
const triples = t.triples.map(
|
|
1177
|
-
new Triple(
|
|
1178
|
-
convertTerm(tr.s, mapping, counter),
|
|
1179
|
-
convertTerm(tr.p, mapping, counter),
|
|
1180
|
-
convertTerm(tr.o, mapping, counter)
|
|
1181
|
-
)
|
|
1154
|
+
const triples = t.triples.map(
|
|
1155
|
+
(tr) => new Triple(convertTerm(tr.s, mapping, counter), convertTerm(tr.p, mapping, counter), convertTerm(tr.o, mapping, counter)),
|
|
1182
1156
|
);
|
|
1183
1157
|
return new FormulaTerm(triples);
|
|
1184
1158
|
}
|
|
@@ -1186,16 +1160,12 @@ function liftBlankRuleVars(premise, conclusion) {
|
|
|
1186
1160
|
}
|
|
1187
1161
|
|
|
1188
1162
|
function convertTriple(tr, mapping, counter) {
|
|
1189
|
-
return new Triple(
|
|
1190
|
-
convertTerm(tr.s, mapping, counter),
|
|
1191
|
-
convertTerm(tr.p, mapping, counter),
|
|
1192
|
-
convertTerm(tr.o, mapping, counter)
|
|
1193
|
-
);
|
|
1163
|
+
return new Triple(convertTerm(tr.s, mapping, counter), convertTerm(tr.p, mapping, counter), convertTerm(tr.o, mapping, counter));
|
|
1194
1164
|
}
|
|
1195
1165
|
|
|
1196
1166
|
const mapping = {};
|
|
1197
1167
|
const counter = [0];
|
|
1198
|
-
const newPremise = premise.map(tr => convertTriple(tr, mapping, counter));
|
|
1168
|
+
const newPremise = premise.map((tr) => convertTriple(tr, mapping, counter));
|
|
1199
1169
|
return [newPremise, conclusion];
|
|
1200
1170
|
}
|
|
1201
1171
|
|
|
@@ -1215,24 +1185,18 @@ function skolemizeTermForHeadBlanks(t, headBlankLabels, mapping, skCounter) {
|
|
|
1215
1185
|
}
|
|
1216
1186
|
|
|
1217
1187
|
if (t instanceof ListTerm) {
|
|
1218
|
-
return new ListTerm(
|
|
1219
|
-
t.elems.map(e => skolemizeTermForHeadBlanks(e, headBlankLabels, mapping, skCounter))
|
|
1220
|
-
);
|
|
1188
|
+
return new ListTerm(t.elems.map((e) => skolemizeTermForHeadBlanks(e, headBlankLabels, mapping, skCounter)));
|
|
1221
1189
|
}
|
|
1222
1190
|
|
|
1223
1191
|
if (t instanceof OpenListTerm) {
|
|
1224
1192
|
return new OpenListTerm(
|
|
1225
|
-
t.prefix.map(e => skolemizeTermForHeadBlanks(e, headBlankLabels, mapping, skCounter)),
|
|
1226
|
-
t.tailVar
|
|
1193
|
+
t.prefix.map((e) => skolemizeTermForHeadBlanks(e, headBlankLabels, mapping, skCounter)),
|
|
1194
|
+
t.tailVar,
|
|
1227
1195
|
);
|
|
1228
1196
|
}
|
|
1229
1197
|
|
|
1230
1198
|
if (t instanceof FormulaTerm) {
|
|
1231
|
-
return new FormulaTerm(
|
|
1232
|
-
t.triples.map(tr =>
|
|
1233
|
-
skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter)
|
|
1234
|
-
)
|
|
1235
|
-
);
|
|
1199
|
+
return new FormulaTerm(t.triples.map((tr) => skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter)));
|
|
1236
1200
|
}
|
|
1237
1201
|
|
|
1238
1202
|
return t;
|
|
@@ -1242,7 +1206,7 @@ function skolemizeTripleForHeadBlanks(tr, headBlankLabels, mapping, skCounter) {
|
|
|
1242
1206
|
return new Triple(
|
|
1243
1207
|
skolemizeTermForHeadBlanks(tr.s, headBlankLabels, mapping, skCounter),
|
|
1244
1208
|
skolemizeTermForHeadBlanks(tr.p, headBlankLabels, mapping, skCounter),
|
|
1245
|
-
skolemizeTermForHeadBlanks(tr.o, headBlankLabels, mapping, skCounter)
|
|
1209
|
+
skolemizeTermForHeadBlanks(tr.o, headBlankLabels, mapping, skCounter),
|
|
1246
1210
|
);
|
|
1247
1211
|
}
|
|
1248
1212
|
|
|
@@ -1280,9 +1244,7 @@ function termsEqual(a, b) {
|
|
|
1280
1244
|
}
|
|
1281
1245
|
|
|
1282
1246
|
function triplesEqual(a, b) {
|
|
1283
|
-
return (
|
|
1284
|
-
termsEqual(a.s, b.s) && termsEqual(a.p, b.p) && termsEqual(a.o, b.o)
|
|
1285
|
-
);
|
|
1247
|
+
return termsEqual(a.s, b.s) && termsEqual(a.p, b.p) && termsEqual(a.o, b.o);
|
|
1286
1248
|
}
|
|
1287
1249
|
|
|
1288
1250
|
function triplesListEqual(xs, ys) {
|
|
@@ -1346,9 +1308,7 @@ function alphaEqTermInFormula(a, b, vmap, bmap) {
|
|
|
1346
1308
|
|
|
1347
1309
|
function alphaEqTripleInFormula(a, b, vmap, bmap) {
|
|
1348
1310
|
return (
|
|
1349
|
-
alphaEqTermInFormula(a.s, b.s, vmap, bmap) &&
|
|
1350
|
-
alphaEqTermInFormula(a.p, b.p, vmap, bmap) &&
|
|
1351
|
-
alphaEqTermInFormula(a.o, b.o, vmap, bmap)
|
|
1311
|
+
alphaEqTermInFormula(a.s, b.s, vmap, bmap) && alphaEqTermInFormula(a.p, b.p, vmap, bmap) && alphaEqTermInFormula(a.o, b.o, vmap, bmap)
|
|
1352
1312
|
);
|
|
1353
1313
|
}
|
|
1354
1314
|
|
|
@@ -1405,8 +1365,7 @@ function alphaEqTerm(a, b, bmap) {
|
|
|
1405
1365
|
return true;
|
|
1406
1366
|
}
|
|
1407
1367
|
if (a instanceof OpenListTerm && b instanceof OpenListTerm) {
|
|
1408
|
-
if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length)
|
|
1409
|
-
return false;
|
|
1368
|
+
if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length) return false;
|
|
1410
1369
|
for (let i = 0; i < a.prefix.length; i++) {
|
|
1411
1370
|
if (!alphaEqTerm(a.prefix[i], b.prefix[i], bmap)) return false;
|
|
1412
1371
|
}
|
|
@@ -1421,15 +1380,11 @@ function alphaEqTerm(a, b, bmap) {
|
|
|
1421
1380
|
|
|
1422
1381
|
function alphaEqTriple(a, b) {
|
|
1423
1382
|
const bmap = {};
|
|
1424
|
-
return (
|
|
1425
|
-
alphaEqTerm(a.s, b.s, bmap) &&
|
|
1426
|
-
alphaEqTerm(a.p, b.p, bmap) &&
|
|
1427
|
-
alphaEqTerm(a.o, b.o, bmap)
|
|
1428
|
-
);
|
|
1383
|
+
return alphaEqTerm(a.s, b.s, bmap) && alphaEqTerm(a.p, b.p, bmap) && alphaEqTerm(a.o, b.o, bmap);
|
|
1429
1384
|
}
|
|
1430
1385
|
|
|
1431
1386
|
function hasAlphaEquiv(triples, tr) {
|
|
1432
|
-
return triples.some(t => alphaEqTriple(t, tr));
|
|
1387
|
+
return triples.some((t) => alphaEqTriple(t, tr));
|
|
1433
1388
|
}
|
|
1434
1389
|
|
|
1435
1390
|
// ============================================================================
|
|
@@ -1446,8 +1401,8 @@ function hasAlphaEquiv(triples, tr) {
|
|
|
1446
1401
|
// - __wildHeadPred: Rule[] (non-IRI head predicate)
|
|
1447
1402
|
|
|
1448
1403
|
function termFastKey(t) {
|
|
1449
|
-
if (t instanceof Iri) return
|
|
1450
|
-
if (t instanceof Literal) return
|
|
1404
|
+
if (t instanceof Iri) return 'I:' + t.value;
|
|
1405
|
+
if (t instanceof Literal) return 'L:' + t.value;
|
|
1451
1406
|
return null;
|
|
1452
1407
|
}
|
|
1453
1408
|
|
|
@@ -1456,15 +1411,27 @@ function tripleFastKey(tr) {
|
|
|
1456
1411
|
const kp = termFastKey(tr.p);
|
|
1457
1412
|
const ko = termFastKey(tr.o);
|
|
1458
1413
|
if (ks === null || kp === null || ko === null) return null;
|
|
1459
|
-
return ks +
|
|
1414
|
+
return ks + '\t' + kp + '\t' + ko;
|
|
1460
1415
|
}
|
|
1461
1416
|
|
|
1462
1417
|
function ensureFactIndexes(facts) {
|
|
1463
1418
|
if (facts.__byPred && facts.__byPO && facts.__keySet) return;
|
|
1464
1419
|
|
|
1465
|
-
Object.defineProperty(facts,
|
|
1466
|
-
|
|
1467
|
-
|
|
1420
|
+
Object.defineProperty(facts, '__byPred', {
|
|
1421
|
+
value: new Map(),
|
|
1422
|
+
enumerable: false,
|
|
1423
|
+
writable: true,
|
|
1424
|
+
});
|
|
1425
|
+
Object.defineProperty(facts, '__byPO', {
|
|
1426
|
+
value: new Map(),
|
|
1427
|
+
enumerable: false,
|
|
1428
|
+
writable: true,
|
|
1429
|
+
});
|
|
1430
|
+
Object.defineProperty(facts, '__keySet', {
|
|
1431
|
+
value: new Set(),
|
|
1432
|
+
enumerable: false,
|
|
1433
|
+
writable: true,
|
|
1434
|
+
});
|
|
1468
1435
|
|
|
1469
1436
|
for (const f of facts) indexFact(facts, f);
|
|
1470
1437
|
}
|
|
@@ -1474,15 +1441,24 @@ function indexFact(facts, tr) {
|
|
|
1474
1441
|
const pk = tr.p.value;
|
|
1475
1442
|
|
|
1476
1443
|
let pb = facts.__byPred.get(pk);
|
|
1477
|
-
if (!pb) {
|
|
1444
|
+
if (!pb) {
|
|
1445
|
+
pb = [];
|
|
1446
|
+
facts.__byPred.set(pk, pb);
|
|
1447
|
+
}
|
|
1478
1448
|
pb.push(tr);
|
|
1479
1449
|
|
|
1480
1450
|
const ok = termFastKey(tr.o);
|
|
1481
1451
|
if (ok !== null) {
|
|
1482
1452
|
let po = facts.__byPO.get(pk);
|
|
1483
|
-
if (!po) {
|
|
1453
|
+
if (!po) {
|
|
1454
|
+
po = new Map();
|
|
1455
|
+
facts.__byPO.set(pk, po);
|
|
1456
|
+
}
|
|
1484
1457
|
let pob = po.get(ok);
|
|
1485
|
-
if (!pob) {
|
|
1458
|
+
if (!pob) {
|
|
1459
|
+
pob = [];
|
|
1460
|
+
po.set(ok, pob);
|
|
1461
|
+
}
|
|
1486
1462
|
pob.push(tr);
|
|
1487
1463
|
}
|
|
1488
1464
|
}
|
|
@@ -1526,12 +1502,12 @@ function hasFactIndexed(facts, tr) {
|
|
|
1526
1502
|
const po = facts.__byPO.get(pk);
|
|
1527
1503
|
if (po) {
|
|
1528
1504
|
const pob = po.get(ok) || [];
|
|
1529
|
-
return pob.some(t => alphaEqTriple(t, tr));
|
|
1505
|
+
return pob.some((t) => alphaEqTriple(t, tr));
|
|
1530
1506
|
}
|
|
1531
1507
|
}
|
|
1532
1508
|
|
|
1533
1509
|
const pb = facts.__byPred.get(pk) || [];
|
|
1534
|
-
return pb.some(t => alphaEqTriple(t, tr));
|
|
1510
|
+
return pb.some((t) => alphaEqTriple(t, tr));
|
|
1535
1511
|
}
|
|
1536
1512
|
|
|
1537
1513
|
return hasAlphaEquiv(facts, tr);
|
|
@@ -1546,8 +1522,16 @@ function pushFactIndexed(facts, tr) {
|
|
|
1546
1522
|
function ensureBackRuleIndexes(backRules) {
|
|
1547
1523
|
if (backRules.__byHeadPred && backRules.__wildHeadPred) return;
|
|
1548
1524
|
|
|
1549
|
-
Object.defineProperty(backRules,
|
|
1550
|
-
|
|
1525
|
+
Object.defineProperty(backRules, '__byHeadPred', {
|
|
1526
|
+
value: new Map(),
|
|
1527
|
+
enumerable: false,
|
|
1528
|
+
writable: true,
|
|
1529
|
+
});
|
|
1530
|
+
Object.defineProperty(backRules, '__wildHeadPred', {
|
|
1531
|
+
value: [],
|
|
1532
|
+
enumerable: false,
|
|
1533
|
+
writable: true,
|
|
1534
|
+
});
|
|
1551
1535
|
|
|
1552
1536
|
for (const r of backRules) indexBackRule(backRules, r);
|
|
1553
1537
|
}
|
|
@@ -1558,7 +1542,10 @@ function indexBackRule(backRules, r) {
|
|
|
1558
1542
|
if (head && head.p instanceof Iri) {
|
|
1559
1543
|
const k = head.p.value;
|
|
1560
1544
|
let bucket = backRules.__byHeadPred.get(k);
|
|
1561
|
-
if (!bucket) {
|
|
1545
|
+
if (!bucket) {
|
|
1546
|
+
bucket = [];
|
|
1547
|
+
backRules.__byHeadPred.set(k, bucket);
|
|
1548
|
+
}
|
|
1562
1549
|
bucket.push(r);
|
|
1563
1550
|
} else {
|
|
1564
1551
|
backRules.__wildHeadPred.push(r);
|
|
@@ -1570,19 +1557,19 @@ function indexBackRule(backRules, r) {
|
|
|
1570
1557
|
// ============================================================================
|
|
1571
1558
|
|
|
1572
1559
|
function isRdfTypePred(p) {
|
|
1573
|
-
return p instanceof Iri && p.value === RDF_NS +
|
|
1560
|
+
return p instanceof Iri && p.value === RDF_NS + 'type';
|
|
1574
1561
|
}
|
|
1575
1562
|
|
|
1576
1563
|
function isOwlSameAsPred(t) {
|
|
1577
|
-
return t instanceof Iri && t.value ===
|
|
1564
|
+
return t instanceof Iri && t.value === OWL_NS + 'sameAs';
|
|
1578
1565
|
}
|
|
1579
1566
|
|
|
1580
1567
|
function isLogImplies(p) {
|
|
1581
|
-
return p instanceof Iri && p.value === LOG_NS +
|
|
1568
|
+
return p instanceof Iri && p.value === LOG_NS + 'implies';
|
|
1582
1569
|
}
|
|
1583
1570
|
|
|
1584
1571
|
function isLogImpliedBy(p) {
|
|
1585
|
-
return p instanceof Iri && p.value === LOG_NS +
|
|
1572
|
+
return p instanceof Iri && p.value === LOG_NS + 'impliedBy';
|
|
1586
1573
|
}
|
|
1587
1574
|
|
|
1588
1575
|
// ============================================================================
|
|
@@ -1595,44 +1582,40 @@ function isConstraintBuiltin(tr) {
|
|
|
1595
1582
|
|
|
1596
1583
|
// math: numeric comparisons (no new bindings, just tests)
|
|
1597
1584
|
if (
|
|
1598
|
-
v === MATH_NS +
|
|
1599
|
-
v === MATH_NS +
|
|
1600
|
-
v === MATH_NS +
|
|
1601
|
-
v === MATH_NS +
|
|
1602
|
-
v === MATH_NS +
|
|
1603
|
-
v === MATH_NS +
|
|
1585
|
+
v === MATH_NS + 'equalTo' ||
|
|
1586
|
+
v === MATH_NS + 'greaterThan' ||
|
|
1587
|
+
v === MATH_NS + 'lessThan' ||
|
|
1588
|
+
v === MATH_NS + 'notEqualTo' ||
|
|
1589
|
+
v === MATH_NS + 'notGreaterThan' ||
|
|
1590
|
+
v === MATH_NS + 'notLessThan'
|
|
1604
1591
|
) {
|
|
1605
1592
|
return true;
|
|
1606
1593
|
}
|
|
1607
1594
|
|
|
1608
1595
|
// list: membership test with no bindings
|
|
1609
|
-
if (v === LIST_NS +
|
|
1596
|
+
if (v === LIST_NS + 'notMember') {
|
|
1610
1597
|
return true;
|
|
1611
1598
|
}
|
|
1612
1599
|
|
|
1613
1600
|
// log: tests that are purely constraints (no new bindings)
|
|
1614
|
-
if (
|
|
1615
|
-
v === LOG_NS + "forAllIn" ||
|
|
1616
|
-
v === LOG_NS + "notEqualTo" ||
|
|
1617
|
-
v === LOG_NS + "notIncludes"
|
|
1618
|
-
) {
|
|
1601
|
+
if (v === LOG_NS + 'forAllIn' || v === LOG_NS + 'notEqualTo' || v === LOG_NS + 'notIncludes') {
|
|
1619
1602
|
return true;
|
|
1620
1603
|
}
|
|
1621
1604
|
|
|
1622
1605
|
// string: relational / membership style tests (no bindings)
|
|
1623
1606
|
if (
|
|
1624
|
-
v === STRING_NS +
|
|
1625
|
-
v === STRING_NS +
|
|
1626
|
-
v === STRING_NS +
|
|
1627
|
-
v === STRING_NS +
|
|
1628
|
-
v === STRING_NS +
|
|
1629
|
-
v === STRING_NS +
|
|
1630
|
-
v === STRING_NS +
|
|
1631
|
-
v === STRING_NS +
|
|
1632
|
-
v === STRING_NS +
|
|
1633
|
-
v === STRING_NS +
|
|
1634
|
-
v === STRING_NS +
|
|
1635
|
-
v === STRING_NS +
|
|
1607
|
+
v === STRING_NS + 'contains' ||
|
|
1608
|
+
v === STRING_NS + 'containsIgnoringCase' ||
|
|
1609
|
+
v === STRING_NS + 'endsWith' ||
|
|
1610
|
+
v === STRING_NS + 'equalIgnoringCase' ||
|
|
1611
|
+
v === STRING_NS + 'greaterThan' ||
|
|
1612
|
+
v === STRING_NS + 'lessThan' ||
|
|
1613
|
+
v === STRING_NS + 'matches' ||
|
|
1614
|
+
v === STRING_NS + 'notEqualIgnoringCase' ||
|
|
1615
|
+
v === STRING_NS + 'notGreaterThan' ||
|
|
1616
|
+
v === STRING_NS + 'notLessThan' ||
|
|
1617
|
+
v === STRING_NS + 'notMatches' ||
|
|
1618
|
+
v === STRING_NS + 'startsWith'
|
|
1636
1619
|
) {
|
|
1637
1620
|
return true;
|
|
1638
1621
|
}
|
|
@@ -1665,18 +1648,10 @@ function reorderPremiseForConstraints(premise) {
|
|
|
1665
1648
|
|
|
1666
1649
|
function containsVarTerm(t, v) {
|
|
1667
1650
|
if (t instanceof Var) return t.name === v;
|
|
1668
|
-
if (t instanceof ListTerm) return t.elems.some(e => containsVarTerm(e, v));
|
|
1669
|
-
if (t instanceof OpenListTerm)
|
|
1670
|
-
return (
|
|
1671
|
-
t.prefix.some(e => containsVarTerm(e, v)) || t.tailVar === v
|
|
1672
|
-
);
|
|
1651
|
+
if (t instanceof ListTerm) return t.elems.some((e) => containsVarTerm(e, v));
|
|
1652
|
+
if (t instanceof OpenListTerm) return t.prefix.some((e) => containsVarTerm(e, v)) || t.tailVar === v;
|
|
1673
1653
|
if (t instanceof FormulaTerm)
|
|
1674
|
-
return t.triples.some(
|
|
1675
|
-
tr =>
|
|
1676
|
-
containsVarTerm(tr.s, v) ||
|
|
1677
|
-
containsVarTerm(tr.p, v) ||
|
|
1678
|
-
containsVarTerm(tr.o, v)
|
|
1679
|
-
);
|
|
1654
|
+
return t.triples.some((tr) => containsVarTerm(tr.s, v) || containsVarTerm(tr.p, v) || containsVarTerm(tr.o, v));
|
|
1680
1655
|
return false;
|
|
1681
1656
|
}
|
|
1682
1657
|
|
|
@@ -1684,25 +1659,21 @@ function isGroundTermInFormula(t) {
|
|
|
1684
1659
|
// EYE-style: variables inside formula terms are treated as local placeholders,
|
|
1685
1660
|
// so they don't make the *surrounding triple* non-ground.
|
|
1686
1661
|
if (t instanceof OpenListTerm) return false;
|
|
1687
|
-
if (t instanceof ListTerm) return t.elems.every(e => isGroundTermInFormula(e));
|
|
1688
|
-
if (t instanceof FormulaTerm) return t.triples.every(tr => isGroundTripleInFormula(tr));
|
|
1662
|
+
if (t instanceof ListTerm) return t.elems.every((e) => isGroundTermInFormula(e));
|
|
1663
|
+
if (t instanceof FormulaTerm) return t.triples.every((tr) => isGroundTripleInFormula(tr));
|
|
1689
1664
|
// Iri/Literal/Blank/Var are all OK inside formulas
|
|
1690
1665
|
return true;
|
|
1691
1666
|
}
|
|
1692
1667
|
|
|
1693
1668
|
function isGroundTripleInFormula(tr) {
|
|
1694
|
-
return (
|
|
1695
|
-
isGroundTermInFormula(tr.s) &&
|
|
1696
|
-
isGroundTermInFormula(tr.p) &&
|
|
1697
|
-
isGroundTermInFormula(tr.o)
|
|
1698
|
-
);
|
|
1669
|
+
return isGroundTermInFormula(tr.s) && isGroundTermInFormula(tr.p) && isGroundTermInFormula(tr.o);
|
|
1699
1670
|
}
|
|
1700
1671
|
|
|
1701
1672
|
function isGroundTerm(t) {
|
|
1702
1673
|
if (t instanceof Var) return false;
|
|
1703
|
-
if (t instanceof ListTerm) return t.elems.every(e => isGroundTerm(e));
|
|
1674
|
+
if (t instanceof ListTerm) return t.elems.every((e) => isGroundTerm(e));
|
|
1704
1675
|
if (t instanceof OpenListTerm) return false;
|
|
1705
|
-
if (t instanceof FormulaTerm) return t.triples.every(tr => isGroundTripleInFormula(tr));
|
|
1676
|
+
if (t instanceof FormulaTerm) return t.triples.every((tr) => isGroundTripleInFormula(tr));
|
|
1706
1677
|
return true;
|
|
1707
1678
|
}
|
|
1708
1679
|
|
|
@@ -1715,23 +1686,14 @@ function isGroundTriple(tr) {
|
|
|
1715
1686
|
// robust to seeing vars/open lists anyway.
|
|
1716
1687
|
function skolemKeyFromTerm(t) {
|
|
1717
1688
|
function enc(u) {
|
|
1718
|
-
if (u instanceof Iri)
|
|
1719
|
-
if (u instanceof Literal)
|
|
1720
|
-
if (u instanceof Blank)
|
|
1721
|
-
if (u instanceof Var)
|
|
1722
|
-
if (u instanceof ListTerm) return [
|
|
1723
|
-
if (u instanceof OpenListTerm)
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
return [
|
|
1727
|
-
"Formula",
|
|
1728
|
-
u.triples.map(tr => [
|
|
1729
|
-
enc(tr.s),
|
|
1730
|
-
enc(tr.p),
|
|
1731
|
-
enc(tr.o)
|
|
1732
|
-
])
|
|
1733
|
-
];
|
|
1734
|
-
return ["Other", String(u)];
|
|
1689
|
+
if (u instanceof Iri) return ['I', u.value];
|
|
1690
|
+
if (u instanceof Literal) return ['L', u.value];
|
|
1691
|
+
if (u instanceof Blank) return ['B', u.label];
|
|
1692
|
+
if (u instanceof Var) return ['V', u.name];
|
|
1693
|
+
if (u instanceof ListTerm) return ['List', u.elems.map(enc)];
|
|
1694
|
+
if (u instanceof OpenListTerm) return ['OpenList', u.prefix.map(enc), u.tailVar];
|
|
1695
|
+
if (u instanceof FormulaTerm) return ['Formula', u.triples.map((tr) => [enc(tr.s), enc(tr.p), enc(tr.o)])];
|
|
1696
|
+
return ['Other', String(u)];
|
|
1735
1697
|
}
|
|
1736
1698
|
return JSON.stringify(enc(t));
|
|
1737
1699
|
}
|
|
@@ -1768,21 +1730,18 @@ function applySubstTerm(t, s) {
|
|
|
1768
1730
|
|
|
1769
1731
|
// Non-variable terms
|
|
1770
1732
|
if (t instanceof ListTerm) {
|
|
1771
|
-
return new ListTerm(t.elems.map(e => applySubstTerm(e, s)));
|
|
1733
|
+
return new ListTerm(t.elems.map((e) => applySubstTerm(e, s)));
|
|
1772
1734
|
}
|
|
1773
1735
|
|
|
1774
1736
|
if (t instanceof OpenListTerm) {
|
|
1775
|
-
const newPrefix = t.prefix.map(e => applySubstTerm(e, s));
|
|
1737
|
+
const newPrefix = t.prefix.map((e) => applySubstTerm(e, s));
|
|
1776
1738
|
const tailTerm = s[t.tailVar];
|
|
1777
1739
|
if (tailTerm !== undefined) {
|
|
1778
1740
|
const tailApplied = applySubstTerm(tailTerm, s);
|
|
1779
1741
|
if (tailApplied instanceof ListTerm) {
|
|
1780
1742
|
return new ListTerm(newPrefix.concat(tailApplied.elems));
|
|
1781
1743
|
} else if (tailApplied instanceof OpenListTerm) {
|
|
1782
|
-
return new OpenListTerm(
|
|
1783
|
-
newPrefix.concat(tailApplied.prefix),
|
|
1784
|
-
tailApplied.tailVar
|
|
1785
|
-
);
|
|
1744
|
+
return new OpenListTerm(newPrefix.concat(tailApplied.prefix), tailApplied.tailVar);
|
|
1786
1745
|
} else {
|
|
1787
1746
|
return new OpenListTerm(newPrefix, t.tailVar);
|
|
1788
1747
|
}
|
|
@@ -1792,18 +1751,14 @@ function applySubstTerm(t, s) {
|
|
|
1792
1751
|
}
|
|
1793
1752
|
|
|
1794
1753
|
if (t instanceof FormulaTerm) {
|
|
1795
|
-
return new FormulaTerm(t.triples.map(tr => applySubstTriple(tr, s)));
|
|
1754
|
+
return new FormulaTerm(t.triples.map((tr) => applySubstTriple(tr, s)));
|
|
1796
1755
|
}
|
|
1797
1756
|
|
|
1798
1757
|
return t;
|
|
1799
1758
|
}
|
|
1800
1759
|
|
|
1801
1760
|
function applySubstTriple(tr, s) {
|
|
1802
|
-
return new Triple(
|
|
1803
|
-
applySubstTerm(tr.s, s),
|
|
1804
|
-
applySubstTerm(tr.p, s),
|
|
1805
|
-
applySubstTerm(tr.o, s)
|
|
1806
|
-
);
|
|
1761
|
+
return new Triple(applySubstTerm(tr.s, s), applySubstTerm(tr.p, s), applySubstTerm(tr.o, s));
|
|
1807
1762
|
}
|
|
1808
1763
|
|
|
1809
1764
|
function unifyOpenWithList(prefix, tailv, ys, subst) {
|
|
@@ -1839,7 +1794,7 @@ function unifyFormulaTriples(xs, ys, subst) {
|
|
|
1839
1794
|
// Cheap pruning when both predicates are IRIs.
|
|
1840
1795
|
if (x.p instanceof Iri && y.p instanceof Iri && x.p.value !== y.p.value) continue;
|
|
1841
1796
|
|
|
1842
|
-
const s2 = unifyTriple(x, y, s);
|
|
1797
|
+
const s2 = unifyTriple(x, y, s); // IMPORTANT: use `s`, not {}
|
|
1843
1798
|
if (s2 === null) continue;
|
|
1844
1799
|
|
|
1845
1800
|
used[j] = true;
|
|
@@ -1873,12 +1828,9 @@ function unifyTerm(a, b, subst) {
|
|
|
1873
1828
|
}
|
|
1874
1829
|
|
|
1875
1830
|
// Exact matches
|
|
1876
|
-
if (a instanceof Iri && b instanceof Iri && a.value === b.value)
|
|
1877
|
-
|
|
1878
|
-
if (a instanceof
|
|
1879
|
-
return { ...subst };
|
|
1880
|
-
if (a instanceof Blank && b instanceof Blank && a.label === b.label)
|
|
1881
|
-
return { ...subst };
|
|
1831
|
+
if (a instanceof Iri && b instanceof Iri && a.value === b.value) return { ...subst };
|
|
1832
|
+
if (a instanceof Literal && b instanceof Literal && a.value === b.value) return { ...subst };
|
|
1833
|
+
if (a instanceof Blank && b instanceof Blank && a.label === b.label) return { ...subst };
|
|
1882
1834
|
|
|
1883
1835
|
// Open list vs concrete list
|
|
1884
1836
|
if (a instanceof OpenListTerm && b instanceof ListTerm) {
|
|
@@ -1890,8 +1842,7 @@ function unifyTerm(a, b, subst) {
|
|
|
1890
1842
|
|
|
1891
1843
|
// Open list vs open list (same tail var)
|
|
1892
1844
|
if (a instanceof OpenListTerm && b instanceof OpenListTerm) {
|
|
1893
|
-
if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length)
|
|
1894
|
-
return null;
|
|
1845
|
+
if (a.tailVar !== b.tailVar || a.prefix.length !== b.prefix.length) return null;
|
|
1895
1846
|
let s2 = { ...subst };
|
|
1896
1847
|
for (let i = 0; i < a.prefix.length; i++) {
|
|
1897
1848
|
s2 = unifyTerm(a.prefix[i], b.prefix[i], s2);
|
|
@@ -1957,14 +1908,14 @@ function literalParts(lit) {
|
|
|
1957
1908
|
// Also strip an optional language tag from the lexical form:
|
|
1958
1909
|
// "\"hello\"@en" -> "\"hello\""
|
|
1959
1910
|
// "\"hello\"@en^^<...>" is rejected earlier in the parser.
|
|
1960
|
-
const idx = lit.indexOf(
|
|
1911
|
+
const idx = lit.indexOf('^^');
|
|
1961
1912
|
let lex = lit;
|
|
1962
1913
|
let dt = null;
|
|
1963
1914
|
|
|
1964
1915
|
if (idx >= 0) {
|
|
1965
1916
|
lex = lit.slice(0, idx);
|
|
1966
1917
|
dt = lit.slice(idx + 2).trim();
|
|
1967
|
-
if (dt.startsWith(
|
|
1918
|
+
if (dt.startsWith('<') && dt.endsWith('>')) {
|
|
1968
1919
|
dt = dt.slice(1, -1);
|
|
1969
1920
|
}
|
|
1970
1921
|
}
|
|
@@ -1972,7 +1923,7 @@ function literalParts(lit) {
|
|
|
1972
1923
|
// Strip LANGTAG from the lexical form when present.
|
|
1973
1924
|
if (lex.length >= 2 && lex[0] === '"') {
|
|
1974
1925
|
const lastQuote = lex.lastIndexOf('"');
|
|
1975
|
-
if (lastQuote > 0 && lastQuote < lex.length - 1 && lex[lastQuote + 1] ===
|
|
1926
|
+
if (lastQuote > 0 && lastQuote < lex.length - 1 && lex[lastQuote + 1] === '@') {
|
|
1976
1927
|
const lang = lex.slice(lastQuote + 2);
|
|
1977
1928
|
if (/^[A-Za-z]+(?:-[A-Za-z0-9]+)*$/.test(lang)) {
|
|
1978
1929
|
lex = lex.slice(0, lastQuote + 1);
|
|
@@ -2019,7 +1970,11 @@ function termToJsStringDecoded(t) {
|
|
|
2019
1970
|
|
|
2020
1971
|
// Short strings: try to decode escapes (this makes "{\"a\":1}" usable too).
|
|
2021
1972
|
if (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') {
|
|
2022
|
-
try {
|
|
1973
|
+
try {
|
|
1974
|
+
return JSON.parse(lex);
|
|
1975
|
+
} catch (e) {
|
|
1976
|
+
/* fall through */
|
|
1977
|
+
}
|
|
2023
1978
|
return stripQuotes(lex);
|
|
2024
1979
|
}
|
|
2025
1980
|
|
|
@@ -2028,14 +1983,17 @@ function termToJsStringDecoded(t) {
|
|
|
2028
1983
|
|
|
2029
1984
|
function jsonPointerUnescape(seg) {
|
|
2030
1985
|
// RFC6901: ~1 -> '/', ~0 -> '~'
|
|
2031
|
-
let out =
|
|
1986
|
+
let out = '';
|
|
2032
1987
|
for (let i = 0; i < seg.length; i++) {
|
|
2033
1988
|
const c = seg[i];
|
|
2034
|
-
if (c !==
|
|
1989
|
+
if (c !== '~') {
|
|
1990
|
+
out += c;
|
|
1991
|
+
continue;
|
|
1992
|
+
}
|
|
2035
1993
|
if (i + 1 >= seg.length) return null;
|
|
2036
1994
|
const n = seg[i + 1];
|
|
2037
|
-
if (n ===
|
|
2038
|
-
else if (n ===
|
|
1995
|
+
if (n === '0') out += '~';
|
|
1996
|
+
else if (n === '1') out += '/';
|
|
2039
1997
|
else return null;
|
|
2040
1998
|
i++;
|
|
2041
1999
|
}
|
|
@@ -2043,13 +2001,13 @@ function jsonPointerUnescape(seg) {
|
|
|
2043
2001
|
}
|
|
2044
2002
|
|
|
2045
2003
|
function jsonToTerm(v) {
|
|
2046
|
-
if (v === null) return makeStringLiteral(
|
|
2047
|
-
if (typeof v ===
|
|
2048
|
-
if (typeof v ===
|
|
2049
|
-
if (typeof v ===
|
|
2004
|
+
if (v === null) return makeStringLiteral('null');
|
|
2005
|
+
if (typeof v === 'string') return makeStringLiteral(v);
|
|
2006
|
+
if (typeof v === 'number') return new Literal(String(v));
|
|
2007
|
+
if (typeof v === 'boolean') return new Literal(v ? 'true' : 'false');
|
|
2050
2008
|
if (Array.isArray(v)) return new ListTerm(v.map(jsonToTerm));
|
|
2051
2009
|
|
|
2052
|
-
if (v && typeof v ===
|
|
2010
|
+
if (v && typeof v === 'object') {
|
|
2053
2011
|
return makeRdfJsonLiteral(JSON.stringify(v));
|
|
2054
2012
|
}
|
|
2055
2013
|
return null;
|
|
@@ -2059,14 +2017,22 @@ function jsonPointerLookup(jsonText, pointer) {
|
|
|
2059
2017
|
let ptr = pointer;
|
|
2060
2018
|
|
|
2061
2019
|
// Support URI fragment form "#/a/b"
|
|
2062
|
-
if (ptr.startsWith(
|
|
2063
|
-
try {
|
|
2020
|
+
if (ptr.startsWith('#')) {
|
|
2021
|
+
try {
|
|
2022
|
+
ptr = decodeURIComponent(ptr.slice(1));
|
|
2023
|
+
} catch {
|
|
2024
|
+
return null;
|
|
2025
|
+
}
|
|
2064
2026
|
}
|
|
2065
2027
|
|
|
2066
2028
|
let entry = jsonPointerCache.get(jsonText);
|
|
2067
2029
|
if (!entry) {
|
|
2068
2030
|
let parsed = null;
|
|
2069
|
-
try {
|
|
2031
|
+
try {
|
|
2032
|
+
parsed = JSON.parse(jsonText);
|
|
2033
|
+
} catch {
|
|
2034
|
+
parsed = null;
|
|
2035
|
+
}
|
|
2070
2036
|
entry = { parsed, ptrCache: new Map() };
|
|
2071
2037
|
jsonPointerCache.set(jsonText, entry);
|
|
2072
2038
|
}
|
|
@@ -2076,25 +2042,40 @@ function jsonPointerLookup(jsonText, pointer) {
|
|
|
2076
2042
|
|
|
2077
2043
|
let cur = entry.parsed;
|
|
2078
2044
|
|
|
2079
|
-
if (ptr ===
|
|
2045
|
+
if (ptr === '') {
|
|
2080
2046
|
const t = jsonToTerm(cur);
|
|
2081
2047
|
entry.ptrCache.set(ptr, t);
|
|
2082
2048
|
return t;
|
|
2083
2049
|
}
|
|
2084
|
-
if (!ptr.startsWith(
|
|
2050
|
+
if (!ptr.startsWith('/')) {
|
|
2051
|
+
entry.ptrCache.set(ptr, null);
|
|
2052
|
+
return null;
|
|
2053
|
+
}
|
|
2085
2054
|
|
|
2086
|
-
const parts = ptr.split(
|
|
2055
|
+
const parts = ptr.split('/').slice(1);
|
|
2087
2056
|
for (const raw of parts) {
|
|
2088
2057
|
const seg = jsonPointerUnescape(raw);
|
|
2089
|
-
if (seg === null) {
|
|
2058
|
+
if (seg === null) {
|
|
2059
|
+
entry.ptrCache.set(ptr, null);
|
|
2060
|
+
return null;
|
|
2061
|
+
}
|
|
2090
2062
|
|
|
2091
2063
|
if (Array.isArray(cur)) {
|
|
2092
|
-
if (!/^(0|[1-9]\d*)$/.test(seg)) {
|
|
2064
|
+
if (!/^(0|[1-9]\d*)$/.test(seg)) {
|
|
2065
|
+
entry.ptrCache.set(ptr, null);
|
|
2066
|
+
return null;
|
|
2067
|
+
}
|
|
2093
2068
|
const idx = Number(seg);
|
|
2094
|
-
if (idx < 0 || idx >= cur.length) {
|
|
2069
|
+
if (idx < 0 || idx >= cur.length) {
|
|
2070
|
+
entry.ptrCache.set(ptr, null);
|
|
2071
|
+
return null;
|
|
2072
|
+
}
|
|
2095
2073
|
cur = cur[idx];
|
|
2096
|
-
} else if (cur !== null && typeof cur ===
|
|
2097
|
-
if (!Object.prototype.hasOwnProperty.call(cur, seg)) {
|
|
2074
|
+
} else if (cur !== null && typeof cur === 'object') {
|
|
2075
|
+
if (!Object.prototype.hasOwnProperty.call(cur, seg)) {
|
|
2076
|
+
entry.ptrCache.set(ptr, null);
|
|
2077
|
+
return null;
|
|
2078
|
+
}
|
|
2098
2079
|
cur = cur[seg];
|
|
2099
2080
|
} else {
|
|
2100
2081
|
entry.ptrCache.set(ptr, null);
|
|
@@ -2110,23 +2091,23 @@ function jsonPointerLookup(jsonText, pointer) {
|
|
|
2110
2091
|
// Tiny subset of sprintf: supports only %s and %%.
|
|
2111
2092
|
// Good enough for most N3 string:format use cases that just splice strings.
|
|
2112
2093
|
function simpleStringFormat(fmt, args) {
|
|
2113
|
-
let out =
|
|
2094
|
+
let out = '';
|
|
2114
2095
|
let argIndex = 0;
|
|
2115
2096
|
|
|
2116
2097
|
for (let i = 0; i < fmt.length; i++) {
|
|
2117
2098
|
const ch = fmt[i];
|
|
2118
|
-
if (ch ===
|
|
2099
|
+
if (ch === '%' && i + 1 < fmt.length) {
|
|
2119
2100
|
const spec = fmt[i + 1];
|
|
2120
2101
|
|
|
2121
|
-
if (spec ===
|
|
2122
|
-
const arg = argIndex < args.length ? args[argIndex++] :
|
|
2102
|
+
if (spec === 's') {
|
|
2103
|
+
const arg = argIndex < args.length ? args[argIndex++] : '';
|
|
2123
2104
|
out += arg;
|
|
2124
2105
|
i++;
|
|
2125
2106
|
continue;
|
|
2126
2107
|
}
|
|
2127
2108
|
|
|
2128
|
-
if (spec ===
|
|
2129
|
-
out +=
|
|
2109
|
+
if (spec === '%') {
|
|
2110
|
+
out += '%';
|
|
2130
2111
|
i++;
|
|
2131
2112
|
continue;
|
|
2132
2113
|
}
|
|
@@ -2140,33 +2121,118 @@ function simpleStringFormat(fmt, args) {
|
|
|
2140
2121
|
return out;
|
|
2141
2122
|
}
|
|
2142
2123
|
|
|
2124
|
+
// -----------------------------------------------------------------------------
|
|
2125
|
+
// Strict numeric literal parsing for math: builtins
|
|
2126
|
+
// -----------------------------------------------------------------------------
|
|
2127
|
+
const XSD_DECIMAL_DT = XSD_NS + 'decimal';
|
|
2128
|
+
const XSD_DOUBLE_DT = XSD_NS + 'double';
|
|
2129
|
+
const XSD_FLOAT_DT = XSD_NS + 'float';
|
|
2130
|
+
const XSD_INTEGER_DT = XSD_NS + 'integer';
|
|
2131
|
+
|
|
2132
|
+
// Integer-derived datatypes from XML Schema Part 2 (and commonly used ones).
|
|
2133
|
+
const XSD_INTEGER_DERIVED_DTS = new Set([
|
|
2134
|
+
XSD_INTEGER_DT,
|
|
2135
|
+
XSD_NS + 'nonPositiveInteger',
|
|
2136
|
+
XSD_NS + 'negativeInteger',
|
|
2137
|
+
XSD_NS + 'long',
|
|
2138
|
+
XSD_NS + 'int',
|
|
2139
|
+
XSD_NS + 'short',
|
|
2140
|
+
XSD_NS + 'byte',
|
|
2141
|
+
XSD_NS + 'nonNegativeInteger',
|
|
2142
|
+
XSD_NS + 'unsignedLong',
|
|
2143
|
+
XSD_NS + 'unsignedInt',
|
|
2144
|
+
XSD_NS + 'unsignedShort',
|
|
2145
|
+
XSD_NS + 'unsignedByte',
|
|
2146
|
+
XSD_NS + 'positiveInteger',
|
|
2147
|
+
]);
|
|
2148
|
+
|
|
2149
|
+
function isQuotedLexical(lex) {
|
|
2150
|
+
// Note: the lexer stores long strings with literal delimiters: """..."""
|
|
2151
|
+
return (
|
|
2152
|
+
(lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') || (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""'))
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
function isXsdNumericDatatype(dt) {
|
|
2157
|
+
if (dt === null) return false;
|
|
2158
|
+
return dt === XSD_DECIMAL_DT || dt === XSD_DOUBLE_DT || dt === XSD_FLOAT_DT || XSD_INTEGER_DERIVED_DTS.has(dt);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
function isXsdIntegerDatatype(dt) {
|
|
2162
|
+
if (dt === null) return false;
|
|
2163
|
+
return XSD_INTEGER_DERIVED_DTS.has(dt);
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
function looksLikeUntypedNumericTokenLex(lex) {
|
|
2167
|
+
// We only treat *unquoted* tokens as "untyped numeric" (Turtle/N3 numeric literal).
|
|
2168
|
+
// Quoted literals without datatype are strings, never numbers.
|
|
2169
|
+
if (isQuotedLexical(lex)) return false;
|
|
2170
|
+
|
|
2171
|
+
// integer
|
|
2172
|
+
if (/^[+-]?\d+$/.test(lex)) return true;
|
|
2173
|
+
|
|
2174
|
+
// decimal (no exponent)
|
|
2175
|
+
if (/^[+-]?(?:\d+\.\d*|\.\d+)$/.test(lex)) return true;
|
|
2176
|
+
|
|
2177
|
+
// double (with exponent)
|
|
2178
|
+
if (/^[+-]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][+-]?\d+)$/.test(lex)) return true;
|
|
2179
|
+
|
|
2180
|
+
return false;
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2143
2183
|
function parseNum(t) {
|
|
2144
|
-
// Parse as JS Number
|
|
2184
|
+
// Parse as JS Number, but ONLY for xsd numeric datatypes or untyped numeric tokens.
|
|
2185
|
+
// Rejects values such as "1"^^<...non-numeric...> or "1" (a string literal).
|
|
2145
2186
|
if (!(t instanceof Literal)) return null;
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
if (
|
|
2151
|
-
|
|
2187
|
+
|
|
2188
|
+
const [lex, dt] = literalParts(t.value);
|
|
2189
|
+
|
|
2190
|
+
// Typed literals: must be xsd numeric.
|
|
2191
|
+
if (dt !== null) {
|
|
2192
|
+
if (!isXsdNumericDatatype(dt)) return null;
|
|
2193
|
+
const val = stripQuotes(lex);
|
|
2194
|
+
const n = Number(val);
|
|
2195
|
+
if (!Number.isFinite(n)) return null;
|
|
2196
|
+
return n;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
// Untyped literals: accept only unquoted numeric tokens.
|
|
2200
|
+
if (!looksLikeUntypedNumericTokenLex(lex)) return null;
|
|
2201
|
+
const n = Number(lex);
|
|
2202
|
+
if (!Number.isFinite(n)) return null;
|
|
2203
|
+
return n;
|
|
2152
2204
|
}
|
|
2153
2205
|
|
|
2154
2206
|
function parseIntLiteral(t) {
|
|
2155
|
-
// Parse as BigInt if
|
|
2207
|
+
// Parse as BigInt if (and only if) it is an integer literal in an integer datatype,
|
|
2208
|
+
// or an untyped integer token.
|
|
2156
2209
|
if (!(t instanceof Literal)) return null;
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
if (
|
|
2210
|
+
|
|
2211
|
+
const [lex, dt] = literalParts(t.value);
|
|
2212
|
+
|
|
2213
|
+
if (dt !== null) {
|
|
2214
|
+
if (!isXsdIntegerDatatype(dt)) return null;
|
|
2215
|
+
const val = stripQuotes(lex);
|
|
2216
|
+
if (!/^[+-]?\d+$/.test(val)) return null;
|
|
2217
|
+
try {
|
|
2218
|
+
return BigInt(val);
|
|
2219
|
+
} catch {
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
// Untyped: only accept unquoted integer tokens.
|
|
2225
|
+
if (isQuotedLexical(lex)) return null;
|
|
2226
|
+
if (!/^[+-]?\d+$/.test(lex)) return null;
|
|
2161
2227
|
try {
|
|
2162
|
-
return BigInt(
|
|
2163
|
-
} catch
|
|
2228
|
+
return BigInt(lex);
|
|
2229
|
+
} catch {
|
|
2164
2230
|
return null;
|
|
2165
2231
|
}
|
|
2166
2232
|
}
|
|
2167
2233
|
|
|
2168
2234
|
function parseNumberLiteral(t) {
|
|
2169
|
-
// Prefer BigInt for integers, fall back to Number for
|
|
2235
|
+
// Prefer BigInt for integers, fall back to Number for other numeric literals.
|
|
2170
2236
|
const bi = parseIntLiteral(t);
|
|
2171
2237
|
if (bi !== null) return bi;
|
|
2172
2238
|
const n = parseNum(t);
|
|
@@ -2180,28 +2246,22 @@ function formatNum(n) {
|
|
|
2180
2246
|
|
|
2181
2247
|
function parseXsdDateTerm(t) {
|
|
2182
2248
|
if (!(t instanceof Literal)) return null;
|
|
2183
|
-
const
|
|
2184
|
-
|
|
2249
|
+
const [lex, dt] = literalParts(t.value);
|
|
2250
|
+
if (dt !== XSD_NS + 'date') return null;
|
|
2185
2251
|
const val = stripQuotes(lex);
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
return d;
|
|
2190
|
-
}
|
|
2191
|
-
return null;
|
|
2252
|
+
const d = new Date(val + 'T00:00:00Z');
|
|
2253
|
+
if (Number.isNaN(d.getTime())) return null;
|
|
2254
|
+
return d;
|
|
2192
2255
|
}
|
|
2193
2256
|
|
|
2194
2257
|
function parseXsdDatetimeTerm(t) {
|
|
2195
2258
|
if (!(t instanceof Literal)) return null;
|
|
2196
|
-
const
|
|
2197
|
-
|
|
2259
|
+
const [lex, dt] = literalParts(t.value);
|
|
2260
|
+
if (dt !== XSD_NS + 'dateTime') return null;
|
|
2198
2261
|
const val = stripQuotes(lex);
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
return d; // Date in local/UTC, we only use timestamp
|
|
2203
|
-
}
|
|
2204
|
-
return null;
|
|
2262
|
+
const d = new Date(val);
|
|
2263
|
+
if (Number.isNaN(d.getTime())) return null;
|
|
2264
|
+
return d; // Date in local/UTC, we only use timestamp
|
|
2205
2265
|
}
|
|
2206
2266
|
|
|
2207
2267
|
function parseDatetimeLike(t) {
|
|
@@ -2212,9 +2272,9 @@ function parseDatetimeLike(t) {
|
|
|
2212
2272
|
|
|
2213
2273
|
function parseIso8601DurationToSeconds(s) {
|
|
2214
2274
|
if (!s) return null;
|
|
2215
|
-
if (s[0] !==
|
|
2275
|
+
if (s[0] !== 'P') return null;
|
|
2216
2276
|
const it = s.slice(1);
|
|
2217
|
-
let num =
|
|
2277
|
+
let num = '';
|
|
2218
2278
|
let inTime = false;
|
|
2219
2279
|
let years = 0,
|
|
2220
2280
|
months = 0,
|
|
@@ -2225,7 +2285,7 @@ function parseIso8601DurationToSeconds(s) {
|
|
|
2225
2285
|
seconds = 0;
|
|
2226
2286
|
|
|
2227
2287
|
for (const c of it) {
|
|
2228
|
-
if (c ===
|
|
2288
|
+
if (c === 'T') {
|
|
2229
2289
|
inTime = true;
|
|
2230
2290
|
continue;
|
|
2231
2291
|
}
|
|
@@ -2236,48 +2296,31 @@ function parseIso8601DurationToSeconds(s) {
|
|
|
2236
2296
|
if (!num) return null;
|
|
2237
2297
|
const val = Number(num);
|
|
2238
2298
|
if (Number.isNaN(val)) return null;
|
|
2239
|
-
num =
|
|
2240
|
-
if (!inTime && c ===
|
|
2241
|
-
else if (!inTime && c ===
|
|
2242
|
-
else if (!inTime && c ===
|
|
2243
|
-
else if (!inTime && c ===
|
|
2244
|
-
else if (inTime && c ===
|
|
2245
|
-
else if (inTime && c ===
|
|
2246
|
-
else if (inTime && c ===
|
|
2299
|
+
num = '';
|
|
2300
|
+
if (!inTime && c === 'Y') years += val;
|
|
2301
|
+
else if (!inTime && c === 'M') months += val;
|
|
2302
|
+
else if (!inTime && c === 'W') weeks += val;
|
|
2303
|
+
else if (!inTime && c === 'D') days += val;
|
|
2304
|
+
else if (inTime && c === 'H') hours += val;
|
|
2305
|
+
else if (inTime && c === 'M') minutes += val;
|
|
2306
|
+
else if (inTime && c === 'S') seconds += val;
|
|
2247
2307
|
else return null;
|
|
2248
2308
|
}
|
|
2249
2309
|
|
|
2250
2310
|
const totalDays =
|
|
2251
|
-
years * 365.2425 +
|
|
2252
|
-
months * 30.436875 +
|
|
2253
|
-
weeks * 7.0 +
|
|
2254
|
-
days +
|
|
2255
|
-
hours / 24.0 +
|
|
2256
|
-
minutes / (24.0 * 60.0) +
|
|
2257
|
-
seconds / (24.0 * 3600.0);
|
|
2311
|
+
years * 365.2425 + months * 30.436875 + weeks * 7.0 + days + hours / 24.0 + minutes / (24.0 * 60.0) + seconds / (24.0 * 3600.0);
|
|
2258
2312
|
|
|
2259
2313
|
return totalDays * 86400.0;
|
|
2260
2314
|
}
|
|
2261
2315
|
|
|
2262
2316
|
function parseNumericForCompareTerm(t) {
|
|
2263
|
-
//
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
} catch (e) {
|
|
2271
|
-
// fall through
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
// durations / dateTimes / floats -> Number (seconds or numeric)
|
|
2275
|
-
const nDur = parseNumOrDuration(t);
|
|
2276
|
-
if (nDur !== null) return { kind: "number", value: nDur };
|
|
2277
|
-
return null;
|
|
2278
|
-
}
|
|
2279
|
-
const n = parseNumOrDuration(t);
|
|
2280
|
-
if (n !== null) return { kind: "number", value: n };
|
|
2317
|
+
// Strict: only accept xsd numeric literals, xsd:duration, xsd:date, xsd:dateTime
|
|
2318
|
+
// (or untyped numeric tokens).
|
|
2319
|
+
const bi = parseIntLiteral(t);
|
|
2320
|
+
if (bi !== null) return { kind: 'bigint', value: bi };
|
|
2321
|
+
|
|
2322
|
+
const nDur = parseNumOrDuration(t);
|
|
2323
|
+
if (nDur !== null) return { kind: 'number', value: nDur };
|
|
2281
2324
|
return null;
|
|
2282
2325
|
}
|
|
2283
2326
|
|
|
@@ -2285,47 +2328,47 @@ function cmpNumericInfo(aInfo, bInfo, op) {
|
|
|
2285
2328
|
// op is one of ">", "<", ">=", "<="
|
|
2286
2329
|
if (!aInfo || !bInfo) return false;
|
|
2287
2330
|
|
|
2288
|
-
if (aInfo.kind ===
|
|
2289
|
-
if (op ===
|
|
2290
|
-
if (op ===
|
|
2291
|
-
if (op ===
|
|
2292
|
-
if (op ===
|
|
2293
|
-
if (op ===
|
|
2294
|
-
if (op ===
|
|
2331
|
+
if (aInfo.kind === 'bigint' && bInfo.kind === 'bigint') {
|
|
2332
|
+
if (op === '>') return aInfo.value > bInfo.value;
|
|
2333
|
+
if (op === '<') return aInfo.value < bInfo.value;
|
|
2334
|
+
if (op === '>=') return aInfo.value >= bInfo.value;
|
|
2335
|
+
if (op === '<=') return aInfo.value <= bInfo.value;
|
|
2336
|
+
if (op === '==') return aInfo.value == bInfo.value;
|
|
2337
|
+
if (op === '!=') return aInfo.value != bInfo.value;
|
|
2295
2338
|
return false;
|
|
2296
2339
|
}
|
|
2297
2340
|
|
|
2298
|
-
const a = typeof aInfo.value ===
|
|
2299
|
-
const b = typeof bInfo.value ===
|
|
2341
|
+
const a = typeof aInfo.value === 'bigint' ? Number(aInfo.value) : aInfo.value;
|
|
2342
|
+
const b = typeof bInfo.value === 'bigint' ? Number(bInfo.value) : bInfo.value;
|
|
2300
2343
|
|
|
2301
|
-
if (op ===
|
|
2302
|
-
if (op ===
|
|
2303
|
-
if (op ===
|
|
2304
|
-
if (op ===
|
|
2305
|
-
if (op ===
|
|
2306
|
-
if (op ===
|
|
2344
|
+
if (op === '>') return a > b;
|
|
2345
|
+
if (op === '<') return a < b;
|
|
2346
|
+
if (op === '>=') return a >= b;
|
|
2347
|
+
if (op === '<=') return a <= b;
|
|
2348
|
+
if (op === '==') return a == b;
|
|
2349
|
+
if (op === '!=') return a != b;
|
|
2307
2350
|
return false;
|
|
2308
2351
|
}
|
|
2309
2352
|
|
|
2310
2353
|
function parseNumOrDuration(t) {
|
|
2311
2354
|
const n = parseNum(t);
|
|
2312
2355
|
if (n !== null) return n;
|
|
2356
|
+
|
|
2357
|
+
// xsd:duration
|
|
2313
2358
|
if (t instanceof Literal) {
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
dt === XSD_NS + "duration" ||
|
|
2319
|
-
val.startsWith("P") ||
|
|
2320
|
-
val.startsWith("-P")
|
|
2321
|
-
) {
|
|
2322
|
-
const negative = val.startsWith("-");
|
|
2359
|
+
const [lex, dt] = literalParts(t.value);
|
|
2360
|
+
if (dt === XSD_NS + 'duration') {
|
|
2361
|
+
const val = stripQuotes(lex);
|
|
2362
|
+
const negative = val.startsWith('-');
|
|
2323
2363
|
const core = negative ? val.slice(1) : val;
|
|
2364
|
+
if (!core.startsWith('P')) return null;
|
|
2324
2365
|
const secs = parseIso8601DurationToSeconds(core);
|
|
2325
2366
|
if (secs === null) return null;
|
|
2326
2367
|
return negative ? -secs : secs;
|
|
2327
2368
|
}
|
|
2328
2369
|
}
|
|
2370
|
+
|
|
2371
|
+
// xsd:date / xsd:dateTime
|
|
2329
2372
|
const dtval = parseDatetimeLike(t);
|
|
2330
2373
|
if (dtval !== null) {
|
|
2331
2374
|
return dtval.getTime() / 1000.0;
|
|
@@ -2377,10 +2420,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2377
2420
|
const [lex, _dt] = literalParts(t.value);
|
|
2378
2421
|
const input = stripQuotes(lex);
|
|
2379
2422
|
try {
|
|
2380
|
-
const digest = nodeCrypto
|
|
2381
|
-
.createHash(algo)
|
|
2382
|
-
.update(input, "utf8")
|
|
2383
|
-
.digest("hex");
|
|
2423
|
+
const digest = nodeCrypto.createHash(algo).update(input, 'utf8').digest('hex');
|
|
2384
2424
|
// plain string literal with the hex digest
|
|
2385
2425
|
return new Literal(JSON.stringify(digest));
|
|
2386
2426
|
} catch (e) {
|
|
@@ -2394,8 +2434,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2394
2434
|
|
|
2395
2435
|
// crypto:sha
|
|
2396
2436
|
// true iff ?o is the SHA-1 hash of the subject string.
|
|
2397
|
-
if (g.p instanceof Iri && g.p.value === CRYPTO_NS +
|
|
2398
|
-
const lit = hashLiteral(g.s,
|
|
2437
|
+
if (g.p instanceof Iri && g.p.value === CRYPTO_NS + 'sha') {
|
|
2438
|
+
const lit = hashLiteral(g.s, 'sha1');
|
|
2399
2439
|
if (!lit) return [];
|
|
2400
2440
|
if (g.o instanceof Var) {
|
|
2401
2441
|
const s2 = { ...subst };
|
|
@@ -2407,8 +2447,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2407
2447
|
}
|
|
2408
2448
|
|
|
2409
2449
|
// crypto:md5
|
|
2410
|
-
if (g.p instanceof Iri && g.p.value === CRYPTO_NS +
|
|
2411
|
-
const lit = hashLiteral(g.s,
|
|
2450
|
+
if (g.p instanceof Iri && g.p.value === CRYPTO_NS + 'md5') {
|
|
2451
|
+
const lit = hashLiteral(g.s, 'md5');
|
|
2412
2452
|
if (!lit) return [];
|
|
2413
2453
|
if (g.o instanceof Var) {
|
|
2414
2454
|
const s2 = { ...subst };
|
|
@@ -2420,8 +2460,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2420
2460
|
}
|
|
2421
2461
|
|
|
2422
2462
|
// crypto:sha256
|
|
2423
|
-
if (g.p instanceof Iri && g.p.value === CRYPTO_NS +
|
|
2424
|
-
const lit = hashLiteral(g.s,
|
|
2463
|
+
if (g.p instanceof Iri && g.p.value === CRYPTO_NS + 'sha256') {
|
|
2464
|
+
const lit = hashLiteral(g.s, 'sha256');
|
|
2425
2465
|
if (!lit) return [];
|
|
2426
2466
|
if (g.o instanceof Var) {
|
|
2427
2467
|
const s2 = { ...subst };
|
|
@@ -2433,8 +2473,8 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2433
2473
|
}
|
|
2434
2474
|
|
|
2435
2475
|
// crypto:sha512
|
|
2436
|
-
if (g.p instanceof Iri && g.p.value === CRYPTO_NS +
|
|
2437
|
-
const lit = hashLiteral(g.s,
|
|
2476
|
+
if (g.p instanceof Iri && g.p.value === CRYPTO_NS + 'sha512') {
|
|
2477
|
+
const lit = hashLiteral(g.s, 'sha512');
|
|
2438
2478
|
if (!lit) return [];
|
|
2439
2479
|
if (g.o instanceof Var) {
|
|
2440
2480
|
const s2 = { ...subst };
|
|
@@ -2450,91 +2490,91 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2450
2490
|
// -----------------------------------------------------------------
|
|
2451
2491
|
|
|
2452
2492
|
// math:greaterThan
|
|
2453
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2493
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'greaterThan') {
|
|
2454
2494
|
const aInfo = parseNumericForCompareTerm(g.s);
|
|
2455
2495
|
const bInfo = parseNumericForCompareTerm(g.o);
|
|
2456
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo,
|
|
2496
|
+
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '>')) return [{ ...subst }];
|
|
2457
2497
|
|
|
2458
2498
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2459
2499
|
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
2460
2500
|
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
2461
|
-
if (a2 && b2 && cmpNumericInfo(a2, b2,
|
|
2501
|
+
if (a2 && b2 && cmpNumericInfo(a2, b2, '>')) return [{ ...subst }];
|
|
2462
2502
|
}
|
|
2463
2503
|
return [];
|
|
2464
2504
|
}
|
|
2465
2505
|
|
|
2466
2506
|
// math:lessThan
|
|
2467
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2507
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'lessThan') {
|
|
2468
2508
|
const aInfo = parseNumericForCompareTerm(g.s);
|
|
2469
2509
|
const bInfo = parseNumericForCompareTerm(g.o);
|
|
2470
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo,
|
|
2510
|
+
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '<')) return [{ ...subst }];
|
|
2471
2511
|
|
|
2472
2512
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2473
2513
|
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
2474
2514
|
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
2475
|
-
if (a2 && b2 && cmpNumericInfo(a2, b2,
|
|
2515
|
+
if (a2 && b2 && cmpNumericInfo(a2, b2, '<')) return [{ ...subst }];
|
|
2476
2516
|
}
|
|
2477
2517
|
return [];
|
|
2478
2518
|
}
|
|
2479
2519
|
|
|
2480
2520
|
// math:notLessThan
|
|
2481
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2521
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'notLessThan') {
|
|
2482
2522
|
const aInfo = parseNumericForCompareTerm(g.s);
|
|
2483
2523
|
const bInfo = parseNumericForCompareTerm(g.o);
|
|
2484
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo,
|
|
2524
|
+
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '>=')) return [{ ...subst }];
|
|
2485
2525
|
|
|
2486
2526
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2487
2527
|
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
2488
2528
|
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
2489
|
-
if (a2 && b2 && cmpNumericInfo(a2, b2,
|
|
2529
|
+
if (a2 && b2 && cmpNumericInfo(a2, b2, '>=')) return [{ ...subst }];
|
|
2490
2530
|
}
|
|
2491
2531
|
return [];
|
|
2492
2532
|
}
|
|
2493
2533
|
|
|
2494
2534
|
// math:notGreaterThan
|
|
2495
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2535
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'notGreaterThan') {
|
|
2496
2536
|
const aInfo = parseNumericForCompareTerm(g.s);
|
|
2497
2537
|
const bInfo = parseNumericForCompareTerm(g.o);
|
|
2498
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo,
|
|
2538
|
+
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '<=')) return [{ ...subst }];
|
|
2499
2539
|
|
|
2500
2540
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2501
2541
|
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
2502
2542
|
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
2503
|
-
if (a2 && b2 && cmpNumericInfo(a2, b2,
|
|
2543
|
+
if (a2 && b2 && cmpNumericInfo(a2, b2, '<=')) return [{ ...subst }];
|
|
2504
2544
|
}
|
|
2505
2545
|
return [];
|
|
2506
2546
|
}
|
|
2507
2547
|
|
|
2508
2548
|
// math:equalTo
|
|
2509
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2549
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'equalTo') {
|
|
2510
2550
|
const aInfo = parseNumericForCompareTerm(g.s);
|
|
2511
2551
|
const bInfo = parseNumericForCompareTerm(g.o);
|
|
2512
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo,
|
|
2552
|
+
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '==')) return [{ ...subst }];
|
|
2513
2553
|
|
|
2514
2554
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2515
2555
|
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
2516
2556
|
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
2517
|
-
if (a2 && b2 && cmpNumericInfo(a2, b2,
|
|
2557
|
+
if (a2 && b2 && cmpNumericInfo(a2, b2, '==')) return [{ ...subst }];
|
|
2518
2558
|
}
|
|
2519
2559
|
return [];
|
|
2520
2560
|
}
|
|
2521
2561
|
|
|
2522
2562
|
// math:notEqualTo
|
|
2523
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2563
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'notEqualTo') {
|
|
2524
2564
|
const aInfo = parseNumericForCompareTerm(g.s);
|
|
2525
2565
|
const bInfo = parseNumericForCompareTerm(g.o);
|
|
2526
|
-
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo,
|
|
2566
|
+
if (aInfo && bInfo && cmpNumericInfo(aInfo, bInfo, '!=')) return [{ ...subst }];
|
|
2527
2567
|
|
|
2528
2568
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2529
2569
|
const a2 = parseNumericForCompareTerm(g.s.elems[0]);
|
|
2530
2570
|
const b2 = parseNumericForCompareTerm(g.s.elems[1]);
|
|
2531
|
-
if (a2 && b2 && cmpNumericInfo(a2, b2,
|
|
2571
|
+
if (a2 && b2 && cmpNumericInfo(a2, b2, '!=')) return [{ ...subst }];
|
|
2532
2572
|
}
|
|
2533
2573
|
return [];
|
|
2534
2574
|
}
|
|
2535
2575
|
|
|
2536
2576
|
// math:sum
|
|
2537
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2577
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'sum') {
|
|
2538
2578
|
if (g.s instanceof ListTerm && g.s.elems.length >= 2) {
|
|
2539
2579
|
const xs = g.s.elems;
|
|
2540
2580
|
const values = [];
|
|
@@ -2545,7 +2585,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2545
2585
|
}
|
|
2546
2586
|
|
|
2547
2587
|
let lit;
|
|
2548
|
-
const allBig = values.every(v => typeof v ===
|
|
2588
|
+
const allBig = values.every((v) => typeof v === 'bigint');
|
|
2549
2589
|
if (allBig) {
|
|
2550
2590
|
let total = 0n;
|
|
2551
2591
|
for (const v of values) total += v;
|
|
@@ -2553,7 +2593,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2553
2593
|
} else {
|
|
2554
2594
|
let total = 0.0;
|
|
2555
2595
|
for (const v of values) {
|
|
2556
|
-
total += typeof v ===
|
|
2596
|
+
total += typeof v === 'bigint' ? Number(v) : v;
|
|
2557
2597
|
}
|
|
2558
2598
|
lit = new Literal(formatNum(total));
|
|
2559
2599
|
}
|
|
@@ -2570,7 +2610,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2570
2610
|
}
|
|
2571
2611
|
|
|
2572
2612
|
// math:product
|
|
2573
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2613
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'product') {
|
|
2574
2614
|
if (g.s instanceof ListTerm && g.s.elems.length >= 2) {
|
|
2575
2615
|
const xs = g.s.elems;
|
|
2576
2616
|
const values = [];
|
|
@@ -2581,7 +2621,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2581
2621
|
}
|
|
2582
2622
|
|
|
2583
2623
|
let lit;
|
|
2584
|
-
const allBig = values.every(v => typeof v ===
|
|
2624
|
+
const allBig = values.every((v) => typeof v === 'bigint');
|
|
2585
2625
|
if (allBig) {
|
|
2586
2626
|
let prod = 1n;
|
|
2587
2627
|
for (const v of values) prod *= v;
|
|
@@ -2589,7 +2629,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2589
2629
|
} else {
|
|
2590
2630
|
let prod = 1.0;
|
|
2591
2631
|
for (const v of values) {
|
|
2592
|
-
prod *= typeof v ===
|
|
2632
|
+
prod *= typeof v === 'bigint' ? Number(v) : v;
|
|
2593
2633
|
}
|
|
2594
2634
|
lit = new Literal(formatNum(prod));
|
|
2595
2635
|
}
|
|
@@ -2607,7 +2647,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2607
2647
|
}
|
|
2608
2648
|
|
|
2609
2649
|
// math:difference
|
|
2610
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2650
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'difference') {
|
|
2611
2651
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2612
2652
|
const [a0, b0] = g.s.elems;
|
|
2613
2653
|
|
|
@@ -2662,7 +2702,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2662
2702
|
}
|
|
2663
2703
|
|
|
2664
2704
|
// math:quotient
|
|
2665
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2705
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'quotient') {
|
|
2666
2706
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2667
2707
|
const a = parseNum(g.s.elems[0]);
|
|
2668
2708
|
const b = parseNum(g.s.elems[1]);
|
|
@@ -2684,7 +2724,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2684
2724
|
// math:integerQuotient
|
|
2685
2725
|
// Schema: ( $a $b ) math:integerQuotient $q
|
|
2686
2726
|
// Cwm: divide first integer by second integer, ignoring remainder. :contentReference[oaicite:1]{index=1}
|
|
2687
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2727
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'integerQuotient') {
|
|
2688
2728
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
2689
2729
|
const [a0, b0] = g.s.elems;
|
|
2690
2730
|
|
|
@@ -2723,7 +2763,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2723
2763
|
}
|
|
2724
2764
|
|
|
2725
2765
|
// math:exponentiation
|
|
2726
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2766
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'exponentiation') {
|
|
2727
2767
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
2728
2768
|
const a = parseNum(g.s.elems[0]);
|
|
2729
2769
|
const b0 = g.s.elems[1];
|
|
@@ -2757,7 +2797,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2757
2797
|
}
|
|
2758
2798
|
|
|
2759
2799
|
// math:negation
|
|
2760
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2800
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'negation') {
|
|
2761
2801
|
const a = parseNum(g.s);
|
|
2762
2802
|
if (a !== null && g.o instanceof Var) {
|
|
2763
2803
|
const s2 = { ...subst };
|
|
@@ -2779,7 +2819,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2779
2819
|
}
|
|
2780
2820
|
|
|
2781
2821
|
// math:absoluteValue
|
|
2782
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2822
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'absoluteValue') {
|
|
2783
2823
|
const a = parseNum(g.s);
|
|
2784
2824
|
if (a !== null && g.o instanceof Var) {
|
|
2785
2825
|
const s2 = { ...subst };
|
|
@@ -2794,7 +2834,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2794
2834
|
}
|
|
2795
2835
|
|
|
2796
2836
|
// math:cos
|
|
2797
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2837
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'cos') {
|
|
2798
2838
|
const a = parseNum(g.s);
|
|
2799
2839
|
if (a !== null) {
|
|
2800
2840
|
const cVal = Math.cos(a);
|
|
@@ -2811,7 +2851,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2811
2851
|
}
|
|
2812
2852
|
|
|
2813
2853
|
// math:sin
|
|
2814
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2854
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'sin') {
|
|
2815
2855
|
const a = parseNum(g.s);
|
|
2816
2856
|
if (a !== null) {
|
|
2817
2857
|
const cVal = Math.sin(a);
|
|
@@ -2828,7 +2868,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2828
2868
|
}
|
|
2829
2869
|
|
|
2830
2870
|
// math:acos
|
|
2831
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2871
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'acos') {
|
|
2832
2872
|
const a = parseNum(g.s);
|
|
2833
2873
|
if (a !== null) {
|
|
2834
2874
|
const cVal = Math.acos(a);
|
|
@@ -2847,7 +2887,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2847
2887
|
}
|
|
2848
2888
|
|
|
2849
2889
|
// math:asin
|
|
2850
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2890
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'asin') {
|
|
2851
2891
|
const a = parseNum(g.s);
|
|
2852
2892
|
if (a !== null) {
|
|
2853
2893
|
const cVal = Math.asin(a);
|
|
@@ -2866,7 +2906,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2866
2906
|
}
|
|
2867
2907
|
|
|
2868
2908
|
// math:atan
|
|
2869
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2909
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'atan') {
|
|
2870
2910
|
const a = parseNum(g.s);
|
|
2871
2911
|
if (a !== null) {
|
|
2872
2912
|
const cVal = Math.atan(a);
|
|
@@ -2886,9 +2926,9 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2886
2926
|
|
|
2887
2927
|
// math:cosh
|
|
2888
2928
|
// Hyperbolic cosine
|
|
2889
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2929
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'cosh') {
|
|
2890
2930
|
const a = parseNum(g.s);
|
|
2891
|
-
if (a !== null && typeof Math.cosh ===
|
|
2931
|
+
if (a !== null && typeof Math.cosh === 'function') {
|
|
2892
2932
|
const cVal = Math.cosh(a);
|
|
2893
2933
|
if (Number.isFinite(cVal)) {
|
|
2894
2934
|
if (g.o instanceof Var) {
|
|
@@ -2906,7 +2946,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2906
2946
|
|
|
2907
2947
|
// math:degrees
|
|
2908
2948
|
// Convert radians -> degrees
|
|
2909
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2949
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'degrees') {
|
|
2910
2950
|
const a = parseNum(g.s);
|
|
2911
2951
|
if (a !== null) {
|
|
2912
2952
|
const cVal = (a * 180.0) / Math.PI;
|
|
@@ -2927,7 +2967,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2927
2967
|
// math:remainder
|
|
2928
2968
|
// Subject is a list (dividend divisor); object is the remainder.
|
|
2929
2969
|
// Schema: ( $a $b ) math:remainder $r
|
|
2930
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2970
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'remainder') {
|
|
2931
2971
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
2932
2972
|
const a = parseNum(g.s.elems[0]);
|
|
2933
2973
|
const b = parseNum(g.s.elems[1]);
|
|
@@ -2949,7 +2989,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2949
2989
|
// Round to nearest integer.
|
|
2950
2990
|
// If there are two such numbers, then the one closest to positive infinity is returned.
|
|
2951
2991
|
// Schema: $s math:rounded $o
|
|
2952
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
2992
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'rounded') {
|
|
2953
2993
|
const a = parseNum(g.s);
|
|
2954
2994
|
if (a === null) return [];
|
|
2955
2995
|
const rVal = Math.round(a);
|
|
@@ -2965,9 +3005,9 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2965
3005
|
}
|
|
2966
3006
|
|
|
2967
3007
|
// math:sinh
|
|
2968
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
3008
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'sinh') {
|
|
2969
3009
|
const a = parseNum(g.s);
|
|
2970
|
-
if (a !== null && typeof Math.sinh ===
|
|
3010
|
+
if (a !== null && typeof Math.sinh === 'function') {
|
|
2971
3011
|
const cVal = Math.sinh(a);
|
|
2972
3012
|
if (Number.isFinite(cVal)) {
|
|
2973
3013
|
if (g.o instanceof Var) {
|
|
@@ -2984,7 +3024,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2984
3024
|
}
|
|
2985
3025
|
|
|
2986
3026
|
// math:tan
|
|
2987
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
3027
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'tan') {
|
|
2988
3028
|
const a = parseNum(g.s);
|
|
2989
3029
|
if (a !== null) {
|
|
2990
3030
|
const cVal = Math.tan(a);
|
|
@@ -3003,9 +3043,9 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3003
3043
|
}
|
|
3004
3044
|
|
|
3005
3045
|
// math:tanh
|
|
3006
|
-
if (g.p instanceof Iri && g.p.value === MATH_NS +
|
|
3046
|
+
if (g.p instanceof Iri && g.p.value === MATH_NS + 'tanh') {
|
|
3007
3047
|
const a = parseNum(g.s);
|
|
3008
|
-
if (a !== null && typeof Math.tanh ===
|
|
3048
|
+
if (a !== null && typeof Math.tanh === 'function') {
|
|
3009
3049
|
const cVal = Math.tanh(a);
|
|
3010
3050
|
if (Number.isFinite(cVal)) {
|
|
3011
3051
|
if (g.o instanceof Var) {
|
|
@@ -3027,7 +3067,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3027
3067
|
|
|
3028
3068
|
// time:localTime
|
|
3029
3069
|
// "" time:localTime ?D. binds ?D to “now” as xsd:dateTime.
|
|
3030
|
-
if (g.p instanceof Iri && g.p.value === TIME_NS +
|
|
3070
|
+
if (g.p instanceof Iri && g.p.value === TIME_NS + 'localTime') {
|
|
3031
3071
|
const now = getNowLex();
|
|
3032
3072
|
|
|
3033
3073
|
if (g.o instanceof Var) {
|
|
@@ -3049,7 +3089,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3049
3089
|
// list:append
|
|
3050
3090
|
// true if and only if $o is the concatenation of all lists $s.i.
|
|
3051
3091
|
// Schema: ( $s.i?[*] )+ list:append $o?
|
|
3052
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3092
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'append') {
|
|
3053
3093
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3054
3094
|
const parts = g.s.elems;
|
|
3055
3095
|
if (g.o instanceof ListTerm) {
|
|
@@ -3073,7 +3113,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3073
3113
|
// list:first
|
|
3074
3114
|
// true iff $s is a list and $o is the first member of that list.
|
|
3075
3115
|
// Schema: $s+ list:first $o-
|
|
3076
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3116
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'first') {
|
|
3077
3117
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3078
3118
|
if (!g.s.elems.length) return [];
|
|
3079
3119
|
const first = g.s.elems[0];
|
|
@@ -3084,7 +3124,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3084
3124
|
// list:rest
|
|
3085
3125
|
// true iff $s is a (non-empty) list and $o is the rest (tail) of that list.
|
|
3086
3126
|
// Schema: $s+ list:rest $o-
|
|
3087
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3127
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'rest') {
|
|
3088
3128
|
// Closed list: (a b c) -> (b c)
|
|
3089
3129
|
if (g.s instanceof ListTerm) {
|
|
3090
3130
|
if (!g.s.elems.length) return [];
|
|
@@ -3113,7 +3153,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3113
3153
|
|
|
3114
3154
|
// rdf:first (alias of list:first)
|
|
3115
3155
|
// Schema: $s+ rdf:first $o-
|
|
3116
|
-
if (g.p instanceof Iri && g.p.value === RDF_NS +
|
|
3156
|
+
if (g.p instanceof Iri && g.p.value === RDF_NS + 'first') {
|
|
3117
3157
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3118
3158
|
if (!g.s.elems.length) return [];
|
|
3119
3159
|
const first = g.s.elems[0];
|
|
@@ -3123,7 +3163,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3123
3163
|
|
|
3124
3164
|
// rdf:rest (alias of list:rest)
|
|
3125
3165
|
// Schema: $s+ rdf:rest $o-
|
|
3126
|
-
if (g.p instanceof Iri && g.p.value === RDF_NS +
|
|
3166
|
+
if (g.p instanceof Iri && g.p.value === RDF_NS + 'rest') {
|
|
3127
3167
|
// Closed list: (a b c) -> (b c)
|
|
3128
3168
|
if (g.s instanceof ListTerm) {
|
|
3129
3169
|
if (!g.s.elems.length) return [];
|
|
@@ -3149,7 +3189,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3149
3189
|
// Multi-solution builtin:
|
|
3150
3190
|
// For a list subject $s, generate solutions by unifying $o with (index value).
|
|
3151
3191
|
// This allows $o to be a variable (e.g., ?Y) or a pattern (e.g., (?i "Dewey")).
|
|
3152
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3192
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'iterate') {
|
|
3153
3193
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3154
3194
|
const xs = g.s.elems;
|
|
3155
3195
|
const outs = [];
|
|
@@ -3165,7 +3205,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3165
3205
|
// list:last
|
|
3166
3206
|
// true iff $s is a list and $o is the last member of that list.
|
|
3167
3207
|
// Schema: $s+ list:last $o-
|
|
3168
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3208
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'last') {
|
|
3169
3209
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3170
3210
|
const xs = g.s.elems;
|
|
3171
3211
|
if (!xs.length) return [];
|
|
@@ -3177,7 +3217,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3177
3217
|
// list:memberAt
|
|
3178
3218
|
// true iff $s.1 is a list, $s.2 is a valid index, and $o is the member at that index.
|
|
3179
3219
|
// Schema: ( $s.1+ $s.2?[*] )+ list:memberAt $o?[*]
|
|
3180
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3220
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'memberAt') {
|
|
3181
3221
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3182
3222
|
const [listTerm, indexTerm] = g.s.elems;
|
|
3183
3223
|
if (!(listTerm instanceof ListTerm)) return [];
|
|
@@ -3198,7 +3238,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3198
3238
|
// list:remove
|
|
3199
3239
|
// true iff $s.1 is a list and $o is that list with all occurrences of $s.2 removed.
|
|
3200
3240
|
// Schema: ( $s.1+ $s.2+ )+ list:remove $o-
|
|
3201
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3241
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'remove') {
|
|
3202
3242
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3203
3243
|
const [listTerm, itemTerm] = g.s.elems;
|
|
3204
3244
|
if (!(listTerm instanceof ListTerm)) return [];
|
|
@@ -3213,7 +3253,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3213
3253
|
}
|
|
3214
3254
|
|
|
3215
3255
|
// list:member
|
|
3216
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3256
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'member') {
|
|
3217
3257
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3218
3258
|
const outs = [];
|
|
3219
3259
|
for (const x of g.s.elems) {
|
|
@@ -3224,7 +3264,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3224
3264
|
}
|
|
3225
3265
|
|
|
3226
3266
|
// list:in
|
|
3227
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3267
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'in') {
|
|
3228
3268
|
if (!(g.o instanceof ListTerm)) return [];
|
|
3229
3269
|
const outs = [];
|
|
3230
3270
|
for (const x of g.o.elems) {
|
|
@@ -3235,7 +3275,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3235
3275
|
}
|
|
3236
3276
|
|
|
3237
3277
|
// list:length
|
|
3238
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3278
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'length') {
|
|
3239
3279
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3240
3280
|
const nTerm = new Literal(String(g.s.elems.length));
|
|
3241
3281
|
const s2 = unifyTerm(g.o, nTerm, subst);
|
|
@@ -3243,7 +3283,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3243
3283
|
}
|
|
3244
3284
|
|
|
3245
3285
|
// list:notMember
|
|
3246
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3286
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'notMember') {
|
|
3247
3287
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3248
3288
|
for (const el of g.s.elems) {
|
|
3249
3289
|
if (unifyTerm(g.o, el, subst) !== null) return [];
|
|
@@ -3252,7 +3292,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3252
3292
|
}
|
|
3253
3293
|
|
|
3254
3294
|
// list:reverse
|
|
3255
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3295
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'reverse') {
|
|
3256
3296
|
if (g.s instanceof ListTerm) {
|
|
3257
3297
|
const rev = [...g.s.elems].reverse();
|
|
3258
3298
|
const rterm = new ListTerm(rev);
|
|
@@ -3269,7 +3309,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3269
3309
|
}
|
|
3270
3310
|
|
|
3271
3311
|
// list:sort
|
|
3272
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3312
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'sort') {
|
|
3273
3313
|
function cmpTermForSort(a, b) {
|
|
3274
3314
|
if (a instanceof Literal && b instanceof Literal) {
|
|
3275
3315
|
const [lexA] = literalParts(a.value);
|
|
@@ -3321,7 +3361,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3321
3361
|
else if (g.o instanceof ListTerm) inputList = g.o.elems;
|
|
3322
3362
|
else return [];
|
|
3323
3363
|
|
|
3324
|
-
if (!inputList.every(e => isGroundTerm(e))) return [];
|
|
3364
|
+
if (!inputList.every((e) => isGroundTerm(e))) return [];
|
|
3325
3365
|
|
|
3326
3366
|
const sortedList = [...inputList].sort(cmpTermForSort);
|
|
3327
3367
|
const sortedTerm = new ListTerm(sortedList);
|
|
@@ -3337,7 +3377,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3337
3377
|
}
|
|
3338
3378
|
|
|
3339
3379
|
// list:map
|
|
3340
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3380
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'map') {
|
|
3341
3381
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3342
3382
|
const [inputTerm, predTerm] = g.s.elems;
|
|
3343
3383
|
if (!(inputTerm instanceof ListTerm)) return [];
|
|
@@ -3345,11 +3385,11 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3345
3385
|
if (!(predTerm instanceof Iri)) return [];
|
|
3346
3386
|
const pred = new Iri(predTerm.value);
|
|
3347
3387
|
if (!isBuiltinPred(pred)) return [];
|
|
3348
|
-
if (!inputList.every(e => isGroundTerm(e))) return [];
|
|
3388
|
+
if (!inputList.every((e) => isGroundTerm(e))) return [];
|
|
3349
3389
|
|
|
3350
3390
|
const results = [];
|
|
3351
3391
|
for (const el of inputList) {
|
|
3352
|
-
const yvar = new Var(
|
|
3392
|
+
const yvar = new Var('_mapY');
|
|
3353
3393
|
const goal2 = new Triple(el, pred, yvar);
|
|
3354
3394
|
const sols = evalBuiltin(goal2, subst, facts, backRules, depth + 1, varGen);
|
|
3355
3395
|
if (!sols.length) return [];
|
|
@@ -3363,7 +3403,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3363
3403
|
}
|
|
3364
3404
|
|
|
3365
3405
|
// list:firstRest
|
|
3366
|
-
if (g.p instanceof Iri && g.p.value === LIST_NS +
|
|
3406
|
+
if (g.p instanceof Iri && g.p.value === LIST_NS + 'firstRest') {
|
|
3367
3407
|
if (g.s instanceof ListTerm) {
|
|
3368
3408
|
if (!g.s.elems.length) return [];
|
|
3369
3409
|
const first = g.s.elems[0];
|
|
@@ -3401,20 +3441,20 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3401
3441
|
// -----------------------------------------------------------------
|
|
3402
3442
|
|
|
3403
3443
|
// log:equalTo
|
|
3404
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3444
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'equalTo') {
|
|
3405
3445
|
const s2 = unifyTerm(goal.s, goal.o, subst);
|
|
3406
3446
|
return s2 !== null ? [s2] : [];
|
|
3407
3447
|
}
|
|
3408
3448
|
|
|
3409
3449
|
// log:notEqualTo
|
|
3410
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3450
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'notEqualTo') {
|
|
3411
3451
|
const s2 = unifyTerm(goal.s, goal.o, subst);
|
|
3412
3452
|
if (s2 !== null) return [];
|
|
3413
3453
|
return [{ ...subst }];
|
|
3414
3454
|
}
|
|
3415
3455
|
|
|
3416
3456
|
// log:implies — expose internal forward rules as data
|
|
3417
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3457
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'implies') {
|
|
3418
3458
|
const allFw = backRules.__allForwardRules || [];
|
|
3419
3459
|
const results = [];
|
|
3420
3460
|
|
|
@@ -3425,9 +3465,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3425
3465
|
const r = standardizeRule(r0, varGen);
|
|
3426
3466
|
|
|
3427
3467
|
const premF = new FormulaTerm(r.premise);
|
|
3428
|
-
const concTerm = r0.isFuse
|
|
3429
|
-
? new Literal("false")
|
|
3430
|
-
: new FormulaTerm(r.conclusion);
|
|
3468
|
+
const concTerm = r0.isFuse ? new Literal('false') : new FormulaTerm(r.conclusion);
|
|
3431
3469
|
|
|
3432
3470
|
// unify subject with the premise formula
|
|
3433
3471
|
let s2 = unifyTerm(goal.s, premF, subst);
|
|
@@ -3444,7 +3482,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3444
3482
|
}
|
|
3445
3483
|
|
|
3446
3484
|
// log:impliedBy — expose internal backward rules as data
|
|
3447
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3485
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'impliedBy') {
|
|
3448
3486
|
const allBw = backRules.__allBackwardRules || backRules;
|
|
3449
3487
|
const results = [];
|
|
3450
3488
|
|
|
@@ -3473,39 +3511,23 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3473
3511
|
}
|
|
3474
3512
|
|
|
3475
3513
|
// log:notIncludes
|
|
3476
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3514
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'notIncludes') {
|
|
3477
3515
|
if (!(g.o instanceof FormulaTerm)) return [];
|
|
3478
3516
|
const body = g.o.triples;
|
|
3479
3517
|
const visited2 = [];
|
|
3480
|
-
const sols = proveGoals(
|
|
3481
|
-
Array.from(body),
|
|
3482
|
-
{},
|
|
3483
|
-
facts,
|
|
3484
|
-
backRules,
|
|
3485
|
-
depth + 1,
|
|
3486
|
-
visited2,
|
|
3487
|
-
varGen
|
|
3488
|
-
);
|
|
3518
|
+
const sols = proveGoals(Array.from(body), {}, facts, backRules, depth + 1, visited2, varGen);
|
|
3489
3519
|
if (!sols.length) return [{ ...subst }];
|
|
3490
3520
|
return [];
|
|
3491
3521
|
}
|
|
3492
3522
|
|
|
3493
3523
|
// log:collectAllIn
|
|
3494
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3524
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'collectAllIn') {
|
|
3495
3525
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 3) return [];
|
|
3496
3526
|
const [valueTempl, clauseTerm, listTerm] = g.s.elems;
|
|
3497
3527
|
if (!(clauseTerm instanceof FormulaTerm)) return [];
|
|
3498
3528
|
const body = clauseTerm.triples;
|
|
3499
3529
|
const visited2 = [];
|
|
3500
|
-
const sols = proveGoals(
|
|
3501
|
-
Array.from(body),
|
|
3502
|
-
{},
|
|
3503
|
-
facts,
|
|
3504
|
-
backRules,
|
|
3505
|
-
depth + 1,
|
|
3506
|
-
visited2,
|
|
3507
|
-
varGen
|
|
3508
|
-
);
|
|
3530
|
+
const sols = proveGoals(Array.from(body), {}, facts, backRules, depth + 1, visited2, varGen);
|
|
3509
3531
|
|
|
3510
3532
|
// Collect one value per *solution*, duplicates allowed
|
|
3511
3533
|
const collected = [];
|
|
@@ -3520,7 +3542,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3520
3542
|
}
|
|
3521
3543
|
|
|
3522
3544
|
// log:forAllIn
|
|
3523
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3545
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'forAllIn') {
|
|
3524
3546
|
// Subject: list with two clauses (where-clause, then-clause)
|
|
3525
3547
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3526
3548
|
const [whereClause, thenClause] = g.s.elems;
|
|
@@ -3529,29 +3551,13 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3529
3551
|
|
|
3530
3552
|
// 1. Find all substitutions that make the first clause true
|
|
3531
3553
|
const visited1 = [];
|
|
3532
|
-
const sols1 = proveGoals(
|
|
3533
|
-
Array.from(whereClause.triples),
|
|
3534
|
-
{},
|
|
3535
|
-
facts,
|
|
3536
|
-
backRules,
|
|
3537
|
-
depth + 1,
|
|
3538
|
-
visited1,
|
|
3539
|
-
varGen
|
|
3540
|
-
);
|
|
3554
|
+
const sols1 = proveGoals(Array.from(whereClause.triples), {}, facts, backRules, depth + 1, visited1, varGen);
|
|
3541
3555
|
|
|
3542
3556
|
// 2. For every such substitution, check that the second clause holds too.
|
|
3543
3557
|
// If there are no matches for the first clause, this is vacuously true.
|
|
3544
3558
|
for (const s1 of sols1) {
|
|
3545
3559
|
const visited2 = [];
|
|
3546
|
-
const sols2 = proveGoals(
|
|
3547
|
-
Array.from(thenClause.triples),
|
|
3548
|
-
s1,
|
|
3549
|
-
facts,
|
|
3550
|
-
backRules,
|
|
3551
|
-
depth + 1,
|
|
3552
|
-
visited2,
|
|
3553
|
-
varGen
|
|
3554
|
-
);
|
|
3560
|
+
const sols2 = proveGoals(Array.from(thenClause.triples), s1, facts, backRules, depth + 1, visited2, varGen);
|
|
3555
3561
|
// Found a counterexample: whereClause holds but thenClause does not
|
|
3556
3562
|
if (!sols2.length) return [];
|
|
3557
3563
|
}
|
|
@@ -3561,7 +3567,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3561
3567
|
}
|
|
3562
3568
|
|
|
3563
3569
|
// log:skolem
|
|
3564
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3570
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'skolem') {
|
|
3565
3571
|
// Subject must be ground; commonly a list, but we allow any ground term.
|
|
3566
3572
|
if (!isGroundTerm(g.s)) return [];
|
|
3567
3573
|
|
|
@@ -3578,10 +3584,10 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3578
3584
|
}
|
|
3579
3585
|
|
|
3580
3586
|
// log:uri
|
|
3581
|
-
if (g.p instanceof Iri && g.p.value === LOG_NS +
|
|
3587
|
+
if (g.p instanceof Iri && g.p.value === LOG_NS + 'uri') {
|
|
3582
3588
|
// Direction 1: subject is an IRI -> object is its string representation
|
|
3583
3589
|
if (g.s instanceof Iri) {
|
|
3584
|
-
const uriStr = g.s.value;
|
|
3590
|
+
const uriStr = g.s.value; // raw IRI string, e.g. "https://www.w3.org"
|
|
3585
3591
|
const lit = makeStringLiteral(uriStr); // "https://www.w3.org"
|
|
3586
3592
|
const s2 = unifyTerm(goal.o, lit, subst);
|
|
3587
3593
|
return s2 !== null ? [s2] : [];
|
|
@@ -3606,7 +3612,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3606
3612
|
// -----------------------------------------------------------------
|
|
3607
3613
|
|
|
3608
3614
|
// string:concatenation
|
|
3609
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3615
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'concatenation') {
|
|
3610
3616
|
if (!(g.s instanceof ListTerm)) return [];
|
|
3611
3617
|
const parts = [];
|
|
3612
3618
|
for (const t of g.s.elems) {
|
|
@@ -3614,7 +3620,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3614
3620
|
if (sStr === null) return [];
|
|
3615
3621
|
parts.push(sStr);
|
|
3616
3622
|
}
|
|
3617
|
-
const lit = makeStringLiteral(parts.join(
|
|
3623
|
+
const lit = makeStringLiteral(parts.join(''));
|
|
3618
3624
|
|
|
3619
3625
|
if (g.o instanceof Var) {
|
|
3620
3626
|
const s2 = { ...subst };
|
|
@@ -3626,7 +3632,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3626
3632
|
}
|
|
3627
3633
|
|
|
3628
3634
|
// string:contains
|
|
3629
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3635
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'contains') {
|
|
3630
3636
|
const sStr = termToJsString(g.s);
|
|
3631
3637
|
const oStr = termToJsString(g.o);
|
|
3632
3638
|
if (sStr === null || oStr === null) return [];
|
|
@@ -3634,17 +3640,15 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3634
3640
|
}
|
|
3635
3641
|
|
|
3636
3642
|
// string:containsIgnoringCase
|
|
3637
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3643
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'containsIgnoringCase') {
|
|
3638
3644
|
const sStr = termToJsString(g.s);
|
|
3639
3645
|
const oStr = termToJsString(g.o);
|
|
3640
3646
|
if (sStr === null || oStr === null) return [];
|
|
3641
|
-
return sStr.toLowerCase().includes(oStr.toLowerCase())
|
|
3642
|
-
? [{ ...subst }]
|
|
3643
|
-
: [];
|
|
3647
|
+
return sStr.toLowerCase().includes(oStr.toLowerCase()) ? [{ ...subst }] : [];
|
|
3644
3648
|
}
|
|
3645
3649
|
|
|
3646
3650
|
// string:endsWith
|
|
3647
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3651
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'endsWith') {
|
|
3648
3652
|
const sStr = termToJsString(g.s);
|
|
3649
3653
|
const oStr = termToJsString(g.o);
|
|
3650
3654
|
if (sStr === null || oStr === null) return [];
|
|
@@ -3652,18 +3656,16 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3652
3656
|
}
|
|
3653
3657
|
|
|
3654
3658
|
// string:equalIgnoringCase
|
|
3655
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3659
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'equalIgnoringCase') {
|
|
3656
3660
|
const sStr = termToJsString(g.s);
|
|
3657
3661
|
const oStr = termToJsString(g.o);
|
|
3658
3662
|
if (sStr === null || oStr === null) return [];
|
|
3659
|
-
return sStr.toLowerCase() === oStr.toLowerCase()
|
|
3660
|
-
? [{ ...subst }]
|
|
3661
|
-
: [];
|
|
3663
|
+
return sStr.toLowerCase() === oStr.toLowerCase() ? [{ ...subst }] : [];
|
|
3662
3664
|
}
|
|
3663
3665
|
|
|
3664
3666
|
// string:format
|
|
3665
3667
|
// (limited: only %s and %% are supported, anything else ⇒ builtin fails)
|
|
3666
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3668
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'format') {
|
|
3667
3669
|
if (!(g.s instanceof ListTerm) || g.s.elems.length < 1) return [];
|
|
3668
3670
|
const fmtStr = termToJsString(g.s.elems[0]);
|
|
3669
3671
|
if (fmtStr === null) return [];
|
|
@@ -3690,11 +3692,11 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3690
3692
|
|
|
3691
3693
|
// string:jsonPointer
|
|
3692
3694
|
// Schema: ( $jsonText $pointer ) string:jsonPointer $value
|
|
3693
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3695
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'jsonPointer') {
|
|
3694
3696
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3695
3697
|
|
|
3696
|
-
const jsonText = termToJsonText(g.s.elems[0]);
|
|
3697
|
-
const ptr
|
|
3698
|
+
const jsonText = termToJsonText(g.s.elems[0]); // <-- changed
|
|
3699
|
+
const ptr = termToJsStringDecoded(g.s.elems[1]);
|
|
3698
3700
|
if (jsonText === null || ptr === null) return [];
|
|
3699
3701
|
|
|
3700
3702
|
const valTerm = jsonPointerLookup(jsonText, ptr);
|
|
@@ -3705,7 +3707,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3705
3707
|
}
|
|
3706
3708
|
|
|
3707
3709
|
// string:greaterThan
|
|
3708
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3710
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'greaterThan') {
|
|
3709
3711
|
const sStr = termToJsString(g.s);
|
|
3710
3712
|
const oStr = termToJsString(g.o);
|
|
3711
3713
|
if (sStr === null || oStr === null) return [];
|
|
@@ -3713,7 +3715,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3713
3715
|
}
|
|
3714
3716
|
|
|
3715
3717
|
// string:lessThan
|
|
3716
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3718
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'lessThan') {
|
|
3717
3719
|
const sStr = termToJsString(g.s);
|
|
3718
3720
|
const oStr = termToJsString(g.o);
|
|
3719
3721
|
if (sStr === null || oStr === null) return [];
|
|
@@ -3721,7 +3723,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3721
3723
|
}
|
|
3722
3724
|
|
|
3723
3725
|
// string:matches
|
|
3724
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3726
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'matches') {
|
|
3725
3727
|
const sStr = termToJsString(g.s);
|
|
3726
3728
|
const pattern = termToJsString(g.o);
|
|
3727
3729
|
if (sStr === null || pattern === null) return [];
|
|
@@ -3736,17 +3738,15 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3736
3738
|
}
|
|
3737
3739
|
|
|
3738
3740
|
// string:notEqualIgnoringCase
|
|
3739
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3741
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'notEqualIgnoringCase') {
|
|
3740
3742
|
const sStr = termToJsString(g.s);
|
|
3741
3743
|
const oStr = termToJsString(g.o);
|
|
3742
3744
|
if (sStr === null || oStr === null) return [];
|
|
3743
|
-
return sStr.toLowerCase() !== oStr.toLowerCase()
|
|
3744
|
-
? [{ ...subst }]
|
|
3745
|
-
: [];
|
|
3745
|
+
return sStr.toLowerCase() !== oStr.toLowerCase() ? [{ ...subst }] : [];
|
|
3746
3746
|
}
|
|
3747
3747
|
|
|
3748
3748
|
// string:notGreaterThan (≤ in Unicode code order)
|
|
3749
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3749
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'notGreaterThan') {
|
|
3750
3750
|
const sStr = termToJsString(g.s);
|
|
3751
3751
|
const oStr = termToJsString(g.o);
|
|
3752
3752
|
if (sStr === null || oStr === null) return [];
|
|
@@ -3754,7 +3754,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3754
3754
|
}
|
|
3755
3755
|
|
|
3756
3756
|
// string:notLessThan (≥ in Unicode code order)
|
|
3757
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3757
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'notLessThan') {
|
|
3758
3758
|
const sStr = termToJsString(g.s);
|
|
3759
3759
|
const oStr = termToJsString(g.o);
|
|
3760
3760
|
if (sStr === null || oStr === null) return [];
|
|
@@ -3762,7 +3762,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3762
3762
|
}
|
|
3763
3763
|
|
|
3764
3764
|
// string:notMatches
|
|
3765
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3765
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'notMatches') {
|
|
3766
3766
|
const sStr = termToJsString(g.s);
|
|
3767
3767
|
const pattern = termToJsString(g.o);
|
|
3768
3768
|
if (sStr === null || pattern === null) return [];
|
|
@@ -3776,17 +3776,17 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3776
3776
|
}
|
|
3777
3777
|
|
|
3778
3778
|
// string:replace
|
|
3779
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3779
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'replace') {
|
|
3780
3780
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 3) return [];
|
|
3781
|
-
const dataStr
|
|
3781
|
+
const dataStr = termToJsString(g.s.elems[0]);
|
|
3782
3782
|
const searchStr = termToJsString(g.s.elems[1]);
|
|
3783
|
-
const replStr
|
|
3783
|
+
const replStr = termToJsString(g.s.elems[2]);
|
|
3784
3784
|
if (dataStr === null || searchStr === null || replStr === null) return [];
|
|
3785
3785
|
|
|
3786
3786
|
let re;
|
|
3787
3787
|
try {
|
|
3788
3788
|
// Global replacement
|
|
3789
|
-
re = new RegExp(searchStr,
|
|
3789
|
+
re = new RegExp(searchStr, 'g');
|
|
3790
3790
|
} catch (e) {
|
|
3791
3791
|
return [];
|
|
3792
3792
|
}
|
|
@@ -3804,7 +3804,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3804
3804
|
}
|
|
3805
3805
|
|
|
3806
3806
|
// string:scrape
|
|
3807
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3807
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'scrape') {
|
|
3808
3808
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3809
3809
|
const dataStr = termToJsString(g.s.elems[0]);
|
|
3810
3810
|
const pattern = termToJsString(g.s.elems[1]);
|
|
@@ -3833,7 +3833,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3833
3833
|
}
|
|
3834
3834
|
|
|
3835
3835
|
// string:startsWith
|
|
3836
|
-
if (g.p instanceof Iri && g.p.value === STRING_NS +
|
|
3836
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + 'startsWith') {
|
|
3837
3837
|
const sStr = termToJsString(g.s);
|
|
3838
3838
|
const oStr = termToJsString(g.o);
|
|
3839
3839
|
if (sStr === null || oStr === null) return [];
|
|
@@ -3849,16 +3849,16 @@ function isBuiltinPred(p) {
|
|
|
3849
3849
|
const v = p.value;
|
|
3850
3850
|
|
|
3851
3851
|
// Treat RDF Collections as list-term builtins too.
|
|
3852
|
-
if (v === RDF_NS +
|
|
3852
|
+
if (v === RDF_NS + 'first' || v === RDF_NS + 'rest') {
|
|
3853
3853
|
return true;
|
|
3854
3854
|
}
|
|
3855
3855
|
|
|
3856
3856
|
return (
|
|
3857
3857
|
v.startsWith(CRYPTO_NS) ||
|
|
3858
|
-
v.startsWith(MATH_NS)
|
|
3859
|
-
v.startsWith(LOG_NS)
|
|
3858
|
+
v.startsWith(MATH_NS) ||
|
|
3859
|
+
v.startsWith(LOG_NS) ||
|
|
3860
3860
|
v.startsWith(STRING_NS) ||
|
|
3861
|
-
v.startsWith(TIME_NS)
|
|
3861
|
+
v.startsWith(TIME_NS) ||
|
|
3862
3862
|
v.startsWith(LIST_NS)
|
|
3863
3863
|
);
|
|
3864
3864
|
}
|
|
@@ -3878,10 +3878,10 @@ function standardizeRule(rule, gen) {
|
|
|
3878
3878
|
return new Var(vmap[t.name]);
|
|
3879
3879
|
}
|
|
3880
3880
|
if (t instanceof ListTerm) {
|
|
3881
|
-
return new ListTerm(t.elems.map(e => renameTerm(e, vmap, genArr)));
|
|
3881
|
+
return new ListTerm(t.elems.map((e) => renameTerm(e, vmap, genArr)));
|
|
3882
3882
|
}
|
|
3883
3883
|
if (t instanceof OpenListTerm) {
|
|
3884
|
-
const newXs = t.prefix.map(e => renameTerm(e, vmap, genArr));
|
|
3884
|
+
const newXs = t.prefix.map((e) => renameTerm(e, vmap, genArr));
|
|
3885
3885
|
if (!vmap.hasOwnProperty(t.tailVar)) {
|
|
3886
3886
|
const name = `${t.tailVar}__${genArr[0]}`;
|
|
3887
3887
|
genArr[0] += 1;
|
|
@@ -3892,13 +3892,7 @@ function standardizeRule(rule, gen) {
|
|
|
3892
3892
|
}
|
|
3893
3893
|
if (t instanceof FormulaTerm) {
|
|
3894
3894
|
return new FormulaTerm(
|
|
3895
|
-
t.triples.map(tr =>
|
|
3896
|
-
new Triple(
|
|
3897
|
-
renameTerm(tr.s, vmap, genArr),
|
|
3898
|
-
renameTerm(tr.p, vmap, genArr),
|
|
3899
|
-
renameTerm(tr.o, vmap, genArr)
|
|
3900
|
-
)
|
|
3901
|
-
)
|
|
3895
|
+
t.triples.map((tr) => new Triple(renameTerm(tr.s, vmap, genArr), renameTerm(tr.p, vmap, genArr), renameTerm(tr.o, vmap, genArr))),
|
|
3902
3896
|
);
|
|
3903
3897
|
}
|
|
3904
3898
|
return t;
|
|
@@ -3906,32 +3900,16 @@ function standardizeRule(rule, gen) {
|
|
|
3906
3900
|
|
|
3907
3901
|
const vmap2 = {};
|
|
3908
3902
|
const premise = rule.premise.map(
|
|
3909
|
-
tr =>
|
|
3910
|
-
new Triple(
|
|
3911
|
-
renameTerm(tr.s, vmap2, gen),
|
|
3912
|
-
renameTerm(tr.p, vmap2, gen),
|
|
3913
|
-
renameTerm(tr.o, vmap2, gen)
|
|
3914
|
-
)
|
|
3903
|
+
(tr) => new Triple(renameTerm(tr.s, vmap2, gen), renameTerm(tr.p, vmap2, gen), renameTerm(tr.o, vmap2, gen)),
|
|
3915
3904
|
);
|
|
3916
3905
|
const conclusion = rule.conclusion.map(
|
|
3917
|
-
tr =>
|
|
3918
|
-
new Triple(
|
|
3919
|
-
renameTerm(tr.s, vmap2, gen),
|
|
3920
|
-
renameTerm(tr.p, vmap2, gen),
|
|
3921
|
-
renameTerm(tr.o, vmap2, gen)
|
|
3922
|
-
)
|
|
3923
|
-
);
|
|
3924
|
-
return new Rule(
|
|
3925
|
-
premise,
|
|
3926
|
-
conclusion,
|
|
3927
|
-
rule.isForward,
|
|
3928
|
-
rule.isFuse,
|
|
3929
|
-
rule.headBlankLabels
|
|
3906
|
+
(tr) => new Triple(renameTerm(tr.s, vmap2, gen), renameTerm(tr.p, vmap2, gen), renameTerm(tr.o, vmap2, gen)),
|
|
3930
3907
|
);
|
|
3908
|
+
return new Rule(premise, conclusion, rule.isForward, rule.isFuse, rule.headBlankLabels);
|
|
3931
3909
|
}
|
|
3932
3910
|
|
|
3933
3911
|
function listHasTriple(list, tr) {
|
|
3934
|
-
return list.some(t => triplesEqual(t, tr));
|
|
3912
|
+
return list.some((t) => triplesEqual(t, tr));
|
|
3935
3913
|
}
|
|
3936
3914
|
|
|
3937
3915
|
// ============================================================================
|
|
@@ -3951,14 +3929,23 @@ function listHasTriple(list, tr) {
|
|
|
3951
3929
|
// This is semantics-preserving for the ongoing proof state.
|
|
3952
3930
|
|
|
3953
3931
|
function gcCollectVarsInTerm(t, out) {
|
|
3954
|
-
if (t instanceof Var) {
|
|
3955
|
-
|
|
3932
|
+
if (t instanceof Var) {
|
|
3933
|
+
out.add(t.name);
|
|
3934
|
+
return;
|
|
3935
|
+
}
|
|
3936
|
+
if (t instanceof ListTerm) {
|
|
3937
|
+
for (const e of t.elems) gcCollectVarsInTerm(e, out);
|
|
3938
|
+
return;
|
|
3939
|
+
}
|
|
3956
3940
|
if (t instanceof OpenListTerm) {
|
|
3957
3941
|
for (const e of t.prefix) gcCollectVarsInTerm(e, out);
|
|
3958
3942
|
out.add(t.tailVar);
|
|
3959
3943
|
return;
|
|
3960
3944
|
}
|
|
3961
|
-
if (t instanceof FormulaTerm) {
|
|
3945
|
+
if (t instanceof FormulaTerm) {
|
|
3946
|
+
for (const tr of t.triples) gcCollectVarsInTriple(tr, out);
|
|
3947
|
+
return;
|
|
3948
|
+
}
|
|
3962
3949
|
}
|
|
3963
3950
|
|
|
3964
3951
|
function gcCollectVarsInTriple(tr, out) {
|
|
@@ -4018,8 +4005,7 @@ function maybeCompactSubst(subst, goals, answerVars, depth) {
|
|
|
4018
4005
|
return gcCompactForGoals(subst, goals, answerVars);
|
|
4019
4006
|
}
|
|
4020
4007
|
|
|
4021
|
-
|
|
4022
|
-
function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
4008
|
+
function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
|
|
4023
4009
|
// Iterative DFS over proof states using an explicit stack.
|
|
4024
4010
|
// Each state carries its own substitution and remaining goals.
|
|
4025
4011
|
const results = [];
|
|
@@ -4028,7 +4014,6 @@ function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
|
4028
4014
|
const initialSubst = subst ? { ...subst } : {};
|
|
4029
4015
|
const initialVisited = visited ? visited.slice() : [];
|
|
4030
4016
|
|
|
4031
|
-
|
|
4032
4017
|
// Variables from the original goal list (needed by the caller to instantiate conclusions)
|
|
4033
4018
|
const answerVars = new Set();
|
|
4034
4019
|
gcCollectVarsInGoals(initialGoals, answerVars);
|
|
@@ -4038,7 +4023,12 @@ function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
|
4038
4023
|
}
|
|
4039
4024
|
|
|
4040
4025
|
const stack = [
|
|
4041
|
-
{
|
|
4026
|
+
{
|
|
4027
|
+
goals: initialGoals,
|
|
4028
|
+
subst: initialSubst,
|
|
4029
|
+
depth: depth || 0,
|
|
4030
|
+
visited: initialVisited,
|
|
4031
|
+
},
|
|
4042
4032
|
];
|
|
4043
4033
|
|
|
4044
4034
|
while (stack.length) {
|
|
@@ -4068,7 +4058,7 @@ function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
|
4068
4058
|
goals: restGoals,
|
|
4069
4059
|
subst: nextSubst,
|
|
4070
4060
|
depth: state.depth + 1,
|
|
4071
|
-
visited: state.visited
|
|
4061
|
+
visited: state.visited,
|
|
4072
4062
|
});
|
|
4073
4063
|
}
|
|
4074
4064
|
}
|
|
@@ -4097,7 +4087,7 @@ function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
|
4097
4087
|
goals: restGoals,
|
|
4098
4088
|
subst: nextSubst,
|
|
4099
4089
|
depth: state.depth + 1,
|
|
4100
|
-
visited: state.visited
|
|
4090
|
+
visited: state.visited,
|
|
4101
4091
|
});
|
|
4102
4092
|
}
|
|
4103
4093
|
}
|
|
@@ -4118,7 +4108,7 @@ function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
|
4118
4108
|
goals: restGoals,
|
|
4119
4109
|
subst: nextSubst,
|
|
4120
4110
|
depth: state.depth + 1,
|
|
4121
|
-
visited: state.visited
|
|
4111
|
+
visited: state.visited,
|
|
4122
4112
|
});
|
|
4123
4113
|
}
|
|
4124
4114
|
}
|
|
@@ -4127,8 +4117,7 @@ function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
|
4127
4117
|
// 4) Backward rules (indexed by head predicate)
|
|
4128
4118
|
if (goal0.p instanceof Iri) {
|
|
4129
4119
|
ensureBackRuleIndexes(backRules);
|
|
4130
|
-
const candRules =
|
|
4131
|
-
(backRules.__byHeadPred.get(goal0.p.value) || []).concat(backRules.__wildHeadPred);
|
|
4120
|
+
const candRules = (backRules.__byHeadPred.get(goal0.p.value) || []).concat(backRules.__wildHeadPred);
|
|
4132
4121
|
|
|
4133
4122
|
for (const r of candRules) {
|
|
4134
4123
|
if (r.conclusion.length !== 1) continue;
|
|
@@ -4141,7 +4130,7 @@ function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
|
4141
4130
|
const deltaHead = unifyTriple(head, goal0, {});
|
|
4142
4131
|
if (deltaHead === null) continue;
|
|
4143
4132
|
|
|
4144
|
-
const body = rStd.premise.map(b => applySubstTriple(b, deltaHead));
|
|
4133
|
+
const body = rStd.premise.map((b) => applySubstTriple(b, deltaHead));
|
|
4145
4134
|
const composed = composeSubst(state.subst, deltaHead);
|
|
4146
4135
|
if (composed === null) continue;
|
|
4147
4136
|
|
|
@@ -4151,7 +4140,7 @@ function proveGoals( goals, subst, facts, backRules, depth, visited, varGen ) {
|
|
|
4151
4140
|
goals: newGoals,
|
|
4152
4141
|
subst: nextSubst,
|
|
4153
4142
|
depth: state.depth + 1,
|
|
4154
|
-
visited: visitedForRules
|
|
4143
|
+
visited: visitedForRules,
|
|
4155
4144
|
});
|
|
4156
4145
|
}
|
|
4157
4146
|
}
|
|
@@ -4189,50 +4178,54 @@ function forwardChain(facts, forwardRules, backRules) {
|
|
|
4189
4178
|
|
|
4190
4179
|
// Inference fuse
|
|
4191
4180
|
if (r.isFuse && sols.length) {
|
|
4192
|
-
console.log(
|
|
4181
|
+
console.log('# Inference fuse triggered: a { ... } => false. rule fired.');
|
|
4193
4182
|
process.exit(2);
|
|
4194
4183
|
}
|
|
4195
4184
|
|
|
4196
4185
|
for (const s of sols) {
|
|
4197
|
-
const instantiatedPremises = r.premise.map(b => applySubstTriple(b, s));
|
|
4186
|
+
const instantiatedPremises = r.premise.map((b) => applySubstTriple(b, s));
|
|
4198
4187
|
|
|
4199
4188
|
for (const cpat of r.conclusion) {
|
|
4200
4189
|
const instantiated = applySubstTriple(cpat, s);
|
|
4201
4190
|
|
|
4202
4191
|
const isFwRuleTriple =
|
|
4203
4192
|
isLogImplies(instantiated.p) &&
|
|
4204
|
-
(
|
|
4205
|
-
(instantiated.s instanceof
|
|
4206
|
-
(instantiated.s instanceof
|
|
4207
|
-
(instantiated.s instanceof FormulaTerm && instantiated.o instanceof Literal && instantiated.o.value === "true")
|
|
4208
|
-
);
|
|
4193
|
+
((instantiated.s instanceof FormulaTerm && instantiated.o instanceof FormulaTerm) ||
|
|
4194
|
+
(instantiated.s instanceof Literal && instantiated.s.value === 'true' && instantiated.o instanceof FormulaTerm) ||
|
|
4195
|
+
(instantiated.s instanceof FormulaTerm && instantiated.o instanceof Literal && instantiated.o.value === 'true'));
|
|
4209
4196
|
|
|
4210
4197
|
const isBwRuleTriple =
|
|
4211
4198
|
isLogImpliedBy(instantiated.p) &&
|
|
4212
|
-
(
|
|
4213
|
-
(instantiated.s instanceof FormulaTerm && instantiated.o instanceof
|
|
4214
|
-
(instantiated.s instanceof
|
|
4215
|
-
(instantiated.s instanceof Literal && instantiated.s.value === "true" && instantiated.o instanceof FormulaTerm)
|
|
4216
|
-
);
|
|
4199
|
+
((instantiated.s instanceof FormulaTerm && instantiated.o instanceof FormulaTerm) ||
|
|
4200
|
+
(instantiated.s instanceof FormulaTerm && instantiated.o instanceof Literal && instantiated.o.value === 'true') ||
|
|
4201
|
+
(instantiated.s instanceof Literal && instantiated.s.value === 'true' && instantiated.o instanceof FormulaTerm));
|
|
4217
4202
|
|
|
4218
4203
|
if (isFwRuleTriple || isBwRuleTriple) {
|
|
4219
4204
|
if (!hasFactIndexed(facts, instantiated)) {
|
|
4220
4205
|
factList.push(instantiated);
|
|
4221
4206
|
pushFactIndexed(facts, instantiated);
|
|
4222
|
-
derivedForward.push(
|
|
4207
|
+
derivedForward.push(
|
|
4208
|
+
new DerivedFact(instantiated, r, instantiatedPremises.slice(), {
|
|
4209
|
+
...s,
|
|
4210
|
+
}),
|
|
4211
|
+
);
|
|
4223
4212
|
changed = true;
|
|
4224
4213
|
}
|
|
4225
4214
|
|
|
4226
4215
|
// Promote rule-producing triples to live rules, treating literal true as {}.
|
|
4227
4216
|
const left =
|
|
4228
|
-
instantiated.s instanceof FormulaTerm
|
|
4229
|
-
|
|
4230
|
-
|
|
4217
|
+
instantiated.s instanceof FormulaTerm
|
|
4218
|
+
? instantiated.s.triples
|
|
4219
|
+
: instantiated.s instanceof Literal && instantiated.s.value === 'true'
|
|
4220
|
+
? []
|
|
4221
|
+
: null;
|
|
4231
4222
|
|
|
4232
4223
|
const right =
|
|
4233
|
-
instantiated.o instanceof FormulaTerm
|
|
4234
|
-
|
|
4235
|
-
|
|
4224
|
+
instantiated.o instanceof FormulaTerm
|
|
4225
|
+
? instantiated.o.triples
|
|
4226
|
+
: instantiated.o instanceof Literal && instantiated.o.value === 'true'
|
|
4227
|
+
? []
|
|
4228
|
+
: null;
|
|
4236
4229
|
|
|
4237
4230
|
if (left !== null && right !== null) {
|
|
4238
4231
|
if (isFwRuleTriple) {
|
|
@@ -4243,11 +4236,11 @@ function forwardChain(facts, forwardRules, backRules) {
|
|
|
4243
4236
|
const newRule = new Rule(premise, conclusion, true, false, headBlankLabels);
|
|
4244
4237
|
|
|
4245
4238
|
const already = forwardRules.some(
|
|
4246
|
-
rr =>
|
|
4239
|
+
(rr) =>
|
|
4247
4240
|
rr.isForward === newRule.isForward &&
|
|
4248
4241
|
rr.isFuse === newRule.isFuse &&
|
|
4249
4242
|
triplesListEqual(rr.premise, newRule.premise) &&
|
|
4250
|
-
triplesListEqual(rr.conclusion, newRule.conclusion)
|
|
4243
|
+
triplesListEqual(rr.conclusion, newRule.conclusion),
|
|
4251
4244
|
);
|
|
4252
4245
|
if (!already) forwardRules.push(newRule);
|
|
4253
4246
|
} else if (isBwRuleTriple) {
|
|
@@ -4257,11 +4250,11 @@ function forwardChain(facts, forwardRules, backRules) {
|
|
|
4257
4250
|
const newRule = new Rule(premise, conclusion, false, false, headBlankLabels);
|
|
4258
4251
|
|
|
4259
4252
|
const already = backRules.some(
|
|
4260
|
-
rr =>
|
|
4253
|
+
(rr) =>
|
|
4261
4254
|
rr.isForward === newRule.isForward &&
|
|
4262
4255
|
rr.isFuse === newRule.isFuse &&
|
|
4263
4256
|
triplesListEqual(rr.premise, newRule.premise) &&
|
|
4264
|
-
triplesListEqual(rr.conclusion, newRule.conclusion)
|
|
4257
|
+
triplesListEqual(rr.conclusion, newRule.conclusion),
|
|
4265
4258
|
);
|
|
4266
4259
|
if (!already) {
|
|
4267
4260
|
backRules.push(newRule);
|
|
@@ -4304,44 +4297,44 @@ function termToN3(t, pref) {
|
|
|
4304
4297
|
const i = t.value;
|
|
4305
4298
|
const q = pref.shrinkIri(i);
|
|
4306
4299
|
if (q !== null) return q;
|
|
4307
|
-
if (i.startsWith(
|
|
4300
|
+
if (i.startsWith('_:')) return i;
|
|
4308
4301
|
return `<${i}>`;
|
|
4309
4302
|
}
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4303
|
+
if (t instanceof Literal) {
|
|
4304
|
+
const [lex, dt] = literalParts(t.value);
|
|
4305
|
+
|
|
4306
|
+
// Pretty-print xsd:boolean as bare true/false
|
|
4307
|
+
if (dt === XSD_NS + 'boolean') {
|
|
4308
|
+
const v = stripQuotes(lex);
|
|
4309
|
+
if (v === 'true' || v === 'false') return v;
|
|
4310
|
+
// optional: normalize 1/0 too
|
|
4311
|
+
if (v === '1') return 'true';
|
|
4312
|
+
if (v === '0') return 'false';
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4315
|
+
if (!dt) return t.value; // keep numbers, booleans, lang-tagged strings, etc.
|
|
4316
|
+
const qdt = pref.shrinkIri(dt);
|
|
4317
|
+
if (qdt !== null) return `${lex}^^${qdt}`; // e.g. ^^rdf:JSON
|
|
4318
|
+
return `${lex}^^<${dt}>`; // fallback
|
|
4319
|
+
}
|
|
4327
4320
|
if (t instanceof Var) return `?${t.name}`;
|
|
4328
4321
|
if (t instanceof Blank) return t.label;
|
|
4329
4322
|
if (t instanceof ListTerm) {
|
|
4330
|
-
const inside = t.elems.map(e => termToN3(e, pref));
|
|
4331
|
-
return
|
|
4323
|
+
const inside = t.elems.map((e) => termToN3(e, pref));
|
|
4324
|
+
return '(' + inside.join(' ') + ')';
|
|
4332
4325
|
}
|
|
4333
4326
|
if (t instanceof OpenListTerm) {
|
|
4334
|
-
const inside = t.prefix.map(e => termToN3(e, pref));
|
|
4335
|
-
inside.push(
|
|
4336
|
-
return
|
|
4327
|
+
const inside = t.prefix.map((e) => termToN3(e, pref));
|
|
4328
|
+
inside.push('?' + t.tailVar);
|
|
4329
|
+
return '(' + inside.join(' ') + ')';
|
|
4337
4330
|
}
|
|
4338
4331
|
if (t instanceof FormulaTerm) {
|
|
4339
|
-
let s =
|
|
4332
|
+
let s = '{\n';
|
|
4340
4333
|
for (const tr of t.triples) {
|
|
4341
4334
|
let line = tripleToN3(tr, pref).trimEnd();
|
|
4342
|
-
if (line) s +=
|
|
4335
|
+
if (line) s += ' ' + line + '\n';
|
|
4343
4336
|
}
|
|
4344
|
-
s +=
|
|
4337
|
+
s += '}';
|
|
4345
4338
|
return s;
|
|
4346
4339
|
}
|
|
4347
4340
|
return JSON.stringify(t);
|
|
@@ -4362,83 +4355,72 @@ function tripleToN3(tr, prefixes) {
|
|
|
4362
4355
|
}
|
|
4363
4356
|
|
|
4364
4357
|
const s = termToN3(tr.s, prefixes);
|
|
4365
|
-
const p =
|
|
4366
|
-
isRdfTypePred(tr.p) ? "a"
|
|
4367
|
-
: isOwlSameAsPred(tr.p) ? "="
|
|
4368
|
-
: termToN3(tr.p, prefixes);
|
|
4358
|
+
const p = isRdfTypePred(tr.p) ? 'a' : isOwlSameAsPred(tr.p) ? '=' : termToN3(tr.p, prefixes);
|
|
4369
4359
|
const o = termToN3(tr.o, prefixes);
|
|
4370
4360
|
|
|
4371
4361
|
return `${s} ${p} ${o} .`;
|
|
4372
4362
|
}
|
|
4373
4363
|
|
|
4374
4364
|
function printExplanation(df, prefixes) {
|
|
4375
|
-
console.log(
|
|
4376
|
-
|
|
4377
|
-
);
|
|
4378
|
-
console.log("# Proof for derived triple:");
|
|
4365
|
+
console.log('# ----------------------------------------------------------------------');
|
|
4366
|
+
console.log('# Proof for derived triple:');
|
|
4379
4367
|
|
|
4380
4368
|
// Fact line(s), indented 2 spaces after '# '
|
|
4381
4369
|
for (const line of tripleToN3(df.fact, prefixes).split(/\r?\n/)) {
|
|
4382
|
-
const stripped = line.replace(/\s+$/,
|
|
4370
|
+
const stripped = line.replace(/\s+$/, '');
|
|
4383
4371
|
if (stripped) {
|
|
4384
|
-
console.log(
|
|
4372
|
+
console.log('# ' + stripped);
|
|
4385
4373
|
}
|
|
4386
4374
|
}
|
|
4387
4375
|
|
|
4388
4376
|
if (!df.premises.length) {
|
|
4389
|
-
console.log(
|
|
4390
|
-
|
|
4391
|
-
);
|
|
4392
|
-
console.log(
|
|
4393
|
-
"# so it holds unconditionally whenever the program is loaded."
|
|
4394
|
-
);
|
|
4377
|
+
console.log('# This triple is the head of a forward rule with an empty premise,');
|
|
4378
|
+
console.log('# so it holds unconditionally whenever the program is loaded.');
|
|
4395
4379
|
} else {
|
|
4396
|
-
console.log(
|
|
4397
|
-
"# It holds because the following instance of the rule body is provable:"
|
|
4398
|
-
);
|
|
4380
|
+
console.log('# It holds because the following instance of the rule body is provable:');
|
|
4399
4381
|
|
|
4400
4382
|
// Premises, also indented 2 spaces after '# '
|
|
4401
4383
|
for (const prem of df.premises) {
|
|
4402
4384
|
for (const line of tripleToN3(prem, prefixes).split(/\r?\n/)) {
|
|
4403
|
-
const stripped = line.replace(/\s+$/,
|
|
4385
|
+
const stripped = line.replace(/\s+$/, '');
|
|
4404
4386
|
if (stripped) {
|
|
4405
|
-
console.log(
|
|
4387
|
+
console.log('# ' + stripped);
|
|
4406
4388
|
}
|
|
4407
4389
|
}
|
|
4408
4390
|
}
|
|
4409
4391
|
|
|
4410
|
-
console.log(
|
|
4392
|
+
console.log('# via the schematic forward rule:');
|
|
4411
4393
|
|
|
4412
4394
|
// Rule pretty-printed
|
|
4413
|
-
console.log(
|
|
4395
|
+
console.log('# {');
|
|
4414
4396
|
for (const tr of df.rule.premise) {
|
|
4415
4397
|
for (const line of tripleToN3(tr, prefixes).split(/\r?\n/)) {
|
|
4416
|
-
const stripped = line.replace(/\s+$/,
|
|
4398
|
+
const stripped = line.replace(/\s+$/, '');
|
|
4417
4399
|
if (stripped) {
|
|
4418
|
-
console.log(
|
|
4400
|
+
console.log('# ' + stripped);
|
|
4419
4401
|
}
|
|
4420
4402
|
}
|
|
4421
4403
|
}
|
|
4422
|
-
console.log(
|
|
4404
|
+
console.log('# } => {');
|
|
4423
4405
|
for (const tr of df.rule.conclusion) {
|
|
4424
4406
|
for (const line of tripleToN3(tr, prefixes).split(/\r?\n/)) {
|
|
4425
|
-
const stripped = line.replace(/\s+$/,
|
|
4407
|
+
const stripped = line.replace(/\s+$/, '');
|
|
4426
4408
|
if (stripped) {
|
|
4427
|
-
console.log(
|
|
4409
|
+
console.log('# ' + stripped);
|
|
4428
4410
|
}
|
|
4429
4411
|
}
|
|
4430
4412
|
}
|
|
4431
|
-
console.log(
|
|
4413
|
+
console.log('# } .');
|
|
4432
4414
|
}
|
|
4433
4415
|
|
|
4434
4416
|
// Substitution block
|
|
4435
4417
|
const ruleVars = varsInRule(df.rule);
|
|
4436
4418
|
const visibleNames = Object.keys(df.subst)
|
|
4437
|
-
.filter(name => ruleVars.has(name))
|
|
4419
|
+
.filter((name) => ruleVars.has(name))
|
|
4438
4420
|
.sort();
|
|
4439
4421
|
|
|
4440
4422
|
if (visibleNames.length) {
|
|
4441
|
-
console.log(
|
|
4423
|
+
console.log('# with substitution (on rule variables):');
|
|
4442
4424
|
for (const v of visibleNames) {
|
|
4443
4425
|
const fullTerm = applySubstTerm(new Var(v), df.subst);
|
|
4444
4426
|
const rendered = termToN3(fullTerm, prefixes);
|
|
@@ -4446,37 +4428,33 @@ function printExplanation(df, prefixes) {
|
|
|
4446
4428
|
|
|
4447
4429
|
if (lines.length === 1) {
|
|
4448
4430
|
// single-line term
|
|
4449
|
-
const stripped = lines[0].replace(/\s+$/,
|
|
4431
|
+
const stripped = lines[0].replace(/\s+$/, '');
|
|
4450
4432
|
if (stripped) {
|
|
4451
|
-
console.log(
|
|
4433
|
+
console.log('# ?' + v + ' = ' + stripped);
|
|
4452
4434
|
}
|
|
4453
4435
|
} else {
|
|
4454
4436
|
// multi-line term (e.g. a formula)
|
|
4455
4437
|
const first = lines[0].trimEnd(); // usually "{"
|
|
4456
4438
|
if (first) {
|
|
4457
|
-
console.log(
|
|
4439
|
+
console.log('# ?' + v + ' = ' + first);
|
|
4458
4440
|
}
|
|
4459
4441
|
for (let i = 1; i < lines.length; i++) {
|
|
4460
4442
|
const stripped = lines[i].trim();
|
|
4461
4443
|
if (!stripped) continue;
|
|
4462
4444
|
if (i === lines.length - 1) {
|
|
4463
4445
|
// closing brace
|
|
4464
|
-
console.log(
|
|
4446
|
+
console.log('# ' + stripped);
|
|
4465
4447
|
} else {
|
|
4466
4448
|
// inner triple lines
|
|
4467
|
-
console.log(
|
|
4449
|
+
console.log('# ' + stripped);
|
|
4468
4450
|
}
|
|
4469
4451
|
}
|
|
4470
4452
|
}
|
|
4471
4453
|
}
|
|
4472
4454
|
}
|
|
4473
4455
|
|
|
4474
|
-
console.log(
|
|
4475
|
-
|
|
4476
|
-
);
|
|
4477
|
-
console.log(
|
|
4478
|
-
"# ----------------------------------------------------------------------\n"
|
|
4479
|
-
);
|
|
4456
|
+
console.log('# Therefore the derived triple above is entailed by the rules and facts.');
|
|
4457
|
+
console.log('# ----------------------------------------------------------------------\n');
|
|
4480
4458
|
}
|
|
4481
4459
|
|
|
4482
4460
|
// ============================================================================
|
|
@@ -4486,19 +4464,19 @@ function printExplanation(df, prefixes) {
|
|
|
4486
4464
|
// Turn RDF Collections described with rdf:first/rdf:rest (+ rdf:nil) into ListTerm terms.
|
|
4487
4465
|
// This mutates triples/rules in-place so list:* builtins work on RDF-serialized lists too.
|
|
4488
4466
|
function materializeRdfLists(triples, forwardRules, backwardRules) {
|
|
4489
|
-
const RDF_FIRST = RDF_NS +
|
|
4490
|
-
const RDF_REST
|
|
4491
|
-
const RDF_NIL
|
|
4467
|
+
const RDF_FIRST = RDF_NS + 'first';
|
|
4468
|
+
const RDF_REST = RDF_NS + 'rest';
|
|
4469
|
+
const RDF_NIL = RDF_NS + 'nil';
|
|
4492
4470
|
|
|
4493
4471
|
function nodeKey(t) {
|
|
4494
|
-
if (t instanceof Blank) return
|
|
4495
|
-
if (t instanceof Iri) return
|
|
4472
|
+
if (t instanceof Blank) return 'B:' + t.label;
|
|
4473
|
+
if (t instanceof Iri) return 'I:' + t.value;
|
|
4496
4474
|
return null;
|
|
4497
4475
|
}
|
|
4498
4476
|
|
|
4499
4477
|
// Collect first/rest arcs from *input triples*
|
|
4500
4478
|
const firstMap = new Map(); // key(subject) -> Term (object)
|
|
4501
|
-
const restMap
|
|
4479
|
+
const restMap = new Map(); // key(subject) -> Term (object)
|
|
4502
4480
|
for (const tr of triples) {
|
|
4503
4481
|
if (!(tr.p instanceof Iri)) continue;
|
|
4504
4482
|
const k = nodeKey(tr.s);
|
|
@@ -4508,8 +4486,8 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
|
|
|
4508
4486
|
}
|
|
4509
4487
|
if (!firstMap.size && !restMap.size) return;
|
|
4510
4488
|
|
|
4511
|
-
const cache = new Map();
|
|
4512
|
-
const visiting = new Set();
|
|
4489
|
+
const cache = new Map(); // key(node) -> ListTerm
|
|
4490
|
+
const visiting = new Set(); // cycle guard
|
|
4513
4491
|
|
|
4514
4492
|
function buildListForKey(k) {
|
|
4515
4493
|
if (cache.has(k)) return cache.get(k);
|
|
@@ -4517,7 +4495,7 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
|
|
|
4517
4495
|
visiting.add(k);
|
|
4518
4496
|
|
|
4519
4497
|
// rdf:nil => ()
|
|
4520
|
-
if (k ===
|
|
4498
|
+
if (k === 'I:' + RDF_NIL) {
|
|
4521
4499
|
const empty = new ListTerm([]);
|
|
4522
4500
|
cache.set(k, empty);
|
|
4523
4501
|
visiting.delete(k);
|
|
@@ -4568,7 +4546,7 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
|
|
|
4568
4546
|
}
|
|
4569
4547
|
if (t instanceof ListTerm) {
|
|
4570
4548
|
let changed = false;
|
|
4571
|
-
const elems = t.elems.map(e => {
|
|
4549
|
+
const elems = t.elems.map((e) => {
|
|
4572
4550
|
const r = rewriteTerm(e);
|
|
4573
4551
|
if (r !== e) changed = true;
|
|
4574
4552
|
return r;
|
|
@@ -4577,7 +4555,7 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
|
|
|
4577
4555
|
}
|
|
4578
4556
|
if (t instanceof OpenListTerm) {
|
|
4579
4557
|
let changed = false;
|
|
4580
|
-
const prefix = t.prefix.map(e => {
|
|
4558
|
+
const prefix = t.prefix.map((e) => {
|
|
4581
4559
|
const r = rewriteTerm(e);
|
|
4582
4560
|
if (r !== e) changed = true;
|
|
4583
4561
|
return r;
|
|
@@ -4614,7 +4592,7 @@ function materializeRdfLists(triples, forwardRules, backwardRules) {
|
|
|
4614
4592
|
|
|
4615
4593
|
function localIsoDateTimeString(d) {
|
|
4616
4594
|
function pad(n, width = 2) {
|
|
4617
|
-
return String(n).padStart(width,
|
|
4595
|
+
return String(n).padStart(width, '0');
|
|
4618
4596
|
}
|
|
4619
4597
|
const year = d.getFullYear();
|
|
4620
4598
|
const month = d.getMonth() + 1;
|
|
@@ -4624,27 +4602,27 @@ function localIsoDateTimeString(d) {
|
|
|
4624
4602
|
const sec = d.getSeconds();
|
|
4625
4603
|
const ms = d.getMilliseconds();
|
|
4626
4604
|
const offsetMin = -d.getTimezoneOffset(); // minutes east of UTC
|
|
4627
|
-
const sign = offsetMin >= 0 ?
|
|
4605
|
+
const sign = offsetMin >= 0 ? '+' : '-';
|
|
4628
4606
|
const abs = Math.abs(offsetMin);
|
|
4629
4607
|
const oh = Math.floor(abs / 60);
|
|
4630
4608
|
const om = abs % 60;
|
|
4631
|
-
const msPart = ms ?
|
|
4609
|
+
const msPart = ms ? '.' + String(ms).padStart(3, '0') : '';
|
|
4632
4610
|
return (
|
|
4633
4611
|
pad(year, 4) +
|
|
4634
|
-
|
|
4612
|
+
'-' +
|
|
4635
4613
|
pad(month) +
|
|
4636
|
-
|
|
4614
|
+
'-' +
|
|
4637
4615
|
pad(day) +
|
|
4638
|
-
|
|
4616
|
+
'T' +
|
|
4639
4617
|
pad(hour) +
|
|
4640
|
-
|
|
4618
|
+
':' +
|
|
4641
4619
|
pad(min) +
|
|
4642
|
-
|
|
4620
|
+
':' +
|
|
4643
4621
|
pad(sec) +
|
|
4644
4622
|
msPart +
|
|
4645
4623
|
sign +
|
|
4646
4624
|
pad(oh) +
|
|
4647
|
-
|
|
4625
|
+
':' +
|
|
4648
4626
|
pad(om)
|
|
4649
4627
|
);
|
|
4650
4628
|
}
|
|
@@ -4662,33 +4640,31 @@ function main() {
|
|
|
4662
4640
|
// --------------------------------------------------------------------------
|
|
4663
4641
|
|
|
4664
4642
|
// --version / -v: print version and exit
|
|
4665
|
-
if (argv.includes(
|
|
4643
|
+
if (argv.includes('--version') || argv.includes('-v')) {
|
|
4666
4644
|
console.log(`eyeling v${version}`);
|
|
4667
4645
|
process.exit(0);
|
|
4668
4646
|
}
|
|
4669
4647
|
|
|
4670
4648
|
// --no-proof-comments / -n: disable proof explanations
|
|
4671
|
-
if (argv.includes(
|
|
4649
|
+
if (argv.includes('--no-proof-comments') || argv.includes('-n')) {
|
|
4672
4650
|
proofCommentsEnabled = false;
|
|
4673
4651
|
}
|
|
4674
4652
|
|
|
4675
4653
|
// --------------------------------------------------------------------------
|
|
4676
4654
|
// Positional args (the N3 file)
|
|
4677
4655
|
// --------------------------------------------------------------------------
|
|
4678
|
-
const positional = argv.filter(a => !a.startsWith(
|
|
4656
|
+
const positional = argv.filter((a) => !a.startsWith('-'));
|
|
4679
4657
|
|
|
4680
4658
|
if (positional.length !== 1) {
|
|
4681
|
-
console.error(
|
|
4682
|
-
"Usage: eyeling.js [--version|-v] [--no-proof-comments|-n] <file.n3>"
|
|
4683
|
-
);
|
|
4659
|
+
console.error('Usage: eyeling.js [--version|-v] [--no-proof-comments|-n] <file.n3>');
|
|
4684
4660
|
process.exit(1);
|
|
4685
4661
|
}
|
|
4686
4662
|
|
|
4687
4663
|
const path = positional[0];
|
|
4688
4664
|
let text;
|
|
4689
4665
|
try {
|
|
4690
|
-
const fs = require(
|
|
4691
|
-
text = fs.readFileSync(path, { encoding:
|
|
4666
|
+
const fs = require('fs');
|
|
4667
|
+
text = fs.readFileSync(path, { encoding: 'utf8' });
|
|
4692
4668
|
} catch (e) {
|
|
4693
4669
|
console.error(`Error reading file ${JSON.stringify(path)}: ${e.message}`);
|
|
4694
4670
|
process.exit(1);
|
|
@@ -4702,14 +4678,14 @@ function main() {
|
|
|
4702
4678
|
// Build internal ListTerm values from rdf:first/rdf:rest (+ rdf:nil) input triples
|
|
4703
4679
|
materializeRdfLists(triples, frules, brules);
|
|
4704
4680
|
|
|
4705
|
-
const facts = triples.filter(tr => isGroundTriple(tr));
|
|
4681
|
+
const facts = triples.filter((tr) => isGroundTriple(tr));
|
|
4706
4682
|
const derived = forwardChain(facts, frules, brules);
|
|
4707
4683
|
|
|
4708
|
-
const derivedTriples = derived.map(df => df.fact);
|
|
4684
|
+
const derivedTriples = derived.map((df) => df.fact);
|
|
4709
4685
|
const usedPrefixes = prefixes.prefixesUsedForOutput(derivedTriples);
|
|
4710
4686
|
|
|
4711
4687
|
for (const [pfx, base] of usedPrefixes) {
|
|
4712
|
-
if (pfx ===
|
|
4688
|
+
if (pfx === '') console.log(`@prefix : <${base}> .`);
|
|
4713
4689
|
else console.log(`@prefix ${pfx}: <${base}> .`);
|
|
4714
4690
|
}
|
|
4715
4691
|
if (derived.length && usedPrefixes.length) console.log();
|
|
@@ -4728,4 +4704,3 @@ function main() {
|
|
|
4728
4704
|
if (require.main === module) {
|
|
4729
4705
|
main();
|
|
4730
4706
|
}
|
|
4731
|
-
|