eyeling 1.24.1 → 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.
Files changed (77) hide show
  1. package/HANDBOOK.md +134 -0
  2. package/dist/browser/eyeling.browser.js +454 -22
  3. package/eyeling.js +454 -22
  4. package/index.js +2 -0
  5. package/lib/cli.js +27 -10
  6. package/lib/engine.js +17 -7
  7. package/lib/lexer.js +295 -2
  8. package/lib/multisource.js +9 -2
  9. package/lib/printing.js +106 -1
  10. package/package.json +1 -1
  11. package/see/README.md +4 -0
  12. package/see/examples/_see.js +33 -2
  13. package/see/examples/age.js +27 -1
  14. package/see/examples/annotation.js +27 -1
  15. package/see/examples/backward.js +27 -1
  16. package/see/examples/backward_recursion.js +27 -1
  17. package/see/examples/bayes_diagnosis.js +27 -1
  18. package/see/examples/bayes_therapy.js +27 -1
  19. package/see/examples/bmi.js +27 -1
  20. package/see/examples/builtin_coverage.js +27 -1
  21. package/see/examples/collection.js +27 -1
  22. package/see/examples/complex.js +27 -1
  23. package/see/examples/complex_matrix_stability.js +27 -1
  24. package/see/examples/composition_of_injective_functions_is_injective.js +27 -1
  25. package/see/examples/control_system.js +27 -1
  26. package/see/examples/crypto_builtins_tests.js +27 -1
  27. package/see/examples/delfour.js +27 -1
  28. package/see/examples/digital_product_passport.js +27 -1
  29. package/see/examples/dijkstra.js +27 -1
  30. package/see/examples/dijkstra_risk_path.js +27 -1
  31. package/see/examples/doc/rdf_dataset.md +26 -0
  32. package/see/examples/doc/triple_terms.md +26 -0
  33. package/see/examples/dog.js +27 -1
  34. package/see/examples/eco_route_insight.js +27 -1
  35. package/see/examples/equals.js +27 -1
  36. package/see/examples/equivalence_classes_overlap_implies_same_class.js +27 -1
  37. package/see/examples/euler_identity.js +27 -1
  38. package/see/examples/ev_roundtrip_planner.js +27 -1
  39. package/see/examples/existential_rule.js +27 -1
  40. package/see/examples/expression_eval.js +27 -1
  41. package/see/examples/family_cousins.js +27 -1
  42. package/see/examples/fastpow.js +27 -1
  43. package/see/examples/fibonacci.js +27 -1
  44. package/see/examples/french_cities.js +27 -1
  45. package/see/examples/fundamental_theorem_arithmetic.js +27 -1
  46. package/see/examples/genetic_knapsack_selection.js +27 -1
  47. package/see/examples/goldbach_1000.js +27 -1
  48. package/see/examples/good_cobbler.js +27 -1
  49. package/see/examples/gps.js +27 -1
  50. package/see/examples/gray_code_counter.js +27 -1
  51. package/see/examples/greatest_lower_bound_uniqueness.js +27 -1
  52. package/see/examples/group_inverse_uniqueness.js +27 -1
  53. package/see/examples/hadamard_approx.js +27 -1
  54. package/see/examples/hanoi.js +27 -1
  55. package/see/examples/input/rdf_dataset.trig +34 -0
  56. package/see/examples/input/triple_terms.trig +28 -0
  57. package/see/examples/n3/rdf_dataset.n3 +34 -0
  58. package/see/examples/n3/triple_terms.n3 +23 -0
  59. package/see/examples/odrl_dpv_risk_ranked.js +27 -1
  60. package/see/examples/output/rdf_dataset.md +54 -0
  61. package/see/examples/output/triple_terms.md +53 -0
  62. package/see/examples/path_discovery.js +27 -1
  63. package/see/examples/rc_discharge_envelope.js +27 -1
  64. package/see/examples/rdf_dataset.js +1512 -0
  65. package/see/examples/rdf_message_flow.js +27 -1
  66. package/see/examples/rdf_messages.js +27 -1
  67. package/see/examples/school_placement_audit.js +27 -1
  68. package/see/examples/smoke_arithmetic.js +27 -1
  69. package/see/examples/socrates.js +27 -1
  70. package/see/examples/triple_terms.js +1442 -0
  71. package/see/examples/wind_turbine.js +27 -1
  72. package/see/examples/witch.js +27 -1
  73. package/see/see.js +101 -4
  74. package/test/api.test.js +86 -0
  75. package/test/see.test.js +0 -0
  76. package/tools/bundle.js +0 -0
  77. 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
- 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
@@ -347,7 +347,300 @@ function stripQuotes(lex) {
347
347
  return lex;
348
348
  }
349
349
 
350
- function lex(inputText) {
350
+
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
+ }
375
+
376
+ function startsWordAt(s, word, at) {
377
+ return s.startsWith(word, at) && !isWordChar(s[at - 1]) && !isWordChar(s[at + word.length]);
378
+ }
379
+
380
+ function readStringAt(s, at) {
381
+ const quote = s[at];
382
+ let i = at;
383
+ let out = quote;
384
+ const long = s.startsWith(quote.repeat(3), i);
385
+ if (long) {
386
+ out = quote.repeat(3);
387
+ i += 3;
388
+ while (i < s.length) {
389
+ if (s.startsWith(quote.repeat(3), i)) {
390
+ out += quote.repeat(3);
391
+ i += 3;
392
+ return { text: out, end: i };
393
+ }
394
+ if (s[i] === '\\' && i + 1 < s.length) {
395
+ out += s.slice(i, i + 2);
396
+ i += 2;
397
+ } else {
398
+ out += s[i++];
399
+ }
400
+ }
401
+ return { text: out, end: i };
402
+ }
403
+ i += 1;
404
+ let escaped = false;
405
+ while (i < s.length) {
406
+ const ch = s[i++];
407
+ out += ch;
408
+ if (escaped) {
409
+ escaped = false;
410
+ } else if (ch === '\\') {
411
+ escaped = true;
412
+ } else if (ch === quote) {
413
+ break;
414
+ }
415
+ }
416
+ return { text: out, end: i };
417
+ }
418
+
419
+ function readIriAt(s, at) {
420
+ let i = at + 1;
421
+ let out = '<';
422
+ while (i < s.length) {
423
+ const ch = s[i++];
424
+ out += ch;
425
+ if (ch === '>') break;
426
+ }
427
+ return { text: out, end: i };
428
+ }
429
+
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;
472
+ }
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;
489
+ continue;
490
+ }
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];
516
+ if (ch === '"' || ch === "'") {
517
+ i = readStringAt(s, i).end;
518
+ continue;
519
+ }
520
+ if (ch === '<') {
521
+ i = readIriAt(s, i).end;
522
+ continue;
523
+ }
524
+ if (ch === '#') {
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 };
533
+ continue;
534
+ }
535
+ i += 1;
536
+ }
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
+
632
+ return out;
633
+ }
634
+
635
+ if (hasTripleTerms) text = convertTripleTerms(text);
636
+ if (hasVersionDirective) text = stripVersionDirectives(text);
637
+ if (hasVersionDirective || hasNamedGraphCandidate) text = normalizeNamedGraphs(text);
638
+ return text;
639
+ }
640
+
641
+ function lex(inputText, opts = {}) {
642
+ const rdf = !!(opts && opts.rdf);
643
+ if (rdf) inputText = normalizeRdfCompatibility(inputText);
351
644
  const chars = Array.from(inputText);
352
645
  const n = chars.length;
353
646
  let i = 0;
@@ -808,4 +1101,4 @@ function lex(inputText) {
808
1101
  return tokens;
809
1102
  }
810
1103
 
811
- 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.1",
3
+ "version": "1.24.3",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/see/README.md CHANGED
@@ -18,6 +18,8 @@ Each example starts from a Notation3 source and is compiled by `see.js` into a s
18
18
 
19
19
  The trust gate is executable verification. If a required fact is missing, the program fails instead of emitting an unsupported entailment.
20
20
 
21
+ The `triple_terms` and `rdf_dataset` examples use RDF 1.2 `<<( ... )>>` triple-term syntax. Eyeling accepts that syntax only with `-r, --rdf`, where it normalizes triple terms to existing N3 singleton graph terms `{ ... }` and prints feasible output graph terms back as RDF 1.2 triple terms. The `rdf_dataset` example also uses an RDF/TriG named graph block, which RDF compatibility mode normalizes to the same `log:nameOf { ... }` graph-term shape emitted by `tools/n3gen` and prints back as a TriG named graph block where feasible; SEE keeps the committed `.trig` input and formal output in RDF 1.2/TriG form.
22
+
21
23
 
22
24
  ## Run
23
25
 
@@ -136,10 +138,12 @@ The repository currently contains **50 N3-compiled SEE examples**. Each example
136
138
  | <a id="example-odrl-dpv-risk-ranking"></a>[ODRL + DPV risk ranking](#example-odrl-dpv-risk-ranking) | compiled from Notation3 by `see.js`; policy and data-protection evidence are ranked by risk level and selected through rule-derived comparisons. | [doc](examples/doc/odrl_dpv_risk_ranked.md), [js](examples/odrl_dpv_risk_ranked.js), [input](examples/input/odrl_dpv_risk_ranked.trig), [output](examples/output/odrl_dpv_risk_ranked.md), [n3](examples/n3/odrl_dpv_risk_ranked.n3) |
137
139
  | <a id="example-path-discovery"></a>[Path discovery](#example-path-discovery) | compiled from the upstream path-discovery rules; the runner loads the full 96k-fact airroutes graph from TriG and evaluates the bounded route query with an indexed adjacency list. | [doc](examples/doc/path_discovery.md), [js](examples/path_discovery.js), [input](examples/input/path-discovery.trig), [output](examples/output/path_discovery.md), [n3](examples/n3/path_discovery.n3) |
138
140
  | <a id="example-rc-discharge-envelope"></a>[RC discharge envelope](#example-rc-discharge-envelope) | compiled from Notation3 by `see.js`; exponentiation over an upper decay bound certifies the first sampled capacitor step below tolerance. | [doc](examples/doc/rc_discharge_envelope.md), [js](examples/rc_discharge_envelope.js), [input](examples/input/rc_discharge_envelope.trig), [output](examples/output/rc_discharge_envelope.md), [n3](examples/n3/rc_discharge_envelope.n3) |
141
+ | <a id="example-rdf-dataset-compatibility"></a>[RDF dataset compatibility](#example-rdf-dataset-compatibility) | compiled from Notation3 by `see.js`; RDF/TriG named graph input and RDF 1.2 triple terms demonstrate the same N3 graph-term normalization used by Eyeling's explicit RDF compatibility mode. | [doc](examples/doc/rdf_dataset.md), [js](examples/rdf_dataset.js), [input](examples/input/rdf_dataset.trig), [output](examples/output/rdf_dataset.md), [n3](examples/n3/rdf_dataset.n3) |
139
142
  | <a id="example-rdf-message-flow"></a>[RDF Message Flow](#example-rdf-message-flow) | compiled from Notation3 by `see.js`; live RDF Messages advance through ingest, validate, interpret, route, and sink stages, with each completed message releasing the next one. | [doc](examples/doc/rdf_message_flow.md), [js](examples/rdf_message_flow.js), [input](examples/input/rdf_message_flow.trig), [output](examples/output/rdf_message_flow.md), [n3](examples/n3/rdf_message_flow.n3) |
140
143
  | <a id="example-rdf-messages"></a>[RDF Messages](#example-rdf-messages) | compiled from Notation3 by `see.js`; ordered RDF Message Log example preserving explicit message boundaries, an empty heartbeat, message-local payloads, and reused blank-node labels. | [doc](examples/doc/rdf_messages.md), [js](examples/rdf_messages.js), [input](examples/input/rdf_messages.trig), [output](examples/output/rdf_messages.md), [n3](examples/n3/rdf_messages.n3) |
141
144
  | <a id="example-school-placement-route-audit"></a>[School placement route audit](#example-school-placement-route-audit) | compiled from Notation3 by `see.js`; student, school, distance, and policy facts derive an auditable school-placement route decision. | [doc](examples/doc/school_placement_audit.md), [js](examples/school_placement_audit.js), [input](examples/input/school_placement_audit.trig), [output](examples/output/school_placement_audit.md), [n3](examples/n3/school_placement_audit.n3) |
142
145
  | <a id="example-smoke-arithmetic"></a>[Smoke Arithmetic](#example-smoke-arithmetic) | compiled from Notation3 by `see.js`; arithmetic compiler smoke test deriving 6 × 7 through rules and builtins. | [doc](examples/doc/smoke_arithmetic.md), [js](examples/smoke_arithmetic.js), [input](examples/input/smoke_arithmetic.trig), [output](examples/output/smoke_arithmetic.md), [n3](examples/n3/smoke_arithmetic.n3) |
143
146
  | <a id="example-socrates-inference"></a>[Socrates inference](#example-socrates-inference) | compiled from Notation3 by `see.js`; subclass closure with top-level `log:query` projection. | [doc](examples/doc/socrates.md), [js](examples/socrates.js), [input](examples/input/socrates.trig), [output](examples/output/socrates.md), [n3](examples/n3/socrates.n3) |
147
+ | <a id="example-triple-terms"></a>[Triple terms](#example-triple-terms) | compiled from Notation3 by `see.js`; RDF 1.2 TriG triple terms are used as input evidence and as a derived entailment. | [doc](examples/doc/triple_terms.md), [js](examples/triple_terms.js), [input](examples/input/triple_terms.trig), [output](examples/output/triple_terms.md), [n3](examples/n3/triple_terms.n3) |
144
148
  | <a id="example-wind-turbine-envelope"></a>[Wind turbine envelope](#example-wind-turbine-envelope) | compiled from Notation3 by `see.js`; wind-speed samples are classified against cut-in, rated, and cut-out thresholds and accumulated into energy. | [doc](examples/doc/wind_turbine.md), [js](examples/wind_turbine.js), [input](examples/input/wind_turbine.trig), [output](examples/output/wind_turbine.md), [n3](examples/n3/wind_turbine.n3) |
145
149
  | <a id="example-burn-the-witch"></a>[Burn the witch](#example-burn-the-witch) | compiled from Notation3 by `see.js`; rule-chain explanation for the witch entailment. | [doc](examples/doc/witch.md), [js](examples/witch.js), [input](examples/input/witch.trig), [output](examples/output/witch.md), [n3](examples/n3/witch.n3) |