eyeling 1.16.1 → 1.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HANDBOOK.md +4 -0
- package/arctifacts/README.md +2 -0
- package/examples/ershov-mixed-computation.n3 +106 -0
- package/examples/output/ershov-mixed-computation.n3 +15 -0
- package/eyeling.js +528 -281
- package/lib/builtins.js +7 -7
- package/lib/cli.js +24 -14
- package/lib/deref.js +1 -1
- package/lib/engine.js +488 -251
- package/lib/explain.js +2 -2
- package/lib/prelude.js +1 -1
- package/package.json +1 -1
- package/test/api.test.js +18 -18
- package/test/playground.test.js +1 -1
- package/tools/bundle.js +7 -5
- package/tools/n3gen.js +22 -22
package/lib/explain.js
CHANGED
|
@@ -118,7 +118,7 @@ function makeExplain(deps) {
|
|
|
118
118
|
// log:outputString support
|
|
119
119
|
// ===========================================================================
|
|
120
120
|
|
|
121
|
-
function
|
|
121
|
+
function compareOutputStringKeys(a, b) {
|
|
122
122
|
// Deterministic ordering of keys. The spec only requires "order of the subject keys"
|
|
123
123
|
// and leaves concrete term ordering reasoner-dependent. We implement:
|
|
124
124
|
// 1) numeric literals (numeric value)
|
|
@@ -196,7 +196,7 @@ function makeExplain(deps) {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
pairs.sort((a, b) => {
|
|
199
|
-
const c =
|
|
199
|
+
const c = compareOutputStringKeys(a.key, b.key, prefixes);
|
|
200
200
|
if (c !== 0) return c;
|
|
201
201
|
return a.idx - b.idx; // stable tie-breaker
|
|
202
202
|
});
|
package/lib/prelude.js
CHANGED
|
@@ -426,7 +426,7 @@ function collectIrisInTerm(t) {
|
|
|
426
426
|
if (t instanceof Iri) {
|
|
427
427
|
out.push(t.value);
|
|
428
428
|
} else if (t instanceof Literal) {
|
|
429
|
-
const [
|
|
429
|
+
const [, dt] = literalParts(t.value);
|
|
430
430
|
if (dt) out.push(dt); // so rdf/xsd prefixes are emitted when only used in ^^...
|
|
431
431
|
} else if (t instanceof ListTerm) {
|
|
432
432
|
for (const x of t.elems) out.push(...collectIrisInTerm(x));
|
package/package.json
CHANGED
package/test/api.test.js
CHANGED
|
@@ -1305,15 +1305,15 @@ ex:a p:trig ex:b.
|
|
|
1305
1305
|
});
|
|
1306
1306
|
|
|
1307
1307
|
// stash for check()
|
|
1308
|
-
this.
|
|
1309
|
-
this.
|
|
1308
|
+
this.seen = seen;
|
|
1309
|
+
this.result = r;
|
|
1310
1310
|
return r.closureN3;
|
|
1311
1311
|
},
|
|
1312
1312
|
expect: [/http:\/\/example\.org\/q/m],
|
|
1313
1313
|
notExpect: [/http:\/\/example\.org\/p/m],
|
|
1314
1314
|
check(out, tc) {
|
|
1315
|
-
assert.equal(tc.
|
|
1316
|
-
assert.match(tc.
|
|
1315
|
+
assert.equal(tc.seen.length, 1, 'Expected onDerived to be called once');
|
|
1316
|
+
assert.match(tc.seen[0], /http:\/\/example\.org\/q/, 'Expected streamed triple to be the derived one');
|
|
1317
1317
|
// closureN3 should be exactly the derived triple (no input facts).
|
|
1318
1318
|
assert.ok(String(out).trim().includes('http://example.org/q'));
|
|
1319
1319
|
assert.ok(!String(out).includes('http://example.org/p'));
|
|
@@ -1584,19 +1584,19 @@ _:x :hates { _:foo :making :mess }.
|
|
|
1584
1584
|
onDerived: ({ quad }) => seen.push(quad),
|
|
1585
1585
|
},
|
|
1586
1586
|
);
|
|
1587
|
-
this.
|
|
1588
|
-
this.
|
|
1587
|
+
this.seen = seen;
|
|
1588
|
+
this.result = result;
|
|
1589
1589
|
return result.closureN3;
|
|
1590
1590
|
},
|
|
1591
1591
|
expect: [/http:\/\/example\.org\/q/m],
|
|
1592
1592
|
notExpect: [/http:\/\/example\.org\/p/m],
|
|
1593
|
-
check(
|
|
1594
|
-
assert.equal(tc.
|
|
1595
|
-
assert.equal(tc.
|
|
1596
|
-
assert.equal(tc.
|
|
1597
|
-
assert.ok(Array.isArray(tc.
|
|
1598
|
-
assert.equal(tc.
|
|
1599
|
-
assert.equal(tc.
|
|
1593
|
+
check(outputIgnored, tc) {
|
|
1594
|
+
assert.equal(tc.seen.length, 1, 'Expected one streamed RDF/JS quad');
|
|
1595
|
+
assert.equal(tc.seen[0].termType, 'Quad');
|
|
1596
|
+
assert.equal(tc.seen[0].predicate.value, 'http://example.org/q');
|
|
1597
|
+
assert.ok(Array.isArray(tc.result.closureQuads), 'Expected closureQuads array');
|
|
1598
|
+
assert.equal(tc.result.closureQuads.length, 1);
|
|
1599
|
+
assert.equal(tc.result.closureQuads[0].object.value, 'http://example.org/o');
|
|
1600
1600
|
},
|
|
1601
1601
|
},
|
|
1602
1602
|
{
|
|
@@ -1633,14 +1633,14 @@ _:x :hates { _:foo :making :mess }.
|
|
|
1633
1633
|
})) {
|
|
1634
1634
|
quads.push(quad);
|
|
1635
1635
|
}
|
|
1636
|
-
this.
|
|
1636
|
+
this.quads = quads;
|
|
1637
1637
|
return quads.map((q) => `${q.subject.value} ${q.predicate.value} ${q.object.value}`).join('\n');
|
|
1638
1638
|
},
|
|
1639
1639
|
expect: [/http:\/\/example\.org\/q/],
|
|
1640
|
-
check(
|
|
1641
|
-
assert.equal(tc.
|
|
1642
|
-
assert.equal(tc.
|
|
1643
|
-
assert.equal(tc.
|
|
1640
|
+
check(outputIgnored, tc) {
|
|
1641
|
+
assert.equal(tc.quads.length, 1, 'Expected one yielded quad');
|
|
1642
|
+
assert.equal(tc.quads[0].predicate.value, 'http://example.org/q');
|
|
1643
|
+
assert.equal(tc.quads[0].graph.termType, 'DefaultGraph');
|
|
1644
1644
|
},
|
|
1645
1645
|
},
|
|
1646
1646
|
{
|
package/test/playground.test.js
CHANGED
|
@@ -74,7 +74,7 @@ function startStaticServer(rootDir) {
|
|
|
74
74
|
|
|
75
75
|
res.writeHead(200, { 'Content-Type': guessContentType(fsPath), 'Cache-Control': 'no-store' });
|
|
76
76
|
fs.createReadStream(fsPath).pipe(res);
|
|
77
|
-
} catch
|
|
77
|
+
} catch {
|
|
78
78
|
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
79
79
|
res.end('Not found');
|
|
80
80
|
}
|
package/tools/bundle.js
CHANGED
|
@@ -140,8 +140,10 @@ out.push(' }');
|
|
|
140
140
|
out.push(' const __entry = __loadEntry();');
|
|
141
141
|
out.push(' const __api = { reasonStream: __entry.reasonStream, reasonRdfJs: __entry.reasonRdfJs };');
|
|
142
142
|
out.push('');
|
|
143
|
-
out.push(
|
|
144
|
-
|
|
143
|
+
out.push(
|
|
144
|
+
' try { if (__outerModule && __outerModule.exports) __outerModule.exports = __api; } catch (ignoredError) {}',
|
|
145
|
+
);
|
|
146
|
+
out.push(' try { if (__outerSelf) __outerSelf.eyeling = __api; } catch (ignoredError) {}');
|
|
145
147
|
out.push('');
|
|
146
148
|
out.push(' // ---- demo.html compatibility ----');
|
|
147
149
|
out.push(' // The original monolithic eyeling.js exposed internal functions/flags as globals.');
|
|
@@ -175,14 +177,14 @@ out.push(' } else {');
|
|
|
175
177
|
out.push(' // Fallback (no live linkage)');
|
|
176
178
|
out.push(' if (typeof getFn === "function") __outerSelf[name] = getFn();');
|
|
177
179
|
out.push(' }');
|
|
178
|
-
out.push(' } catch (
|
|
180
|
+
out.push(' } catch (ignoredError) {}');
|
|
179
181
|
out.push(' };');
|
|
180
182
|
out.push('');
|
|
181
183
|
out.push(' def("enforceHttpsEnabled", __entry.getEnforceHttpsEnabled, __entry.setEnforceHttpsEnabled);');
|
|
182
184
|
out.push(' def("proofCommentsEnabled", __entry.getProofCommentsEnabled, __entry.setProofCommentsEnabled);');
|
|
183
185
|
out.push(' def("__tracePrefixes", __entry.getTracePrefixes, __entry.setTracePrefixes);');
|
|
184
186
|
out.push(' }');
|
|
185
|
-
out.push(' } catch (
|
|
187
|
+
out.push(' } catch (ignoredError) {}');
|
|
186
188
|
out.push('');
|
|
187
189
|
out.push(' try {');
|
|
188
190
|
out.push(
|
|
@@ -190,7 +192,7 @@ out.push(
|
|
|
190
192
|
);
|
|
191
193
|
out.push(' __entry.main();');
|
|
192
194
|
out.push(' }');
|
|
193
|
-
out.push(' } catch (
|
|
195
|
+
out.push(' } catch (ignoredError) {}');
|
|
194
196
|
out.push('})();');
|
|
195
197
|
|
|
196
198
|
fs.writeFileSync(OUT, out.join('\n') + '\n', { encoding: 'utf8' });
|
package/tools/n3gen.js
CHANGED
|
@@ -32,7 +32,7 @@ const process = require('node:process');
|
|
|
32
32
|
|
|
33
33
|
const crypto = require('node:crypto');
|
|
34
34
|
|
|
35
|
-
function
|
|
35
|
+
function stripIriRef(s) {
|
|
36
36
|
// Allow passing an IRIREF like <...>
|
|
37
37
|
if (typeof s !== 'string') return '';
|
|
38
38
|
s = s.trim();
|
|
@@ -41,7 +41,7 @@ function _stripIriRef(s) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function normalizeSkolemRoot(root) {
|
|
44
|
-
root =
|
|
44
|
+
root = stripIriRef(root);
|
|
45
45
|
if (!root) return '';
|
|
46
46
|
// Ensure it ends with '/.well-known/genid/' OR at least with '/'
|
|
47
47
|
if (!root.endsWith('/')) root += '/';
|
|
@@ -64,7 +64,7 @@ const SKOLEM_ROOT = normalizeSkolemRoot(process.env.SKOLEM_ROOT) || DEFAULT_SKOL
|
|
|
64
64
|
let SKOLEM_UUID = null; // e.g., '3f2504e0-4f89-5d3a-9a0c-0305e82c3301'
|
|
65
65
|
let SKOLEM_PREFIX_IRI = null; // e.g., 'https://.../.well-known/genid/<UUID>#'
|
|
66
66
|
|
|
67
|
-
function
|
|
67
|
+
function deterministicUuidFromText(inputText) {
|
|
68
68
|
const h = crypto.createHash('sha256').update(inputText, 'utf8').digest();
|
|
69
69
|
const b = Buffer.from(h.subarray(0, 16));
|
|
70
70
|
|
|
@@ -77,11 +77,11 @@ function _deterministicUuidFromText(inputText) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function initSkolemForInput(inputText) {
|
|
80
|
-
SKOLEM_UUID =
|
|
80
|
+
SKOLEM_UUID = deterministicUuidFromText(inputText);
|
|
81
81
|
SKOLEM_PREFIX_IRI = `${SKOLEM_ROOT}${SKOLEM_UUID}#`;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function
|
|
84
|
+
function pnLocalSafe(s) {
|
|
85
85
|
// Turtle PN_LOCAL allows percent escapes (PLX). We make sure all "special"
|
|
86
86
|
// encodeURIComponent survivors are percent-escaped too.
|
|
87
87
|
return encodeURIComponent(s).replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
|
|
@@ -786,7 +786,7 @@ class TurtleParser {
|
|
|
786
786
|
this.blankCounter = 0;
|
|
787
787
|
this.pendingTriples = [];
|
|
788
788
|
this.reifierCounter = 0;
|
|
789
|
-
this.
|
|
789
|
+
this.reifiesEmitted = new Set();
|
|
790
790
|
}
|
|
791
791
|
|
|
792
792
|
peek() {
|
|
@@ -811,16 +811,16 @@ class TurtleParser {
|
|
|
811
811
|
return new Blank(`_:n3r${this.reifierCounter}`);
|
|
812
812
|
}
|
|
813
813
|
|
|
814
|
-
|
|
814
|
+
termKey(t) {
|
|
815
815
|
if (t == null) return '[]';
|
|
816
816
|
if (t instanceof Iri) return `I:${t.value}`;
|
|
817
817
|
if (t instanceof Blank) return `B:${t.label}`;
|
|
818
818
|
if (t instanceof Literal) return `L:${t.value}`;
|
|
819
819
|
if (t instanceof Var) return `V:${t.name}`;
|
|
820
|
-
if (t instanceof ListTerm) return `T:(` + t.elems.map((x) => this.
|
|
820
|
+
if (t instanceof ListTerm) return `T:(` + t.elems.map((x) => this.termKey(x)).join(' ') + `)`;
|
|
821
821
|
if (t instanceof GraphTerm) {
|
|
822
822
|
const inner = t.triples
|
|
823
|
-
.map((tr) => `${this.
|
|
823
|
+
.map((tr) => `${this.termKey(tr.s)} ${this.termKey(tr.p)} ${this.termKey(tr.o)}`)
|
|
824
824
|
.join(' | ');
|
|
825
825
|
return `G:{${inner}}`;
|
|
826
826
|
}
|
|
@@ -831,9 +831,9 @@ class TurtleParser {
|
|
|
831
831
|
// reifier log:nameOf tripleTerm .
|
|
832
832
|
// We represent tripleTerm in N3 as a quoted graph term: { s p o . }
|
|
833
833
|
emitReifies(reifier, tripleGraph) {
|
|
834
|
-
const key = `${this.
|
|
835
|
-
if (this.
|
|
836
|
-
this.
|
|
834
|
+
const key = `${this.termKey(reifier)}|${this.termKey(tripleGraph)}`;
|
|
835
|
+
if (this.reifiesEmitted.has(key)) return;
|
|
836
|
+
this.reifiesEmitted.add(key);
|
|
837
837
|
this.pendingTriples.push(new Triple(reifier, internIri(LOG_NS + 'nameOf'), tripleGraph));
|
|
838
838
|
}
|
|
839
839
|
|
|
@@ -1519,7 +1519,7 @@ function buildSkolemMapForBnodesThatCrossScopes(triples) {
|
|
|
1519
1519
|
if (scopes.size <= 1) continue;
|
|
1520
1520
|
|
|
1521
1521
|
const id = lbl.startsWith('_:') ? lbl.slice(2) : lbl;
|
|
1522
|
-
const local =
|
|
1522
|
+
const local = pnLocalSafe(id);
|
|
1523
1523
|
skolemMap.set(lbl, `${SKOLEM_PREFIX}:${local}`);
|
|
1524
1524
|
}
|
|
1525
1525
|
return skolemMap;
|
|
@@ -1538,16 +1538,16 @@ function buildSkolemMapForBnodesThatCrossScopes(triples) {
|
|
|
1538
1538
|
// semantics-preserving.
|
|
1539
1539
|
// ---------------------------------------------------------------------------
|
|
1540
1540
|
|
|
1541
|
-
function
|
|
1541
|
+
function termKey(t) {
|
|
1542
1542
|
if (t == null) return 'N:null';
|
|
1543
1543
|
if (t instanceof Iri) return `I:${t.value}`;
|
|
1544
1544
|
if (t instanceof Blank) return `B:${t.label}`;
|
|
1545
1545
|
if (t instanceof Literal) return `L:${t.value}`;
|
|
1546
1546
|
if (t instanceof Var) return `V:${t.name}`;
|
|
1547
|
-
if (t instanceof ListTerm) return `T:(` + t.elems.map(
|
|
1548
|
-
if (t instanceof OpenListTerm) return `T:(` + t.prefix.map(
|
|
1547
|
+
if (t instanceof ListTerm) return `T:(` + t.elems.map(termKey).join(' ') + `)`;
|
|
1548
|
+
if (t instanceof OpenListTerm) return `T:(` + t.prefix.map(termKey).join(' ') + ` ... ?${t.tailVar})`;
|
|
1549
1549
|
if (t instanceof GraphTerm)
|
|
1550
|
-
return `G:{` + t.triples.map((tr) => `${
|
|
1550
|
+
return `G:{` + t.triples.map((tr) => `${termKey(tr.s)} ${termKey(tr.p)} ${termKey(tr.o)}`).join(' ; ') + `}`;
|
|
1551
1551
|
return `X:${String(t)}`;
|
|
1552
1552
|
}
|
|
1553
1553
|
|
|
@@ -1567,11 +1567,11 @@ function foldRdfLists(triples) {
|
|
|
1567
1567
|
|
|
1568
1568
|
for (let i = 0; i < triples.length; i++) {
|
|
1569
1569
|
const tr = triples[i];
|
|
1570
|
-
const sKey =
|
|
1570
|
+
const sKey = termKey(tr.s);
|
|
1571
1571
|
if (!outBySubj.has(sKey)) outBySubj.set(sKey, { term: tr.s, idxs: [] });
|
|
1572
1572
|
outBySubj.get(sKey).idxs.push(i);
|
|
1573
1573
|
|
|
1574
|
-
const oKey =
|
|
1574
|
+
const oKey = termKey(tr.o);
|
|
1575
1575
|
const viaRest = isIri(tr.p, rdfRest);
|
|
1576
1576
|
addIncoming(oKey, viaRest);
|
|
1577
1577
|
}
|
|
@@ -1655,7 +1655,7 @@ function foldRdfLists(triples) {
|
|
|
1655
1655
|
break;
|
|
1656
1656
|
}
|
|
1657
1657
|
|
|
1658
|
-
const nextKey =
|
|
1658
|
+
const nextKey = termKey(next);
|
|
1659
1659
|
|
|
1660
1660
|
// Intermediate node safety: only referenced via rdf:rest and exactly once.
|
|
1661
1661
|
const inc = incoming.get(nextKey) || 0;
|
|
@@ -1694,7 +1694,7 @@ function foldRdfLists(triples) {
|
|
|
1694
1694
|
if (t == null) return t;
|
|
1695
1695
|
|
|
1696
1696
|
if (t instanceof Blank) {
|
|
1697
|
-
const m = listMap.get(
|
|
1697
|
+
const m = listMap.get(termKey(t));
|
|
1698
1698
|
if (m) return replaceTerm(m.listTerm);
|
|
1699
1699
|
return t;
|
|
1700
1700
|
}
|
|
@@ -1813,7 +1813,7 @@ function ensureSkolemPrefix(prefixes, skolemMap) {
|
|
|
1813
1813
|
const base = prefixes ? prefixes.baseIri || '' : '';
|
|
1814
1814
|
const labels = [...skolemMap.keys()].sort().join('\n');
|
|
1815
1815
|
const seed = ['n3gen-skolem', SKOLEM_ROOT, base, labels, ''].join('\n');
|
|
1816
|
-
const uuid =
|
|
1816
|
+
const uuid = deterministicUuidFromText(seed);
|
|
1817
1817
|
SKOLEM_PREFIX_IRI = `${SKOLEM_ROOT}${uuid}#`;
|
|
1818
1818
|
} else if (!SKOLEM_PREFIX_IRI) {
|
|
1819
1819
|
SKOLEM_PREFIX_IRI = `${SKOLEM_ROOT}${SKOLEM_UUID}#`;
|