eyeling 1.16.1 → 1.16.2
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/arctifacts/README.md +2 -0
- package/eyeling.js +18 -18
- package/lib/builtins.js +7 -7
- package/lib/cli.js +2 -2
- package/lib/deref.js +1 -1
- 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/arctifacts/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
ARCtifacts are trustworthy programs telling a concise story in three parts. First comes the **Answer** to a specific question. This is followed by the **Reason Why** that answer is correct, articulated in everyday language and supported by the relevant identities, rules, or ideas. Finally, every case includes a **Check**—a concrete test designed to fail loudly if an assumption doesn't hold or an edge case bites. The result is a computation with a complete, auditable trail: you can see precisely what was done, why it was valid, and how the page verifies its own work.
|
|
4
4
|
|
|
5
|
+
At the core of this approach are three familiar ingredients: **Data**, **Logic**, and a **Question**. We summarize the workflow as **P3 — Prompt → Program → Proof**: the prompt supplies the task and materials, the program turns them into a concrete, repeatable procedure, and the proof is practical rather than ceremonial, consisting of the **Reason Why** together with the **Check**. This is what makes each ARCtifact not just a result, but a portable, auditable, and trustworthy computational artifact.
|
|
6
|
+
|
|
5
7
|
## Science
|
|
6
8
|
|
|
7
9
|
- [**Body Mass Index**](https://eyereasoner.github.io/eyeling/arctifacts/bmi.html) — Compute BMI categories with explainable thresholds and sanity checks.
|
package/eyeling.js
CHANGED
|
@@ -294,7 +294,7 @@ function termToJsString(t) {
|
|
|
294
294
|
if (t instanceof Iri) return t.value;
|
|
295
295
|
if (!(t instanceof Literal)) return null;
|
|
296
296
|
|
|
297
|
-
const [lex
|
|
297
|
+
const [lex] = literalParts(t.value);
|
|
298
298
|
|
|
299
299
|
if (isQuotedLexical(lex)) {
|
|
300
300
|
// Interpret N3/Turtle string escapes (\" \n \uXXXX \UXXXXXXXX …)
|
|
@@ -316,7 +316,7 @@ function termToJsStringDecoded(t) {
|
|
|
316
316
|
// Like termToJsString, but for short literals it *also* interprets escapes
|
|
317
317
|
// (\" \n \uXXXX …) by attempting JSON.parse on the quoted lexical form.
|
|
318
318
|
if (!(t instanceof Literal)) return null;
|
|
319
|
-
const [lex
|
|
319
|
+
const [lex] = literalParts(t.value);
|
|
320
320
|
|
|
321
321
|
// Long strings: """ ... """ are taken verbatim.
|
|
322
322
|
if (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""')) {
|
|
@@ -327,7 +327,7 @@ function termToJsStringDecoded(t) {
|
|
|
327
327
|
if (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') {
|
|
328
328
|
try {
|
|
329
329
|
return JSON.parse(lex);
|
|
330
|
-
} catch
|
|
330
|
+
} catch {
|
|
331
331
|
/* fall through */
|
|
332
332
|
}
|
|
333
333
|
return stripQuotes(lex);
|
|
@@ -392,13 +392,13 @@ function compileSwapRegex(pattern, extraFlags) {
|
|
|
392
392
|
const flags = (extraFlags || '') + (needU ? 'u' : '');
|
|
393
393
|
try {
|
|
394
394
|
return new RegExp(pattern, flags);
|
|
395
|
-
} catch
|
|
395
|
+
} catch {
|
|
396
396
|
if (needU) {
|
|
397
397
|
const p2 = sanitizeForUnicodeMode(pattern);
|
|
398
398
|
if (p2 !== pattern) {
|
|
399
399
|
try {
|
|
400
400
|
return new RegExp(p2, flags);
|
|
401
|
-
} catch
|
|
401
|
+
} catch {}
|
|
402
402
|
}
|
|
403
403
|
}
|
|
404
404
|
return null;
|
|
@@ -1420,7 +1420,7 @@ function hashLiteralTerm(t, algo) {
|
|
|
1420
1420
|
try {
|
|
1421
1421
|
const digest = nodeCrypto.createHash(algo).update(input, 'utf8').digest('hex');
|
|
1422
1422
|
return internLiteral(JSON.stringify(digest));
|
|
1423
|
-
} catch
|
|
1423
|
+
} catch {
|
|
1424
1424
|
return null;
|
|
1425
1425
|
}
|
|
1426
1426
|
}
|
|
@@ -2617,7 +2617,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
2617
2617
|
|
|
2618
2618
|
const results = [];
|
|
2619
2619
|
for (const el of inputList) {
|
|
2620
|
-
const yvar = new Var('
|
|
2620
|
+
const yvar = new Var('mapY');
|
|
2621
2621
|
const goal2 = new Triple(el, pred, yvar);
|
|
2622
2622
|
const sols = proveGoals([goal2], subst, facts, backRules, depth + 1, [], varGen);
|
|
2623
2623
|
|
|
@@ -4020,7 +4020,7 @@ function main() {
|
|
|
4020
4020
|
}
|
|
4021
4021
|
|
|
4022
4022
|
if (showAst) {
|
|
4023
|
-
function astReplacer(
|
|
4023
|
+
function astReplacer(unusedJsonKey, value) {
|
|
4024
4024
|
if (value instanceof Set) return Array.from(value);
|
|
4025
4025
|
if (value && typeof value === 'object' && value.constructor) {
|
|
4026
4026
|
const t = value.constructor.name;
|
|
@@ -4160,7 +4160,7 @@ function main() {
|
|
|
4160
4160
|
engine.setTracePrefixes(outPrefixes);
|
|
4161
4161
|
|
|
4162
4162
|
const entries = Object.entries(outPrefixes.map)
|
|
4163
|
-
.filter(([
|
|
4163
|
+
.filter(([, base]) => !!base)
|
|
4164
4164
|
.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
4165
4165
|
|
|
4166
4166
|
for (const [pfx, base] of entries) {
|
|
@@ -4594,7 +4594,7 @@ function parseSemanticsToFormula(text, baseIri) {
|
|
|
4594
4594
|
const parser = new Parser(toks);
|
|
4595
4595
|
if (typeof baseIri === 'string' && baseIri) parser.prefixes.setBase(baseIri);
|
|
4596
4596
|
|
|
4597
|
-
const [
|
|
4597
|
+
const [, triples, frules, brules] = parser.parseDocument();
|
|
4598
4598
|
|
|
4599
4599
|
const all = triples.slice();
|
|
4600
4600
|
|
|
@@ -7974,7 +7974,7 @@ function makeExplain(deps) {
|
|
|
7974
7974
|
// log:outputString support
|
|
7975
7975
|
// ===========================================================================
|
|
7976
7976
|
|
|
7977
|
-
function
|
|
7977
|
+
function compareOutputStringKeys(a, b) {
|
|
7978
7978
|
// Deterministic ordering of keys. The spec only requires "order of the subject keys"
|
|
7979
7979
|
// and leaves concrete term ordering reasoner-dependent. We implement:
|
|
7980
7980
|
// 1) numeric literals (numeric value)
|
|
@@ -8052,7 +8052,7 @@ function makeExplain(deps) {
|
|
|
8052
8052
|
}
|
|
8053
8053
|
|
|
8054
8054
|
pairs.sort((a, b) => {
|
|
8055
|
-
const c =
|
|
8055
|
+
const c = compareOutputStringKeys(a.key, b.key, prefixes);
|
|
8056
8056
|
if (c !== 0) return c;
|
|
8057
8057
|
return a.idx - b.idx; // stable tie-breaker
|
|
8058
8058
|
});
|
|
@@ -10053,7 +10053,7 @@ function collectIrisInTerm(t) {
|
|
|
10053
10053
|
if (t instanceof Iri) {
|
|
10054
10054
|
out.push(t.value);
|
|
10055
10055
|
} else if (t instanceof Literal) {
|
|
10056
|
-
const [
|
|
10056
|
+
const [, dt] = literalParts(t.value);
|
|
10057
10057
|
if (dt) out.push(dt); // so rdf/xsd prefixes are emitted when only used in ^^...
|
|
10058
10058
|
} else if (t instanceof ListTerm) {
|
|
10059
10059
|
for (const x of t.elems) out.push(...collectIrisInTerm(x));
|
|
@@ -11624,8 +11624,8 @@ module.exports = {
|
|
|
11624
11624
|
const __entry = __loadEntry();
|
|
11625
11625
|
const __api = { reasonStream: __entry.reasonStream, reasonRdfJs: __entry.reasonRdfJs };
|
|
11626
11626
|
|
|
11627
|
-
try { if (__outerModule && __outerModule.exports) __outerModule.exports = __api; } catch (
|
|
11628
|
-
try { if (__outerSelf) __outerSelf.eyeling = __api; } catch (
|
|
11627
|
+
try { if (__outerModule && __outerModule.exports) __outerModule.exports = __api; } catch (ignoredError) {}
|
|
11628
|
+
try { if (__outerSelf) __outerSelf.eyeling = __api; } catch (ignoredError) {}
|
|
11629
11629
|
|
|
11630
11630
|
// ---- demo.html compatibility ----
|
|
11631
11631
|
// The original monolithic eyeling.js exposed internal functions/flags as globals.
|
|
@@ -11653,18 +11653,18 @@ module.exports = {
|
|
|
11653
11653
|
// Fallback (no live linkage)
|
|
11654
11654
|
if (typeof getFn === "function") __outerSelf[name] = getFn();
|
|
11655
11655
|
}
|
|
11656
|
-
} catch (
|
|
11656
|
+
} catch (ignoredError) {}
|
|
11657
11657
|
};
|
|
11658
11658
|
|
|
11659
11659
|
def("enforceHttpsEnabled", __entry.getEnforceHttpsEnabled, __entry.setEnforceHttpsEnabled);
|
|
11660
11660
|
def("proofCommentsEnabled", __entry.getProofCommentsEnabled, __entry.setProofCommentsEnabled);
|
|
11661
11661
|
def("__tracePrefixes", __entry.getTracePrefixes, __entry.setTracePrefixes);
|
|
11662
11662
|
}
|
|
11663
|
-
} catch (
|
|
11663
|
+
} catch (ignoredError) {}
|
|
11664
11664
|
|
|
11665
11665
|
try {
|
|
11666
11666
|
if (__outerModule && __outerRequire && __outerRequire.main === __outerModule && typeof __entry.main === "function") {
|
|
11667
11667
|
__entry.main();
|
|
11668
11668
|
}
|
|
11669
|
-
} catch (
|
|
11669
|
+
} catch (ignoredError) {}
|
|
11670
11670
|
})();
|
package/lib/builtins.js
CHANGED
|
@@ -282,7 +282,7 @@ function termToJsString(t) {
|
|
|
282
282
|
if (t instanceof Iri) return t.value;
|
|
283
283
|
if (!(t instanceof Literal)) return null;
|
|
284
284
|
|
|
285
|
-
const [lex
|
|
285
|
+
const [lex] = literalParts(t.value);
|
|
286
286
|
|
|
287
287
|
if (isQuotedLexical(lex)) {
|
|
288
288
|
// Interpret N3/Turtle string escapes (\" \n \uXXXX \UXXXXXXXX …)
|
|
@@ -304,7 +304,7 @@ function termToJsStringDecoded(t) {
|
|
|
304
304
|
// Like termToJsString, but for short literals it *also* interprets escapes
|
|
305
305
|
// (\" \n \uXXXX …) by attempting JSON.parse on the quoted lexical form.
|
|
306
306
|
if (!(t instanceof Literal)) return null;
|
|
307
|
-
const [lex
|
|
307
|
+
const [lex] = literalParts(t.value);
|
|
308
308
|
|
|
309
309
|
// Long strings: """ ... """ are taken verbatim.
|
|
310
310
|
if (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""')) {
|
|
@@ -315,7 +315,7 @@ function termToJsStringDecoded(t) {
|
|
|
315
315
|
if (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') {
|
|
316
316
|
try {
|
|
317
317
|
return JSON.parse(lex);
|
|
318
|
-
} catch
|
|
318
|
+
} catch {
|
|
319
319
|
/* fall through */
|
|
320
320
|
}
|
|
321
321
|
return stripQuotes(lex);
|
|
@@ -380,13 +380,13 @@ function compileSwapRegex(pattern, extraFlags) {
|
|
|
380
380
|
const flags = (extraFlags || '') + (needU ? 'u' : '');
|
|
381
381
|
try {
|
|
382
382
|
return new RegExp(pattern, flags);
|
|
383
|
-
} catch
|
|
383
|
+
} catch {
|
|
384
384
|
if (needU) {
|
|
385
385
|
const p2 = sanitizeForUnicodeMode(pattern);
|
|
386
386
|
if (p2 !== pattern) {
|
|
387
387
|
try {
|
|
388
388
|
return new RegExp(p2, flags);
|
|
389
|
-
} catch
|
|
389
|
+
} catch {}
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
return null;
|
|
@@ -1408,7 +1408,7 @@ function hashLiteralTerm(t, algo) {
|
|
|
1408
1408
|
try {
|
|
1409
1409
|
const digest = nodeCrypto.createHash(algo).update(input, 'utf8').digest('hex');
|
|
1410
1410
|
return internLiteral(JSON.stringify(digest));
|
|
1411
|
-
} catch
|
|
1411
|
+
} catch {
|
|
1412
1412
|
return null;
|
|
1413
1413
|
}
|
|
1414
1414
|
}
|
|
@@ -2605,7 +2605,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
2605
2605
|
|
|
2606
2606
|
const results = [];
|
|
2607
2607
|
for (const el of inputList) {
|
|
2608
|
-
const yvar = new Var('
|
|
2608
|
+
const yvar = new Var('mapY');
|
|
2609
2609
|
const goal2 = new Triple(el, pred, yvar);
|
|
2610
2610
|
const sols = proveGoals([goal2], subst, facts, backRules, depth + 1, [], varGen);
|
|
2611
2611
|
|
package/lib/cli.js
CHANGED
|
@@ -153,7 +153,7 @@ function main() {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
if (showAst) {
|
|
156
|
-
function astReplacer(
|
|
156
|
+
function astReplacer(unusedJsonKey, value) {
|
|
157
157
|
if (value instanceof Set) return Array.from(value);
|
|
158
158
|
if (value && typeof value === 'object' && value.constructor) {
|
|
159
159
|
const t = value.constructor.name;
|
|
@@ -293,7 +293,7 @@ function main() {
|
|
|
293
293
|
engine.setTracePrefixes(outPrefixes);
|
|
294
294
|
|
|
295
295
|
const entries = Object.entries(outPrefixes.map)
|
|
296
|
-
.filter(([
|
|
296
|
+
.filter(([, base]) => !!base)
|
|
297
297
|
.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
298
298
|
|
|
299
299
|
for (const [pfx, base] of entries) {
|
package/lib/deref.js
CHANGED
|
@@ -359,7 +359,7 @@ function parseSemanticsToFormula(text, baseIri) {
|
|
|
359
359
|
const parser = new Parser(toks);
|
|
360
360
|
if (typeof baseIri === 'string' && baseIri) parser.prefixes.setBase(baseIri);
|
|
361
361
|
|
|
362
|
-
const [
|
|
362
|
+
const [, triples, frules, brules] = parser.parseDocument();
|
|
363
363
|
|
|
364
364
|
const all = triples.slice();
|
|
365
365
|
|
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}#`;
|