eyeling 1.30.4 → 1.30.6

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 (34) hide show
  1. package/README.md +29 -25
  2. package/dist/browser/eyeling.browser.js +79 -5
  3. package/examples/input/{rdf-surfaces-all-values-from-reverse.ttl → rdf-surfaces-all-values-from-reverse.trig} +1 -0
  4. package/examples/input/{rdf-surfaces-all-values-from.ttl → rdf-surfaces-all-values-from.trig} +1 -0
  5. package/examples/input/{rdf-surfaces-ancestor.ttl → rdf-surfaces-ancestor.trig} +1 -0
  6. package/examples/input/{rdf-surfaces-city.ttl → rdf-surfaces-city.trig} +1 -0
  7. package/examples/input/{rdf-surfaces-disjunction-elimination.ttl → rdf-surfaces-disjunction-elimination.trig} +1 -0
  8. package/examples/input/{rdf-surfaces-disjunction-route-filter.ttl → rdf-surfaces-disjunction-route-filter.trig} +1 -0
  9. package/examples/input/{rdf-surfaces-domain.ttl → rdf-surfaces-domain.trig} +1 -0
  10. package/examples/input/{rdf-surfaces-explicit-disjunction.ttl → rdf-surfaces-explicit-disjunction.trig} +1 -0
  11. package/examples/input/{rdf-surfaces-multi-premise.ttl → rdf-surfaces-multi-premise.trig} +1 -0
  12. package/examples/input/{rdf-surfaces-owl-all-values-from-codex.ttl → rdf-surfaces-owl-all-values-from-codex.trig} +1 -0
  13. package/examples/input/{rdf-surfaces-property-chain.ttl → rdf-surfaces-property-chain.trig} +1 -0
  14. package/examples/input/{rdf-surfaces-range.ttl → rdf-surfaces-range.trig} +1 -0
  15. package/examples/input/rdf-surfaces-rdf12-graph-triple-term.trig +15 -0
  16. package/examples/input/rdf-surfaces-rdf12-named-graph.trig +14 -0
  17. package/examples/input/rdf-surfaces-rdf12-triple-term.trig +13 -0
  18. package/examples/input/{rdf-surfaces-rdfs-range-codex.ttl → rdf-surfaces-rdfs-range-codex.trig} +1 -0
  19. package/examples/input/{rdf-surfaces-rdfs-subclass-codex.ttl → rdf-surfaces-rdfs-subclass-codex.trig} +1 -0
  20. package/examples/input/{rdf-surfaces-strong-negation-access.ttl → rdf-surfaces-strong-negation-access.trig} +1 -0
  21. package/examples/output/rdf-surfaces-rdf12-graph-triple-term.n3 +3 -0
  22. package/examples/output/rdf-surfaces-rdf12-named-graph.n3 +3 -0
  23. package/examples/output/rdf-surfaces-rdf12-triple-term.n3 +3 -0
  24. package/examples/rdf-surfaces-rdf12-graph-triple-term.n3 +6 -0
  25. package/examples/rdf-surfaces-rdf12-named-graph.n3 +6 -0
  26. package/examples/rdf-surfaces-rdf12-triple-term.n3 +6 -0
  27. package/eyeling.js +79 -5
  28. package/lib/lexer.js +2 -2
  29. package/lib/rdf_surfaces.js +77 -3
  30. package/package.json +3 -5
  31. package/test/examples.test.js +1 -1
  32. package/test/playground.test.js +1 -1
  33. package/test/rdf_surfaces.test.js +16 -3
  34. package/test/run.js +61 -0
package/README.md CHANGED
@@ -593,16 +593,17 @@ RDF 1.2 triple terms require explicit RDF compatibility mode. This protects ordi
593
593
 
594
594
  ### RDF Surfaces
595
595
 
596
- RDF Surfaces are enabled with `--rdf-surfaces` on the CLI or `{ rdfSurfaces: true }` in the API. The option implies RDF compatibility mode. The syntax follows the BLOGIC text convention from Pat Hayes' ISWC 2009 slides: `%not[` and `%]` surface parentheses plus explicit blank-node binding graffiti at the beginning of a surface. Because this is Turtle plus the surface extension, the examples use `.ttl` for RDF Surface input files.
596
+ RDF Surfaces are enabled with `--rdf-surfaces` on the CLI or `{ rdfSurfaces: true }` in the API. The option implies RDF compatibility mode. The syntax follows the BLOGIC text convention from Pat Hayes' ISWC 2009 slides: `%not[` and `%]` surface parentheses plus explicit blank-node binding graffiti at the beginning of a surface. Because RDF Surfaces may now combine the surface extension with RDF 1.2 TriG features, the examples use `.trig` for RDF Surface input files and start with `VERSION "1.2-surfaces"`.
597
597
 
598
598
  The examples keep RDF Surface input separate from Eyeling queries:
599
599
 
600
- - `examples/input/<name>.ttl` contains ordinary RDF triples and RDF Surface rules.
600
+ - `examples/input/<name>.trig` contains RDF 1.2 TriG input, RDF Surface rules, and the `VERSION "1.2-surfaces"` header.
601
601
  - `examples/<name>.n3` contains only the corresponding `log:query`.
602
602
 
603
603
  Use separate non-indented lines for triples and surface closes. Put only newly bound blank marks on the surface-opening line; marks already bound by an outer surface are reused in inner triples and are not repeated after `%not[`.
604
604
 
605
- ```turtle
605
+ ```trig
606
+ VERSION "1.2-surfaces"
606
607
  @prefix ex: <http://example.org/> .
607
608
 
608
609
  ex:Brussels a ex:City .
@@ -625,7 +626,7 @@ The slide-32 shape above is normalized as:
625
626
 
626
627
  The first slide-33 abbreviation, range, is written as:
627
628
 
628
- ```turtle
629
+ ```trig
629
630
  %not[ _:x _:y
630
631
  _:x ex:parent _:y .
631
632
  %not[
@@ -644,7 +645,7 @@ and behaves like:
644
645
 
645
646
  Eyeling also recognizes codex-style surfaces that introduce rules from RDF/OWL vocabulary facts. For example, this `rdfs:range` codex:
646
647
 
647
- ```turtle
648
+ ```trig
648
649
  @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
649
650
 
650
651
  %not[ _:p _:c
@@ -673,7 +674,7 @@ is normalized to the Horn rule shape:
673
674
 
674
675
  The slide-33 `owl:allValuesFrom` codex is supported in both directions:
675
676
 
676
- ```turtle
677
+ ```trig
677
678
  @prefix owl: <http://www.w3.org/2002/07/owl#> .
678
679
 
679
680
  %not[ _:a _:b _:c
@@ -706,7 +707,7 @@ The forward part derives filler types. The reverse part is compiled into a data-
706
707
 
707
708
  A top-level negative surface without a nested negative child is treated as an inference fuse:
708
709
 
709
- ```turtle
710
+ ```trig
710
711
  %not[ _:x
711
712
  _:x a ex:Impossible .
712
713
  %]
@@ -723,24 +724,27 @@ The implementation is intentionally conservative: it supports the practical Horn
723
724
  Run the included examples:
724
725
 
725
726
  ```bash
726
- eyeling --rdf-surfaces examples/input/rdf-surfaces-city.ttl examples/rdf-surfaces-city.n3
727
- eyeling --rdf-surfaces examples/input/rdf-surfaces-range.ttl examples/rdf-surfaces-range.n3
728
- eyeling --rdf-surfaces examples/input/rdf-surfaces-domain.ttl examples/rdf-surfaces-domain.n3
729
- eyeling --rdf-surfaces examples/input/rdf-surfaces-property-chain.ttl examples/rdf-surfaces-property-chain.n3
730
- eyeling --rdf-surfaces examples/input/rdf-surfaces-ancestor.ttl examples/rdf-surfaces-ancestor.n3
731
- eyeling --rdf-surfaces examples/input/rdf-surfaces-multi-premise.ttl examples/rdf-surfaces-multi-premise.n3
732
- eyeling --rdf-surfaces examples/input/rdf-surfaces-all-values-from.ttl examples/rdf-surfaces-all-values-from.n3
733
- eyeling --rdf-surfaces examples/input/rdf-surfaces-all-values-from-reverse.ttl examples/rdf-surfaces-all-values-from-reverse.n3
734
- eyeling --rdf-surfaces examples/input/rdf-surfaces-rdfs-range-codex.ttl examples/rdf-surfaces-rdfs-range-codex.n3
735
- eyeling --rdf-surfaces examples/input/rdf-surfaces-rdfs-subclass-codex.ttl examples/rdf-surfaces-rdfs-subclass-codex.n3
736
- eyeling --rdf-surfaces examples/input/rdf-surfaces-owl-all-values-from-codex.ttl examples/rdf-surfaces-owl-all-values-from-codex.n3
737
- eyeling --rdf-surfaces examples/input/rdf-surfaces-strong-negation-access.ttl examples/rdf-surfaces-strong-negation-access.n3
738
- eyeling --rdf-surfaces examples/input/rdf-surfaces-disjunction-route-filter.ttl examples/rdf-surfaces-disjunction-route-filter.n3
739
- eyeling --rdf-surfaces examples/input/rdf-surfaces-explicit-disjunction.ttl examples/rdf-surfaces-explicit-disjunction.n3
740
- eyeling --rdf-surfaces examples/input/rdf-surfaces-disjunction-elimination.ttl examples/rdf-surfaces-disjunction-elimination.n3
741
- ```
742
-
743
- The additional examples show ordinary Horn-style RDF Surface patterns beyond the exact slide-33 abbreviations: subject typing (`rdf-surfaces-domain`), a two-hop property chain (`rdf-surfaces-property-chain`), recursive transitive closure (`rdf-surfaces-ancestor`), a conjunctive classification rule (`rdf-surfaces-multi-premise`), and codex-style RDFS/OWL rule generation (`rdf-surfaces-rdfs-range-codex`, `rdf-surfaces-rdfs-subclass-codex`, `rdf-surfaces-owl-all-values-from-codex`). More challenging examples keep the engine unchanged and add N3 helper rules on top: `rdf-surfaces-strong-negation-access` uses a top-level negative surface as a strong-negation fuse for an access policy, `rdf-surfaces-disjunction-route-filter` represents a disjunction as explicit candidate routes and filters one with a strong-negation policy, `rdf-surfaces-explicit-disjunction` puts the disjunction directly in the RDF Surface file with one outer negative surface and two inner alternatives, and `rdf-surfaces-disjunction-elimination` uses `log:forAllIn` to derive a conclusion only when every option in a disjunction implies it.
727
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-city.trig examples/rdf-surfaces-city.n3
728
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-range.trig examples/rdf-surfaces-range.n3
729
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-domain.trig examples/rdf-surfaces-domain.n3
730
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-property-chain.trig examples/rdf-surfaces-property-chain.n3
731
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-ancestor.trig examples/rdf-surfaces-ancestor.n3
732
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-multi-premise.trig examples/rdf-surfaces-multi-premise.n3
733
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-all-values-from.trig examples/rdf-surfaces-all-values-from.n3
734
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-all-values-from-reverse.trig examples/rdf-surfaces-all-values-from-reverse.n3
735
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-rdfs-range-codex.trig examples/rdf-surfaces-rdfs-range-codex.n3
736
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-rdfs-subclass-codex.trig examples/rdf-surfaces-rdfs-subclass-codex.n3
737
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-rdf12-named-graph.trig examples/rdf-surfaces-rdf12-named-graph.n3
738
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-rdf12-triple-term.trig examples/rdf-surfaces-rdf12-triple-term.n3
739
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-rdf12-graph-triple-term.trig examples/rdf-surfaces-rdf12-graph-triple-term.n3
740
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-owl-all-values-from-codex.trig examples/rdf-surfaces-owl-all-values-from-codex.n3
741
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-strong-negation-access.trig examples/rdf-surfaces-strong-negation-access.n3
742
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-disjunction-route-filter.trig examples/rdf-surfaces-disjunction-route-filter.n3
743
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-explicit-disjunction.trig examples/rdf-surfaces-explicit-disjunction.n3
744
+ eyeling --rdf-surfaces examples/input/rdf-surfaces-disjunction-elimination.trig examples/rdf-surfaces-disjunction-elimination.n3
745
+ ```
746
+
747
+ The additional examples show ordinary Horn-style RDF Surface patterns beyond the exact slide-33 abbreviations: subject typing (`rdf-surfaces-domain`), a two-hop property chain (`rdf-surfaces-property-chain`), recursive transitive closure (`rdf-surfaces-ancestor`), a conjunctive classification rule (`rdf-surfaces-multi-premise`), codex-style RDFS/OWL rule generation (`rdf-surfaces-rdfs-range-codex`, `rdf-surfaces-rdfs-subclass-codex`, `rdf-surfaces-owl-all-values-from-codex`), and RDF 1.2 inputs with named graphs and triple terms (`rdf-surfaces-rdf12-named-graph`, `rdf-surfaces-rdf12-triple-term`, `rdf-surfaces-rdf12-graph-triple-term`). More challenging examples keep the engine unchanged and add N3 helper rules on top: `rdf-surfaces-strong-negation-access` uses a top-level negative surface as a strong-negation fuse for an access policy, `rdf-surfaces-disjunction-route-filter` represents a disjunction as explicit candidate routes and filters one with a strong-negation policy, `rdf-surfaces-explicit-disjunction` puts the disjunction directly in the RDF Surface file with one outer negative surface and two inner alternatives, and `rdf-surfaces-disjunction-elimination` uses `log:forAllIn` to derive a conclusion only when every option in a disjunction implies it.
744
748
 
745
749
  ---
746
750
 
@@ -12852,7 +12852,7 @@ function normalizeRdfCompatibility(inputText) {
12852
12852
  // unless they actually contain RDF 1.2 triple terms, VERSION directives, or a
12853
12853
  // plausible top-level TriG named graph block.
12854
12854
  const hasTripleTerms = text.includes('<<');
12855
- const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)\1\s*\.?\s*(?:#.*)?$/im.test(text);
12855
+ const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic|1\.2-surfaces)\1\s*\.?\s*(?:#.*)?$/im.test(text);
12856
12856
  const hasMessageVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im.test(text);
12857
12857
  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{};,.()[\]]+)\s*\{/m.test(text);
12858
12858
  const hasAnnotationSyntax = /(?:^|\s)~|\{\|/.test(text);
@@ -13321,7 +13321,7 @@ function normalizeRdfCompatibility(inputText) {
13321
13321
  }
13322
13322
 
13323
13323
  function stripVersionDirectives(s) {
13324
- return s.replace(/^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)(?:-messages)?\1\s*\.?\s*(?:#.*)?$/gim, '');
13324
+ return s.replace(/^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic|1\.2-surfaces|(?:1\.1|1\.2|1\.2-basic)-messages)\1\s*\.?\s*(?:#.*)?$/gim, '');
13325
13325
  }
13326
13326
 
13327
13327
  function skipWsAndComments(s, at) {
@@ -16988,6 +16988,24 @@ function extractLeadingBinders(raw) {
16988
16988
  const contentStart = skipWsAndComments(text, 0);
16989
16989
  if (contentStart >= text.length) return { binders: [], text };
16990
16990
 
16991
+ // Preferred BLOGIC graffiti style: put newly bound marks on the `%not[`
16992
+ // line and put triples on following non-indented lines, e.g.
16993
+ // `%not[ _:x _:y\n_:x :p _:y .`. Reading that first line directly
16994
+ // avoids guessing from the first triple shape, which matters for RDF 1.2
16995
+ // formula objects and TriG named graph blocks.
16996
+ let lineEnd = text.indexOf('\n', contentStart);
16997
+ if (lineEnd < 0) lineEnd = text.length;
16998
+ let lineText = text.slice(contentStart, lineEnd);
16999
+ let newlineEnd = lineEnd < text.length ? lineEnd + 1 : lineEnd;
17000
+ if (lineText.endsWith('\r')) {
17001
+ lineText = lineText.slice(0, -1);
17002
+ }
17003
+ const lineTrim = lineText.trim();
17004
+ if (/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(lineTrim)) {
17005
+ const binders = lineTrim.split(/\s+/).map((tok) => tok.slice(2));
17006
+ return { binders, text: text.slice(0, contentStart) + text.slice(newlineEnd) };
17007
+ }
17008
+
16991
17009
  const segment = readStatementSegment(text.slice(contentStart));
16992
17010
  const toks = tokenizeLeadingSegment(segment);
16993
17011
  let leadingBlankCount = 0;
@@ -17015,6 +17033,57 @@ function extractLeadingBinders(raw) {
17015
17033
  return { binders, text: text.slice(0, contentStart) + text.slice(contentStart + cut) };
17016
17034
  }
17017
17035
 
17036
+
17037
+ function readBalancedCurlyAt(s, at) {
17038
+ if (s[at] !== '{') return null;
17039
+ let i = at;
17040
+ let depth = 0;
17041
+ while (i < s.length) {
17042
+ const ch = s[i];
17043
+ if (ch === '"' || ch === "'") {
17044
+ i = readStringAt(s, i).end;
17045
+ continue;
17046
+ }
17047
+ if (ch === '<') {
17048
+ i = readIriAt(s, i).end;
17049
+ continue;
17050
+ }
17051
+ if (ch === '#') {
17052
+ while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
17053
+ continue;
17054
+ }
17055
+ if (ch === '{') depth += 1;
17056
+ else if (ch === '}') {
17057
+ depth -= 1;
17058
+ if (depth === 0) return { text: s.slice(at, i + 1), end: i + 1 };
17059
+ }
17060
+ i += 1;
17061
+ }
17062
+ throw syntaxError('Unterminated named graph block inside RDF Surface', at);
17063
+ }
17064
+
17065
+ function normalizeSurfaceStatement(statement) {
17066
+ const raw = String(statement || '').trim();
17067
+ if (!raw) return raw;
17068
+
17069
+ let i = 0;
17070
+ const first = readBareTokenAt(raw, i);
17071
+ if (first && /^GRAPH$/i.test(first.text)) {
17072
+ i = first.end;
17073
+ }
17074
+
17075
+ const term = readBareTokenAt(raw, i);
17076
+ if (!term) return raw;
17077
+ const afterTerm = skipWsAndComments(raw, term.end);
17078
+ if (raw[afterTerm] !== '{') return raw;
17079
+
17080
+ const block = readBalancedCurlyAt(raw, afterTerm);
17081
+ const afterBlock = skipWsAndComments(raw, block.end);
17082
+ if (afterBlock !== raw.length) return raw;
17083
+
17084
+ return `${term.text} ${LOG_NAME_OF_IRI} ${block.text}`;
17085
+ }
17086
+
17018
17087
  function splitTopLevelStatements(raw, surfaceOffset = null) {
17019
17088
  const text = String(raw || '');
17020
17089
  const out = [];
@@ -17046,7 +17115,7 @@ function splitTopLevelStatements(raw, surfaceOffset = null) {
17046
17115
  else if (ch === ')' && depthParen > 0) depthParen -= 1;
17047
17116
  else if (ch === '.' && depthBrace === 0 && depthBracket === 0 && depthParen === 0) {
17048
17117
  const stmt = text.slice(start, i).trim();
17049
- if (stmt) out.push(stmt);
17118
+ if (stmt) out.push(normalizeSurfaceStatement(stmt));
17050
17119
  start = i + 1;
17051
17120
  }
17052
17121
  i += 1;
@@ -17054,9 +17123,13 @@ function splitTopLevelStatements(raw, surfaceOffset = null) {
17054
17123
 
17055
17124
  const tail = text.slice(start).trim();
17056
17125
  if (tail) {
17057
- // A raw binder-only segment is OK; any other dangling text is most likely a
17126
+ // A raw binder-only segment is OK; RDF 1.2 TriG named graph blocks are
17127
+ // also OK without a trailing dot. Any other dangling text is most likely a
17058
17128
  // missing dot in the surface body.
17059
- if (!/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(tail)) {
17129
+ const normalizedTail = normalizeSurfaceStatement(tail);
17130
+ if (normalizedTail !== tail) {
17131
+ out.push(normalizedTail);
17132
+ } else if (!/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(tail)) {
17060
17133
  throw syntaxError('RDF Surface statement is missing a terminating dot', surfaceOffset);
17061
17134
  }
17062
17135
  }
@@ -17126,6 +17199,7 @@ function readSurfaceAt(s, at) {
17126
17199
  }
17127
17200
 
17128
17201
  const LOG_FOR_ALL_IN_IRI = '<http://www.w3.org/2000/10/swap/log#forAllIn>';
17202
+ const LOG_NAME_OF_IRI = '<http://www.w3.org/2000/10/swap/log#nameOf>';
17129
17203
 
17130
17204
  function rewriteBlankMarksWithMap(statement, labelToVarName) {
17131
17205
  const map = labelToVarName instanceof Map ? labelToVarName : new Map();
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
  @prefix owl: <http://www.w3.org/2002/07/owl#> .
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -0,0 +1,15 @@
1
+ VERSION "1.2-surfaces"
2
+ # eyeling-options: --rdf-surfaces
3
+ @prefix ex: <http://example.org/> .
4
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
5
+
6
+ ex:auditGraph {
7
+ ex:obs1 rdf:reifies <<( ex:sensor17 ex:reports ex:Overheating )>> .
8
+ }
9
+
10
+ %not[ _:obs
11
+ ex:auditGraph { _:obs rdf:reifies <<( ex:sensor17 ex:reports ex:Overheating )>> . }
12
+ %not[
13
+ _:obs a ex:AuditedObservation .
14
+ %]
15
+ %]
@@ -0,0 +1,14 @@
1
+ VERSION "1.2-surfaces"
2
+ # eyeling-options: --rdf-surfaces
3
+ @prefix ex: <http://example.org/> .
4
+
5
+ ex:regionalReport {
6
+ ex:Brussels a ex:City .
7
+ }
8
+
9
+ %not[ _:x
10
+ ex:regionalReport { _:x a ex:City . }
11
+ %not[
12
+ _:x a ex:ReportedCity .
13
+ %]
14
+ %]
@@ -0,0 +1,13 @@
1
+ VERSION "1.2-surfaces"
2
+ # eyeling-options: --rdf-surfaces
3
+ @prefix ex: <http://example.org/> .
4
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
5
+
6
+ ex:claim1 rdf:reifies <<( ex:Brussels rdf:type ex:City )>> .
7
+
8
+ %not[ _:claim
9
+ _:claim rdf:reifies <<( ex:Brussels rdf:type ex:City )>> .
10
+ %not[
11
+ _:claim a ex:CityClaim .
12
+ %]
13
+ %]
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
  @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
  @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@@ -1,3 +1,4 @@
1
+ VERSION "1.2-surfaces"
1
2
  # eyeling-options: --rdf-surfaces
2
3
  @prefix ex: <http://example.org/> .
3
4
 
@@ -0,0 +1,3 @@
1
+ @prefix ex: <http://example.org/> .
2
+
3
+ ex:obs1 a ex:AuditedObservation .
@@ -0,0 +1,3 @@
1
+ @prefix ex: <http://example.org/> .
2
+
3
+ ex:Brussels a ex:ReportedCity .
@@ -0,0 +1,3 @@
1
+ @prefix ex: <http://example.org/> .
2
+
3
+ ex:claim1 a ex:CityClaim .
@@ -0,0 +1,6 @@
1
+ @prefix ex: <http://example.org/> .
2
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
3
+
4
+ { ?s a ex:AuditedObservation . }
5
+ log:query
6
+ { ?s a ex:AuditedObservation . } .
@@ -0,0 +1,6 @@
1
+ @prefix ex: <http://example.org/> .
2
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
3
+
4
+ { ?s a ex:ReportedCity . }
5
+ log:query
6
+ { ?s a ex:ReportedCity . } .
@@ -0,0 +1,6 @@
1
+ @prefix ex: <http://example.org/> .
2
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
3
+
4
+ { ?s a ex:CityClaim . }
5
+ log:query
6
+ { ?s a ex:CityClaim . } .
package/eyeling.js CHANGED
@@ -12852,7 +12852,7 @@ function normalizeRdfCompatibility(inputText) {
12852
12852
  // unless they actually contain RDF 1.2 triple terms, VERSION directives, or a
12853
12853
  // plausible top-level TriG named graph block.
12854
12854
  const hasTripleTerms = text.includes('<<');
12855
- const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)\1\s*\.?\s*(?:#.*)?$/im.test(text);
12855
+ const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic|1\.2-surfaces)\1\s*\.?\s*(?:#.*)?$/im.test(text);
12856
12856
  const hasMessageVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im.test(text);
12857
12857
  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{};,.()[\]]+)\s*\{/m.test(text);
12858
12858
  const hasAnnotationSyntax = /(?:^|\s)~|\{\|/.test(text);
@@ -13321,7 +13321,7 @@ function normalizeRdfCompatibility(inputText) {
13321
13321
  }
13322
13322
 
13323
13323
  function stripVersionDirectives(s) {
13324
- return s.replace(/^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)(?:-messages)?\1\s*\.?\s*(?:#.*)?$/gim, '');
13324
+ return s.replace(/^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic|1\.2-surfaces|(?:1\.1|1\.2|1\.2-basic)-messages)\1\s*\.?\s*(?:#.*)?$/gim, '');
13325
13325
  }
13326
13326
 
13327
13327
  function skipWsAndComments(s, at) {
@@ -16988,6 +16988,24 @@ function extractLeadingBinders(raw) {
16988
16988
  const contentStart = skipWsAndComments(text, 0);
16989
16989
  if (contentStart >= text.length) return { binders: [], text };
16990
16990
 
16991
+ // Preferred BLOGIC graffiti style: put newly bound marks on the `%not[`
16992
+ // line and put triples on following non-indented lines, e.g.
16993
+ // `%not[ _:x _:y\n_:x :p _:y .`. Reading that first line directly
16994
+ // avoids guessing from the first triple shape, which matters for RDF 1.2
16995
+ // formula objects and TriG named graph blocks.
16996
+ let lineEnd = text.indexOf('\n', contentStart);
16997
+ if (lineEnd < 0) lineEnd = text.length;
16998
+ let lineText = text.slice(contentStart, lineEnd);
16999
+ let newlineEnd = lineEnd < text.length ? lineEnd + 1 : lineEnd;
17000
+ if (lineText.endsWith('\r')) {
17001
+ lineText = lineText.slice(0, -1);
17002
+ }
17003
+ const lineTrim = lineText.trim();
17004
+ if (/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(lineTrim)) {
17005
+ const binders = lineTrim.split(/\s+/).map((tok) => tok.slice(2));
17006
+ return { binders, text: text.slice(0, contentStart) + text.slice(newlineEnd) };
17007
+ }
17008
+
16991
17009
  const segment = readStatementSegment(text.slice(contentStart));
16992
17010
  const toks = tokenizeLeadingSegment(segment);
16993
17011
  let leadingBlankCount = 0;
@@ -17015,6 +17033,57 @@ function extractLeadingBinders(raw) {
17015
17033
  return { binders, text: text.slice(0, contentStart) + text.slice(contentStart + cut) };
17016
17034
  }
17017
17035
 
17036
+
17037
+ function readBalancedCurlyAt(s, at) {
17038
+ if (s[at] !== '{') return null;
17039
+ let i = at;
17040
+ let depth = 0;
17041
+ while (i < s.length) {
17042
+ const ch = s[i];
17043
+ if (ch === '"' || ch === "'") {
17044
+ i = readStringAt(s, i).end;
17045
+ continue;
17046
+ }
17047
+ if (ch === '<') {
17048
+ i = readIriAt(s, i).end;
17049
+ continue;
17050
+ }
17051
+ if (ch === '#') {
17052
+ while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
17053
+ continue;
17054
+ }
17055
+ if (ch === '{') depth += 1;
17056
+ else if (ch === '}') {
17057
+ depth -= 1;
17058
+ if (depth === 0) return { text: s.slice(at, i + 1), end: i + 1 };
17059
+ }
17060
+ i += 1;
17061
+ }
17062
+ throw syntaxError('Unterminated named graph block inside RDF Surface', at);
17063
+ }
17064
+
17065
+ function normalizeSurfaceStatement(statement) {
17066
+ const raw = String(statement || '').trim();
17067
+ if (!raw) return raw;
17068
+
17069
+ let i = 0;
17070
+ const first = readBareTokenAt(raw, i);
17071
+ if (first && /^GRAPH$/i.test(first.text)) {
17072
+ i = first.end;
17073
+ }
17074
+
17075
+ const term = readBareTokenAt(raw, i);
17076
+ if (!term) return raw;
17077
+ const afterTerm = skipWsAndComments(raw, term.end);
17078
+ if (raw[afterTerm] !== '{') return raw;
17079
+
17080
+ const block = readBalancedCurlyAt(raw, afterTerm);
17081
+ const afterBlock = skipWsAndComments(raw, block.end);
17082
+ if (afterBlock !== raw.length) return raw;
17083
+
17084
+ return `${term.text} ${LOG_NAME_OF_IRI} ${block.text}`;
17085
+ }
17086
+
17018
17087
  function splitTopLevelStatements(raw, surfaceOffset = null) {
17019
17088
  const text = String(raw || '');
17020
17089
  const out = [];
@@ -17046,7 +17115,7 @@ function splitTopLevelStatements(raw, surfaceOffset = null) {
17046
17115
  else if (ch === ')' && depthParen > 0) depthParen -= 1;
17047
17116
  else if (ch === '.' && depthBrace === 0 && depthBracket === 0 && depthParen === 0) {
17048
17117
  const stmt = text.slice(start, i).trim();
17049
- if (stmt) out.push(stmt);
17118
+ if (stmt) out.push(normalizeSurfaceStatement(stmt));
17050
17119
  start = i + 1;
17051
17120
  }
17052
17121
  i += 1;
@@ -17054,9 +17123,13 @@ function splitTopLevelStatements(raw, surfaceOffset = null) {
17054
17123
 
17055
17124
  const tail = text.slice(start).trim();
17056
17125
  if (tail) {
17057
- // A raw binder-only segment is OK; any other dangling text is most likely a
17126
+ // A raw binder-only segment is OK; RDF 1.2 TriG named graph blocks are
17127
+ // also OK without a trailing dot. Any other dangling text is most likely a
17058
17128
  // missing dot in the surface body.
17059
- if (!/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(tail)) {
17129
+ const normalizedTail = normalizeSurfaceStatement(tail);
17130
+ if (normalizedTail !== tail) {
17131
+ out.push(normalizedTail);
17132
+ } else if (!/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(tail)) {
17060
17133
  throw syntaxError('RDF Surface statement is missing a terminating dot', surfaceOffset);
17061
17134
  }
17062
17135
  }
@@ -17126,6 +17199,7 @@ function readSurfaceAt(s, at) {
17126
17199
  }
17127
17200
 
17128
17201
  const LOG_FOR_ALL_IN_IRI = '<http://www.w3.org/2000/10/swap/log#forAllIn>';
17202
+ const LOG_NAME_OF_IRI = '<http://www.w3.org/2000/10/swap/log#nameOf>';
17129
17203
 
17130
17204
  function rewriteBlankMarksWithMap(statement, labelToVarName) {
17131
17205
  const map = labelToVarName instanceof Map ? labelToVarName : new Map();
package/lib/lexer.js CHANGED
@@ -402,7 +402,7 @@ function normalizeRdfCompatibility(inputText) {
402
402
  // unless they actually contain RDF 1.2 triple terms, VERSION directives, or a
403
403
  // plausible top-level TriG named graph block.
404
404
  const hasTripleTerms = text.includes('<<');
405
- const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)\1\s*\.?\s*(?:#.*)?$/im.test(text);
405
+ const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic|1\.2-surfaces)\1\s*\.?\s*(?:#.*)?$/im.test(text);
406
406
  const hasMessageVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im.test(text);
407
407
  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{};,.()[\]]+)\s*\{/m.test(text);
408
408
  const hasAnnotationSyntax = /(?:^|\s)~|\{\|/.test(text);
@@ -871,7 +871,7 @@ function normalizeRdfCompatibility(inputText) {
871
871
  }
872
872
 
873
873
  function stripVersionDirectives(s) {
874
- return s.replace(/^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)(?:-messages)?\1\s*\.?\s*(?:#.*)?$/gim, '');
874
+ return s.replace(/^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic|1\.2-surfaces|(?:1\.1|1\.2|1\.2-basic)-messages)\1\s*\.?\s*(?:#.*)?$/gim, '');
875
875
  }
876
876
 
877
877
  function skipWsAndComments(s, at) {
@@ -158,6 +158,24 @@ function extractLeadingBinders(raw) {
158
158
  const contentStart = skipWsAndComments(text, 0);
159
159
  if (contentStart >= text.length) return { binders: [], text };
160
160
 
161
+ // Preferred BLOGIC graffiti style: put newly bound marks on the `%not[`
162
+ // line and put triples on following non-indented lines, e.g.
163
+ // `%not[ _:x _:y\n_:x :p _:y .`. Reading that first line directly
164
+ // avoids guessing from the first triple shape, which matters for RDF 1.2
165
+ // formula objects and TriG named graph blocks.
166
+ let lineEnd = text.indexOf('\n', contentStart);
167
+ if (lineEnd < 0) lineEnd = text.length;
168
+ let lineText = text.slice(contentStart, lineEnd);
169
+ let newlineEnd = lineEnd < text.length ? lineEnd + 1 : lineEnd;
170
+ if (lineText.endsWith('\r')) {
171
+ lineText = lineText.slice(0, -1);
172
+ }
173
+ const lineTrim = lineText.trim();
174
+ if (/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(lineTrim)) {
175
+ const binders = lineTrim.split(/\s+/).map((tok) => tok.slice(2));
176
+ return { binders, text: text.slice(0, contentStart) + text.slice(newlineEnd) };
177
+ }
178
+
161
179
  const segment = readStatementSegment(text.slice(contentStart));
162
180
  const toks = tokenizeLeadingSegment(segment);
163
181
  let leadingBlankCount = 0;
@@ -185,6 +203,57 @@ function extractLeadingBinders(raw) {
185
203
  return { binders, text: text.slice(0, contentStart) + text.slice(contentStart + cut) };
186
204
  }
187
205
 
206
+
207
+ function readBalancedCurlyAt(s, at) {
208
+ if (s[at] !== '{') return null;
209
+ let i = at;
210
+ let depth = 0;
211
+ while (i < s.length) {
212
+ const ch = s[i];
213
+ if (ch === '"' || ch === "'") {
214
+ i = readStringAt(s, i).end;
215
+ continue;
216
+ }
217
+ if (ch === '<') {
218
+ i = readIriAt(s, i).end;
219
+ continue;
220
+ }
221
+ if (ch === '#') {
222
+ while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
223
+ continue;
224
+ }
225
+ if (ch === '{') depth += 1;
226
+ else if (ch === '}') {
227
+ depth -= 1;
228
+ if (depth === 0) return { text: s.slice(at, i + 1), end: i + 1 };
229
+ }
230
+ i += 1;
231
+ }
232
+ throw syntaxError('Unterminated named graph block inside RDF Surface', at);
233
+ }
234
+
235
+ function normalizeSurfaceStatement(statement) {
236
+ const raw = String(statement || '').trim();
237
+ if (!raw) return raw;
238
+
239
+ let i = 0;
240
+ const first = readBareTokenAt(raw, i);
241
+ if (first && /^GRAPH$/i.test(first.text)) {
242
+ i = first.end;
243
+ }
244
+
245
+ const term = readBareTokenAt(raw, i);
246
+ if (!term) return raw;
247
+ const afterTerm = skipWsAndComments(raw, term.end);
248
+ if (raw[afterTerm] !== '{') return raw;
249
+
250
+ const block = readBalancedCurlyAt(raw, afterTerm);
251
+ const afterBlock = skipWsAndComments(raw, block.end);
252
+ if (afterBlock !== raw.length) return raw;
253
+
254
+ return `${term.text} ${LOG_NAME_OF_IRI} ${block.text}`;
255
+ }
256
+
188
257
  function splitTopLevelStatements(raw, surfaceOffset = null) {
189
258
  const text = String(raw || '');
190
259
  const out = [];
@@ -216,7 +285,7 @@ function splitTopLevelStatements(raw, surfaceOffset = null) {
216
285
  else if (ch === ')' && depthParen > 0) depthParen -= 1;
217
286
  else if (ch === '.' && depthBrace === 0 && depthBracket === 0 && depthParen === 0) {
218
287
  const stmt = text.slice(start, i).trim();
219
- if (stmt) out.push(stmt);
288
+ if (stmt) out.push(normalizeSurfaceStatement(stmt));
220
289
  start = i + 1;
221
290
  }
222
291
  i += 1;
@@ -224,9 +293,13 @@ function splitTopLevelStatements(raw, surfaceOffset = null) {
224
293
 
225
294
  const tail = text.slice(start).trim();
226
295
  if (tail) {
227
- // A raw binder-only segment is OK; any other dangling text is most likely a
296
+ // A raw binder-only segment is OK; RDF 1.2 TriG named graph blocks are
297
+ // also OK without a trailing dot. Any other dangling text is most likely a
228
298
  // missing dot in the surface body.
229
- if (!/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(tail)) {
299
+ const normalizedTail = normalizeSurfaceStatement(tail);
300
+ if (normalizedTail !== tail) {
301
+ out.push(normalizedTail);
302
+ } else if (!/^_:[A-Za-z_][A-Za-z0-9._-]*(?:\s+_:[A-Za-z_][A-Za-z0-9._-]*)*$/.test(tail)) {
230
303
  throw syntaxError('RDF Surface statement is missing a terminating dot', surfaceOffset);
231
304
  }
232
305
  }
@@ -296,6 +369,7 @@ function readSurfaceAt(s, at) {
296
369
  }
297
370
 
298
371
  const LOG_FOR_ALL_IN_IRI = '<http://www.w3.org/2000/10/swap/log#forAllIn>';
372
+ const LOG_NAME_OF_IRI = '<http://www.w3.org/2000/10/swap/log#nameOf>';
299
373
 
300
374
  function rewriteBlankMarksWithMap(statement, labelToVarName) {
301
375
  const map = labelToVarName instanceof Map ? labelToVarName : new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.30.4",
3
+ "version": "1.30.6",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -42,7 +42,7 @@
42
42
  "lint:fix": "eslint . --fix",
43
43
  "build": "node tools/bundle.js",
44
44
  "test:packlist": "node test/packlist.test.js",
45
- "test:api": "node test/api.test.js && node test/stream_messages.test.js && node test/rdf_surfaces.test.js",
45
+ "test:api": "node test/api.test.js",
46
46
  "test:rdf-surfaces": "node test/rdf_surfaces.test.js",
47
47
  "test:builtins": "node test/builtins.test.js",
48
48
  "test:store": "node test/store.test.js",
@@ -57,9 +57,7 @@
57
57
  "test:rdf12:trig": "node test/rdf12.test.js trig",
58
58
  "test:playground": "node test/playground.test.js",
59
59
  "test:package": "node test/package.test.js",
60
- "pretest": "npm run build && npm run test:packlist",
61
- "test": "npm run test:api && npm run test:builtins && npm run test:store && npm run test:examples && npm run test:examples:proof && npm run test:manifest && npm run test:rdf12 && npm run test:playground",
62
- "posttest": "npm run test:package",
60
+ "test": "node test/run.js",
63
61
  "preversion": "npm test",
64
62
  "postversion": "git push origin HEAD --follow-tags"
65
63
  },
@@ -154,7 +154,7 @@ function resolveExampleTrigInput(root, inputFile) {
154
154
 
155
155
  function resolveExampleRdfSurfaceInput(root, inputFile) {
156
156
  const stem = path.basename(inputFile, path.extname(inputFile));
157
- const rel = path.join('input', `${stem}.ttl`);
157
+ const rel = path.join('input', `${stem}.trig`);
158
158
  const abs = path.join(root, 'examples', rel);
159
159
  if (!fs.existsSync(abs)) return null;
160
160
  const text = fs.readFileSync(abs, 'utf8');
@@ -62,7 +62,7 @@ function guessContentType(p) {
62
62
  if (ext === '.js') return 'application/javascript; charset=utf-8';
63
63
  if (ext === '.css') return 'text/css; charset=utf-8';
64
64
  if (ext === '.json') return 'application/json; charset=utf-8';
65
- if (ext === '.ttl' || ext === '.n3') return 'text/plain; charset=utf-8';
65
+ if (ext === '.ttl' || ext === '.trig' || ext === '.n3') return 'text/plain; charset=utf-8';
66
66
  if (ext === '.txt' || ext === '.md') return 'text/plain; charset=utf-8';
67
67
  return 'application/octet-stream';
68
68
  }
@@ -6,7 +6,7 @@ const cp = require('node:child_process');
6
6
  const path = require('node:path');
7
7
 
8
8
  const { reason } = require('../index.js');
9
- const { C, failResult, pass } = require('./report');
9
+ const { C, failResult, formatDuration, info, pass } = require('./report');
10
10
 
11
11
  const ROOT = path.resolve(__dirname, '..');
12
12
  const EYELING = path.join(ROOT, 'eyeling.js');
@@ -22,7 +22,7 @@ function runCli(input, args = ['--rdf-surfaces']) {
22
22
  function runExample(name) {
23
23
  return cp.spawnSync(
24
24
  process.execPath,
25
- [EYELING, '--rdf-surfaces', path.join(ROOT, 'examples', 'input', `${name}.ttl`), path.join(ROOT, 'examples', `${name}.n3`)],
25
+ [EYELING, '--rdf-surfaces', path.join(ROOT, 'examples', 'input', `${name}.trig`), path.join(ROOT, 'examples', `${name}.n3`)],
26
26
  { encoding: 'utf8', maxBuffer: 20 * 1024 * 1024 },
27
27
  );
28
28
  }
@@ -128,6 +128,9 @@ const exampleCases = [
128
128
  ['rdf-surfaces-all-values-from-reverse', '@prefix ex: <http://example.org/> .\n\nex:box a ex:AllowedContainer .'],
129
129
  ['rdf-surfaces-rdfs-range-codex', '@prefix ex: <http://example.org/> .\n\nex:bob a ex:Person .'],
130
130
  ['rdf-surfaces-rdfs-subclass-codex', '@prefix ex: <http://example.org/> .\n\nex:Brussels a ex:HumanCommunity .'],
131
+ ['rdf-surfaces-rdf12-named-graph', '@prefix ex: <http://example.org/> .\n\nex:Brussels a ex:ReportedCity .'],
132
+ ['rdf-surfaces-rdf12-triple-term', '@prefix ex: <http://example.org/> .\n\nex:claim1 a ex:CityClaim .'],
133
+ ['rdf-surfaces-rdf12-graph-triple-term', '@prefix ex: <http://example.org/> .\n\nex:obs1 a ex:AuditedObservation .'],
131
134
  [
132
135
  'rdf-surfaces-owl-all-values-from-codex',
133
136
  '@prefix ex: <http://example.org/> .\n\nex:item43 a ex:AllowedItem .\nex:item42 a ex:AllowedItem .\nex:box a ex:AllowedContainer .\nex:crate a ex:AllowedContainer .',
@@ -144,6 +147,10 @@ const exampleCases = [
144
147
  ],
145
148
  ];
146
149
 
150
+ const suiteStart = Date.now();
151
+
152
+ info('RDF Surfaces tests');
153
+
147
154
  let seq = 0;
148
155
  let failed = 0;
149
156
 
@@ -211,4 +218,10 @@ _:x a ex:Impossible .
211
218
  }
212
219
  }
213
220
 
214
- if (failed) process.exit(1);
221
+ const suiteMs = Date.now() - suiteStart;
222
+ info(`Total elapsed: ${formatDuration(suiteMs)}`);
223
+ if (failed) {
224
+ info(`Some RDF Surfaces tests failed (${seq - failed}/${seq})`);
225
+ process.exit(1);
226
+ }
227
+ info(`All RDF Surfaces tests passed (${seq}/${seq})`);
package/test/run.js ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const cp = require('node:child_process');
5
+
6
+ const { C, formatDuration } = require('./report');
7
+
8
+ const sections = [
9
+ ['Build bundle', 'npm', ['run', 'build']],
10
+ ['Packlist checks', 'npm', ['run', 'test:packlist']],
11
+ ['API tests', 'npm', ['run', 'test:api']],
12
+ ['Streaming RDF Messages tests', 'npm', ['run', 'test:stream-messages']],
13
+ ['RDF Surfaces tests', 'npm', ['run', 'test:rdf-surfaces']],
14
+ ['Builtin contract tests', 'npm', ['run', 'test:builtins']],
15
+ ['Store tests', 'npm', ['run', 'test:store']],
16
+ ['Examples tests', 'npm', ['run', 'test:examples']],
17
+ ['Proof examples tests', 'npm', ['run', 'test:examples:proof']],
18
+ ['Manifest tests', 'npm', ['run', 'test:manifest']],
19
+ ['RDF 1.2 syntax tests', 'npm', ['run', 'test:rdf12']],
20
+ ['Playground tests', 'npm', ['run', 'test:playground']],
21
+ ['Package tests', 'npm', ['run', 'test:package']],
22
+ ];
23
+
24
+ function sectionLine(kind, label, ms) {
25
+ const suffix = typeof ms === 'number' ? ` (${formatDuration(ms)})` : '';
26
+ console.log(`${C.y}==${C.n} ${kind} ${label}${suffix}`);
27
+ }
28
+
29
+ function runSection(label, cmd, args) {
30
+ console.log('');
31
+ sectionLine('Start', label);
32
+ const startedAt = Date.now();
33
+ const r = cp.spawnSync(cmd, args, {
34
+ cwd: process.cwd(),
35
+ env: process.env,
36
+ shell: process.platform === 'win32',
37
+ stdio: 'inherit',
38
+ });
39
+ const elapsed = Date.now() - startedAt;
40
+ if (r.error) {
41
+ console.error(`${C.r}FAIL${C.n} ${label}: ${r.error.message || String(r.error)}`);
42
+ sectionLine('End', `${label} failed`, elapsed);
43
+ console.log('');
44
+ return 1;
45
+ }
46
+ const status = typeof r.status === 'number' ? r.status : 1;
47
+ sectionLine('End', status === 0 ? `${label} passed` : `${label} failed`, elapsed);
48
+ console.log('');
49
+ return status;
50
+ }
51
+
52
+ let status = 0;
53
+ for (const [label, cmd, args] of sections) {
54
+ const sectionStatus = runSection(label, cmd, args);
55
+ if (sectionStatus !== 0) {
56
+ status = sectionStatus;
57
+ break;
58
+ }
59
+ }
60
+
61
+ process.exit(status);