eyeling 1.29.2 → 1.30.0

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 (50) hide show
  1. package/README.md +153 -0
  2. package/dist/browser/eyeling.browser.js +711 -21
  3. package/examples/input/rdf-surfaces-all-values-from-reverse.ttl +17 -0
  4. package/examples/input/rdf-surfaces-all-values-from.ttl +13 -0
  5. package/examples/input/rdf-surfaces-ancestor.ttl +20 -0
  6. package/examples/input/rdf-surfaces-city.ttl +11 -0
  7. package/examples/input/rdf-surfaces-domain.ttl +11 -0
  8. package/examples/input/rdf-surfaces-multi-premise.ttl +13 -0
  9. package/examples/input/rdf-surfaces-owl-all-values-from-codex.ttl +35 -0
  10. package/examples/input/rdf-surfaces-property-chain.ttl +13 -0
  11. package/examples/input/rdf-surfaces-range.ttl +11 -0
  12. package/examples/input/rdf-surfaces-rdfs-range-codex.ttl +18 -0
  13. package/examples/input/rdf-surfaces-rdfs-subclass-codex.ttl +18 -0
  14. package/examples/output/rdf-surfaces-all-values-from-reverse.n3 +3 -0
  15. package/examples/output/rdf-surfaces-all-values-from.n3 +3 -0
  16. package/examples/output/rdf-surfaces-ancestor.n3 +5 -0
  17. package/examples/output/rdf-surfaces-city.n3 +3 -0
  18. package/examples/output/rdf-surfaces-domain.n3 +3 -0
  19. package/examples/output/rdf-surfaces-multi-premise.n3 +3 -0
  20. package/examples/output/rdf-surfaces-owl-all-values-from-codex.n3 +6 -0
  21. package/examples/output/rdf-surfaces-property-chain.n3 +3 -0
  22. package/examples/output/rdf-surfaces-range.n3 +3 -0
  23. package/examples/output/rdf-surfaces-rdfs-range-codex.n3 +3 -0
  24. package/examples/output/rdf-surfaces-rdfs-subclass-codex.n3 +3 -0
  25. package/examples/rdf-surfaces-all-values-from-reverse.n3 +6 -0
  26. package/examples/rdf-surfaces-all-values-from.n3 +6 -0
  27. package/examples/rdf-surfaces-ancestor.n3 +6 -0
  28. package/examples/rdf-surfaces-city.n3 +6 -0
  29. package/examples/rdf-surfaces-domain.n3 +6 -0
  30. package/examples/rdf-surfaces-multi-premise.n3 +6 -0
  31. package/examples/rdf-surfaces-owl-all-values-from-codex.n3 +10 -0
  32. package/examples/rdf-surfaces-property-chain.n3 +6 -0
  33. package/examples/rdf-surfaces-range.n3 +6 -0
  34. package/examples/rdf-surfaces-rdfs-range-codex.n3 +6 -0
  35. package/examples/rdf-surfaces-rdfs-subclass-codex.n3 +6 -0
  36. package/eyeling.js +711 -21
  37. package/index.d.ts +4 -0
  38. package/index.js +2 -1
  39. package/lib/builtins.js +128 -3
  40. package/lib/cli.js +11 -4
  41. package/lib/engine.js +15 -7
  42. package/lib/lexer.js +4 -0
  43. package/lib/multisource.js +5 -3
  44. package/lib/prelude.js +18 -0
  45. package/lib/rdf_surfaces.js +524 -0
  46. package/lib/rules.js +3 -4
  47. package/package.json +8 -5
  48. package/test/builtins.test.js +36 -0
  49. package/test/examples.test.js +37 -5
  50. package/test/rdf_surfaces.test.js +204 -0
package/index.d.ts CHANGED
@@ -169,6 +169,8 @@ declare module 'eyeling' {
169
169
  store?: string | StoreOptions;
170
170
  storePath?: string;
171
171
  storeClear?: boolean;
172
+ rdf?: boolean;
173
+ rdfSurfaces?: boolean;
172
174
  }
173
175
 
174
176
  export interface BuiltinRegistrationContext {
@@ -191,6 +193,8 @@ declare module 'eyeling' {
191
193
  includeInputFactsInClosure?: boolean;
192
194
  enforceHttps?: boolean;
193
195
  rdfjs?: boolean;
196
+ rdf?: boolean;
197
+ rdfSurfaces?: boolean;
194
198
  dataFactory?: RdfJsDataFactory | null;
195
199
  skipUnsupportedRdfJs?: boolean;
196
200
  builtinModules?: string | string[];
package/index.js CHANGED
@@ -40,7 +40,8 @@ function reason(opt = {}, input = '') {
40
40
  else args.push('--no-proof-comments');
41
41
  }
42
42
 
43
- if (opt.rdf) args.push('--rdf');
43
+ if (opt.rdf || opt.rdfSurfaces) args.push('--rdf');
44
+ if (opt.rdfSurfaces) args.push('--rdf-surfaces');
44
45
 
45
46
  if (typeof opt.store === 'string' && opt.store) args.push('--store', opt.store);
46
47
  else if (opt.store && typeof opt.store === 'object') {
package/lib/builtins.js CHANGED
@@ -30,6 +30,8 @@ const {
30
30
  PrefixEnv,
31
31
  literalParts,
32
32
  copyQuotedGraphMetadata,
33
+ isInternalBlankVarName,
34
+ internalBlankVarSuffix,
33
35
  } = require('./prelude');
34
36
 
35
37
  const { decodeN3StringEscapes } = require('./lexer');
@@ -132,6 +134,127 @@ function __assertBuiltinHandlerResult(iri, out) {
132
134
  }
133
135
  }
134
136
 
137
+ function collectVarNamesInTerm(t, acc) {
138
+ if (t instanceof Var) {
139
+ acc.add(t.name);
140
+ return;
141
+ }
142
+ if (t instanceof ListTerm) {
143
+ for (const e of t.elems) collectVarNamesInTerm(e, acc);
144
+ return;
145
+ }
146
+ if (t instanceof OpenListTerm) {
147
+ for (const e of t.prefix) collectVarNamesInTerm(e, acc);
148
+ acc.add(t.tailVar);
149
+ return;
150
+ }
151
+ if (t instanceof GraphTerm) {
152
+ for (const tr of t.triples) collectVarNamesInTriple(tr, acc);
153
+ }
154
+ }
155
+
156
+ function collectVarNamesInTriple(tr, acc) {
157
+ collectVarNamesInTerm(tr.s, acc);
158
+ collectVarNamesInTerm(tr.p, acc);
159
+ collectVarNamesInTerm(tr.o, acc);
160
+ }
161
+
162
+ function cloneSubstWithMappedKeys(subst, keyMap, externalizeTerm) {
163
+ const out = Object.create(null);
164
+ for (const [key, val] of Object.entries(subst || {})) {
165
+ out[keyMap.get(key) || key] = externalizeTerm ? externalizeTerm(val) : val;
166
+ }
167
+ return out;
168
+ }
169
+
170
+ function hasInternalBlankVarInSubst(subst) {
171
+ for (const key of Object.keys(subst || {})) {
172
+ if (isInternalBlankVarName(key)) return true;
173
+ }
174
+ return false;
175
+ }
176
+
177
+ function makeCustomBuiltinPublicBridge(goal, subst) {
178
+ const used = new Set(Object.keys(subst || {}).filter((name) => !isInternalBlankVarName(name)));
179
+ collectVarNamesInTriple(goal, used);
180
+ for (const name of Array.from(used)) {
181
+ if (isInternalBlankVarName(name)) used.delete(name);
182
+ }
183
+
184
+ const internalNames = new Set();
185
+ collectVarNamesInTriple(goal, internalNames);
186
+ for (const key of Object.keys(subst || {})) internalNames.add(key);
187
+ for (const name of Array.from(internalNames)) {
188
+ if (!isInternalBlankVarName(name)) internalNames.delete(name);
189
+ }
190
+
191
+ if (internalNames.size === 0 && !hasInternalBlankVarInSubst(subst)) return null;
192
+
193
+ const internalToPublic = new Map();
194
+ const publicToInternal = new Map();
195
+
196
+ function allocatePublicName(internalName) {
197
+ const suffix = internalBlankVarSuffix(internalName) || String(internalToPublic.size + 1);
198
+ const base = /^[$A-Za-z_][0-9A-Za-z_]*$/u.test(`_b${suffix}`) ? `_b${suffix}` : `_b${internalToPublic.size + 1}`;
199
+ let name = base;
200
+ let n = 1;
201
+ while (used.has(name) || publicToInternal.has(name)) {
202
+ n += 1;
203
+ name = `${base}_${n}`;
204
+ }
205
+ used.add(name);
206
+ internalToPublic.set(internalName, name);
207
+ publicToInternal.set(name, internalName);
208
+ return name;
209
+ }
210
+
211
+ for (const name of internalNames) allocatePublicName(name);
212
+
213
+ function externalName(name) {
214
+ if (!isInternalBlankVarName(name)) return name;
215
+ return internalToPublic.get(name) || allocatePublicName(name);
216
+ }
217
+
218
+ function externalizeTerm(t) {
219
+ if (t instanceof Var) return new Var(externalName(t.name));
220
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map(externalizeTerm));
221
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(externalizeTerm), externalName(t.tailVar));
222
+ if (t instanceof GraphTerm) {
223
+ const triples = t.triples.map((tr) => new Triple(externalizeTerm(tr.s), externalizeTerm(tr.p), externalizeTerm(tr.o)));
224
+ return copyQuotedGraphMetadata(t, new GraphTerm(triples));
225
+ }
226
+ return t;
227
+ }
228
+
229
+ function internalizeTerm(t) {
230
+ if (t instanceof Var) return new Var(publicToInternal.get(t.name) || t.name);
231
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map(internalizeTerm));
232
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(internalizeTerm), publicToInternal.get(t.tailVar) || t.tailVar);
233
+ if (t instanceof GraphTerm) {
234
+ const triples = t.triples.map((tr) => new Triple(internalizeTerm(tr.s), internalizeTerm(tr.p), internalizeTerm(tr.o)));
235
+ return copyQuotedGraphMetadata(t, new GraphTerm(triples));
236
+ }
237
+ return t;
238
+ }
239
+
240
+ function internalizeDelta(delta) {
241
+ const out = Object.create(null);
242
+ for (const [key, val] of Object.entries(delta || {})) {
243
+ out[publicToInternal.get(key) || key] = internalizeTerm(val);
244
+ }
245
+ return out;
246
+ }
247
+
248
+ return {
249
+ goal: new Triple(externalizeTerm(goal.s), externalizeTerm(goal.p), externalizeTerm(goal.o)),
250
+ subst: cloneSubstWithMappedKeys(subst, internalToPublic, externalizeTerm),
251
+ internalizeResults(out) {
252
+ if (out == null) return out;
253
+ return out.map(internalizeDelta);
254
+ },
255
+ };
256
+ }
257
+
135
258
  function apiTermToN3(term, prefixes = PrefixEnv.newDefault()) {
136
259
  return termToN3(term, prefixes || PrefixEnv.newDefault());
137
260
  }
@@ -231,10 +354,11 @@ function __evalRegisteredBuiltin(pv, goal, subst, facts, backRules, depth, varGe
231
354
  const handler = __customBuiltinHandlers.get(pv);
232
355
  if (typeof handler !== 'function') return null;
233
356
 
357
+ const bridge = makeCustomBuiltinPublicBridge(goal, subst);
234
358
  const ctx = {
235
359
  iri: pv,
236
- goal,
237
- subst,
360
+ goal: bridge ? bridge.goal : goal,
361
+ subst: bridge ? bridge.subst : subst,
238
362
  facts,
239
363
  backRules,
240
364
  depth,
@@ -244,7 +368,8 @@ function __evalRegisteredBuiltin(pv, goal, subst, facts, backRules, depth, varGe
244
368
  };
245
369
 
246
370
  try {
247
- const out = handler(ctx);
371
+ const raw = handler(ctx);
372
+ const out = bridge ? bridge.internalizeResults(raw) : raw;
248
373
  if (out == null) return [];
249
374
  __assertBuiltinHandlerResult(pv, out);
250
375
  return out;
package/lib/cli.js CHANGED
@@ -685,7 +685,7 @@ async function ingestLineBasedRdfSourceToStore(sourceLabel, store, { rdfMode = t
685
685
  }
686
686
 
687
687
 
688
- async function runStreamMessagesMode(sourceLabels, { rdfMode, storeName = null, storePath = null, storeClear = false } = {}) {
688
+ async function runStreamMessagesMode(sourceLabels, { rdfMode, rdfSurfacesMode = false, storeName = null, storePath = null, storeClear = false } = {}) {
689
689
  const ordinarySourceLabels = [];
690
690
  const messageSourceLabels = [];
691
691
 
@@ -723,6 +723,7 @@ async function runStreamMessagesMode(sourceLabels, { rdfMode, storeName = null,
723
723
  keepSourceArtifacts: false,
724
724
  sourceLocations: false,
725
725
  rdf: rdfMode,
726
+ rdfSurfaces: rdfSurfacesMode,
726
727
  }),
727
728
  );
728
729
  } catch (e) {
@@ -756,6 +757,7 @@ async function runStreamMessagesMode(sourceLabels, { rdfMode, storeName = null,
756
757
  keepSourceArtifacts: false,
757
758
  sourceLocations: false,
758
759
  rdf: false,
760
+ rdfSurfaces: false,
759
761
  });
760
762
  } catch (e) {
761
763
  if (e && e.name === 'N3SyntaxError') {
@@ -810,6 +812,7 @@ async function main() {
810
812
  ` -h, --help Show this help and exit.\n` +
811
813
  ` -p, --proof Enable proof explanations.\n` +
812
814
  ` -r, --rdf Enable RDF/TriG input/output compatibility.\n` +
815
+ ` --rdf-surfaces Enable RDF Surfaces %not[...%] syntax (implies --rdf).\n` +
813
816
  ` --stream-messages Process RDF Message Logs one message at a time under -r.\n` +
814
817
  ` --store <name> Use an optional persistent fact store.\n` +
815
818
  ` --store-clear Clear the named store before this run.\n` +
@@ -885,7 +888,8 @@ async function main() {
885
888
  const showAst = argv.includes('--ast') || argv.includes('-a');
886
889
  const streamMode = argv.includes('--stream') || argv.includes('-t');
887
890
  const streamMessagesMode = argv.includes('--stream-messages');
888
- const rdfMode = argv.includes('--rdf') || argv.includes('-r');
891
+ const rdfSurfacesMode = argv.includes('--rdf-surfaces');
892
+ const rdfMode = argv.includes('--rdf') || argv.includes('-r') || rdfSurfacesMode;
889
893
  const storeName = argv.__storeName || null;
890
894
  const storePath = argv.__storePath || null;
891
895
  const storeClear = argv.includes('--store-clear');
@@ -954,7 +958,7 @@ async function main() {
954
958
  }
955
959
 
956
960
  if (streamMessagesMode) {
957
- await runStreamMessagesMode(sourceLabels, { rdfMode, storeName, storePath, storeClear });
961
+ await runStreamMessagesMode(sourceLabels, { rdfMode, rdfSurfacesMode, storeName, storePath, storeClear });
958
962
  return;
959
963
  }
960
964
 
@@ -1008,6 +1012,7 @@ async function main() {
1008
1012
  keepSourceArtifacts: false,
1009
1013
  sourceLocations: false,
1010
1014
  rdf: rdfMode,
1015
+ rdfSurfaces: rdfSurfacesMode,
1011
1016
  }),
1012
1017
  );
1013
1018
  } catch (e) {
@@ -1020,7 +1025,7 @@ async function main() {
1020
1025
  }
1021
1026
 
1022
1027
  const mergedRuleDocument = mergeParsedDocuments(parsedRuleSources);
1023
- const result = await engine.runStoreBacked(mergedRuleDocument, store, { rdf: rdfMode });
1028
+ const result = await engine.runStoreBacked(mergedRuleDocument, store, { rdf: rdfMode, rdfSurfaces: rdfSurfacesMode });
1024
1029
  const outTriples = result.queryMode ? result.queryTriples || [] : (result.derived || []).map((df) => df.fact);
1025
1030
  if (result.queryMode) {
1026
1031
  const bodyText = engine.prettyPrintQueryTriples(outTriples, result.prefixes);
@@ -1055,6 +1060,7 @@ async function main() {
1055
1060
  keepSourceArtifacts: false,
1056
1061
  sourceLocations: engine.getProofCommentsEnabled(),
1057
1062
  rdf: rdfMode,
1063
+ rdfSurfaces: rdfSurfacesMode,
1058
1064
  }),
1059
1065
  );
1060
1066
  } catch (e) {
@@ -1155,6 +1161,7 @@ function factsContainOutputStrings(triplesForOutput) {
1155
1161
  {
1156
1162
  proof: engine.getProofCommentsEnabled(),
1157
1163
  rdf: rdfMode,
1164
+ rdfSurfaces: rdfSurfacesMode,
1158
1165
  store: { name: storeName, clear: storeClear, path: storePath || undefined },
1159
1166
  },
1160
1167
  );
package/lib/engine.js CHANGED
@@ -3623,12 +3623,14 @@ function reasonStream(input, opts = {}) {
3623
3623
  skipUnsupportedRdfJs = false,
3624
3624
  builtinModules = null,
3625
3625
  rdf = false,
3626
+ rdfSurfaces = false,
3626
3627
  sourceLabel = '<input>',
3627
3628
  } = opts;
3628
3629
 
3629
- const useRdfCompatibility = !!rdf;
3630
+ const useRdfCompatibility = !!rdf || !!rdfSurfaces;
3631
+ const useRdfSurfaces = !!rdfSurfaces;
3630
3632
 
3631
- const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: useRdfCompatibility, sourceLocations: proof });
3633
+ const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: useRdfCompatibility, rdfSurfaces: useRdfSurfaces, sourceLocations: proof });
3632
3634
  const parsedTextInput = (!parsedSourceList && proof && typeof input === 'string')
3633
3635
  ? parseN3Text(input, {
3634
3636
  baseIri: baseIri || '',
@@ -3636,6 +3638,7 @@ function reasonStream(input, opts = {}) {
3636
3638
  keepSourceArtifacts: false,
3637
3639
  sourceLocations: true,
3638
3640
  rdf: useRdfCompatibility,
3641
+ rdfSurfaces: useRdfSurfaces,
3639
3642
  })
3640
3643
  : null;
3641
3644
  const hasInlineN3 = input && typeof input === 'object' && !Array.isArray(input) && typeof input.n3 === 'string';
@@ -3680,6 +3683,7 @@ function reasonStream(input, opts = {}) {
3680
3683
  keepSourceArtifacts: false,
3681
3684
  sourceLocations: proof,
3682
3685
  rdf: useRdfCompatibility,
3686
+ rdfSurfaces: useRdfSurfaces,
3683
3687
  });
3684
3688
  prefixes = directDoc.prefixes;
3685
3689
  triples = directDoc.triples;
@@ -3791,10 +3795,11 @@ async function __parseRunAsyncInput(input, opts) {
3791
3795
  baseIri = null,
3792
3796
  proof = false,
3793
3797
  rdf = false,
3798
+ rdfSurfaces = false,
3794
3799
  sourceLabel = '<input>',
3795
3800
  } = opts || {};
3796
3801
 
3797
- const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: !!rdf, sourceLocations: !!proof });
3802
+ const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: !!rdf || !!rdfSurfaces, rdfSurfaces: !!rdfSurfaces, sourceLocations: !!proof });
3798
3803
  if (parsedSourceList) return parsedSourceList;
3799
3804
 
3800
3805
  const parsedObject = await normalizeParsedReasonerInputAsync(input);
@@ -3809,7 +3814,8 @@ async function __parseRunAsyncInput(input, opts) {
3809
3814
  label: sourceLabel || '<input>',
3810
3815
  keepSourceArtifacts: false,
3811
3816
  sourceLocations: !!proof,
3812
- rdf: !!rdf,
3817
+ rdf: !!rdf || !!rdfSurfaces,
3818
+ rdfSurfaces: !!rdfSurfaces,
3813
3819
  });
3814
3820
  }
3815
3821
 
@@ -3885,7 +3891,8 @@ async function __proveGoalsAgainstStore(goals, subst, store, backRules, localFac
3885
3891
  async function runStoreBacked(input, store, opts = {}) {
3886
3892
  const parsed = parseN3SourceList(input, {
3887
3893
  baseIri: opts.baseIri || null,
3888
- rdf: !!opts.rdf,
3894
+ rdf: !!opts.rdf || !!opts.rdfSurfaces,
3895
+ rdfSurfaces: !!opts.rdfSurfaces,
3889
3896
  sourceLocations: !!opts.proof,
3890
3897
  }) || input;
3891
3898
 
@@ -3930,7 +3937,7 @@ async function runStoreBacked(input, store, opts = {}) {
3930
3937
  }
3931
3938
 
3932
3939
  let queryTriples = [];
3933
- const queryDerived = [];
3940
+ let queryDerived = [];
3934
3941
  if (qrules.length) {
3935
3942
  for (const r of qrules) {
3936
3943
  const solutions = await __proveGoalsAgainstStore(r.premise || [], __emptySubst(), store, brules, triples, 0, varGen, {});
@@ -4002,7 +4009,8 @@ async function runAsync(input, opts = {}) {
4002
4009
  if (!storeConfig) {
4003
4010
  const normalizedInput = parseN3SourceList(input, {
4004
4011
  baseIri: runOpts.baseIri || null,
4005
- rdf: !!runOpts.rdf,
4012
+ rdf: !!runOpts.rdf || !!runOpts.rdfSurfaces,
4013
+ rdfSurfaces: !!runOpts.rdfSurfaces,
4006
4014
  sourceLocations: !!runOpts.proof,
4007
4015
  }) || (await normalizeReasonerInputAsync(input));
4008
4016
  return reasonStream(normalizedInput, runOpts);
package/lib/lexer.js CHANGED
@@ -7,6 +7,8 @@
7
7
 
8
8
  'use strict';
9
9
 
10
+ const { normalizeRdfSurfaces } = require('./rdf_surfaces');
11
+
10
12
  class Token {
11
13
  constructor(typ, value = null, offset = null) {
12
14
  this.typ = typ;
@@ -1347,7 +1349,9 @@ function isNumericLikeIdentifier(word) {
1347
1349
 
1348
1350
  function lex(inputText, opts = {}) {
1349
1351
  const rdf = !!(opts && opts.rdf);
1352
+ const rdfSurfaces = !!(opts && opts.rdfSurfaces);
1350
1353
  if (rdf) inputText = normalizeRdfCompatibility(inputText);
1354
+ if (rdfSurfaces) inputText = normalizeRdfSurfaces(inputText);
1351
1355
  // Avoid copying large ASCII/BMP inputs into an Array. Array.from() is
1352
1356
  // only needed when the text contains surrogate pairs and we want the old
1353
1357
  // code-point iteration behavior for non-BMP characters.
@@ -172,9 +172,10 @@ function parseN3Text(text, opts = {}) {
172
172
  collectUsedPrefixes = false,
173
173
  sourceLocations = false,
174
174
  rdf = false,
175
+ rdfSurfaces = false,
175
176
  } = opts || {};
176
177
 
177
- if (rdf) {
178
+ if (rdf && !rdfSurfaces) {
178
179
  const fastDoc = tryParseFastRdfText(text, { baseIri, label });
179
180
  if (fastDoc) {
180
181
  if (sourceLocations) annotateParsedSourceLocations(fastDoc, text, label);
@@ -183,7 +184,7 @@ function parseN3Text(text, opts = {}) {
183
184
  }
184
185
  }
185
186
 
186
- const tokens = lex(text, { rdf });
187
+ const tokens = lex(text, { rdf, rdfSurfaces });
187
188
  const parser = new Parser(tokens);
188
189
  if (baseIri) parser.prefixes.setBase(baseIri);
189
190
  const [prefixes, triples, frules, brules, logQueryRules] = parser.parseDocument();
@@ -382,7 +383,8 @@ function parseN3SourceList(input, opts = {}) {
382
383
  collectUsedPrefixes: true,
383
384
  keepSourceArtifacts: !!opts.keepSourceArtifacts,
384
385
  sourceLocations: !!opts.sourceLocations,
385
- rdf: !!opts.rdf,
386
+ rdf: !!opts.rdf || !!opts.rdfSurfaces,
387
+ rdfSurfaces: !!opts.rdfSurfaces,
386
388
  }),
387
389
  );
388
390
  return mergeParsedDocuments(parsed, {
package/lib/prelude.js CHANGED
@@ -25,6 +25,21 @@ const DT_NS = 'https://eyereasoner.github.io/eyeling/datatype#';
25
25
  const SKOLEM_NS = 'https://eyereasoner.github.io/.well-known/genid/';
26
26
  const RDF_JSON_DT = RDF_NS + 'JSON';
27
27
 
28
+ // Private rule-lifting variable prefix used for blank nodes that appear in
29
+ // rule-body patterns. The prefix is intentionally not spellable in N3 input,
30
+ // but public extension APIs should present these variables with stable,
31
+ // ordinary names and translate them back internally.
32
+ const INTERNAL_BLANK_VAR_PREFIX = '\uE000eyeling_b';
33
+
34
+ function isInternalBlankVarName(name) {
35
+ return typeof name === 'string' && name.startsWith(INTERNAL_BLANK_VAR_PREFIX);
36
+ }
37
+
38
+ function internalBlankVarSuffix(name) {
39
+ if (!isInternalBlankVarName(name)) return '';
40
+ return name.slice(INTERNAL_BLANK_VAR_PREFIX.length);
41
+ }
42
+
28
43
  function parseUriReferenceForResolution(uri) {
29
44
  // RFC 3986 Appendix B-style component parser, with the scheme tightened to
30
45
  // the RFC scheme grammar. Capturing delimiter presence matters: `?` with an
@@ -728,6 +743,9 @@ module.exports = {
728
743
  DT_NS,
729
744
  SKOLEM_NS,
730
745
  RDF_JSON_DT,
746
+ INTERNAL_BLANK_VAR_PREFIX,
747
+ isInternalBlankVarName,
748
+ internalBlankVarSuffix,
731
749
  resolveIriRef,
732
750
  literalParts,
733
751
  normalizeLiteralForTid,