eyeling 1.29.0 → 1.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/browser/eyeling.browser.js +375 -39
- package/eyeling.js +375 -39
- package/lib/cli.js +211 -37
- package/lib/engine.js +160 -1
- package/lib/store.js +4 -1
- package/package.json +1 -1
- package/test/store.test.js +29 -0
- package/test/stream_messages.test.js +13 -0
package/README.md
CHANGED
|
@@ -454,7 +454,7 @@ await runAsync(input, {
|
|
|
454
454
|
});
|
|
455
455
|
```
|
|
456
456
|
|
|
457
|
-
Persistent stores use a term dictionary plus `spo`, `pos`, and `osp` triple indexes. Exact lookup and all subject/predicate/object bound-pattern scans are available through the `FactStore` API:
|
|
457
|
+
Named persistent stores are created automatically when first opened. Persistent stores use a term dictionary plus `spo`, `pos`, and `osp` triple indexes. Exact lookup and all subject/predicate/object bound-pattern scans are available through the `FactStore` API:
|
|
458
458
|
|
|
459
459
|
```js
|
|
460
460
|
const { createFactStore, rdfjs } = require('eyeling');
|
|
@@ -482,6 +482,12 @@ eyeling input.n3 --store my-dataset --store-clear
|
|
|
482
482
|
|
|
483
483
|
eyeling input.n3 --store my-dataset --store-path ./.eyeling-store
|
|
484
484
|
# Node.js path override
|
|
485
|
+
|
|
486
|
+
# Stream line-oriented RDF input into a store without reading one giant string.
|
|
487
|
+
eyeling --rdf big.nt --store my-dataset --store-path ./.eyeling-store
|
|
488
|
+
|
|
489
|
+
# Stream RDF Message Logs one message at a time and persist facts/inferences.
|
|
490
|
+
eyeling --rdf --stream-messages rules.n3 messages.trig --store my-dataset
|
|
485
491
|
```
|
|
486
492
|
|
|
487
493
|
### `reasonRdfJs(input, options)`
|
|
@@ -617,6 +623,12 @@ For one-message-at-a-time processing:
|
|
|
617
623
|
eyeling --rdf --stream-messages rules.n3 messages.trig
|
|
618
624
|
```
|
|
619
625
|
|
|
626
|
+
`--stream-messages` can also be combined with `--store` to create/reuse a named store and persist each message's explicit facts and inferred facts while keeping only one replay message in memory at a time:
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
eyeling --rdf --stream-messages rules.n3 messages.trig --store my-dataset --store-path ./.eyeling-store
|
|
630
|
+
```
|
|
631
|
+
|
|
620
632
|
Eyeling materializes a replay view under the `eymsg:` vocabulary:
|
|
621
633
|
|
|
622
634
|
```n3
|
|
@@ -1253,7 +1265,7 @@ reasonStream(input, { rdf: true });
|
|
|
1253
1265
|
|
|
1254
1266
|
### `--stream-messages` fails immediately
|
|
1255
1267
|
|
|
1256
|
-
`--stream-messages` requires RDF mode and cannot be combined with `--ast`, `--stream`, or proof output.
|
|
1268
|
+
`--stream-messages` requires RDF mode and cannot be combined with `--ast`, `--stream`, or proof output. It can be combined with `--store`.
|
|
1257
1269
|
|
|
1258
1270
|
Use:
|
|
1259
1271
|
|
|
@@ -5842,10 +5842,12 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
|
|
|
5842
5842
|
|
|
5843
5843
|
let derived = [];
|
|
5844
5844
|
let outTriples = [];
|
|
5845
|
+
let queryDerived = [];
|
|
5845
5846
|
if (hasQueries) {
|
|
5846
5847
|
const res = engine.forwardChainAndCollectLogQueryConclusions(facts, frules, brules, qrules, { prefixes });
|
|
5847
5848
|
derived = res.derived;
|
|
5848
5849
|
outTriples = res.queryTriples;
|
|
5850
|
+
queryDerived = res.queryDerived || [];
|
|
5849
5851
|
} else {
|
|
5850
5852
|
const skipDerivedCollection = mayAutoRenderOutputStrings;
|
|
5851
5853
|
derived = engine.forwardChain(facts, frules, brules, null, {
|
|
@@ -5859,7 +5861,7 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
|
|
|
5859
5861
|
const renderedOutputTriples = hasQueries ? outTriples : facts;
|
|
5860
5862
|
if (factsContainOutputStrings(renderedOutputTriples)) {
|
|
5861
5863
|
process.stdout.write(engine.collectOutputStringsFromFacts(renderedOutputTriples, prefixes));
|
|
5862
|
-
return;
|
|
5864
|
+
return { facts, derived, outTriples, queryDerived, queryMode: !!hasQueries };
|
|
5863
5865
|
}
|
|
5864
5866
|
|
|
5865
5867
|
const outPrefixEnv = outputPrefixes || prefixes;
|
|
@@ -5871,9 +5873,99 @@ function runParsedDocumentOnce(mergedDocument, { rdfMode = false, outputPrefixes
|
|
|
5871
5873
|
} else {
|
|
5872
5874
|
for (const df of derived) console.log(engine.tripleToN3(df.fact, outPrefixEnv));
|
|
5873
5875
|
}
|
|
5876
|
+
return { facts, derived, outTriples, queryDerived, queryMode: !!hasQueries };
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
async function createCliStore({ storeName, storePath = null, storeClear = false } = {}) {
|
|
5880
|
+
if (!storeName) return null;
|
|
5881
|
+
return engine.createFactStore({ name: storeName, path: storePath || undefined, clear: !!storeClear });
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
async function persistRunResultToStore(store, runResult) {
|
|
5885
|
+
if (!store || !runResult) return;
|
|
5886
|
+
await store.batchAdd(runResult.facts || [], 'explicit');
|
|
5887
|
+
await store.batchAdd((runResult.derived || []).map((df) => df.fact), 'inferred');
|
|
5888
|
+
await store.batchAdd((runResult.queryDerived || []).map((df) => df.fact), 'inferred');
|
|
5889
|
+
await store.batchAdd(runResult.outTriples || [], 'inferred');
|
|
5890
|
+
}
|
|
5891
|
+
|
|
5892
|
+
function sourceLooksLikeLineBasedRdf(sourceLabel) {
|
|
5893
|
+
if (typeof sourceLabel !== 'string') return false;
|
|
5894
|
+
const clean = sourceLabel.split(/[?#]/, 1)[0].toLowerCase();
|
|
5895
|
+
return /\.(?:nt|nq)(?:\.gz|\.br|\.deflate)?$/.test(clean);
|
|
5896
|
+
}
|
|
5897
|
+
|
|
5898
|
+
function parseLineBasedRdfLine(line, { sourceLabel, lineNumber, rdfMode }) {
|
|
5899
|
+
const text = String(line || '');
|
|
5900
|
+
if (!text.trim() || /^\s*#/.test(text)) return [];
|
|
5901
|
+
const doc = parseN3Text(text, {
|
|
5902
|
+
baseIri: __sourceLabelToBaseIri(sourceLabel),
|
|
5903
|
+
label: `${sourceLabel}:${lineNumber}`,
|
|
5904
|
+
keepSourceArtifacts: false,
|
|
5905
|
+
sourceLocations: false,
|
|
5906
|
+
rdf: rdfMode,
|
|
5907
|
+
});
|
|
5908
|
+
return doc.triples || [];
|
|
5909
|
+
}
|
|
5910
|
+
|
|
5911
|
+
async function ingestLineBasedRdfSourceToStore(sourceLabel, store, { rdfMode = true } = {}) {
|
|
5912
|
+
let lineNumber = 0;
|
|
5913
|
+
let count = 0;
|
|
5914
|
+
const onLine = async (line) => {
|
|
5915
|
+
lineNumber += 1;
|
|
5916
|
+
let triples;
|
|
5917
|
+
try {
|
|
5918
|
+
triples = parseLineBasedRdfLine(line, { sourceLabel, lineNumber, rdfMode });
|
|
5919
|
+
} catch (e) {
|
|
5920
|
+
if (e && e.name === 'N3SyntaxError') {
|
|
5921
|
+
throw new Error(formatN3SyntaxError(e, line, `${sourceLabel}:${lineNumber}`));
|
|
5922
|
+
}
|
|
5923
|
+
throw e;
|
|
5924
|
+
}
|
|
5925
|
+
count += await store.batchAdd(triples, 'explicit');
|
|
5926
|
+
};
|
|
5927
|
+
|
|
5928
|
+
const filePath = __localPathForSource(sourceLabel);
|
|
5929
|
+
if (filePath) {
|
|
5930
|
+
const fd = fs.openSync(filePath, 'r');
|
|
5931
|
+
const decoder = new TextDecoder('utf8');
|
|
5932
|
+
const buf = Buffer.allocUnsafe(64 * 1024);
|
|
5933
|
+
let carry = '';
|
|
5934
|
+
try {
|
|
5935
|
+
for (;;) {
|
|
5936
|
+
const n = fs.readSync(fd, buf, 0, buf.length, null);
|
|
5937
|
+
if (n === 0) break;
|
|
5938
|
+
carry += decoder.decode(buf.subarray(0, n), { stream: true });
|
|
5939
|
+
for (;;) {
|
|
5940
|
+
const m = /\r\n|\n|\r/.exec(carry);
|
|
5941
|
+
if (!m) break;
|
|
5942
|
+
const end = m.index + m[0].length;
|
|
5943
|
+
await onLine(carry.slice(0, end));
|
|
5944
|
+
carry = carry.slice(end);
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5947
|
+
carry += decoder.decode();
|
|
5948
|
+
if (carry) await onLine(carry);
|
|
5949
|
+
} finally {
|
|
5950
|
+
fs.closeSync(fd);
|
|
5951
|
+
}
|
|
5952
|
+
return count;
|
|
5953
|
+
}
|
|
5954
|
+
|
|
5955
|
+
if (__isHttpSource(sourceLabel)) {
|
|
5956
|
+
const body = await __openHttpTextStream(sourceLabel);
|
|
5957
|
+
const rl = readline.createInterface({ input: body, crlfDelay: Infinity });
|
|
5958
|
+
for await (const line of rl) await onLine(line + '\n');
|
|
5959
|
+
return count;
|
|
5960
|
+
}
|
|
5961
|
+
|
|
5962
|
+
const text = __readInputSourceSync(sourceLabel);
|
|
5963
|
+
for (const line of text.match(/.*(?:\r\n|\n|\r)|.+$/g) || []) await onLine(line);
|
|
5964
|
+
return count;
|
|
5874
5965
|
}
|
|
5875
5966
|
|
|
5876
|
-
|
|
5967
|
+
|
|
5968
|
+
async function runStreamMessagesMode(sourceLabels, { rdfMode, storeName = null, storePath = null, storeClear = false } = {}) {
|
|
5877
5969
|
const ordinarySourceLabels = [];
|
|
5878
5970
|
const messageSourceLabels = [];
|
|
5879
5971
|
|
|
@@ -5922,40 +6014,49 @@ async function runStreamMessagesMode(sourceLabels, { rdfMode }) {
|
|
|
5922
6014
|
}
|
|
5923
6015
|
}
|
|
5924
6016
|
|
|
6017
|
+
const store = await createCliStore({ storeName, storePath, storeClear });
|
|
6018
|
+
let pendingStoreWrites = Promise.resolve();
|
|
6019
|
+
|
|
5925
6020
|
const fullIriPrefixes = new PrefixEnv({});
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
let messageDoc;
|
|
5936
|
-
try {
|
|
5937
|
-
messageDoc = parseN3Text(messageText, {
|
|
5938
|
-
baseIri: `${__sourceLabelToBaseIri(messageSourceLabel)}#message-${messageIndex}`,
|
|
5939
|
-
label: `${messageSourceLabel}#message-${messageIndex}`,
|
|
5940
|
-
keepSourceArtifacts: false,
|
|
5941
|
-
sourceLocations: false,
|
|
5942
|
-
rdf: false,
|
|
6021
|
+
try {
|
|
6022
|
+
for (const messageSourceLabel of messageSourceLabels) {
|
|
6023
|
+
try {
|
|
6024
|
+
await __forEachRdfMessageChunk(messageSourceLabel, ({ messageIndex, chunk, directives }) => {
|
|
6025
|
+
const messageText = buildSingleMessageReplayDocument({
|
|
6026
|
+
sourceLabel: messageSourceLabel,
|
|
6027
|
+
messageIndex,
|
|
6028
|
+
chunk,
|
|
6029
|
+
directives,
|
|
5943
6030
|
});
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
6031
|
+
let messageDoc;
|
|
6032
|
+
try {
|
|
6033
|
+
messageDoc = parseN3Text(messageText, {
|
|
6034
|
+
baseIri: `${__sourceLabelToBaseIri(messageSourceLabel)}#message-${messageIndex}`,
|
|
6035
|
+
label: `${messageSourceLabel}#message-${messageIndex}`,
|
|
6036
|
+
keepSourceArtifacts: false,
|
|
6037
|
+
sourceLocations: false,
|
|
6038
|
+
rdf: false,
|
|
6039
|
+
});
|
|
6040
|
+
} catch (e) {
|
|
6041
|
+
if (e && e.name === 'N3SyntaxError') {
|
|
6042
|
+
console.error(formatN3SyntaxError(e, messageText, `${messageSourceLabel}#message-${messageIndex}`));
|
|
6043
|
+
process.exit(1);
|
|
6044
|
+
}
|
|
6045
|
+
throw e;
|
|
5948
6046
|
}
|
|
5949
|
-
throw e;
|
|
5950
|
-
}
|
|
5951
6047
|
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
6048
|
+
const merged = mergeParsedDocuments(programSources.concat([messageDoc]));
|
|
6049
|
+
const result = runParsedDocumentOnce(merged, { rdfMode, outputPrefixes: fullIriPrefixes });
|
|
6050
|
+
if (store) pendingStoreWrites = pendingStoreWrites.then(() => persistRunResultToStore(store, result));
|
|
6051
|
+
});
|
|
6052
|
+
} catch (e) {
|
|
6053
|
+
console.error(`Error streaming RDF Message Log ${JSON.stringify(messageSourceLabel)}: ${e && e.message ? e.message : String(e)}`);
|
|
6054
|
+
process.exit(1);
|
|
6055
|
+
}
|
|
5958
6056
|
}
|
|
6057
|
+
await pendingStoreWrites;
|
|
6058
|
+
} finally {
|
|
6059
|
+
if (store && typeof store.close === 'function') await store.close();
|
|
5959
6060
|
}
|
|
5960
6061
|
}
|
|
5961
6062
|
|
|
@@ -6091,10 +6192,6 @@ async function main() {
|
|
|
6091
6192
|
|
|
6092
6193
|
|
|
6093
6194
|
if (streamMessagesMode) {
|
|
6094
|
-
if (storeName) {
|
|
6095
|
-
console.error('Error: --store cannot be combined with --stream-messages yet.');
|
|
6096
|
-
process.exit(1);
|
|
6097
|
-
}
|
|
6098
6195
|
if (!rdfMode) {
|
|
6099
6196
|
console.error('Error: --stream-messages requires -r/--rdf.');
|
|
6100
6197
|
process.exit(1);
|
|
@@ -6130,17 +6227,94 @@ async function main() {
|
|
|
6130
6227
|
}
|
|
6131
6228
|
}
|
|
6132
6229
|
|
|
6133
|
-
|
|
6230
|
+
let sourceLabels = useImplicitStdin ? ['<stdin>'] : positional.map((item) => (item === '-' ? '<stdin>' : item));
|
|
6134
6231
|
if (sourceLabels.filter((item) => item === '<stdin>').length > 1) {
|
|
6135
6232
|
console.error('Error: stdin can only be used once.');
|
|
6136
6233
|
process.exit(1);
|
|
6137
6234
|
}
|
|
6138
6235
|
|
|
6139
6236
|
if (streamMessagesMode) {
|
|
6140
|
-
await runStreamMessagesMode(sourceLabels, { rdfMode });
|
|
6237
|
+
await runStreamMessagesMode(sourceLabels, { rdfMode, storeName, storePath, storeClear });
|
|
6141
6238
|
return;
|
|
6142
6239
|
}
|
|
6143
6240
|
|
|
6241
|
+
if (storeName && rdfMode) {
|
|
6242
|
+
const lineBasedSources = sourceLabels.filter(sourceLooksLikeLineBasedRdf);
|
|
6243
|
+
if (lineBasedSources.length) {
|
|
6244
|
+
if (showAst) {
|
|
6245
|
+
console.error('Error: line-based --store ingestion cannot be combined with --ast.');
|
|
6246
|
+
process.exit(1);
|
|
6247
|
+
}
|
|
6248
|
+
if (streamMode) {
|
|
6249
|
+
console.error('Error: line-based --store ingestion cannot be combined with --stream.');
|
|
6250
|
+
process.exit(1);
|
|
6251
|
+
}
|
|
6252
|
+
if (engine.getProofCommentsEnabled()) {
|
|
6253
|
+
console.error('Error: line-based --store ingestion currently does not support proof output.');
|
|
6254
|
+
process.exit(1);
|
|
6255
|
+
}
|
|
6256
|
+
|
|
6257
|
+
const store = await createCliStore({ storeName, storePath, storeClear });
|
|
6258
|
+
try {
|
|
6259
|
+
for (const sourceLabel of lineBasedSources) {
|
|
6260
|
+
try {
|
|
6261
|
+
await ingestLineBasedRdfSourceToStore(sourceLabel, store, { rdfMode });
|
|
6262
|
+
} catch (e) {
|
|
6263
|
+
console.error(`Error streaming RDF source ${JSON.stringify(sourceLabel)}: ${e && e.message ? e.message : String(e)}`);
|
|
6264
|
+
process.exit(1);
|
|
6265
|
+
}
|
|
6266
|
+
}
|
|
6267
|
+
|
|
6268
|
+
sourceLabels = sourceLabels.filter((sourceLabel) => !sourceLooksLikeLineBasedRdf(sourceLabel));
|
|
6269
|
+
if (!sourceLabels.length) return;
|
|
6270
|
+
|
|
6271
|
+
const parsedRuleSources = [];
|
|
6272
|
+
for (const sourceLabel of sourceLabels) {
|
|
6273
|
+
let text;
|
|
6274
|
+
try {
|
|
6275
|
+
text = __readInputSourceSync(sourceLabel);
|
|
6276
|
+
} catch (e) {
|
|
6277
|
+
if (sourceLabel === '<stdin>') console.error(`Error reading stdin: ${e.message}`);
|
|
6278
|
+
else console.error(`Error reading source ${JSON.stringify(sourceLabel)}: ${e.message}`);
|
|
6279
|
+
process.exit(1);
|
|
6280
|
+
}
|
|
6281
|
+
|
|
6282
|
+
try {
|
|
6283
|
+
parsedRuleSources.push(
|
|
6284
|
+
parseN3Text(text, {
|
|
6285
|
+
baseIri: __sourceLabelToBaseIri(sourceLabel),
|
|
6286
|
+
label: sourceLabel,
|
|
6287
|
+
collectUsedPrefixes: false,
|
|
6288
|
+
keepSourceArtifacts: false,
|
|
6289
|
+
sourceLocations: false,
|
|
6290
|
+
rdf: rdfMode,
|
|
6291
|
+
}),
|
|
6292
|
+
);
|
|
6293
|
+
} catch (e) {
|
|
6294
|
+
if (e && e.name === 'N3SyntaxError') {
|
|
6295
|
+
console.error(formatN3SyntaxError(e, text, sourceLabel));
|
|
6296
|
+
process.exit(1);
|
|
6297
|
+
}
|
|
6298
|
+
throw e;
|
|
6299
|
+
}
|
|
6300
|
+
}
|
|
6301
|
+
|
|
6302
|
+
const mergedRuleDocument = mergeParsedDocuments(parsedRuleSources);
|
|
6303
|
+
const result = await engine.runStoreBacked(mergedRuleDocument, store, { rdf: rdfMode });
|
|
6304
|
+
const outTriples = result.queryMode ? result.queryTriples || [] : (result.derived || []).map((df) => df.fact);
|
|
6305
|
+
if (result.queryMode) {
|
|
6306
|
+
const bodyText = engine.prettyPrintQueryTriples(outTriples, result.prefixes);
|
|
6307
|
+
if (bodyText) process.stdout.write(String(bodyText).replace(/\s*$/g, '') + '\n');
|
|
6308
|
+
} else {
|
|
6309
|
+
for (const tr of outTriples) console.log(engine.tripleToRdfCompatible(tr, result.prefixes));
|
|
6310
|
+
}
|
|
6311
|
+
return;
|
|
6312
|
+
} finally {
|
|
6313
|
+
if (store && typeof store.close === 'function') await store.close();
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
6317
|
+
|
|
6144
6318
|
const parsedSources = [];
|
|
6145
6319
|
for (const sourceLabel of sourceLabels) {
|
|
6146
6320
|
let text;
|
|
@@ -6997,7 +7171,7 @@ const trace = require('./trace');
|
|
|
6997
7171
|
const { deterministicSkolemIdFromKey } = require('./skolem');
|
|
6998
7172
|
|
|
6999
7173
|
const deref = require('./deref');
|
|
7000
|
-
const { createFactStore, collectStore, MemoryFactStore, PersistentFactStore } = require('./store');
|
|
7174
|
+
const { createFactStore, collectStore, MemoryFactStore, PersistentFactStore, tripleToStoreKey } = require('./store');
|
|
7001
7175
|
|
|
7002
7176
|
const hasOwn = Object.prototype.hasOwnProperty;
|
|
7003
7177
|
|
|
@@ -10704,6 +10878,164 @@ async function __parseRunAsyncInput(input, opts) {
|
|
|
10704
10878
|
});
|
|
10705
10879
|
}
|
|
10706
10880
|
|
|
10881
|
+
function __storePatternTerm(t, subst) {
|
|
10882
|
+
const applied = applySubstTerm(t, subst || __emptySubst());
|
|
10883
|
+
return containsVarTerm(applied) ? null : applied;
|
|
10884
|
+
}
|
|
10885
|
+
|
|
10886
|
+
function __mergeStoreSubst(base, delta) {
|
|
10887
|
+
let out = base;
|
|
10888
|
+
for (const k of Object.keys(delta || {})) {
|
|
10889
|
+
const v = delta[k];
|
|
10890
|
+
if (Object.prototype.hasOwnProperty.call(out, k)) {
|
|
10891
|
+
if (!termsEqual(out[k], v)) return null;
|
|
10892
|
+
} else {
|
|
10893
|
+
if (out === base) out = __cloneSubst(base);
|
|
10894
|
+
out[k] = v;
|
|
10895
|
+
}
|
|
10896
|
+
}
|
|
10897
|
+
return out;
|
|
10898
|
+
}
|
|
10899
|
+
|
|
10900
|
+
async function __proveGoalsAgainstStore(goals, subst, store, backRules, localFacts, depth, varGen, opts = {}) {
|
|
10901
|
+
if (!Array.isArray(goals) || goals.length === 0) return [subst || __emptySubst()];
|
|
10902
|
+
if (depth > 64) return [];
|
|
10903
|
+
|
|
10904
|
+
const goal0 = applySubstTriple(goals[0], subst || __emptySubst());
|
|
10905
|
+
const rest = goals.slice(1);
|
|
10906
|
+
const out = [];
|
|
10907
|
+
|
|
10908
|
+
if (isBuiltinPred(goal0.p)) {
|
|
10909
|
+
const deltas = evalBuiltin(goal0, __emptySubst(), localFacts || [], backRules || [], depth, varGen, undefined);
|
|
10910
|
+
for (const delta of deltas) {
|
|
10911
|
+
const nextSubst = __mergeStoreSubst(subst || __emptySubst(), delta);
|
|
10912
|
+
if (nextSubst === null) continue;
|
|
10913
|
+
out.push(...await __proveGoalsAgainstStore(rest, nextSubst, store, backRules, localFacts, depth + 1, varGen, opts));
|
|
10914
|
+
if (opts.maxResults && out.length >= opts.maxResults) return out.slice(0, opts.maxResults);
|
|
10915
|
+
}
|
|
10916
|
+
return out;
|
|
10917
|
+
}
|
|
10918
|
+
|
|
10919
|
+
// Backward rules are kept in memory; they may call back into the persistent
|
|
10920
|
+
// fact store for their own premises.
|
|
10921
|
+
if (goal0.p instanceof Iri && Array.isArray(backRules) && backRules.length) {
|
|
10922
|
+
ensureBackRuleIndexes(backRules);
|
|
10923
|
+
const candRules = (backRules.__byHeadPred.get(goal0.p.__tid) || []).concat(backRules.__wildHeadPred || []);
|
|
10924
|
+
for (const r of candRules) {
|
|
10925
|
+
if (!r || !Array.isArray(r.conclusion) || r.conclusion.length !== 1) continue;
|
|
10926
|
+
const rStd = standardizeRule(r, varGen);
|
|
10927
|
+
const head = rStd.conclusion[0];
|
|
10928
|
+
const s2 = unifyTriple(head, goal0, subst || __emptySubst());
|
|
10929
|
+
if (s2 === null) continue;
|
|
10930
|
+
const newGoals = (rStd.premise || []).concat(rest);
|
|
10931
|
+
out.push(...await __proveGoalsAgainstStore(newGoals, s2, store, backRules, localFacts, depth + 1, varGen, opts));
|
|
10932
|
+
if (opts.maxResults && out.length >= opts.maxResults) return out.slice(0, opts.maxResults);
|
|
10933
|
+
}
|
|
10934
|
+
}
|
|
10935
|
+
|
|
10936
|
+
const sPat = __storePatternTerm(goal0.s, subst || __emptySubst());
|
|
10937
|
+
const pPat = __storePatternTerm(goal0.p, subst || __emptySubst());
|
|
10938
|
+
const oPat = __storePatternTerm(goal0.o, subst || __emptySubst());
|
|
10939
|
+
|
|
10940
|
+
for await (const fact of store.match(sPat, pPat, oPat)) {
|
|
10941
|
+
const s2 = unifyTriple(goal0, fact, subst || __emptySubst());
|
|
10942
|
+
if (s2 === null) continue;
|
|
10943
|
+
out.push(...await __proveGoalsAgainstStore(rest, s2, store, backRules, localFacts, depth + 1, varGen, opts));
|
|
10944
|
+
if (opts.maxResults && out.length >= opts.maxResults) return out.slice(0, opts.maxResults);
|
|
10945
|
+
}
|
|
10946
|
+
|
|
10947
|
+
return out;
|
|
10948
|
+
}
|
|
10949
|
+
|
|
10950
|
+
async function runStoreBacked(input, store, opts = {}) {
|
|
10951
|
+
const parsed = parseN3SourceList(input, {
|
|
10952
|
+
baseIri: opts.baseIri || null,
|
|
10953
|
+
rdf: !!opts.rdf,
|
|
10954
|
+
sourceLocations: !!opts.proof,
|
|
10955
|
+
}) || input;
|
|
10956
|
+
|
|
10957
|
+
const prefixes = parsed.prefixes;
|
|
10958
|
+
const triples = parsed.triples || [];
|
|
10959
|
+
const frules = parsed.frules || [];
|
|
10960
|
+
const brules = parsed.brules || [];
|
|
10961
|
+
const qrules = parsed.logQueryRules || [];
|
|
10962
|
+
|
|
10963
|
+
materializeRdfLists(triples, frules.concat(qrules || []), brules);
|
|
10964
|
+
await store.batchAdd(triples, 'explicit');
|
|
10965
|
+
|
|
10966
|
+
const derived = [];
|
|
10967
|
+
const derivedKeys = new Set();
|
|
10968
|
+
const varGen = [0];
|
|
10969
|
+
const maxIterations = Number.isInteger(opts.maxIterations) && opts.maxIterations > 0 ? opts.maxIterations : 1024;
|
|
10970
|
+
|
|
10971
|
+
for (let iteration = 0; iteration < maxIterations; iteration += 1) {
|
|
10972
|
+
let changed = false;
|
|
10973
|
+
for (let ruleIndex = 0; ruleIndex < frules.length; ruleIndex += 1) {
|
|
10974
|
+
const r = frules[ruleIndex];
|
|
10975
|
+
if (!r || r.isFuse || r.__dynamicConclusionTerm) continue;
|
|
10976
|
+
__prepareForwardRule(r);
|
|
10977
|
+
const solutions = await __proveGoalsAgainstStore(r.premise || [], __emptySubst(), store, brules, triples, 0, varGen, {});
|
|
10978
|
+
for (const subst of solutions) {
|
|
10979
|
+
for (const cpat of r.conclusion || []) {
|
|
10980
|
+
const fact = applySubstTriple(cpat, subst);
|
|
10981
|
+
if (!isGroundTriple(fact)) continue;
|
|
10982
|
+
if (await store.add(fact, 'inferred')) {
|
|
10983
|
+
const df = makeDerivedRecord(fact, r, (r.premise || []).map((p) => applySubstTriple(p, subst)), subst, false);
|
|
10984
|
+
const key = tripleToStoreKey(fact);
|
|
10985
|
+
if (!derivedKeys.has(key)) {
|
|
10986
|
+
derivedKeys.add(key);
|
|
10987
|
+
derived.push(df);
|
|
10988
|
+
}
|
|
10989
|
+
changed = true;
|
|
10990
|
+
}
|
|
10991
|
+
}
|
|
10992
|
+
}
|
|
10993
|
+
}
|
|
10994
|
+
if (!changed) break;
|
|
10995
|
+
}
|
|
10996
|
+
|
|
10997
|
+
let queryTriples = [];
|
|
10998
|
+
const queryDerived = [];
|
|
10999
|
+
if (qrules.length) {
|
|
11000
|
+
for (const r of qrules) {
|
|
11001
|
+
const solutions = await __proveGoalsAgainstStore(r.premise || [], __emptySubst(), store, brules, triples, 0, varGen, {});
|
|
11002
|
+
for (const subst of solutions) {
|
|
11003
|
+
for (const cpat of r.conclusion || []) {
|
|
11004
|
+
const fact = applySubstTriple(cpat, subst);
|
|
11005
|
+
if (!isGroundTriple(fact)) continue;
|
|
11006
|
+
queryTriples.push(fact);
|
|
11007
|
+
queryDerived.push(makeDerivedRecord(fact, r, (r.premise || []).map((p) => applySubstTriple(p, subst)), subst, false));
|
|
11008
|
+
}
|
|
11009
|
+
}
|
|
11010
|
+
}
|
|
11011
|
+
}
|
|
11012
|
+
|
|
11013
|
+
if (queryTriples.length) {
|
|
11014
|
+
const seen = new Set();
|
|
11015
|
+
queryTriples = queryTriples.filter((tr) => {
|
|
11016
|
+
const key = tripleToStoreKey(tr);
|
|
11017
|
+
if (seen.has(key)) return false;
|
|
11018
|
+
seen.add(key);
|
|
11019
|
+
return true;
|
|
11020
|
+
});
|
|
11021
|
+
}
|
|
11022
|
+
|
|
11023
|
+
return {
|
|
11024
|
+
prefixes,
|
|
11025
|
+
facts: [],
|
|
11026
|
+
derived,
|
|
11027
|
+
queryMode: !!qrules.length,
|
|
11028
|
+
queryTriples,
|
|
11029
|
+
queryDerived,
|
|
11030
|
+
closureN3: qrules.length
|
|
11031
|
+
? prettyPrintQueryTriples(queryTriples, prefixes)
|
|
11032
|
+
: derived.map((df) => (opts.rdf ? tripleToRdfCompatible(df.fact, prefixes) : tripleToN3(df.fact, prefixes))).join('\n'),
|
|
11033
|
+
store,
|
|
11034
|
+
storeBacked: true,
|
|
11035
|
+
};
|
|
11036
|
+
}
|
|
11037
|
+
|
|
11038
|
+
|
|
10707
11039
|
function __withoutStoreOptions(opts) {
|
|
10708
11040
|
const out = { ...(opts || {}) };
|
|
10709
11041
|
delete out.store;
|
|
@@ -10898,6 +11230,7 @@ function setTracePrefixes(v) {
|
|
|
10898
11230
|
module.exports = {
|
|
10899
11231
|
reasonStream,
|
|
10900
11232
|
runAsync,
|
|
11233
|
+
runStoreBacked,
|
|
10901
11234
|
reasonRdfJs,
|
|
10902
11235
|
collectLogQueryConclusions,
|
|
10903
11236
|
forwardChainAndCollectLogQueryConclusions,
|
|
@@ -17705,7 +18038,10 @@ class ClassicLevelKv {
|
|
|
17705
18038
|
const classic = __dynamicRequire('classic-level');
|
|
17706
18039
|
const ClassicLevel = classic && (classic.ClassicLevel || classic.Level || classic.default);
|
|
17707
18040
|
if (!ClassicLevel) throw new Error('classic-level is not installed');
|
|
17708
|
-
|
|
18041
|
+
const fs = __dynamicRequire('node:fs');
|
|
18042
|
+
const path = __dynamicRequire('node:path');
|
|
18043
|
+
if (fs && path) fs.mkdirSync(path.dirname(location), { recursive: true });
|
|
18044
|
+
this.db = new ClassicLevel(location, { valueEncoding: 'json', createIfMissing: true, errorIfExists: false });
|
|
17709
18045
|
this.opened = false;
|
|
17710
18046
|
}
|
|
17711
18047
|
|