eyeling 1.22.6 → 1.22.7
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/HANDBOOK.md +245 -0
- package/dist/browser/eyeling.browser.js +128 -11
- package/examples/act-alarm-bit-interoperability.n3 +180 -0
- package/examples/act-barley-seed-lineage.n3 +565 -0
- package/examples/act-docking-abort.n3 +285 -0
- package/examples/act-gravity-mediator-witness.n3 +235 -0
- package/examples/act-isolation-breach.n3 +354 -0
- package/examples/act-photosynthetic-exciton-transfer.n3 +245 -0
- package/examples/act-sensor-memory-reset.n3 +190 -0
- package/examples/act-tunnel-junction-wake-switch.n3 +225 -0
- package/examples/act-yeast-self-reproduction.n3 +248 -0
- package/examples/complex-matrix-stability.n3 +288 -0
- package/examples/deck/act-barley-seed-lineage.md +593 -0
- package/examples/fundamental-theorem-arithmetic.n3 +244 -0
- package/examples/harborsmr.n3 +233 -0
- package/examples/meta-rule-audit.n3 +135 -0
- package/examples/output/act-alarm-bit-interoperability.txt +20 -0
- package/examples/output/act-barley-seed-lineage.txt +25 -0
- package/examples/output/act-docking-abort.txt +22 -0
- package/examples/output/act-gravity-mediator-witness.txt +24 -0
- package/examples/output/act-isolation-breach.txt +27 -0
- package/examples/output/act-photosynthetic-exciton-transfer.txt +20 -0
- package/examples/output/act-sensor-memory-reset.txt +20 -0
- package/examples/output/act-tunnel-junction-wake-switch.txt +21 -0
- package/examples/output/act-yeast-self-reproduction.txt +23 -0
- package/examples/output/complex-matrix-stability.txt +14 -0
- package/examples/output/fundamental-theorem-arithmetic.txt +15 -0
- package/examples/output/get-uuid.n3 +2 -2
- package/examples/output/harborsmr.txt +20 -0
- package/examples/output/meta-rule-audit.n3 +44 -0
- package/examples/output/theory-diff.n3 +22 -0
- package/examples/theory-diff.n3 +125 -0
- package/eyeling.js +128 -11
- package/lib/builtins.js +18 -1
- package/lib/cli.js +31 -5
- package/lib/engine.js +79 -5
- package/package.json +1 -1
- package/test/api.test.js +69 -0
package/lib/cli.js
CHANGED
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const { pathToFileURL } = require('node:url');
|
|
12
|
+
|
|
10
13
|
const engine = require('./engine');
|
|
14
|
+
const deref = require('./deref');
|
|
11
15
|
const { PrefixEnv } = require('./prelude');
|
|
12
16
|
|
|
13
17
|
function offsetToLineCol(text, offset) {
|
|
@@ -50,6 +54,29 @@ function readTextFromStdinSync() {
|
|
|
50
54
|
return fs.readFileSync(0, { encoding: 'utf8' });
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
function __isNetworkOrFileIri(s) {
|
|
58
|
+
return typeof s === 'string' && /^(https?:|file:\/\/)/i.test(s);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function __sourceLabelToBaseIri(sourceLabel) {
|
|
62
|
+
if (!sourceLabel || sourceLabel === '<stdin>') return '';
|
|
63
|
+
if (__isNetworkOrFileIri(sourceLabel)) return deref.stripFragment(sourceLabel);
|
|
64
|
+
return pathToFileURL(path.resolve(sourceLabel)).toString();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function __readInputSourceSync(sourceLabel) {
|
|
68
|
+
if (sourceLabel === '<stdin>') return readTextFromStdinSync();
|
|
69
|
+
|
|
70
|
+
if (__isNetworkOrFileIri(sourceLabel)) {
|
|
71
|
+
const txt = deref.derefTextSync(sourceLabel);
|
|
72
|
+
if (typeof txt !== 'string') throw new Error(`Failed to dereference ${sourceLabel}`);
|
|
73
|
+
return txt;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const fs = require('node:fs');
|
|
77
|
+
return fs.readFileSync(sourceLabel, { encoding: 'utf8' });
|
|
78
|
+
}
|
|
79
|
+
|
|
53
80
|
function main() {
|
|
54
81
|
// Drop "node" and script name; keep only user-provided args
|
|
55
82
|
// Expand combined short options: -pt == -p -t
|
|
@@ -163,13 +190,11 @@ function main() {
|
|
|
163
190
|
}
|
|
164
191
|
|
|
165
192
|
const sourceLabel = useImplicitStdin || positional[0] === '-' ? '<stdin>' : positional[0];
|
|
193
|
+
const baseIri = __sourceLabelToBaseIri(sourceLabel);
|
|
194
|
+
|
|
166
195
|
let text;
|
|
167
196
|
try {
|
|
168
|
-
|
|
169
|
-
else {
|
|
170
|
-
const fs = require('node:fs');
|
|
171
|
-
text = fs.readFileSync(sourceLabel, { encoding: 'utf8' });
|
|
172
|
-
}
|
|
197
|
+
text = __readInputSourceSync(sourceLabel);
|
|
173
198
|
} catch (e) {
|
|
174
199
|
if (sourceLabel === '<stdin>') console.error(`Error reading stdin: ${e.message}`);
|
|
175
200
|
else console.error(`Error reading file ${JSON.stringify(sourceLabel)}: ${e.message}`);
|
|
@@ -181,6 +206,7 @@ function main() {
|
|
|
181
206
|
try {
|
|
182
207
|
toks = engine.lex(text);
|
|
183
208
|
const parser = new engine.Parser(toks);
|
|
209
|
+
if (baseIri) parser.prefixes.setBase(baseIri);
|
|
184
210
|
[prefixes, triples, frules, brules, qrules] = parser.parseDocument();
|
|
185
211
|
// Make the parsed prefixes available to log:trace output (CLI path)
|
|
186
212
|
engine.setTracePrefixes(prefixes);
|
package/lib/engine.js
CHANGED
|
@@ -199,21 +199,80 @@ function __isStrictGroundTriple(tr) {
|
|
|
199
199
|
// -----------------------------------------------------------------------------
|
|
200
200
|
// Used to maintain O(1) membership sets for dynamically promoted rules, and to
|
|
201
201
|
// memoize per-firing head-blank skolemization.
|
|
202
|
+
//
|
|
203
|
+
// Important: variables and blank nodes *inside quoted formulas* are local to the
|
|
204
|
+
// formula. Canonicalize those labels by first occurrence so alpha-equivalent
|
|
205
|
+
// formulas (for example the repeated results of log:semantics after
|
|
206
|
+
// standardize-apart) get the same identity key. Keep top-level blank labels
|
|
207
|
+
// untouched so distinct existential witnesses in the global fact set do not
|
|
208
|
+
// collapse together.
|
|
209
|
+
function __keyFromTermForRuleIdentity(term) {
|
|
210
|
+
const ctx = { quotedVar: new Map(), quotedBlank: new Map() };
|
|
211
|
+
|
|
212
|
+
function canonQuotedVar(name) {
|
|
213
|
+
let out = ctx.quotedVar.get(name);
|
|
214
|
+
if (!out) {
|
|
215
|
+
out = `v${ctx.quotedVar.size}`;
|
|
216
|
+
ctx.quotedVar.set(name, out);
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function canonQuotedBlank(label) {
|
|
222
|
+
let out = ctx.quotedBlank.get(label);
|
|
223
|
+
if (!out) {
|
|
224
|
+
out = `b${ctx.quotedBlank.size}`;
|
|
225
|
+
ctx.quotedBlank.set(label, out);
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function enc(u, inQuotedFormula) {
|
|
231
|
+
if (u instanceof Iri) return ['I', u.value];
|
|
232
|
+
if (u instanceof Literal) return ['L', u.value];
|
|
233
|
+
if (u instanceof Blank) return inQuotedFormula ? ['BQ', canonQuotedBlank(u.label)] : ['B', u.label];
|
|
234
|
+
if (u instanceof Var) return inQuotedFormula ? ['VQ', canonQuotedVar(u.name)] : ['V', u.name];
|
|
235
|
+
if (u instanceof ListTerm) return ['List', u.elems.map((e) => enc(e, inQuotedFormula))];
|
|
236
|
+
if (u instanceof OpenListTerm) {
|
|
237
|
+
return [
|
|
238
|
+
'OpenList',
|
|
239
|
+
u.prefix.map((e) => enc(e, inQuotedFormula)),
|
|
240
|
+
inQuotedFormula ? ['TailVQ', canonQuotedVar(u.tailVar)] : ['TailV', u.tailVar],
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
if (u instanceof GraphTerm)
|
|
244
|
+
return ['Graph', u.triples.map((tr) => [enc(tr.s, true), enc(tr.p, true), enc(tr.o, true)])];
|
|
245
|
+
return ['Other', String(u)];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return JSON.stringify(enc(term, false));
|
|
249
|
+
}
|
|
250
|
+
|
|
202
251
|
function __ruleKey(isForward, isFuse, premise, conclusion, dynamicConclusionTerm /* optional */) {
|
|
203
252
|
let out = (isForward ? 'F' : 'B') + (isFuse ? '!' : '') + '|P|';
|
|
204
253
|
for (let i = 0; i < premise.length; i++) {
|
|
205
254
|
const tr = premise[i];
|
|
206
255
|
if (i) out += '\n';
|
|
207
|
-
out +=
|
|
256
|
+
out +=
|
|
257
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
258
|
+
'\t' +
|
|
259
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
260
|
+
'\t' +
|
|
261
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
208
262
|
}
|
|
209
263
|
out += '|C|';
|
|
210
264
|
for (let i = 0; i < conclusion.length; i++) {
|
|
211
265
|
const tr = conclusion[i];
|
|
212
266
|
if (i) out += '\n';
|
|
213
|
-
out +=
|
|
267
|
+
out +=
|
|
268
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
269
|
+
'\t' +
|
|
270
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
271
|
+
'\t' +
|
|
272
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
214
273
|
}
|
|
215
274
|
if (dynamicConclusionTerm) {
|
|
216
|
-
out += '|T|' +
|
|
275
|
+
out += '|T|' + __keyFromTermForRuleIdentity(dynamicConclusionTerm);
|
|
217
276
|
}
|
|
218
277
|
return out;
|
|
219
278
|
}
|
|
@@ -224,7 +283,12 @@ function __firingKey(ruleIndex, instantiatedPremises) {
|
|
|
224
283
|
for (let i = 0; i < instantiatedPremises.length; i++) {
|
|
225
284
|
const tr = instantiatedPremises[i];
|
|
226
285
|
if (i) out += '\n';
|
|
227
|
-
out +=
|
|
286
|
+
out +=
|
|
287
|
+
__keyFromTermForRuleIdentity(tr.s) +
|
|
288
|
+
'\t' +
|
|
289
|
+
__keyFromTermForRuleIdentity(tr.p) +
|
|
290
|
+
'\t' +
|
|
291
|
+
__keyFromTermForRuleIdentity(tr.o);
|
|
228
292
|
}
|
|
229
293
|
return out;
|
|
230
294
|
}
|
|
@@ -2464,7 +2528,17 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxR
|
|
|
2464
2528
|
if (remaining <= 0) continue;
|
|
2465
2529
|
const builtinMax = Number.isFinite(remaining) && !restGoals.length ? remaining : undefined;
|
|
2466
2530
|
|
|
2467
|
-
|
|
2531
|
+
const builtinGoalForEval = goalPredicateIri === LOG_NS + 'rawType' ? rawGoal : goal0;
|
|
2532
|
+
const builtinSubstForEval = goalPredicateIri === LOG_NS + 'rawType' ? substMut : {};
|
|
2533
|
+
let deltas = evalBuiltin(
|
|
2534
|
+
builtinGoalForEval,
|
|
2535
|
+
builtinSubstForEval,
|
|
2536
|
+
facts,
|
|
2537
|
+
backRules,
|
|
2538
|
+
frame.curDepth,
|
|
2539
|
+
varGen,
|
|
2540
|
+
builtinMax,
|
|
2541
|
+
);
|
|
2468
2542
|
|
|
2469
2543
|
const dc = typeof frame.deferCount === 'number' ? frame.deferCount : 0;
|
|
2470
2544
|
const builtinDeltasAreVacuous = deltas.length > 0 && deltas.every((d) => Object.keys(d).length === 0);
|
package/package.json
CHANGED
package/test/api.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const assert = require('node:assert/strict');
|
|
4
|
+
const fs = require('node:fs');
|
|
4
5
|
const path = require('node:path');
|
|
5
6
|
const { spawnSync } = require('node:child_process');
|
|
6
7
|
const ROOT = path.resolve(__dirname, '..');
|
|
@@ -2236,6 +2237,74 @@ _:x :hates { _:foo :making :mess }.
|
|
|
2236
2237
|
`,
|
|
2237
2238
|
expect: [/^:result\s+:is\s+\{[\s\S]*\?A\s+a\s+\?B\s*\.[\s\S]*\?B\s+rdfs:subClassOf\s+\?C\s*\.[\s\S]*\}\s*\./m],
|
|
2238
2239
|
},
|
|
2240
|
+
{
|
|
2241
|
+
name: 'regression: log:rawType accepts quoted variables found via log:includes',
|
|
2242
|
+
opt: { proofComments: false },
|
|
2243
|
+
input: `@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
2244
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
2245
|
+
@prefix : <http://example.org/ns#> .
|
|
2246
|
+
|
|
2247
|
+
{ ?X :likes ?Y } <= { ?X :loves ?Y }.
|
|
2248
|
+
|
|
2249
|
+
{
|
|
2250
|
+
?X log:impliedBy ?Y .
|
|
2251
|
+
?X log:includes { ?Z1 :likes ?Z2 }.
|
|
2252
|
+
?Z1 log:rawType ?T.
|
|
2253
|
+
}
|
|
2254
|
+
=>
|
|
2255
|
+
{
|
|
2256
|
+
:test :is true .
|
|
2257
|
+
}.
|
|
2258
|
+
`,
|
|
2259
|
+
expect: [/^:test\s+:is\s+true\s*\./m],
|
|
2260
|
+
},
|
|
2261
|
+
{
|
|
2262
|
+
name: 'regression: log:semantics body alpha-renaming does not refire blank-head rule forever',
|
|
2263
|
+
async run() {
|
|
2264
|
+
const os = require('node:os');
|
|
2265
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-alpha-fire-'));
|
|
2266
|
+
const mainPath = path.join(tmp, 'main.n3');
|
|
2267
|
+
const examplePath = path.join(tmp, 'example.n3');
|
|
2268
|
+
|
|
2269
|
+
fs.writeFileSync(
|
|
2270
|
+
mainPath,
|
|
2271
|
+
`@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
2272
|
+
@prefix : <urn:test#>.
|
|
2273
|
+
|
|
2274
|
+
<> :facts <./example.n3> .
|
|
2275
|
+
|
|
2276
|
+
{ ?X :load ?S } <= { <> :facts ?F . ?F log:semantics ?S . }.
|
|
2277
|
+
|
|
2278
|
+
{ ?This :load ?S . } => { [] a :Test . }.
|
|
2279
|
+
`,
|
|
2280
|
+
'utf8',
|
|
2281
|
+
);
|
|
2282
|
+
|
|
2283
|
+
fs.writeFileSync(
|
|
2284
|
+
examplePath,
|
|
2285
|
+
`@prefix : <urn:test#>.
|
|
2286
|
+
{ ?A :p ?B } <= { ?A :q ?B }.
|
|
2287
|
+
`,
|
|
2288
|
+
'utf8',
|
|
2289
|
+
);
|
|
2290
|
+
|
|
2291
|
+
try {
|
|
2292
|
+
const r = spawnSync(process.execPath, [path.join(ROOT, 'bin', 'eyeling.cjs'), mainPath], {
|
|
2293
|
+
cwd: ROOT,
|
|
2294
|
+
encoding: 'utf8',
|
|
2295
|
+
maxBuffer: DEFAULT_MAX_BUFFER,
|
|
2296
|
+
timeout: 5000,
|
|
2297
|
+
});
|
|
2298
|
+
|
|
2299
|
+
if (r.error) throw r.error;
|
|
2300
|
+
assert.equal(r.status, 0, r.stderr || `unexpected exit ${r.status}`);
|
|
2301
|
+
mustOccurExactly(r.stdout, /^_:sk_\d+\s+a\s+:Test\s*\.$/gm, 1, 'expected exactly one derived :Test witness');
|
|
2302
|
+
return r.stdout;
|
|
2303
|
+
} finally {
|
|
2304
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
2305
|
+
}
|
|
2306
|
+
},
|
|
2307
|
+
},
|
|
2239
2308
|
];
|
|
2240
2309
|
|
|
2241
2310
|
let passed = 0;
|