eyeling 1.24.1 → 1.24.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 +134 -0
- package/dist/browser/eyeling.browser.js +454 -22
- package/eyeling.js +454 -22
- package/index.js +2 -0
- package/lib/cli.js +27 -10
- package/lib/engine.js +17 -7
- package/lib/lexer.js +295 -2
- package/lib/multisource.js +9 -2
- package/lib/printing.js +106 -1
- package/package.json +1 -1
- package/see/README.md +4 -0
- package/see/examples/_see.js +33 -2
- package/see/examples/age.js +27 -1
- package/see/examples/annotation.js +27 -1
- package/see/examples/backward.js +27 -1
- package/see/examples/backward_recursion.js +27 -1
- package/see/examples/bayes_diagnosis.js +27 -1
- package/see/examples/bayes_therapy.js +27 -1
- package/see/examples/bmi.js +27 -1
- package/see/examples/builtin_coverage.js +27 -1
- package/see/examples/collection.js +27 -1
- package/see/examples/complex.js +27 -1
- package/see/examples/complex_matrix_stability.js +27 -1
- package/see/examples/composition_of_injective_functions_is_injective.js +27 -1
- package/see/examples/control_system.js +27 -1
- package/see/examples/crypto_builtins_tests.js +27 -1
- package/see/examples/delfour.js +27 -1
- package/see/examples/digital_product_passport.js +27 -1
- package/see/examples/dijkstra.js +27 -1
- package/see/examples/dijkstra_risk_path.js +27 -1
- package/see/examples/doc/rdf_dataset.md +26 -0
- package/see/examples/doc/triple_terms.md +26 -0
- package/see/examples/dog.js +27 -1
- package/see/examples/eco_route_insight.js +27 -1
- package/see/examples/equals.js +27 -1
- package/see/examples/equivalence_classes_overlap_implies_same_class.js +27 -1
- package/see/examples/euler_identity.js +27 -1
- package/see/examples/ev_roundtrip_planner.js +27 -1
- package/see/examples/existential_rule.js +27 -1
- package/see/examples/expression_eval.js +27 -1
- package/see/examples/family_cousins.js +27 -1
- package/see/examples/fastpow.js +27 -1
- package/see/examples/fibonacci.js +27 -1
- package/see/examples/french_cities.js +27 -1
- package/see/examples/fundamental_theorem_arithmetic.js +27 -1
- package/see/examples/genetic_knapsack_selection.js +27 -1
- package/see/examples/goldbach_1000.js +27 -1
- package/see/examples/good_cobbler.js +27 -1
- package/see/examples/gps.js +27 -1
- package/see/examples/gray_code_counter.js +27 -1
- package/see/examples/greatest_lower_bound_uniqueness.js +27 -1
- package/see/examples/group_inverse_uniqueness.js +27 -1
- package/see/examples/hadamard_approx.js +27 -1
- package/see/examples/hanoi.js +27 -1
- package/see/examples/input/rdf_dataset.trig +34 -0
- package/see/examples/input/triple_terms.trig +28 -0
- package/see/examples/n3/rdf_dataset.n3 +34 -0
- package/see/examples/n3/triple_terms.n3 +23 -0
- package/see/examples/odrl_dpv_risk_ranked.js +27 -1
- package/see/examples/output/rdf_dataset.md +54 -0
- package/see/examples/output/triple_terms.md +53 -0
- package/see/examples/path_discovery.js +27 -1
- package/see/examples/rc_discharge_envelope.js +27 -1
- package/see/examples/rdf_dataset.js +1512 -0
- package/see/examples/rdf_message_flow.js +27 -1
- package/see/examples/rdf_messages.js +27 -1
- package/see/examples/school_placement_audit.js +27 -1
- package/see/examples/smoke_arithmetic.js +27 -1
- package/see/examples/socrates.js +27 -1
- package/see/examples/triple_terms.js +1442 -0
- package/see/examples/wind_turbine.js +27 -1
- package/see/examples/witch.js +27 -1
- package/see/see.js +101 -4
- package/test/api.test.js +86 -0
- package/test/see.test.js +0 -0
- package/tools/bundle.js +0 -0
- package/tools/n3gen.js +0 -0
package/lib/cli.js
CHANGED
|
@@ -107,6 +107,7 @@ function main() {
|
|
|
107
107
|
` -e, --enforce-https Rewrite http:// IRIs to https:// for log dereferencing builtins.\n` +
|
|
108
108
|
` -h, --help Show this help and exit.\n` +
|
|
109
109
|
` -p, --proof-comments Enable proof explanations.\n` +
|
|
110
|
+
` -r, --rdf Enable RDF/TriG input/output compatibility.\n` +
|
|
110
111
|
` -s, --super-restricted Disable all builtins except => and <=.\n` +
|
|
111
112
|
` -t, --stream Stream derived triples as soon as they are derived.\n` +
|
|
112
113
|
` -v, --version Print version and exit.\n`;
|
|
@@ -148,6 +149,7 @@ function main() {
|
|
|
148
149
|
|
|
149
150
|
const showAst = argv.includes('--ast') || argv.includes('-a');
|
|
150
151
|
const streamMode = argv.includes('--stream') || argv.includes('-t');
|
|
152
|
+
const rdfMode = argv.includes('--rdf') || argv.includes('-r');
|
|
151
153
|
|
|
152
154
|
// --enforce-https: rewrite http:// -> https:// for log dereferencing builtins
|
|
153
155
|
if (argv.includes('--enforce-https') || argv.includes('-e')) {
|
|
@@ -210,6 +212,7 @@ function main() {
|
|
|
210
212
|
label: sourceLabel,
|
|
211
213
|
collectUsedPrefixes: true,
|
|
212
214
|
keepSourceArtifacts: false,
|
|
215
|
+
rdf: rdfMode,
|
|
213
216
|
}),
|
|
214
217
|
);
|
|
215
218
|
} catch (e) {
|
|
@@ -322,10 +325,10 @@ function main() {
|
|
|
322
325
|
(df) => {
|
|
323
326
|
if (engine.getProofCommentsEnabled()) {
|
|
324
327
|
engine.printExplanation(df, outPrefixes);
|
|
325
|
-
console.log(engine.tripleToN3(df.fact, outPrefixes));
|
|
328
|
+
console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, outPrefixes) : engine.tripleToN3(df.fact, outPrefixes));
|
|
326
329
|
console.log();
|
|
327
330
|
} else {
|
|
328
|
-
console.log(engine.tripleToN3(df.fact, outPrefixes));
|
|
331
|
+
console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, outPrefixes) : engine.tripleToN3(df.fact, outPrefixes));
|
|
329
332
|
}
|
|
330
333
|
},
|
|
331
334
|
{ captureExplanations: engine.getProofCommentsEnabled(), prefixes: outPrefixes },
|
|
@@ -361,7 +364,24 @@ function main() {
|
|
|
361
364
|
return;
|
|
362
365
|
}
|
|
363
366
|
|
|
364
|
-
|
|
367
|
+
let bodyText = '';
|
|
368
|
+
if (rdfMode && !engine.getProofCommentsEnabled()) {
|
|
369
|
+
bodyText = outTriples.map((tr) => engine.tripleToRdfCompatible(tr, prefixes)).join('\n');
|
|
370
|
+
} else if (hasQueries && !engine.getProofCommentsEnabled()) {
|
|
371
|
+
// In log:query mode, when proof comments are disabled, pretty-print blank-node
|
|
372
|
+
// shaped outputs as Turtle property lists ("[ ... ] .") for readability.
|
|
373
|
+
bodyText = engine.prettyPrintQueryTriples(outTriples, prefixes);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let usedPrefixes = prefixes.prefixesUsedForOutput(outTriples);
|
|
377
|
+
if (rdfMode && bodyText) {
|
|
378
|
+
usedPrefixes = usedPrefixes.filter(([pfx]) => pfx === '' || bodyText.includes(pfx + ':'));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (rdfMode && bodyText.includes('<<(')) {
|
|
382
|
+
console.log('VERSION "1.2"');
|
|
383
|
+
console.log();
|
|
384
|
+
}
|
|
365
385
|
|
|
366
386
|
for (const [pfx, base] of usedPrefixes) {
|
|
367
387
|
if (pfx === '') console.log(`@prefix : <${base}> .`);
|
|
@@ -369,21 +389,18 @@ function main() {
|
|
|
369
389
|
}
|
|
370
390
|
if (outTriples.length && usedPrefixes.length) console.log();
|
|
371
391
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (hasQueries && !engine.getProofCommentsEnabled()) {
|
|
375
|
-
const s = engine.prettyPrintQueryTriples(outTriples, prefixes);
|
|
376
|
-
if (s) process.stdout.write(String(s).replace(/\s*$/g, '') + '\n');
|
|
392
|
+
if (bodyText) {
|
|
393
|
+
process.stdout.write(String(bodyText).replace(/\s*$/g, '') + '\n');
|
|
377
394
|
return;
|
|
378
395
|
}
|
|
379
396
|
|
|
380
397
|
for (const df of outDerived) {
|
|
381
398
|
if (engine.getProofCommentsEnabled()) {
|
|
382
399
|
engine.printExplanation(df, prefixes);
|
|
383
|
-
console.log(engine.tripleToN3(df.fact, prefixes));
|
|
400
|
+
console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, prefixes) : engine.tripleToN3(df.fact, prefixes));
|
|
384
401
|
console.log();
|
|
385
402
|
} else {
|
|
386
|
-
console.log(engine.tripleToN3(df.fact, prefixes));
|
|
403
|
+
console.log(rdfMode ? engine.tripleToRdfCompatible(df.fact, prefixes) : engine.tripleToN3(df.fact, prefixes));
|
|
387
404
|
}
|
|
388
405
|
}
|
|
389
406
|
}
|
package/lib/engine.js
CHANGED
|
@@ -61,7 +61,12 @@ const {
|
|
|
61
61
|
|
|
62
62
|
const { makeExplain } = require('./explain');
|
|
63
63
|
|
|
64
|
-
const {
|
|
64
|
+
const {
|
|
65
|
+
termToN3,
|
|
66
|
+
tripleToN3,
|
|
67
|
+
prettyPrintQueryTriples,
|
|
68
|
+
tripleToRdfCompatible,
|
|
69
|
+
} = require('./printing');
|
|
65
70
|
const {
|
|
66
71
|
getDataFactory,
|
|
67
72
|
internalTripleToRdfJsQuad,
|
|
@@ -3374,9 +3379,12 @@ function reasonStream(input, opts = {}) {
|
|
|
3374
3379
|
dataFactory = null,
|
|
3375
3380
|
skipUnsupportedRdfJs = false,
|
|
3376
3381
|
builtinModules = null,
|
|
3382
|
+
rdf = false,
|
|
3377
3383
|
} = opts;
|
|
3378
3384
|
|
|
3379
|
-
const
|
|
3385
|
+
const useRdfCompatibility = !!rdf;
|
|
3386
|
+
|
|
3387
|
+
const parsedSourceList = parseN3SourceList(input, { baseIri, rdf: useRdfCompatibility });
|
|
3380
3388
|
const parsedInput = parsedSourceList || normalizeParsedReasonerInputSync(input);
|
|
3381
3389
|
const rdfFactory = rdfjs ? getDataFactory(dataFactory) : null;
|
|
3382
3390
|
|
|
@@ -3412,7 +3420,7 @@ function reasonStream(input, opts = {}) {
|
|
|
3412
3420
|
if (baseIri) prefixes.setBase(baseIri);
|
|
3413
3421
|
} else {
|
|
3414
3422
|
const n3Text = normalizeReasonerInputSync(input);
|
|
3415
|
-
const toks = lex(n3Text);
|
|
3423
|
+
const toks = lex(n3Text, { rdf: useRdfCompatibility });
|
|
3416
3424
|
const parser = new Parser(toks);
|
|
3417
3425
|
if (baseIri) parser.prefixes.setBase(baseIri);
|
|
3418
3426
|
|
|
@@ -3446,7 +3454,7 @@ function reasonStream(input, opts = {}) {
|
|
|
3446
3454
|
// query-selected triples (not all derived facts).
|
|
3447
3455
|
if (typeof onDerived === 'function') {
|
|
3448
3456
|
for (const qdf of queryDerived) {
|
|
3449
|
-
const payload = { triple: tripleToN3(qdf.fact, prefixes), df: qdf };
|
|
3457
|
+
const payload = { triple: useRdfCompatibility ? tripleToRdfCompatible(qdf.fact, prefixes) : tripleToN3(qdf.fact, prefixes), df: qdf };
|
|
3450
3458
|
if (rdfFactory) {
|
|
3451
3459
|
const quad = maybeTripleToRdfJsQuad(qdf.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
3452
3460
|
if (quad) payload.quad = quad;
|
|
@@ -3463,7 +3471,7 @@ function reasonStream(input, opts = {}) {
|
|
|
3463
3471
|
(df) => {
|
|
3464
3472
|
if (typeof onDerived === 'function') {
|
|
3465
3473
|
const payload = {
|
|
3466
|
-
triple: tripleToN3(df.fact, prefixes),
|
|
3474
|
+
triple: useRdfCompatibility ? tripleToRdfCompatible(df.fact, prefixes) : tripleToN3(df.fact, prefixes),
|
|
3467
3475
|
df,
|
|
3468
3476
|
};
|
|
3469
3477
|
if (rdfFactory) {
|
|
@@ -3484,8 +3492,9 @@ function reasonStream(input, opts = {}) {
|
|
|
3484
3492
|
? facts
|
|
3485
3493
|
: derived.map((d) => d.fact);
|
|
3486
3494
|
|
|
3487
|
-
const closureN3 =
|
|
3488
|
-
|
|
3495
|
+
const closureN3 = useRdfCompatibility
|
|
3496
|
+
? closureTriples.map((t) => tripleToRdfCompatible(t, prefixes)).join('\n')
|
|
3497
|
+
: Array.isArray(logQueryRules) && logQueryRules.length && !proof
|
|
3489
3498
|
? prettyPrintQueryTriples(closureTriples, prefixes)
|
|
3490
3499
|
: closureTriples.map((t) => tripleToN3(t, prefixes)).join('\n');
|
|
3491
3500
|
|
|
@@ -3626,6 +3635,7 @@ module.exports = {
|
|
|
3626
3635
|
printExplanation,
|
|
3627
3636
|
// used by demo worker to stringify derived triples with prefixes
|
|
3628
3637
|
tripleToN3,
|
|
3638
|
+
tripleToRdfCompatible,
|
|
3629
3639
|
// pretty log:query output (when proof comments are disabled)
|
|
3630
3640
|
prettyPrintQueryTriples,
|
|
3631
3641
|
getEnforceHttpsEnabled,
|
package/lib/lexer.js
CHANGED
|
@@ -347,7 +347,300 @@ function stripQuotes(lex) {
|
|
|
347
347
|
return lex;
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
|
|
350
|
+
|
|
351
|
+
// RDF/TriG compatibility is an opt-in syntax-normalization layer, not a new
|
|
352
|
+
// reasoning model. Eyeling remains strict N3 by default and N3 internally:
|
|
353
|
+
// - RDF 1.2 triple terms <<( s p o )>> become singleton graph terms { s p o }.
|
|
354
|
+
// - TriG named graph blocks g { ... } become g log:nameOf { ... } .
|
|
355
|
+
// - A top-level default graph block { ... } is unwrapped into ordinary triples.
|
|
356
|
+
// This mirrors tools/n3gen.js and keeps all downstream parsing/reasoning N3-only.
|
|
357
|
+
const LOG_NAME_OF_IRI = '<http://www.w3.org/2000/10/swap/log#nameOf>';
|
|
358
|
+
|
|
359
|
+
function normalizeRdfCompatibility(inputText) {
|
|
360
|
+
let text = String(inputText ?? '');
|
|
361
|
+
|
|
362
|
+
// Fast path: most Eyeling inputs are ordinary N3 and do not need RDF/TriG
|
|
363
|
+
// surface-syntax normalization. Avoid scanning large files character-by-character
|
|
364
|
+
// unless they actually contain RDF 1.2 triple terms, VERSION directives, or a
|
|
365
|
+
// plausible top-level TriG named graph block.
|
|
366
|
+
const hasTripleTerms = text.includes('<<(');
|
|
367
|
+
const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/im.test(text);
|
|
368
|
+
const hasNamedGraphCandidate = /(?:^|[.\r\n])\s*(?:GRAPH\s+)?(?:<[^>\r\n]*>|_:[A-Za-z][A-Za-z0-9_-]*|[A-Za-z][A-Za-z0-9_-]*:[^\s{};,.()[\]]*)\s*\{/m.test(text);
|
|
369
|
+
|
|
370
|
+
if (!hasTripleTerms && !hasVersionDirective && !hasNamedGraphCandidate) return text;
|
|
371
|
+
|
|
372
|
+
function isWordChar(ch) {
|
|
373
|
+
return ch != null && /[A-Za-z0-9_:-]/.test(ch);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function startsWordAt(s, word, at) {
|
|
377
|
+
return s.startsWith(word, at) && !isWordChar(s[at - 1]) && !isWordChar(s[at + word.length]);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function readStringAt(s, at) {
|
|
381
|
+
const quote = s[at];
|
|
382
|
+
let i = at;
|
|
383
|
+
let out = quote;
|
|
384
|
+
const long = s.startsWith(quote.repeat(3), i);
|
|
385
|
+
if (long) {
|
|
386
|
+
out = quote.repeat(3);
|
|
387
|
+
i += 3;
|
|
388
|
+
while (i < s.length) {
|
|
389
|
+
if (s.startsWith(quote.repeat(3), i)) {
|
|
390
|
+
out += quote.repeat(3);
|
|
391
|
+
i += 3;
|
|
392
|
+
return { text: out, end: i };
|
|
393
|
+
}
|
|
394
|
+
if (s[i] === '\\' && i + 1 < s.length) {
|
|
395
|
+
out += s.slice(i, i + 2);
|
|
396
|
+
i += 2;
|
|
397
|
+
} else {
|
|
398
|
+
out += s[i++];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return { text: out, end: i };
|
|
402
|
+
}
|
|
403
|
+
i += 1;
|
|
404
|
+
let escaped = false;
|
|
405
|
+
while (i < s.length) {
|
|
406
|
+
const ch = s[i++];
|
|
407
|
+
out += ch;
|
|
408
|
+
if (escaped) {
|
|
409
|
+
escaped = false;
|
|
410
|
+
} else if (ch === '\\') {
|
|
411
|
+
escaped = true;
|
|
412
|
+
} else if (ch === quote) {
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return { text: out, end: i };
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function readIriAt(s, at) {
|
|
420
|
+
let i = at + 1;
|
|
421
|
+
let out = '<';
|
|
422
|
+
while (i < s.length) {
|
|
423
|
+
const ch = s[i++];
|
|
424
|
+
out += ch;
|
|
425
|
+
if (ch === '>') break;
|
|
426
|
+
}
|
|
427
|
+
return { text: out, end: i };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function convertTripleTerms(s) {
|
|
431
|
+
let i = 0;
|
|
432
|
+
|
|
433
|
+
function startsAt(needle, at = i) {
|
|
434
|
+
return s.startsWith(needle, at);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function convertUntil(stopToken) {
|
|
438
|
+
let out = '';
|
|
439
|
+
while (i < s.length) {
|
|
440
|
+
if (stopToken && startsAt(stopToken)) {
|
|
441
|
+
i += stopToken.length;
|
|
442
|
+
return out;
|
|
443
|
+
}
|
|
444
|
+
if (startsAt('<<(')) {
|
|
445
|
+
i += 3;
|
|
446
|
+
out += '{ ' + convertUntil(')>>').trim() + ' }';
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const ch = s[i];
|
|
450
|
+
if (ch === '"' || ch === "'") {
|
|
451
|
+
const str = readStringAt(s, i);
|
|
452
|
+
out += str.text;
|
|
453
|
+
i = str.end;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (ch === '<') {
|
|
457
|
+
const iri = readIriAt(s, i);
|
|
458
|
+
out += iri.text;
|
|
459
|
+
i = iri.end;
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (ch === '#') {
|
|
463
|
+
while (i < s.length) {
|
|
464
|
+
const c = s[i++];
|
|
465
|
+
out += c;
|
|
466
|
+
if (c === '\n' || c === '\r') break;
|
|
467
|
+
}
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
out += ch;
|
|
471
|
+
i += 1;
|
|
472
|
+
}
|
|
473
|
+
if (stopToken) throw new N3SyntaxError(`Unterminated RDF 1.2 triple term, expected ${stopToken}`);
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return convertUntil(null);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function stripVersionDirectives(s) {
|
|
481
|
+
return s.replace(/^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/gim, '');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function skipWsAndComments(s, at) {
|
|
485
|
+
let i = at;
|
|
486
|
+
while (i < s.length) {
|
|
487
|
+
if (/\s/.test(s[i])) {
|
|
488
|
+
i += 1;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (s[i] === '#') {
|
|
492
|
+
while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
return i;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function readTermAt(s, at) {
|
|
501
|
+
if (s[at] === '<') return readIriAt(s, at);
|
|
502
|
+
let i = at;
|
|
503
|
+
while (i < s.length && !/\s/.test(s[i]) && !'{}[](),;.'.includes(s[i])) i += 1;
|
|
504
|
+
if (i === at) return null;
|
|
505
|
+
const value = s.slice(at, i);
|
|
506
|
+
if (!value || value.startsWith('@')) return null;
|
|
507
|
+
return { text: value, end: i };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function readBalancedBlock(s, at) {
|
|
511
|
+
if (s[at] !== '{') return null;
|
|
512
|
+
let i = at;
|
|
513
|
+
let depth = 0;
|
|
514
|
+
while (i < s.length) {
|
|
515
|
+
const ch = s[i];
|
|
516
|
+
if (ch === '"' || ch === "'") {
|
|
517
|
+
i = readStringAt(s, i).end;
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
if (ch === '<') {
|
|
521
|
+
i = readIriAt(s, i).end;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (ch === '#') {
|
|
525
|
+
while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
if (ch === '{') depth += 1;
|
|
529
|
+
if (ch === '}') {
|
|
530
|
+
depth -= 1;
|
|
531
|
+
i += 1;
|
|
532
|
+
if (depth === 0) return { text: s.slice(at, i), inner: s.slice(at + 1, i - 1), end: i };
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
i += 1;
|
|
536
|
+
}
|
|
537
|
+
throw new N3SyntaxError('Unterminated RDF/TriG graph block, expected }');
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function normalizeNamedGraphs(s) {
|
|
541
|
+
let out = '';
|
|
542
|
+
let i = 0;
|
|
543
|
+
let statementStart = true;
|
|
544
|
+
let braceDepth = 0;
|
|
545
|
+
|
|
546
|
+
while (i < s.length) {
|
|
547
|
+
if (statementStart && braceDepth === 0) {
|
|
548
|
+
const termStart = skipWsAndComments(s, i);
|
|
549
|
+
out += s.slice(i, termStart);
|
|
550
|
+
i = termStart;
|
|
551
|
+
|
|
552
|
+
// Top-level TriG default graph block: { ... } .
|
|
553
|
+
if (s[i] === '{') {
|
|
554
|
+
const block = readBalancedBlock(s, i);
|
|
555
|
+
const after = skipWsAndComments(s, block.end);
|
|
556
|
+
if (after >= s.length || s[after] === '.') {
|
|
557
|
+
out += block.inner.trim();
|
|
558
|
+
if (block.inner.trim() && !/\n$/.test(block.inner)) out += '\n';
|
|
559
|
+
i = after < s.length && s[after] === '.' ? after + 1 : after;
|
|
560
|
+
statementStart = true;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
// It is an ordinary N3 formula subject, not a TriG default graph.
|
|
564
|
+
out += block.text;
|
|
565
|
+
i = block.end;
|
|
566
|
+
statementStart = false;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
let graphKeyword = false;
|
|
571
|
+
if (startsWordAt(s, 'GRAPH', i)) {
|
|
572
|
+
graphKeyword = true;
|
|
573
|
+
i += 'GRAPH'.length;
|
|
574
|
+
i = skipWsAndComments(s, i);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const term = readTermAt(s, i);
|
|
578
|
+
if (term && !['@prefix', '@base', 'PREFIX', 'BASE', 'VERSION'].includes(term.text)) {
|
|
579
|
+
const afterTerm = skipWsAndComments(s, term.end);
|
|
580
|
+
if (s[afterTerm] === '{') {
|
|
581
|
+
const block = readBalancedBlock(s, afterTerm);
|
|
582
|
+
const afterBlock = skipWsAndComments(s, block.end);
|
|
583
|
+
out += `${term.text} ${LOG_NAME_OF_IRI} ${block.text} .`;
|
|
584
|
+
i = afterBlock < s.length && s[afterBlock] === '.' ? afterBlock + 1 : block.end;
|
|
585
|
+
statementStart = true;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Not TriG named-graph syntax after all; copy the first character and
|
|
591
|
+
// continue as ordinary N3.
|
|
592
|
+
if (graphKeyword) {
|
|
593
|
+
out += 'GRAPH ';
|
|
594
|
+
statementStart = false;
|
|
595
|
+
} else if (i < s.length) {
|
|
596
|
+
const copied = s[i++];
|
|
597
|
+
out += copied;
|
|
598
|
+
if (!/\s/.test(copied)) statementStart = false;
|
|
599
|
+
}
|
|
600
|
+
} else {
|
|
601
|
+
const ch = s[i];
|
|
602
|
+
out += ch;
|
|
603
|
+
if (ch === '"' || ch === "'") {
|
|
604
|
+
const str = readStringAt(s, i);
|
|
605
|
+
out = out.slice(0, -1) + str.text;
|
|
606
|
+
i = str.end;
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
if (ch === '<') {
|
|
610
|
+
const iri = readIriAt(s, i);
|
|
611
|
+
out = out.slice(0, -1) + iri.text;
|
|
612
|
+
i = iri.end;
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (ch === '#') {
|
|
616
|
+
i += 1;
|
|
617
|
+
while (i < s.length) {
|
|
618
|
+
const c = s[i++];
|
|
619
|
+
out += c;
|
|
620
|
+
if (c === '\n' || c === '\r') break;
|
|
621
|
+
}
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
if (ch === '{') braceDepth += 1;
|
|
625
|
+
else if (ch === '}' && braceDepth > 0) braceDepth -= 1;
|
|
626
|
+
else if (ch === '.' && braceDepth === 0) statementStart = true;
|
|
627
|
+
else if (!/\s/.test(ch)) statementStart = false;
|
|
628
|
+
i += 1;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
return out;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (hasTripleTerms) text = convertTripleTerms(text);
|
|
636
|
+
if (hasVersionDirective) text = stripVersionDirectives(text);
|
|
637
|
+
if (hasVersionDirective || hasNamedGraphCandidate) text = normalizeNamedGraphs(text);
|
|
638
|
+
return text;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function lex(inputText, opts = {}) {
|
|
642
|
+
const rdf = !!(opts && opts.rdf);
|
|
643
|
+
if (rdf) inputText = normalizeRdfCompatibility(inputText);
|
|
351
644
|
const chars = Array.from(inputText);
|
|
352
645
|
const n = chars.length;
|
|
353
646
|
let i = 0;
|
|
@@ -808,4 +1101,4 @@ function lex(inputText) {
|
|
|
808
1101
|
return tokens;
|
|
809
1102
|
}
|
|
810
1103
|
|
|
811
|
-
module.exports = { Token, N3SyntaxError, lex, decodeN3StringEscapes };
|
|
1104
|
+
module.exports = { Token, N3SyntaxError, lex, normalizeRdfCompatibility, decodeN3StringEscapes };
|
package/lib/multisource.js
CHANGED
|
@@ -103,8 +103,14 @@ function prefixesUsedInTokens(tokens, prefEnv) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function parseN3Text(text, opts = {}) {
|
|
106
|
-
const {
|
|
107
|
-
|
|
106
|
+
const {
|
|
107
|
+
baseIri = '',
|
|
108
|
+
label = '<input>',
|
|
109
|
+
keepSourceArtifacts = true,
|
|
110
|
+
collectUsedPrefixes = false,
|
|
111
|
+
rdf = false,
|
|
112
|
+
} = opts || {};
|
|
113
|
+
const tokens = lex(text, { rdf });
|
|
108
114
|
const parser = new Parser(tokens);
|
|
109
115
|
if (baseIri) parser.prefixes.setBase(baseIri);
|
|
110
116
|
const [prefixes, triples, frules, brules, logQueryRules] = parser.parseDocument();
|
|
@@ -298,6 +304,7 @@ function parseN3SourceList(input, opts = {}) {
|
|
|
298
304
|
baseIri: source.baseIri || (sources.length === 1 ? defaultBaseIri : ''),
|
|
299
305
|
collectUsedPrefixes: true,
|
|
300
306
|
keepSourceArtifacts: !!opts.keepSourceArtifacts,
|
|
307
|
+
rdf: !!opts.rdf,
|
|
301
308
|
}),
|
|
302
309
|
);
|
|
303
310
|
return mergeParsedDocuments(parsed, {
|
package/lib/printing.js
CHANGED
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
isOwlSameAsPred,
|
|
22
22
|
isLogImplies,
|
|
23
23
|
isLogImpliedBy,
|
|
24
|
+
LOG_NS,
|
|
24
25
|
} = require('./prelude');
|
|
25
26
|
|
|
26
27
|
function stripQuotes(lex) {
|
|
@@ -350,4 +351,108 @@ function prettyPrintQueryTriples(triples, prefixes) {
|
|
|
350
351
|
return blocks.join('\n');
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
|
|
354
|
+
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
356
|
+
// RDF compatibility output
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
|
|
359
|
+
function isRdfTripleTermSubject(t) {
|
|
360
|
+
return t instanceof Iri || t instanceof Blank;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function isRdfTripleTermPredicate(t) {
|
|
364
|
+
return t instanceof Iri;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function isRdfTripleTermObject(t) {
|
|
368
|
+
return t instanceof Iri || t instanceof Blank || t instanceof Literal;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function isRdfTripleTermGraph(t) {
|
|
372
|
+
if (!(t instanceof GraphTerm)) return false;
|
|
373
|
+
if (!Array.isArray(t.triples) || t.triples.length !== 1) return false;
|
|
374
|
+
const tr = t.triples[0];
|
|
375
|
+
return isRdfTripleTermSubject(tr.s) && isRdfTripleTermPredicate(tr.p) && isRdfTripleTermObject(tr.o);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
function isLogNameOfPred(p) {
|
|
380
|
+
return p instanceof Iri && p.value === LOG_NS + 'nameOf';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function isRdfNamedGraphLabel(t) {
|
|
384
|
+
return t instanceof Iri || t instanceof Blank;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function rdfCompatibleGraphBlock(graph, prefixes) {
|
|
388
|
+
const indent = ' ';
|
|
389
|
+
const indentBlock = (str) =>
|
|
390
|
+
str
|
|
391
|
+
.split(/\r?\n/)
|
|
392
|
+
.map((ln) => (ln.length ? indent + ln : ln))
|
|
393
|
+
.join('\n');
|
|
394
|
+
|
|
395
|
+
let s = '{\n';
|
|
396
|
+
for (const inner of graph.triples || []) {
|
|
397
|
+
const block = tripleToRdfCompatible(inner, prefixes).trimEnd();
|
|
398
|
+
if (block) s += indentBlock(block) + '\n';
|
|
399
|
+
}
|
|
400
|
+
s += '}';
|
|
401
|
+
return s;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function rdfPredicateToText(p, prefixes) {
|
|
405
|
+
return isRdfTypePred(p) ? 'a' : termToN3(p, prefixes);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function termToRdfCompatible(t, pref) {
|
|
409
|
+
if (isRdfTripleTermGraph(t)) {
|
|
410
|
+
const tr = t.triples[0];
|
|
411
|
+
const s = termToN3(tr.s, pref);
|
|
412
|
+
const p = rdfPredicateToText(tr.p, pref);
|
|
413
|
+
const o = termToN3(tr.o, pref);
|
|
414
|
+
return `<<( ${s} ${p} ${o} )>>`;
|
|
415
|
+
}
|
|
416
|
+
return termToN3(t, pref);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function tripleToRdfCompatible(tr, prefixes) {
|
|
420
|
+
if (isLogNameOfPred(tr.p) && isRdfNamedGraphLabel(tr.s) && tr.o instanceof GraphTerm) {
|
|
421
|
+
return `${termToN3(tr.s, prefixes)} ${rdfCompatibleGraphBlock(tr.o, prefixes)}`;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (isLogImplies(tr.p)) {
|
|
425
|
+
const s = termToRdfCompatible(tr.s, prefixes);
|
|
426
|
+
const o = termToRdfCompatible(tr.o, prefixes);
|
|
427
|
+
return `${s} => ${o} .`;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (isLogImpliedBy(tr.p)) {
|
|
431
|
+
const s = termToRdfCompatible(tr.s, prefixes);
|
|
432
|
+
const o = termToRdfCompatible(tr.o, prefixes);
|
|
433
|
+
return `${s} <= ${o} .`;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const s = termToRdfCompatible(tr.s, prefixes);
|
|
437
|
+
const p = isRdfTypePred(tr.p) ? 'a' : isOwlSameAsPred(tr.p) ? '=' : termToN3(tr.p, prefixes);
|
|
438
|
+
const o = termToRdfCompatible(tr.o, prefixes);
|
|
439
|
+
return `${s} ${p} ${o} .`;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function prettyPrintQueryTriplesRdfCompatible(triples, prefixes) {
|
|
443
|
+
return triples.map((tr) => tripleToRdfCompatible(tr, prefixes)).join('\n');
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function needsRdf12Version(text) {
|
|
447
|
+
return typeof text === 'string' && text.includes('<<(');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
module.exports = {
|
|
451
|
+
termToN3,
|
|
452
|
+
tripleToN3,
|
|
453
|
+
prettyPrintQueryTriples,
|
|
454
|
+
termToRdfCompatible,
|
|
455
|
+
tripleToRdfCompatible,
|
|
456
|
+
prettyPrintQueryTriplesRdfCompatible,
|
|
457
|
+
needsRdf12Version,
|
|
458
|
+
};
|
package/package.json
CHANGED
package/see/README.md
CHANGED
|
@@ -18,6 +18,8 @@ Each example starts from a Notation3 source and is compiled by `see.js` into a s
|
|
|
18
18
|
|
|
19
19
|
The trust gate is executable verification. If a required fact is missing, the program fails instead of emitting an unsupported entailment.
|
|
20
20
|
|
|
21
|
+
The `triple_terms` and `rdf_dataset` examples use RDF 1.2 `<<( ... )>>` triple-term syntax. Eyeling accepts that syntax only with `-r, --rdf`, where it normalizes triple terms to existing N3 singleton graph terms `{ ... }` and prints feasible output graph terms back as RDF 1.2 triple terms. The `rdf_dataset` example also uses an RDF/TriG named graph block, which RDF compatibility mode normalizes to the same `log:nameOf { ... }` graph-term shape emitted by `tools/n3gen` and prints back as a TriG named graph block where feasible; SEE keeps the committed `.trig` input and formal output in RDF 1.2/TriG form.
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
## Run
|
|
23
25
|
|
|
@@ -136,10 +138,12 @@ The repository currently contains **50 N3-compiled SEE examples**. Each example
|
|
|
136
138
|
| <a id="example-odrl-dpv-risk-ranking"></a>[ODRL + DPV risk ranking](#example-odrl-dpv-risk-ranking) | compiled from Notation3 by `see.js`; policy and data-protection evidence are ranked by risk level and selected through rule-derived comparisons. | [doc](examples/doc/odrl_dpv_risk_ranked.md), [js](examples/odrl_dpv_risk_ranked.js), [input](examples/input/odrl_dpv_risk_ranked.trig), [output](examples/output/odrl_dpv_risk_ranked.md), [n3](examples/n3/odrl_dpv_risk_ranked.n3) |
|
|
137
139
|
| <a id="example-path-discovery"></a>[Path discovery](#example-path-discovery) | compiled from the upstream path-discovery rules; the runner loads the full 96k-fact airroutes graph from TriG and evaluates the bounded route query with an indexed adjacency list. | [doc](examples/doc/path_discovery.md), [js](examples/path_discovery.js), [input](examples/input/path-discovery.trig), [output](examples/output/path_discovery.md), [n3](examples/n3/path_discovery.n3) |
|
|
138
140
|
| <a id="example-rc-discharge-envelope"></a>[RC discharge envelope](#example-rc-discharge-envelope) | compiled from Notation3 by `see.js`; exponentiation over an upper decay bound certifies the first sampled capacitor step below tolerance. | [doc](examples/doc/rc_discharge_envelope.md), [js](examples/rc_discharge_envelope.js), [input](examples/input/rc_discharge_envelope.trig), [output](examples/output/rc_discharge_envelope.md), [n3](examples/n3/rc_discharge_envelope.n3) |
|
|
141
|
+
| <a id="example-rdf-dataset-compatibility"></a>[RDF dataset compatibility](#example-rdf-dataset-compatibility) | compiled from Notation3 by `see.js`; RDF/TriG named graph input and RDF 1.2 triple terms demonstrate the same N3 graph-term normalization used by Eyeling's explicit RDF compatibility mode. | [doc](examples/doc/rdf_dataset.md), [js](examples/rdf_dataset.js), [input](examples/input/rdf_dataset.trig), [output](examples/output/rdf_dataset.md), [n3](examples/n3/rdf_dataset.n3) |
|
|
139
142
|
| <a id="example-rdf-message-flow"></a>[RDF Message Flow](#example-rdf-message-flow) | compiled from Notation3 by `see.js`; live RDF Messages advance through ingest, validate, interpret, route, and sink stages, with each completed message releasing the next one. | [doc](examples/doc/rdf_message_flow.md), [js](examples/rdf_message_flow.js), [input](examples/input/rdf_message_flow.trig), [output](examples/output/rdf_message_flow.md), [n3](examples/n3/rdf_message_flow.n3) |
|
|
140
143
|
| <a id="example-rdf-messages"></a>[RDF Messages](#example-rdf-messages) | compiled from Notation3 by `see.js`; ordered RDF Message Log example preserving explicit message boundaries, an empty heartbeat, message-local payloads, and reused blank-node labels. | [doc](examples/doc/rdf_messages.md), [js](examples/rdf_messages.js), [input](examples/input/rdf_messages.trig), [output](examples/output/rdf_messages.md), [n3](examples/n3/rdf_messages.n3) |
|
|
141
144
|
| <a id="example-school-placement-route-audit"></a>[School placement route audit](#example-school-placement-route-audit) | compiled from Notation3 by `see.js`; student, school, distance, and policy facts derive an auditable school-placement route decision. | [doc](examples/doc/school_placement_audit.md), [js](examples/school_placement_audit.js), [input](examples/input/school_placement_audit.trig), [output](examples/output/school_placement_audit.md), [n3](examples/n3/school_placement_audit.n3) |
|
|
142
145
|
| <a id="example-smoke-arithmetic"></a>[Smoke Arithmetic](#example-smoke-arithmetic) | compiled from Notation3 by `see.js`; arithmetic compiler smoke test deriving 6 × 7 through rules and builtins. | [doc](examples/doc/smoke_arithmetic.md), [js](examples/smoke_arithmetic.js), [input](examples/input/smoke_arithmetic.trig), [output](examples/output/smoke_arithmetic.md), [n3](examples/n3/smoke_arithmetic.n3) |
|
|
143
146
|
| <a id="example-socrates-inference"></a>[Socrates inference](#example-socrates-inference) | compiled from Notation3 by `see.js`; subclass closure with top-level `log:query` projection. | [doc](examples/doc/socrates.md), [js](examples/socrates.js), [input](examples/input/socrates.trig), [output](examples/output/socrates.md), [n3](examples/n3/socrates.n3) |
|
|
147
|
+
| <a id="example-triple-terms"></a>[Triple terms](#example-triple-terms) | compiled from Notation3 by `see.js`; RDF 1.2 TriG triple terms are used as input evidence and as a derived entailment. | [doc](examples/doc/triple_terms.md), [js](examples/triple_terms.js), [input](examples/input/triple_terms.trig), [output](examples/output/triple_terms.md), [n3](examples/n3/triple_terms.n3) |
|
|
144
148
|
| <a id="example-wind-turbine-envelope"></a>[Wind turbine envelope](#example-wind-turbine-envelope) | compiled from Notation3 by `see.js`; wind-speed samples are classified against cut-in, rated, and cut-out thresholds and accumulated into energy. | [doc](examples/doc/wind_turbine.md), [js](examples/wind_turbine.js), [input](examples/input/wind_turbine.trig), [output](examples/output/wind_turbine.md), [n3](examples/n3/wind_turbine.n3) |
|
|
145
149
|
| <a id="example-burn-the-witch"></a>[Burn the witch](#example-burn-the-witch) | compiled from Notation3 by `see.js`; rule-chain explanation for the witch entailment. | [doc](examples/doc/witch.md), [js](examples/witch.js), [input](examples/input/witch.trig), [output](examples/output/witch.md), [n3](examples/n3/witch.n3) |
|