eyeling 1.24.2 → 1.24.3
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/HANDBOOK.md +43 -7
- package/dist/browser/eyeling.browser.js +401 -72
- package/eyeling.js +401 -72
- package/index.js +2 -0
- package/lib/cli.js +27 -10
- package/lib/engine.js +17 -7
- package/lib/lexer.js +242 -52
- package/lib/multisource.js +9 -2
- package/lib/printing.js +106 -1
- package/package.json +1 -1
- package/see/README.md +2 -1
- package/see/examples/doc/rdf_dataset.md +26 -0
- package/see/examples/input/rdf_dataset.trig +34 -0
- package/see/examples/n3/rdf_dataset.n3 +34 -0
- package/see/examples/output/rdf_dataset.md +54 -0
- package/see/examples/rdf_dataset.js +1512 -0
- package/see/see.js +26 -2
- package/test/api.test.js +68 -2
- package/test/see.test.js +0 -0
- package/tools/bundle.js +0 -0
- package/tools/n3gen.js +0 -0
package/lib/cli.js
CHANGED
|
@@ -107,6 +107,7 @@ function main() {
|
|
|
107
107
|
` -e, --enforce-https Rewrite http:// IRIs to https:// for log dereferencing builtins.\n` +
|
|
108
108
|
` -h, --help Show this help and exit.\n` +
|
|
109
109
|
` -p, --proof-comments Enable proof explanations.\n` +
|
|
110
|
+
` -r, --rdf Enable RDF/TriG input/output compatibility.\n` +
|
|
110
111
|
` -s, --super-restricted Disable all builtins except => and <=.\n` +
|
|
111
112
|
` -t, --stream Stream derived triples as soon as they are derived.\n` +
|
|
112
113
|
` -v, --version Print version and exit.\n`;
|
|
@@ -148,6 +149,7 @@ function main() {
|
|
|
148
149
|
|
|
149
150
|
const showAst = argv.includes('--ast') || argv.includes('-a');
|
|
150
151
|
const streamMode = argv.includes('--stream') || argv.includes('-t');
|
|
152
|
+
const rdfMode = argv.includes('--rdf') || argv.includes('-r');
|
|
151
153
|
|
|
152
154
|
// --enforce-https: rewrite http:// -> https:// for log dereferencing builtins
|
|
153
155
|
if (argv.includes('--enforce-https') || argv.includes('-e')) {
|
|
@@ -210,6 +212,7 @@ function main() {
|
|
|
210
212
|
label: sourceLabel,
|
|
211
213
|
collectUsedPrefixes: true,
|
|
212
214
|
keepSourceArtifacts: false,
|
|
215
|
+
rdf: rdfMode,
|
|
213
216
|
}),
|
|
214
217
|
);
|
|
215
218
|
} catch (e) {
|
|
@@ -322,10 +325,10 @@ function main() {
|
|
|
322
325
|
(df) => {
|
|
323
326
|
if (engine.getProofCommentsEnabled()) {
|
|
324
327
|
engine.printExplanation(df, outPrefixes);
|
|
325
|
-
console.log(engine.tripleToN3(df.fact, outPrefixes));
|
|
328
|
+
console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, outPrefixes) : engine.tripleToN3(df.fact, outPrefixes));
|
|
326
329
|
console.log();
|
|
327
330
|
} else {
|
|
328
|
-
console.log(engine.tripleToN3(df.fact, outPrefixes));
|
|
331
|
+
console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, outPrefixes) : engine.tripleToN3(df.fact, outPrefixes));
|
|
329
332
|
}
|
|
330
333
|
},
|
|
331
334
|
{ captureExplanations: engine.getProofCommentsEnabled(), prefixes: outPrefixes },
|
|
@@ -361,7 +364,24 @@ function main() {
|
|
|
361
364
|
return;
|
|
362
365
|
}
|
|
363
366
|
|
|
364
|
-
|
|
367
|
+
let bodyText = '';
|
|
368
|
+
if (rdfMode && !engine.getProofCommentsEnabled()) {
|
|
369
|
+
bodyText = outTriples.map((tr) => engine.tripleToRdfCompatible(tr, prefixes)).join('\n');
|
|
370
|
+
} else if (hasQueries && !engine.getProofCommentsEnabled()) {
|
|
371
|
+
// In log:query mode, when proof comments are disabled, pretty-print blank-node
|
|
372
|
+
// shaped outputs as Turtle property lists ("[ ... ] .") for readability.
|
|
373
|
+
bodyText = engine.prettyPrintQueryTriples(outTriples, prefixes);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let usedPrefixes = prefixes.prefixesUsedForOutput(outTriples);
|
|
377
|
+
if (rdfMode && bodyText) {
|
|
378
|
+
usedPrefixes = usedPrefixes.filter(([pfx]) => pfx === '' || bodyText.includes(pfx + ':'));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (rdfMode && bodyText.includes('<<(')) {
|
|
382
|
+
console.log('VERSION "1.2"');
|
|
383
|
+
console.log();
|
|
384
|
+
}
|
|
365
385
|
|
|
366
386
|
for (const [pfx, base] of usedPrefixes) {
|
|
367
387
|
if (pfx === '') console.log(`@prefix : <${base}> .`);
|
|
@@ -369,21 +389,18 @@ function main() {
|
|
|
369
389
|
}
|
|
370
390
|
if (outTriples.length && usedPrefixes.length) console.log();
|
|
371
391
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (hasQueries && !engine.getProofCommentsEnabled()) {
|
|
375
|
-
const s = engine.prettyPrintQueryTriples(outTriples, prefixes);
|
|
376
|
-
if (s) process.stdout.write(String(s).replace(/\s*$/g, '') + '\n');
|
|
392
|
+
if (bodyText) {
|
|
393
|
+
process.stdout.write(String(bodyText).replace(/\s*$/g, '') + '\n');
|
|
377
394
|
return;
|
|
378
395
|
}
|
|
379
396
|
|
|
380
397
|
for (const df of outDerived) {
|
|
381
398
|
if (engine.getProofCommentsEnabled()) {
|
|
382
399
|
engine.printExplanation(df, prefixes);
|
|
383
|
-
console.log(engine.tripleToN3(df.fact, prefixes));
|
|
400
|
+
console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, prefixes) : engine.tripleToN3(df.fact, prefixes));
|
|
384
401
|
console.log();
|
|
385
402
|
} else {
|
|
386
|
-
console.log(engine.tripleToN3(df.fact, prefixes));
|
|
403
|
+
console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, prefixes) : engine.tripleToN3(df.fact, prefixes));
|
|
387
404
|
}
|
|
388
405
|
}
|
|
389
406
|
}
|
package/lib/engine.js
CHANGED
|
@@ -61,7 +61,12 @@ const {
|
|
|
61
61
|
|
|
62
62
|
const { makeExplain } = require('./explain');
|
|
63
63
|
|
|
64
|
-
const {
|
|
64
|
+
const {
|
|
65
|
+
termToN3,
|
|
66
|
+
tripleToN3,
|
|
67
|
+
prettyPrintQueryTriples,
|
|
68
|
+
tripleToRdfCompatible,
|
|
69
|
+
} = require('./printing');
|
|
65
70
|
const {
|
|
66
71
|
getDataFactory,
|
|
67
72
|
internalTripleToRdfJsQuad,
|
|
@@ -3374,9 +3379,12 @@ function reasonStream(input, opts = {}) {
|
|
|
3374
3379
|
dataFactory = null,
|
|
3375
3380
|
skipUnsupportedRdfJs = false,
|
|
3376
3381
|
builtinModules = null,
|
|
3382
|
+
rdf = false,
|
|
3377
3383
|
} = opts;
|
|
3378
3384
|
|
|
3379
|
-
const
|
|
3385
|
+
const useRdfCompatibility = !!rdf;
|
|
3386
|
+
|
|
3387
|
+
const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: useRdfCompatibility });
|
|
3380
3388
|
const parsedInput = parsedSourceList || normalizeParsedReasonerInputSync(input);
|
|
3381
3389
|
const rdfFactory = rdfjs ? getDataFactory(dataFactory) : null;
|
|
3382
3390
|
|
|
@@ -3412,7 +3420,7 @@ function reasonStream(input, opts = {}) {
|
|
|
3412
3420
|
if (baseIri) prefixes.setBase(baseIri);
|
|
3413
3421
|
} else {
|
|
3414
3422
|
const n3Text = normalizeReasonerInputSync(input);
|
|
3415
|
-
const toks = lex(n3Text);
|
|
3423
|
+
const toks = lex(n3Text, { rdf: useRdfCompatibility });
|
|
3416
3424
|
const parser = new Parser(toks);
|
|
3417
3425
|
if (baseIri) parser.prefixes.setBase(baseIri);
|
|
3418
3426
|
|
|
@@ -3446,7 +3454,7 @@ function reasonStream(input, opts = {}) {
|
|
|
3446
3454
|
// query-selected triples (not all derived facts).
|
|
3447
3455
|
if (typeof onDerived === 'function') {
|
|
3448
3456
|
for (const qdf of queryDerived) {
|
|
3449
|
-
const payload = { triple: tripleToN3(qdf.fact, prefixes), df: qdf };
|
|
3457
|
+
const payload = { triple: useRdfCompatibility ? tripleToRdfCompatible(qdf.fact, prefixes) : tripleToN3(qdf.fact, prefixes), df: qdf };
|
|
3450
3458
|
if (rdfFactory) {
|
|
3451
3459
|
const quad = maybeTripleToRdfJsQuad(qdf.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
3452
3460
|
if (quad) payload.quad = quad;
|
|
@@ -3463,7 +3471,7 @@ function reasonStream(input, opts = {}) {
|
|
|
3463
3471
|
(df) => {
|
|
3464
3472
|
if (typeof onDerived === 'function') {
|
|
3465
3473
|
const payload = {
|
|
3466
|
-
triple: tripleToN3(df.fact, prefixes),
|
|
3474
|
+
triple: useRdfCompatibility ? tripleToRdfCompatible(df.fact, prefixes) : tripleToN3(df.fact, prefixes),
|
|
3467
3475
|
df,
|
|
3468
3476
|
};
|
|
3469
3477
|
if (rdfFactory) {
|
|
@@ -3484,8 +3492,9 @@ function reasonStream(input, opts = {}) {
|
|
|
3484
3492
|
? facts
|
|
3485
3493
|
: derived.map((d) => d.fact);
|
|
3486
3494
|
|
|
3487
|
-
const closureN3 =
|
|
3488
|
-
|
|
3495
|
+
const closureN3 = useRdfCompatibility
|
|
3496
|
+
? closureTriples.map((t) => tripleToRdfCompatible(t, prefixes)).join('\n')
|
|
3497
|
+
: Array.isArray(logQueryRules) && logQueryRules.length && !proof
|
|
3489
3498
|
? prettyPrintQueryTriples(closureTriples, prefixes)
|
|
3490
3499
|
: closureTriples.map((t) => tripleToN3(t, prefixes)).join('\n');
|
|
3491
3500
|
|
|
@@ -3626,6 +3635,7 @@ module.exports = {
|
|
|
3626
3635
|
printExplanation,
|
|
3627
3636
|
// used by demo worker to stringify derived triples with prefixes
|
|
3628
3637
|
tripleToN3,
|
|
3638
|
+
tripleToRdfCompatible,
|
|
3629
3639
|
// pretty log:query output (when proof comments are disabled)
|
|
3630
3640
|
prettyPrintQueryTriples,
|
|
3631
3641
|
getEnforceHttpsEnabled,
|
package/lib/lexer.js
CHANGED
|
@@ -348,45 +348,62 @@ function stripQuotes(lex) {
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
|
|
351
|
-
// RDF
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
// graph
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
351
|
+
// RDF/TriG compatibility is an opt-in syntax-normalization layer, not a new
|
|
352
|
+
// reasoning model. Eyeling remains strict N3 by default and N3 internally:
|
|
353
|
+
// - RDF 1.2 triple terms <<( s p o )>> become singleton graph terms { s p o }.
|
|
354
|
+
// - TriG named graph blocks g { ... } become g log:nameOf { ... } .
|
|
355
|
+
// - A top-level default graph block { ... } is unwrapped into ordinary triples.
|
|
356
|
+
// This mirrors tools/n3gen.js and keeps all downstream parsing/reasoning N3-only.
|
|
357
|
+
const LOG_NAME_OF_IRI = '<http://www.w3.org/2000/10/swap/log#nameOf>';
|
|
358
|
+
|
|
359
|
+
function normalizeRdfCompatibility(inputText) {
|
|
360
|
+
let text = String(inputText ?? '');
|
|
361
|
+
|
|
362
|
+
// Fast path: most Eyeling inputs are ordinary N3 and do not need RDF/TriG
|
|
363
|
+
// surface-syntax normalization. Avoid scanning large files character-by-character
|
|
364
|
+
// unless they actually contain RDF 1.2 triple terms, VERSION directives, or a
|
|
365
|
+
// plausible top-level TriG named graph block.
|
|
366
|
+
const hasTripleTerms = text.includes('<<(');
|
|
367
|
+
const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/im.test(text);
|
|
368
|
+
const hasNamedGraphCandidate = /(?:^|[.\r\n])\s*(?:GRAPH\s+)?(?:<[^>\r\n]*>|_:[A-Za-z][A-Za-z0-9_-]*|[A-Za-z][A-Za-z0-9_-]*:[^\s{};,.()[\]]*)\s*\{/m.test(text);
|
|
369
|
+
|
|
370
|
+
if (!hasTripleTerms && !hasVersionDirective && !hasNamedGraphCandidate) return text;
|
|
371
|
+
|
|
372
|
+
function isWordChar(ch) {
|
|
373
|
+
return ch != null && /[A-Za-z0-9_:-]/.test(ch);
|
|
374
|
+
}
|
|
359
375
|
|
|
360
|
-
function
|
|
361
|
-
return
|
|
376
|
+
function startsWordAt(s, word, at) {
|
|
377
|
+
return s.startsWith(word, at) && !isWordChar(s[at - 1]) && !isWordChar(s[at + word.length]);
|
|
362
378
|
}
|
|
363
379
|
|
|
364
|
-
function
|
|
365
|
-
const quote =
|
|
380
|
+
function readStringAt(s, at) {
|
|
381
|
+
const quote = s[at];
|
|
382
|
+
let i = at;
|
|
366
383
|
let out = quote;
|
|
367
|
-
const long =
|
|
384
|
+
const long = s.startsWith(quote.repeat(3), i);
|
|
368
385
|
if (long) {
|
|
369
386
|
out = quote.repeat(3);
|
|
370
387
|
i += 3;
|
|
371
|
-
while (i <
|
|
372
|
-
if (
|
|
388
|
+
while (i < s.length) {
|
|
389
|
+
if (s.startsWith(quote.repeat(3), i)) {
|
|
373
390
|
out += quote.repeat(3);
|
|
374
391
|
i += 3;
|
|
375
|
-
return out;
|
|
392
|
+
return { text: out, end: i };
|
|
376
393
|
}
|
|
377
|
-
if (
|
|
378
|
-
out +=
|
|
394
|
+
if (s[i] === '\\' && i + 1 < s.length) {
|
|
395
|
+
out += s.slice(i, i + 2);
|
|
379
396
|
i += 2;
|
|
380
397
|
} else {
|
|
381
|
-
out +=
|
|
398
|
+
out += s[i++];
|
|
382
399
|
}
|
|
383
400
|
}
|
|
384
|
-
return out;
|
|
401
|
+
return { text: out, end: i };
|
|
385
402
|
}
|
|
386
403
|
i += 1;
|
|
387
404
|
let escaped = false;
|
|
388
|
-
while (i <
|
|
389
|
-
const ch =
|
|
405
|
+
while (i < s.length) {
|
|
406
|
+
const ch = s[i++];
|
|
390
407
|
out += ch;
|
|
391
408
|
if (escaped) {
|
|
392
409
|
escaped = false;
|
|
@@ -396,61 +413,234 @@ function normalizeRdf12TripleTerms(inputText) {
|
|
|
396
413
|
break;
|
|
397
414
|
}
|
|
398
415
|
}
|
|
399
|
-
return out;
|
|
416
|
+
return { text: out, end: i };
|
|
400
417
|
}
|
|
401
418
|
|
|
402
|
-
function
|
|
403
|
-
let
|
|
404
|
-
|
|
405
|
-
|
|
419
|
+
function readIriAt(s, at) {
|
|
420
|
+
let i = at + 1;
|
|
421
|
+
let out = '<';
|
|
422
|
+
while (i < s.length) {
|
|
423
|
+
const ch = s[i++];
|
|
406
424
|
out += ch;
|
|
407
425
|
if (ch === '>') break;
|
|
408
426
|
}
|
|
409
|
-
return out;
|
|
427
|
+
return { text: out, end: i };
|
|
410
428
|
}
|
|
411
429
|
|
|
412
|
-
function
|
|
413
|
-
let
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
430
|
+
function convertTripleTerms(s) {
|
|
431
|
+
let i = 0;
|
|
432
|
+
|
|
433
|
+
function startsAt(needle, at = i) {
|
|
434
|
+
return s.startsWith(needle, at);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function convertUntil(stopToken) {
|
|
438
|
+
let out = '';
|
|
439
|
+
while (i < s.length) {
|
|
440
|
+
if (stopToken && startsAt(stopToken)) {
|
|
441
|
+
i += stopToken.length;
|
|
442
|
+
return out;
|
|
443
|
+
}
|
|
444
|
+
if (startsAt('<<(')) {
|
|
445
|
+
i += 3;
|
|
446
|
+
out += '{ ' + convertUntil(')>>').trim() + ' }';
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const ch = s[i];
|
|
450
|
+
if (ch === '"' || ch === "'") {
|
|
451
|
+
const str = readStringAt(s, i);
|
|
452
|
+
out += str.text;
|
|
453
|
+
i = str.end;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (ch === '<') {
|
|
457
|
+
const iri = readIriAt(s, i);
|
|
458
|
+
out += iri.text;
|
|
459
|
+
i = iri.end;
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (ch === '#') {
|
|
463
|
+
while (i < s.length) {
|
|
464
|
+
const c = s[i++];
|
|
465
|
+
out += c;
|
|
466
|
+
if (c === '\n' || c === '\r') break;
|
|
467
|
+
}
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
out += ch;
|
|
471
|
+
i += 1;
|
|
418
472
|
}
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
473
|
+
if (stopToken) throw new N3SyntaxError(`Unterminated RDF 1.2 triple term, expected ${stopToken}`);
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return convertUntil(null);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function stripVersionDirectives(s) {
|
|
481
|
+
return s.replace(/^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/gim, '');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function skipWsAndComments(s, at) {
|
|
485
|
+
let i = at;
|
|
486
|
+
while (i < s.length) {
|
|
487
|
+
if (/\s/.test(s[i])) {
|
|
488
|
+
i += 1;
|
|
422
489
|
continue;
|
|
423
490
|
}
|
|
424
|
-
|
|
491
|
+
if (s[i] === '#') {
|
|
492
|
+
while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
return i;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function readTermAt(s, at) {
|
|
501
|
+
if (s[at] === '<') return readIriAt(s, at);
|
|
502
|
+
let i = at;
|
|
503
|
+
while (i < s.length && !/\s/.test(s[i]) && !'{}[](),;.'.includes(s[i])) i += 1;
|
|
504
|
+
if (i === at) return null;
|
|
505
|
+
const value = s.slice(at, i);
|
|
506
|
+
if (!value || value.startsWith('@')) return null;
|
|
507
|
+
return { text: value, end: i };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function readBalancedBlock(s, at) {
|
|
511
|
+
if (s[at] !== '{') return null;
|
|
512
|
+
let i = at;
|
|
513
|
+
let depth = 0;
|
|
514
|
+
while (i < s.length) {
|
|
515
|
+
const ch = s[i];
|
|
425
516
|
if (ch === '"' || ch === "'") {
|
|
426
|
-
|
|
517
|
+
i = readStringAt(s, i).end;
|
|
427
518
|
continue;
|
|
428
519
|
}
|
|
429
520
|
if (ch === '<') {
|
|
430
|
-
|
|
521
|
+
i = readIriAt(s, i).end;
|
|
431
522
|
continue;
|
|
432
523
|
}
|
|
433
524
|
if (ch === '#') {
|
|
434
|
-
while (i <
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
525
|
+
while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
if (ch === '{') depth += 1;
|
|
529
|
+
if (ch === '}') {
|
|
530
|
+
depth -= 1;
|
|
531
|
+
i += 1;
|
|
532
|
+
if (depth === 0) return { text: s.slice(at, i), inner: s.slice(at + 1, i - 1), end: i };
|
|
439
533
|
continue;
|
|
440
534
|
}
|
|
441
|
-
out += ch;
|
|
442
535
|
i += 1;
|
|
443
536
|
}
|
|
444
|
-
|
|
537
|
+
throw new N3SyntaxError('Unterminated RDF/TriG graph block, expected }');
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function normalizeNamedGraphs(s) {
|
|
541
|
+
let out = '';
|
|
542
|
+
let i = 0;
|
|
543
|
+
let statementStart = true;
|
|
544
|
+
let braceDepth = 0;
|
|
545
|
+
|
|
546
|
+
while (i < s.length) {
|
|
547
|
+
if (statementStart && braceDepth === 0) {
|
|
548
|
+
const termStart = skipWsAndComments(s, i);
|
|
549
|
+
out += s.slice(i, termStart);
|
|
550
|
+
i = termStart;
|
|
551
|
+
|
|
552
|
+
// Top-level TriG default graph block: { ... } .
|
|
553
|
+
if (s[i] === '{') {
|
|
554
|
+
const block = readBalancedBlock(s, i);
|
|
555
|
+
const after = skipWsAndComments(s, block.end);
|
|
556
|
+
if (after >= s.length || s[after] === '.') {
|
|
557
|
+
out += block.inner.trim();
|
|
558
|
+
if (block.inner.trim() && !/\n$/.test(block.inner)) out += '\n';
|
|
559
|
+
i = after < s.length && s[after] === '.' ? after + 1 : after;
|
|
560
|
+
statementStart = true;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
// It is an ordinary N3 formula subject, not a TriG default graph.
|
|
564
|
+
out += block.text;
|
|
565
|
+
i = block.end;
|
|
566
|
+
statementStart = false;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
let graphKeyword = false;
|
|
571
|
+
if (startsWordAt(s, 'GRAPH', i)) {
|
|
572
|
+
graphKeyword = true;
|
|
573
|
+
i += 'GRAPH'.length;
|
|
574
|
+
i = skipWsAndComments(s, i);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const term = readTermAt(s, i);
|
|
578
|
+
if (term && !['@prefix', '@base', 'PREFIX', 'BASE', 'VERSION'].includes(term.text)) {
|
|
579
|
+
const afterTerm = skipWsAndComments(s, term.end);
|
|
580
|
+
if (s[afterTerm] === '{') {
|
|
581
|
+
const block = readBalancedBlock(s, afterTerm);
|
|
582
|
+
const afterBlock = skipWsAndComments(s, block.end);
|
|
583
|
+
out += `${term.text} ${LOG_NAME_OF_IRI} ${block.text} .`;
|
|
584
|
+
i = afterBlock < s.length && s[afterBlock] === '.' ? afterBlock + 1 : block.end;
|
|
585
|
+
statementStart = true;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Not TriG named-graph syntax after all; copy the first character and
|
|
591
|
+
// continue as ordinary N3.
|
|
592
|
+
if (graphKeyword) {
|
|
593
|
+
out += 'GRAPH ';
|
|
594
|
+
statementStart = false;
|
|
595
|
+
} else if (i < s.length) {
|
|
596
|
+
const copied = s[i++];
|
|
597
|
+
out += copied;
|
|
598
|
+
if (!/\s/.test(copied)) statementStart = false;
|
|
599
|
+
}
|
|
600
|
+
} else {
|
|
601
|
+
const ch = s[i];
|
|
602
|
+
out += ch;
|
|
603
|
+
if (ch === '"' || ch === "'") {
|
|
604
|
+
const str = readStringAt(s, i);
|
|
605
|
+
out = out.slice(0, -1) + str.text;
|
|
606
|
+
i = str.end;
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
if (ch === '<') {
|
|
610
|
+
const iri = readIriAt(s, i);
|
|
611
|
+
out = out.slice(0, -1) + iri.text;
|
|
612
|
+
i = iri.end;
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (ch === '#') {
|
|
616
|
+
i += 1;
|
|
617
|
+
while (i < s.length) {
|
|
618
|
+
const c = s[i++];
|
|
619
|
+
out += c;
|
|
620
|
+
if (c === '\n' || c === '\r') break;
|
|
621
|
+
}
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
if (ch === '{') braceDepth += 1;
|
|
625
|
+
else if (ch === '}' && braceDepth > 0) braceDepth -= 1;
|
|
626
|
+
else if (ch === '.' && braceDepth === 0) statementStart = true;
|
|
627
|
+
else if (!/\s/.test(ch)) statementStart = false;
|
|
628
|
+
i += 1;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
445
632
|
return out;
|
|
446
633
|
}
|
|
447
634
|
|
|
448
|
-
|
|
449
|
-
|
|
635
|
+
if (hasTripleTerms) text = convertTripleTerms(text);
|
|
636
|
+
if (hasVersionDirective) text = stripVersionDirectives(text);
|
|
637
|
+
if (hasVersionDirective || hasNamedGraphCandidate) text = normalizeNamedGraphs(text);
|
|
638
|
+
return text;
|
|
450
639
|
}
|
|
451
640
|
|
|
452
|
-
function lex(inputText) {
|
|
453
|
-
|
|
641
|
+
function lex(inputText, opts = {}) {
|
|
642
|
+
const rdf = !!(opts && opts.rdf);
|
|
643
|
+
if (rdf) inputText = normalizeRdfCompatibility(inputText);
|
|
454
644
|
const chars = Array.from(inputText);
|
|
455
645
|
const n = chars.length;
|
|
456
646
|
let i = 0;
|
|
@@ -911,4 +1101,4 @@ function lex(inputText) {
|
|
|
911
1101
|
return tokens;
|
|
912
1102
|
}
|
|
913
1103
|
|
|
914
|
-
module.exports = { Token, N3SyntaxError, lex, decodeN3StringEscapes };
|
|
1104
|
+
module.exports = { Token, N3SyntaxError, lex, normalizeRdfCompatibility, decodeN3StringEscapes };
|
package/lib/multisource.js
CHANGED
|
@@ -103,8 +103,14 @@ function prefixesUsedInTokens(tokens, prefEnv) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function parseN3Text(text, opts = {}) {
|
|
106
|
-
const {
|
|
107
|
-
|
|
106
|
+
const {
|
|
107
|
+
baseIri = '',
|
|
108
|
+
label = '<input>',
|
|
109
|
+
keepSourceArtifacts = true,
|
|
110
|
+
collectUsedPrefixes = false,
|
|
111
|
+
rdf = false,
|
|
112
|
+
} = opts || {};
|
|
113
|
+
const tokens = lex(text, { rdf });
|
|
108
114
|
const parser = new Parser(tokens);
|
|
109
115
|
if (baseIri) parser.prefixes.setBase(baseIri);
|
|
110
116
|
const [prefixes, triples, frules, brules, logQueryRules] = parser.parseDocument();
|
|
@@ -298,6 +304,7 @@ function parseN3SourceList(input, opts = {}) {
|
|
|
298
304
|
baseIri: source.baseIri || (sources.length === 1 ? defaultBaseIri : ''),
|
|
299
305
|
collectUsedPrefixes: true,
|
|
300
306
|
keepSourceArtifacts: !!opts.keepSourceArtifacts,
|
|
307
|
+
rdf: !!opts.rdf,
|
|
301
308
|
}),
|
|
302
309
|
);
|
|
303
310
|
return mergeParsedDocuments(parsed, {
|
package/lib/printing.js
CHANGED
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
isOwlSameAsPred,
|
|
22
22
|
isLogImplies,
|
|
23
23
|
isLogImpliedBy,
|
|
24
|
+
LOG_NS,
|
|
24
25
|
} = require('./prelude');
|
|
25
26
|
|
|
26
27
|
function stripQuotes(lex) {
|
|
@@ -350,4 +351,108 @@ function prettyPrintQueryTriples(triples, prefixes) {
|
|
|
350
351
|
return blocks.join('\n');
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
|
|
354
|
+
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
356
|
+
// RDF compatibility output
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
|
|
359
|
+
function isRdfTripleTermSubject(t) {
|
|
360
|
+
return t instanceof Iri || t instanceof Blank;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function isRdfTripleTermPredicate(t) {
|
|
364
|
+
return t instanceof Iri;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function isRdfTripleTermObject(t) {
|
|
368
|
+
return t instanceof Iri || t instanceof Blank || t instanceof Literal;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function isRdfTripleTermGraph(t) {
|
|
372
|
+
if (!(t instanceof GraphTerm)) return false;
|
|
373
|
+
if (!Array.isArray(t.triples) || t.triples.length !== 1) return false;
|
|
374
|
+
const tr = t.triples[0];
|
|
375
|
+
return isRdfTripleTermSubject(tr.s) && isRdfTripleTermPredicate(tr.p) && isRdfTripleTermObject(tr.o);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
function isLogNameOfPred(p) {
|
|
380
|
+
return p instanceof Iri && p.value === LOG_NS + 'nameOf';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function isRdfNamedGraphLabel(t) {
|
|
384
|
+
return t instanceof Iri || t instanceof Blank;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function rdfCompatibleGraphBlock(graph, prefixes) {
|
|
388
|
+
const indent = ' ';
|
|
389
|
+
const indentBlock = (str) =>
|
|
390
|
+
str
|
|
391
|
+
.split(/\r?\n/)
|
|
392
|
+
.map((ln) => (ln.length ? indent + ln : ln))
|
|
393
|
+
.join('\n');
|
|
394
|
+
|
|
395
|
+
let s = '{\n';
|
|
396
|
+
for (const inner of graph.triples || []) {
|
|
397
|
+
const block = tripleToRdfCompatible(inner, prefixes).trimEnd();
|
|
398
|
+
if (block) s += indentBlock(block) + '\n';
|
|
399
|
+
}
|
|
400
|
+
s += '}';
|
|
401
|
+
return s;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function rdfPredicateToText(p, prefixes) {
|
|
405
|
+
return isRdfTypePred(p) ? 'a' : termToN3(p, prefixes);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function termToRdfCompatible(t, pref) {
|
|
409
|
+
if (isRdfTripleTermGraph(t)) {
|
|
410
|
+
const tr = t.triples[0];
|
|
411
|
+
const s = termToN3(tr.s, pref);
|
|
412
|
+
const p = rdfPredicateToText(tr.p, pref);
|
|
413
|
+
const o = termToN3(tr.o, pref);
|
|
414
|
+
return `<<( ${s} ${p} ${o} )>>`;
|
|
415
|
+
}
|
|
416
|
+
return termToN3(t, pref);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function tripleToRdfCompatible(tr, prefixes) {
|
|
420
|
+
if (isLogNameOfPred(tr.p) && isRdfNamedGraphLabel(tr.s) && tr.o instanceof GraphTerm) {
|
|
421
|
+
return `${termToN3(tr.s, prefixes)} ${rdfCompatibleGraphBlock(tr.o, prefixes)}`;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (isLogImplies(tr.p)) {
|
|
425
|
+
const s = termToRdfCompatible(tr.s, prefixes);
|
|
426
|
+
const o = termToRdfCompatible(tr.o, prefixes);
|
|
427
|
+
return `${s} => ${o} .`;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (isLogImpliedBy(tr.p)) {
|
|
431
|
+
const s = termToRdfCompatible(tr.s, prefixes);
|
|
432
|
+
const o = termToRdfCompatible(tr.o, prefixes);
|
|
433
|
+
return `${s} <= ${o} .`;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const s = termToRdfCompatible(tr.s, prefixes);
|
|
437
|
+
const p = isRdfTypePred(tr.p) ? 'a' : isOwlSameAsPred(tr.p) ? '=' : termToN3(tr.p, prefixes);
|
|
438
|
+
const o = termToRdfCompatible(tr.o, prefixes);
|
|
439
|
+
return `${s} ${p} ${o} .`;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function prettyPrintQueryTriplesRdfCompatible(triples, prefixes) {
|
|
443
|
+
return triples.map((tr) => tripleToRdfCompatible(tr, prefixes)).join('\n');
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function needsRdf12Version(text) {
|
|
447
|
+
return typeof text === 'string' && text.includes('<<(');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
module.exports = {
|
|
451
|
+
termToN3,
|
|
452
|
+
tripleToN3,
|
|
453
|
+
prettyPrintQueryTriples,
|
|
454
|
+
termToRdfCompatible,
|
|
455
|
+
tripleToRdfCompatible,
|
|
456
|
+
prettyPrintQueryTriplesRdfCompatible,
|
|
457
|
+
needsRdf12Version,
|
|
458
|
+
};
|