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/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
- const usedPrefixes = prefixes.prefixesUsedForOutput(outTriples);
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
- // In log:query mode, when proof comments are disabled, pretty-print blank-node
373
- // shaped outputs as Turtle property lists ("[ ... ] .") for readability.
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 { termToN3, tripleToN3, prettyPrintQueryTriples } = require('./printing');
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 parsedSourceList = parseN3SourceList(input, { baseIri });
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
- Array.isArray(logQueryRules) && logQueryRules.length && !proof
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 1.2 triple terms use <<( s p o )>>. Eyeling's N3 engine does not
352
- // implement a new RDF 1.2 term kind; instead, it accepts this syntax as a
353
- // compatibility surface and normalizes it to the existing N3 singleton quoted
354
- // graph term { s p o }. This keeps ordinary N3 reasoning unchanged while making
355
- // RDF 1.2 examples parseable by the current engine.
356
- function normalizeRdf12TripleTerms(inputText) {
357
- const text = String(inputText ?? '');
358
- let i = 0;
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 startsAt(needle, at = i) {
361
- return text.startsWith(needle, at);
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 readString() {
365
- const quote = text[i];
380
+ function readStringAt(s, at) {
381
+ const quote = s[at];
382
+ let i = at;
366
383
  let out = quote;
367
- const long = text.startsWith(quote.repeat(3), i);
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 < text.length) {
372
- if (text.startsWith(quote.repeat(3), i)) {
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 (text[i] === '\\' && i + 1 < text.length) {
378
- out += text.slice(i, i + 2);
394
+ if (s[i] === '\\' && i + 1 < s.length) {
395
+ out += s.slice(i, i + 2);
379
396
  i += 2;
380
397
  } else {
381
- out += text[i++];
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 < text.length) {
389
- const ch = text[i++];
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 readIri() {
403
- let out = text[i++];
404
- while (i < text.length) {
405
- const ch = text[i++];
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 convertUntil(stopToken) {
413
- let out = '';
414
- while (i < text.length) {
415
- if (stopToken && startsAt(stopToken)) {
416
- i += stopToken.length;
417
- return out;
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 (startsAt('<<(')) {
420
- i += 3;
421
- out += '{ ' + convertUntil(')>>').trim() + ' }';
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
- const ch = text[i];
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
- out += readString();
517
+ i = readStringAt(s, i).end;
427
518
  continue;
428
519
  }
429
520
  if (ch === '<') {
430
- out += readIri();
521
+ i = readIriAt(s, i).end;
431
522
  continue;
432
523
  }
433
524
  if (ch === '#') {
434
- while (i < text.length) {
435
- const c = text[i++];
436
- out += c;
437
- if (c === '\n' || c === '\r') break;
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
- if (stopToken) throw new N3SyntaxError(`Unterminated RDF 1.2 triple term, expected ${stopToken}`);
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
- const converted = convertUntil(null);
449
- return converted.replace(/^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/gim, '');
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
- inputText = normalizeRdf12TripleTerms(inputText);
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 };
@@ -103,8 +103,14 @@ function prefixesUsedInTokens(tokens, prefEnv) {
103
103
  }
104
104
 
105
105
  function parseN3Text(text, opts = {}) {
106
- const { baseIri = '', label = '<input>', keepSourceArtifacts = true, collectUsedPrefixes = false } = opts || {};
107
- const tokens = lex(text);
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
- module.exports = { termToN3, tripleToN3, prettyPrintQueryTriples };
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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.24.2",
3
+ "version": "1.24.3",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [