eyeling 1.28.1 → 1.28.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/dist/browser/eyeling.browser.js +580 -5
- package/eyeling.js +580 -5
- package/lib/engine.js +12 -5
- package/lib/fast_rdf.js +451 -0
- package/lib/lexer.js +103 -0
- package/lib/multisource.js +11 -0
- package/notes/rdf-message-logs.md +13 -14
- package/notes/rdf12-roundtrip.md +4 -6
- package/package.json +1 -1
- package/test/api.test.js +57 -0
|
@@ -9796,11 +9796,18 @@ function reasonStream(input, opts = {}) {
|
|
|
9796
9796
|
if (baseIri) prefixes.setBase(baseIri);
|
|
9797
9797
|
} else {
|
|
9798
9798
|
const n3Text = normalizeReasonerInputSync(input);
|
|
9799
|
-
const
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9799
|
+
const directDoc = parseN3Text(n3Text, {
|
|
9800
|
+
baseIri: baseIri || '',
|
|
9801
|
+
label: sourceLabel || '<input>',
|
|
9802
|
+
keepSourceArtifacts: false,
|
|
9803
|
+
sourceLocations: proof,
|
|
9804
|
+
rdf: useRdfCompatibility,
|
|
9805
|
+
});
|
|
9806
|
+
prefixes = directDoc.prefixes;
|
|
9807
|
+
triples = directDoc.triples;
|
|
9808
|
+
frules = directDoc.frules;
|
|
9809
|
+
brules = directDoc.brules;
|
|
9810
|
+
logQueryRules = directDoc.logQueryRules;
|
|
9804
9811
|
}
|
|
9805
9812
|
// Make the parsed prefixes available to log:trace output
|
|
9806
9813
|
trace.setTracePrefixes(prefixes);
|
|
@@ -10621,6 +10628,460 @@ ${proofBody}
|
|
|
10621
10628
|
|
|
10622
10629
|
module.exports = { makeExplain };
|
|
10623
10630
|
|
|
10631
|
+
};
|
|
10632
|
+
__modules["lib/fast_rdf.js"] = function(require, module, exports){
|
|
10633
|
+
/**
|
|
10634
|
+
* Fast RDF compatibility parser for line-oriented RDF inputs.
|
|
10635
|
+
*
|
|
10636
|
+
* This is deliberately conservative. It directly builds Eyeling AST terms for
|
|
10637
|
+
* common RDF parser workloads (N-Triples-style Turtle, simple prefixed triples,
|
|
10638
|
+
* N-Quads, and RDF Message Logs) and returns null for richer N3/Turtle/TriG
|
|
10639
|
+
* constructs so the full lexer/parser remains the source of truth.
|
|
10640
|
+
*/
|
|
10641
|
+
|
|
10642
|
+
'use strict';
|
|
10643
|
+
|
|
10644
|
+
const { N3SyntaxError, decodeN3StringEscapes } = require('./lexer');
|
|
10645
|
+
const {
|
|
10646
|
+
RDF_NS,
|
|
10647
|
+
LOG_NS,
|
|
10648
|
+
XSD_NS,
|
|
10649
|
+
Blank,
|
|
10650
|
+
GraphTerm,
|
|
10651
|
+
ListTerm,
|
|
10652
|
+
Triple,
|
|
10653
|
+
PrefixEnv,
|
|
10654
|
+
internIri,
|
|
10655
|
+
internLiteral,
|
|
10656
|
+
resolveIriRef,
|
|
10657
|
+
annotateQuotedGraphTerm,
|
|
10658
|
+
} = require('./prelude');
|
|
10659
|
+
|
|
10660
|
+
const RDF_TYPE = internIri(RDF_NS + 'type');
|
|
10661
|
+
const LOG_NAME_OF = internIri(LOG_NS + 'nameOf');
|
|
10662
|
+
const XSD_INTEGER_IRI = XSD_NS + 'integer';
|
|
10663
|
+
const XSD_INTEGER_LITERAL_SUFFIX = `^^<${XSD_INTEGER_IRI}>`;
|
|
10664
|
+
|
|
10665
|
+
const EYMSG_NS = 'https://eyereasoner.github.io/eyeling/vocab/message#';
|
|
10666
|
+
const EYMSG_IRIS = Object.freeze({
|
|
10667
|
+
RDFMessageStream: internIri(`${EYMSG_NS}RDFMessageStream`),
|
|
10668
|
+
MessageEnvelope: internIri(`${EYMSG_NS}MessageEnvelope`),
|
|
10669
|
+
envelope: internIri(`${EYMSG_NS}envelope`),
|
|
10670
|
+
firstEnvelope: internIri(`${EYMSG_NS}firstEnvelope`),
|
|
10671
|
+
lastEnvelope: internIri(`${EYMSG_NS}lastEnvelope`),
|
|
10672
|
+
orderedEnvelopes: internIri(`${EYMSG_NS}orderedEnvelopes`),
|
|
10673
|
+
messageCount: internIri(`${EYMSG_NS}messageCount`),
|
|
10674
|
+
offset: internIri(`${EYMSG_NS}offset`),
|
|
10675
|
+
nextEnvelope: internIri(`${EYMSG_NS}nextEnvelope`),
|
|
10676
|
+
payloadGraph: internIri(`${EYMSG_NS}payloadGraph`),
|
|
10677
|
+
payloadKind: internIri(`${EYMSG_NS}payloadKind`),
|
|
10678
|
+
empty: internIri(`${EYMSG_NS}empty`),
|
|
10679
|
+
nonEmpty: internIri(`${EYMSG_NS}nonEmpty`),
|
|
10680
|
+
});
|
|
10681
|
+
|
|
10682
|
+
const VERSION_LINE_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)(?:-messages)?\1\s*\.?\s*(?:#.*)?$/im;
|
|
10683
|
+
const MESSAGE_VERSION_LINE_RE = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im;
|
|
10684
|
+
const MESSAGE_LINE_RE = /^\s*(?:MESSAGE\b|@message\s*\.?)\s*(?:#.*)?$/i;
|
|
10685
|
+
|
|
10686
|
+
function simpleHashText(s) {
|
|
10687
|
+
let h = 0x811c9dc5;
|
|
10688
|
+
for (let i = 0; i < s.length; i += 1) {
|
|
10689
|
+
h ^= s.charCodeAt(i);
|
|
10690
|
+
h = Math.imul(h, 0x01000193) >>> 0;
|
|
10691
|
+
}
|
|
10692
|
+
return h.toString(16).padStart(8, '0');
|
|
10693
|
+
}
|
|
10694
|
+
|
|
10695
|
+
function addOffset(obj, offset) {
|
|
10696
|
+
if (!obj || typeof offset !== 'number') return obj;
|
|
10697
|
+
Object.defineProperty(obj, '__sourceOffset', {
|
|
10698
|
+
value: offset,
|
|
10699
|
+
enumerable: false,
|
|
10700
|
+
writable: false,
|
|
10701
|
+
configurable: true,
|
|
10702
|
+
});
|
|
10703
|
+
return obj;
|
|
10704
|
+
}
|
|
10705
|
+
|
|
10706
|
+
function skipWs(s, i) {
|
|
10707
|
+
while (i < s.length) {
|
|
10708
|
+
const code = s.charCodeAt(i);
|
|
10709
|
+
if (code !== 0x20 && code !== 0x09 && code !== 0x0a && code !== 0x0d && code !== 0x0c) break;
|
|
10710
|
+
i += 1;
|
|
10711
|
+
}
|
|
10712
|
+
return i;
|
|
10713
|
+
}
|
|
10714
|
+
|
|
10715
|
+
function maybeDecodeIriRef(raw, offset, prefixes) {
|
|
10716
|
+
const iri = raw.includes('\\') ? decodeN3StringEscapes(raw, offset) : raw;
|
|
10717
|
+
if (!prefixes.baseIri || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(iri)) return iri;
|
|
10718
|
+
return resolveIriRef(iri, prefixes.baseIri || '');
|
|
10719
|
+
}
|
|
10720
|
+
|
|
10721
|
+
function readIriRef(s, i) {
|
|
10722
|
+
if (s[i] !== '<' || s[i + 1] === '<') return null;
|
|
10723
|
+
let j = i + 1;
|
|
10724
|
+
while (j < s.length) {
|
|
10725
|
+
const ch = s[j];
|
|
10726
|
+
if (ch === '\\') {
|
|
10727
|
+
j += 2;
|
|
10728
|
+
continue;
|
|
10729
|
+
}
|
|
10730
|
+
if (ch === '>') return { raw: s.slice(i + 1, j), end: j + 1 };
|
|
10731
|
+
j += 1;
|
|
10732
|
+
}
|
|
10733
|
+
throw new N3SyntaxError('Unterminated IRI <...>', i);
|
|
10734
|
+
}
|
|
10735
|
+
|
|
10736
|
+
function readQuoted(s, i) {
|
|
10737
|
+
const quote = s[i];
|
|
10738
|
+
if (quote !== '"' && quote !== "'") return null;
|
|
10739
|
+
const long = s.startsWith(quote.repeat(3), i);
|
|
10740
|
+
const start = i;
|
|
10741
|
+
if (long) {
|
|
10742
|
+
i += 3;
|
|
10743
|
+
const contentStart = i;
|
|
10744
|
+
let hasEscape = false;
|
|
10745
|
+
while (i < s.length) {
|
|
10746
|
+
if (s.startsWith(quote.repeat(3), i)) {
|
|
10747
|
+
return { text: s.slice(contentStart, i), end: i + 3, hasEscape };
|
|
10748
|
+
}
|
|
10749
|
+
const ch = s[i];
|
|
10750
|
+
if (ch === '\\') {
|
|
10751
|
+
hasEscape = true;
|
|
10752
|
+
i += 2;
|
|
10753
|
+
} else {
|
|
10754
|
+
i += 1;
|
|
10755
|
+
}
|
|
10756
|
+
}
|
|
10757
|
+
throw new N3SyntaxError(`Unterminated long string literal ${quote.repeat(3)}...${quote.repeat(3)}`, start);
|
|
10758
|
+
}
|
|
10759
|
+
|
|
10760
|
+
i += 1;
|
|
10761
|
+
const contentStart = i;
|
|
10762
|
+
let hasEscape = false;
|
|
10763
|
+
while (i < s.length) {
|
|
10764
|
+
const ch = s[i];
|
|
10765
|
+
if (ch === '\\') {
|
|
10766
|
+
hasEscape = true;
|
|
10767
|
+
i += 2;
|
|
10768
|
+
continue;
|
|
10769
|
+
}
|
|
10770
|
+
if (ch === quote) return { text: s.slice(contentStart, i), end: i + 1, hasEscape };
|
|
10771
|
+
i += 1;
|
|
10772
|
+
}
|
|
10773
|
+
throw new N3SyntaxError(`Unterminated string literal ${quote}...${quote}`, start);
|
|
10774
|
+
}
|
|
10775
|
+
|
|
10776
|
+
function readToken(s, i) {
|
|
10777
|
+
const start = i;
|
|
10778
|
+
while (i < s.length) {
|
|
10779
|
+
const ch = s[i];
|
|
10780
|
+
if (/\s/.test(ch) || ch === '.' || ch === ',' || ch === ';' || ch === '[' || ch === ']' || ch === '{' || ch === '}' || ch === '(' || ch === ')') break;
|
|
10781
|
+
i += 1;
|
|
10782
|
+
}
|
|
10783
|
+
if (i === start) return null;
|
|
10784
|
+
return { text: s.slice(start, i), end: i };
|
|
10785
|
+
}
|
|
10786
|
+
|
|
10787
|
+
function expandQNameToken(token, prefixes, usedPrefixes) {
|
|
10788
|
+
if (token === 'a') return RDF_NS + 'type';
|
|
10789
|
+
const sep = token.indexOf(':');
|
|
10790
|
+
if (sep < 0) return null;
|
|
10791
|
+
if (sep === 1 && token.charCodeAt(0) === 95) return null;
|
|
10792
|
+
const pfx = token.slice(0, sep);
|
|
10793
|
+
const local = token.slice(sep + 1);
|
|
10794
|
+
const base = prefixes.map[pfx] || '';
|
|
10795
|
+
if (!base && pfx !== '') return null;
|
|
10796
|
+
if (usedPrefixes && pfx) usedPrefixes.add(pfx);
|
|
10797
|
+
return base ? base + local : token;
|
|
10798
|
+
}
|
|
10799
|
+
|
|
10800
|
+
function parseIriOrQName(s, i, prefixes, usedPrefixes) {
|
|
10801
|
+
i = skipWs(s, i);
|
|
10802
|
+
const iri = readIriRef(s, i);
|
|
10803
|
+
if (iri) {
|
|
10804
|
+
return { term: internIri(maybeDecodeIriRef(iri.raw, i, prefixes)), end: iri.end };
|
|
10805
|
+
}
|
|
10806
|
+
const tok = readToken(s, i);
|
|
10807
|
+
if (!tok) return null;
|
|
10808
|
+
const expanded = expandQNameToken(tok.text, prefixes, usedPrefixes);
|
|
10809
|
+
if (expanded === null) return null;
|
|
10810
|
+
return { term: internIri(expanded), end: tok.end };
|
|
10811
|
+
}
|
|
10812
|
+
|
|
10813
|
+
function parseTerm(s, i, prefixes, usedPrefixes, blankPrefix = '') {
|
|
10814
|
+
i = skipWs(s, i);
|
|
10815
|
+
if (i >= s.length) return null;
|
|
10816
|
+
const ch = s[i];
|
|
10817
|
+
|
|
10818
|
+
if (ch === '<') return parseIriOrQName(s, i, prefixes, usedPrefixes);
|
|
10819
|
+
|
|
10820
|
+
if (ch === '_' && s[i + 1] === ':') {
|
|
10821
|
+
const tok = readToken(s, i);
|
|
10822
|
+
if (!tok) return null;
|
|
10823
|
+
let label = tok.text;
|
|
10824
|
+
if (blankPrefix) label = blankPrefix + label.slice(2).replace(/[^A-Za-z0-9_]/g, '_');
|
|
10825
|
+
return { term: new Blank(label), end: tok.end };
|
|
10826
|
+
}
|
|
10827
|
+
|
|
10828
|
+
if (ch === '"' || ch === "'") {
|
|
10829
|
+
const q = readQuoted(s, i);
|
|
10830
|
+
let end = q.end;
|
|
10831
|
+
const rawText = q.text;
|
|
10832
|
+
let value = q.hasEscape ? JSON.stringify(decodeN3StringEscapes(rawText, i)) : `"${rawText}"`;
|
|
10833
|
+
|
|
10834
|
+
if (s[end] === '@') {
|
|
10835
|
+
let j = end + 1;
|
|
10836
|
+
if (!/[A-Za-z]/.test(s[j] || '')) return null;
|
|
10837
|
+
while (j < s.length && /[A-Za-z0-9-]/.test(s[j])) j += 1;
|
|
10838
|
+
if (s[j] === '-' && s[j + 1] === '-') {
|
|
10839
|
+
const dir = s.slice(j + 2, j + 5);
|
|
10840
|
+
if ((dir === 'ltr' || dir === 'rtl') && !/[A-Za-z0-9-]/.test(s[j + 5] || '')) j += 5;
|
|
10841
|
+
}
|
|
10842
|
+
value += s.slice(end, j);
|
|
10843
|
+
end = j;
|
|
10844
|
+
} else if (s.startsWith('^^', end)) {
|
|
10845
|
+
const dt = parseIriOrQName(s, end + 2, prefixes, usedPrefixes);
|
|
10846
|
+
if (!dt || !(dt.term && typeof dt.term.value === 'string')) return null;
|
|
10847
|
+
value += `^^<${dt.term.value}>`;
|
|
10848
|
+
end = dt.end;
|
|
10849
|
+
}
|
|
10850
|
+
|
|
10851
|
+
return { term: internLiteral(value), end };
|
|
10852
|
+
}
|
|
10853
|
+
|
|
10854
|
+
if (ch === '(') {
|
|
10855
|
+
let pos = i + 1;
|
|
10856
|
+
const items = [];
|
|
10857
|
+
for (;;) {
|
|
10858
|
+
pos = skipWs(s, pos);
|
|
10859
|
+
if (pos >= s.length) throw new N3SyntaxError('Unterminated collection (...)', i);
|
|
10860
|
+
if (s[pos] === ')') return { term: new ListTerm(items), end: pos + 1 };
|
|
10861
|
+
const item = parseTerm(s, pos, prefixes, usedPrefixes, blankPrefix);
|
|
10862
|
+
if (!item) return null;
|
|
10863
|
+
items.push(item.term);
|
|
10864
|
+
pos = item.end;
|
|
10865
|
+
}
|
|
10866
|
+
}
|
|
10867
|
+
|
|
10868
|
+
if (ch === '[' || ch === '{') return null;
|
|
10869
|
+
|
|
10870
|
+
const tok = readToken(s, i);
|
|
10871
|
+
if (!tok) return null;
|
|
10872
|
+
const word = tok.text;
|
|
10873
|
+
if (word === 'true' || word === 'false' || /^-?(?:[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?|\.[0-9]+(?:[eE][+-]?[0-9]+)?)$/.test(word)) {
|
|
10874
|
+
return { term: internLiteral(word), end: tok.end };
|
|
10875
|
+
}
|
|
10876
|
+
|
|
10877
|
+
const expanded = expandQNameToken(word, prefixes, usedPrefixes);
|
|
10878
|
+
if (expanded !== null) return { term: internIri(expanded), end: tok.end };
|
|
10879
|
+
return null;
|
|
10880
|
+
}
|
|
10881
|
+
|
|
10882
|
+
function parseDirective(line, prefixes, usedPrefixes) {
|
|
10883
|
+
let m = /^\s*@prefix\s+([^\s]+)\s+<([^>]*)>\s*\.\s*(?:#.*)?$/i.exec(line);
|
|
10884
|
+
if (!m) m = /^\s*PREFIX\s+([^\s]+)\s+<([^>]*)>\s*\.?\s*(?:#.*)?$/i.exec(line);
|
|
10885
|
+
if (m) {
|
|
10886
|
+
const raw = m[1];
|
|
10887
|
+
if (!raw.endsWith(':')) throw new N3SyntaxError("Invalid @prefix directive: prefix name must end with ':'");
|
|
10888
|
+
const pfx = raw.slice(0, -1);
|
|
10889
|
+
prefixes.set(pfx, maybeDecodeIriRef(m[2], 0, prefixes));
|
|
10890
|
+
if (usedPrefixes && pfx) usedPrefixes.add(pfx);
|
|
10891
|
+
return true;
|
|
10892
|
+
}
|
|
10893
|
+
|
|
10894
|
+
m = /^\s*@base\s+<([^>]*)>\s*\.\s*(?:#.*)?$/i.exec(line);
|
|
10895
|
+
if (!m) m = /^\s*BASE\s+<([^>]*)>\s*\.?\s*(?:#.*)?$/i.exec(line);
|
|
10896
|
+
if (m) {
|
|
10897
|
+
prefixes.setBase(maybeDecodeIriRef(m[1], 0, prefixes));
|
|
10898
|
+
return true;
|
|
10899
|
+
}
|
|
10900
|
+
|
|
10901
|
+
return false;
|
|
10902
|
+
}
|
|
10903
|
+
|
|
10904
|
+
function parseFastStatement(line, prefixes, usedPrefixes, blankPrefix, sourceOffset = null) {
|
|
10905
|
+
let s = String(line || '');
|
|
10906
|
+
const leading = s.length - s.trimStart().length;
|
|
10907
|
+
s = s.slice(leading);
|
|
10908
|
+
if (!s || s[0] === '#') return [];
|
|
10909
|
+
if (VERSION_LINE_RE.test(s)) return [];
|
|
10910
|
+
if (MESSAGE_LINE_RE.test(s)) return '__MESSAGE__';
|
|
10911
|
+
if (parseDirective(s, prefixes, usedPrefixes)) return [];
|
|
10912
|
+
|
|
10913
|
+
let pos = 0;
|
|
10914
|
+
const subj = parseTerm(s, pos, prefixes, usedPrefixes, blankPrefix);
|
|
10915
|
+
if (!subj) return null;
|
|
10916
|
+
pos = subj.end;
|
|
10917
|
+
const pred = parseIriOrQName(s, pos, prefixes, usedPrefixes);
|
|
10918
|
+
if (!pred) return null;
|
|
10919
|
+
pos = pred.end;
|
|
10920
|
+
const obj = parseTerm(s, pos, prefixes, usedPrefixes, blankPrefix);
|
|
10921
|
+
if (!obj) return null;
|
|
10922
|
+
pos = skipWs(s, obj.end);
|
|
10923
|
+
|
|
10924
|
+
let graph = null;
|
|
10925
|
+
if (s[pos] !== '.') {
|
|
10926
|
+
const graphTerm = parseIriOrQName(s, pos, prefixes, usedPrefixes);
|
|
10927
|
+
if (!graphTerm) return null;
|
|
10928
|
+
graph = graphTerm.term;
|
|
10929
|
+
pos = skipWs(s, graphTerm.end);
|
|
10930
|
+
}
|
|
10931
|
+
if (s[pos] !== '.') return null;
|
|
10932
|
+
pos = skipWs(s, pos + 1);
|
|
10933
|
+
if (pos !== s.length && s[pos] !== '#') return null;
|
|
10934
|
+
|
|
10935
|
+
const triple = addOffset(new Triple(subj.term, pred.term, obj.term), typeof sourceOffset === 'number' ? sourceOffset + leading : sourceOffset);
|
|
10936
|
+
if (!graph) return [triple];
|
|
10937
|
+
return [addOffset(new Triple(graph, LOG_NAME_OF, annotateQuotedGraphTerm(new GraphTerm([triple]))), sourceOffset)];
|
|
10938
|
+
}
|
|
10939
|
+
|
|
10940
|
+
function lineIterator(text) {
|
|
10941
|
+
const s = String(text ?? '');
|
|
10942
|
+
let start = 0;
|
|
10943
|
+
return {
|
|
10944
|
+
*[Symbol.iterator]() {
|
|
10945
|
+
for (let i = 0; i <= s.length; i += 1) {
|
|
10946
|
+
if (i === s.length || s[i] === '\n' || s[i] === '\r') {
|
|
10947
|
+
const line = s.slice(start, i);
|
|
10948
|
+
const offset = start;
|
|
10949
|
+
if (i < s.length && s[i] === '\r' && s[i + 1] === '\n') i += 1;
|
|
10950
|
+
start = i + 1;
|
|
10951
|
+
yield { line, offset };
|
|
10952
|
+
}
|
|
10953
|
+
}
|
|
10954
|
+
},
|
|
10955
|
+
};
|
|
10956
|
+
}
|
|
10957
|
+
|
|
10958
|
+
function makeDoc(prefixes, triples, label, usedPrefixes) {
|
|
10959
|
+
const doc = {
|
|
10960
|
+
prefixes,
|
|
10961
|
+
triples,
|
|
10962
|
+
frules: [],
|
|
10963
|
+
brules: [],
|
|
10964
|
+
logQueryRules: [],
|
|
10965
|
+
label,
|
|
10966
|
+
};
|
|
10967
|
+
Object.defineProperty(doc, 'usedPrefixes', {
|
|
10968
|
+
value: usedPrefixes,
|
|
10969
|
+
enumerable: false,
|
|
10970
|
+
writable: false,
|
|
10971
|
+
configurable: true,
|
|
10972
|
+
});
|
|
10973
|
+
Object.defineProperty(doc, '__fastRdf', {
|
|
10974
|
+
value: true,
|
|
10975
|
+
enumerable: false,
|
|
10976
|
+
writable: false,
|
|
10977
|
+
configurable: true,
|
|
10978
|
+
});
|
|
10979
|
+
return doc;
|
|
10980
|
+
}
|
|
10981
|
+
|
|
10982
|
+
function parseLineOrAbort(line, prefixes, usedPrefixes, blankPrefix, sourceOffset) {
|
|
10983
|
+
const parsed = parseFastStatement(line, prefixes, usedPrefixes, blankPrefix, sourceOffset);
|
|
10984
|
+
if (parsed === null || parsed === '__MESSAGE__') return parsed;
|
|
10985
|
+
return parsed;
|
|
10986
|
+
}
|
|
10987
|
+
|
|
10988
|
+
function parseFastRdfText(text, opts = {}) {
|
|
10989
|
+
const source = String(text ?? '');
|
|
10990
|
+
// Try the fast line parser directly. It returns null on richer multi-line
|
|
10991
|
+
// Turtle/N3 constructs, so ordinary complex programs still fall back safely.
|
|
10992
|
+
const prefixes = PrefixEnv.newDefault();
|
|
10993
|
+
if (opts.baseIri) prefixes.setBase(opts.baseIri);
|
|
10994
|
+
const triples = [];
|
|
10995
|
+
const usedPrefixes = new Set();
|
|
10996
|
+
let sawUsefulLine = false;
|
|
10997
|
+
|
|
10998
|
+
for (const { line, offset } of lineIterator(source)) {
|
|
10999
|
+
const parsed = parseLineOrAbort(line, prefixes, usedPrefixes, '', offset);
|
|
11000
|
+
if (parsed === null || parsed === '__MESSAGE__') return null;
|
|
11001
|
+
if (parsed.length) sawUsefulLine = true;
|
|
11002
|
+
triples.push(...parsed);
|
|
11003
|
+
}
|
|
11004
|
+
|
|
11005
|
+
if (!sawUsefulLine && !/^\s*(?:@prefix|PREFIX|@base|BASE|@version|VERSION)\b/im.test(source)) return null;
|
|
11006
|
+
return makeDoc(prefixes, triples, opts.label || '<input>', usedPrefixes);
|
|
11007
|
+
}
|
|
11008
|
+
|
|
11009
|
+
function parseFastRdfMessageLog(text, opts = {}) {
|
|
11010
|
+
const source = String(text ?? '');
|
|
11011
|
+
if (!MESSAGE_VERSION_LINE_RE.test(source)) return null;
|
|
11012
|
+
|
|
11013
|
+
const prefixes = PrefixEnv.newDefault();
|
|
11014
|
+
if (opts.baseIri) prefixes.setBase(opts.baseIri);
|
|
11015
|
+
const usedPrefixes = new Set();
|
|
11016
|
+
const payloads = [[]];
|
|
11017
|
+
let current = payloads[0];
|
|
11018
|
+
let messageIndex = 1;
|
|
11019
|
+
let sawVersion = false;
|
|
11020
|
+
|
|
11021
|
+
for (const { line, offset } of lineIterator(source)) {
|
|
11022
|
+
const stripped = String(line || '').trimStart();
|
|
11023
|
+
if (!stripped || stripped[0] === '#') continue;
|
|
11024
|
+
if (MESSAGE_VERSION_LINE_RE.test(stripped)) {
|
|
11025
|
+
sawVersion = true;
|
|
11026
|
+
continue;
|
|
11027
|
+
}
|
|
11028
|
+
if (VERSION_LINE_RE.test(stripped)) continue;
|
|
11029
|
+
if (MESSAGE_LINE_RE.test(stripped)) {
|
|
11030
|
+
payloads.push([]);
|
|
11031
|
+
current = payloads[payloads.length - 1];
|
|
11032
|
+
messageIndex += 1;
|
|
11033
|
+
continue;
|
|
11034
|
+
}
|
|
11035
|
+
|
|
11036
|
+
const blankPrefix = `_:eyeling_m${String(messageIndex).padStart(3, '0')}_`;
|
|
11037
|
+
const parsed = parseLineOrAbort(line, prefixes, usedPrefixes, blankPrefix, offset);
|
|
11038
|
+
if (parsed === null || parsed === '__MESSAGE__') return null;
|
|
11039
|
+
current.push(...parsed);
|
|
11040
|
+
}
|
|
11041
|
+
|
|
11042
|
+
if (!sawVersion) return null;
|
|
11043
|
+
|
|
11044
|
+
const hash = simpleHashText(source);
|
|
11045
|
+
const base = `urn:eyeling:message-log:${hash}`;
|
|
11046
|
+
const stream = internIri(`${base}#stream`);
|
|
11047
|
+
const envelopes = payloads.map((unused, idx) => internIri(`${base}#m${String(idx + 1).padStart(3, '0')}`));
|
|
11048
|
+
const payloadIris = payloads.map((unused, idx) => internIri(`${base}#m${String(idx + 1).padStart(3, '0')}/payload`));
|
|
11049
|
+
const triples = [];
|
|
11050
|
+
|
|
11051
|
+
triples.push(new Triple(stream, RDF_TYPE, EYMSG_IRIS.RDFMessageStream));
|
|
11052
|
+
triples.push(new Triple(stream, EYMSG_IRIS.messageCount, internLiteral(`"${payloads.length}"${XSD_INTEGER_LITERAL_SUFFIX}`)));
|
|
11053
|
+
if (envelopes.length) {
|
|
11054
|
+
triples.push(new Triple(stream, EYMSG_IRIS.orderedEnvelopes, new ListTerm(envelopes)));
|
|
11055
|
+
triples.push(new Triple(stream, EYMSG_IRIS.firstEnvelope, envelopes[0]));
|
|
11056
|
+
triples.push(new Triple(stream, EYMSG_IRIS.lastEnvelope, envelopes[envelopes.length - 1]));
|
|
11057
|
+
}
|
|
11058
|
+
|
|
11059
|
+
for (let idx = 0; idx < payloads.length; idx += 1) {
|
|
11060
|
+
const envelope = envelopes[idx];
|
|
11061
|
+
const payload = payloadIris[idx];
|
|
11062
|
+
const bodyTriples = payloads[idx];
|
|
11063
|
+
const hasBody = bodyTriples.length > 0;
|
|
11064
|
+
|
|
11065
|
+
triples.push(new Triple(stream, EYMSG_IRIS.envelope, envelope));
|
|
11066
|
+
triples.push(new Triple(envelope, RDF_TYPE, EYMSG_IRIS.MessageEnvelope));
|
|
11067
|
+
triples.push(new Triple(envelope, EYMSG_IRIS.offset, internLiteral(`"${idx + 1}"${XSD_INTEGER_LITERAL_SUFFIX}`)));
|
|
11068
|
+
triples.push(new Triple(envelope, EYMSG_IRIS.payloadKind, hasBody ? EYMSG_IRIS.nonEmpty : EYMSG_IRIS.empty));
|
|
11069
|
+
if (idx + 1 < envelopes.length) triples.push(new Triple(envelope, EYMSG_IRIS.nextEnvelope, envelopes[idx + 1]));
|
|
11070
|
+
if (hasBody) {
|
|
11071
|
+
triples.push(new Triple(envelope, EYMSG_IRIS.payloadGraph, payload));
|
|
11072
|
+
triples.push(new Triple(payload, LOG_NAME_OF, annotateQuotedGraphTerm(new GraphTerm(bodyTriples))));
|
|
11073
|
+
}
|
|
11074
|
+
}
|
|
11075
|
+
|
|
11076
|
+
return makeDoc(prefixes, triples, opts.label || '<input>', usedPrefixes);
|
|
11077
|
+
}
|
|
11078
|
+
|
|
11079
|
+
function tryParseFastRdfText(text, opts = {}) {
|
|
11080
|
+
return parseFastRdfMessageLog(text, opts) || parseFastRdfText(text, opts);
|
|
11081
|
+
}
|
|
11082
|
+
|
|
11083
|
+
module.exports = { tryParseFastRdfText, parseFastRdfText, parseFastRdfMessageLog };
|
|
11084
|
+
|
|
10624
11085
|
};
|
|
10625
11086
|
__modules["lib/lexer.js"] = function(require, module, exports){
|
|
10626
11087
|
/**
|
|
@@ -11140,11 +11601,114 @@ function normalizeRdfCompatibility(inputText) {
|
|
|
11140
11601
|
return text.slice(at, j);
|
|
11141
11602
|
}
|
|
11142
11603
|
|
|
11604
|
+
function readBalancedTermAt(text, at) {
|
|
11605
|
+
const open = text[at];
|
|
11606
|
+
const matching = { '{': '}', '[': ']', '(': ')' };
|
|
11607
|
+
if (!matching[open]) return null;
|
|
11608
|
+
|
|
11609
|
+
const stack = [matching[open]];
|
|
11610
|
+
let j = at + 1;
|
|
11611
|
+
while (j < text.length) {
|
|
11612
|
+
const ch = text[j];
|
|
11613
|
+
if (ch === '"' || ch === "'") {
|
|
11614
|
+
const str = readStringAt(text, j);
|
|
11615
|
+
j = str.end;
|
|
11616
|
+
continue;
|
|
11617
|
+
}
|
|
11618
|
+
if (ch === '<' && !text.startsWith('<<', j)) {
|
|
11619
|
+
const iri = readIriAt(text, j);
|
|
11620
|
+
j = iri.end;
|
|
11621
|
+
continue;
|
|
11622
|
+
}
|
|
11623
|
+
if (ch === '#') {
|
|
11624
|
+
while (j < text.length && text[j] !== '\n' && text[j] !== '\r') j += 1;
|
|
11625
|
+
continue;
|
|
11626
|
+
}
|
|
11627
|
+
if (matching[ch]) {
|
|
11628
|
+
stack.push(matching[ch]);
|
|
11629
|
+
j += 1;
|
|
11630
|
+
continue;
|
|
11631
|
+
}
|
|
11632
|
+
if (ch === stack[stack.length - 1]) {
|
|
11633
|
+
stack.pop();
|
|
11634
|
+
j += 1;
|
|
11635
|
+
if (stack.length === 0) return { text: text.slice(at, j), end: j };
|
|
11636
|
+
continue;
|
|
11637
|
+
}
|
|
11638
|
+
j += 1;
|
|
11639
|
+
}
|
|
11640
|
+
|
|
11641
|
+
throw new N3SyntaxError(`Unterminated term inside RDF 1.2 triple term, expected ${stack[stack.length - 1]}`);
|
|
11642
|
+
}
|
|
11643
|
+
|
|
11644
|
+
function readRdfTripleTermComponent(text, at) {
|
|
11645
|
+
const j = skipWsAndComments(text, at);
|
|
11646
|
+
if (j >= text.length) return null;
|
|
11647
|
+
const ch = text[j];
|
|
11648
|
+
|
|
11649
|
+
if (ch === '<') return readIriAt(text, j);
|
|
11650
|
+
|
|
11651
|
+
if (ch === '"' || ch === "'") {
|
|
11652
|
+
const str = readStringAt(text, j);
|
|
11653
|
+
let end = str.end;
|
|
11654
|
+
let termText = str.text;
|
|
11655
|
+
if (text.startsWith('^^', end)) {
|
|
11656
|
+
const datatype = readRdfTripleTermComponent(text, end + 2);
|
|
11657
|
+
if (datatype) {
|
|
11658
|
+
termText += '^^' + datatype.text;
|
|
11659
|
+
end = datatype.end;
|
|
11660
|
+
}
|
|
11661
|
+
} else if (text[end] === '@') {
|
|
11662
|
+
let k = end + 1;
|
|
11663
|
+
if (/[A-Za-z]/.test(text[k] || '')) {
|
|
11664
|
+
while (k < text.length && /[A-Za-z0-9-]/.test(text[k])) k += 1;
|
|
11665
|
+
termText += text.slice(end, k);
|
|
11666
|
+
end = k;
|
|
11667
|
+
}
|
|
11668
|
+
}
|
|
11669
|
+
return { text: termText, end };
|
|
11670
|
+
}
|
|
11671
|
+
|
|
11672
|
+
if (ch === '{' || ch === '[' || ch === '(') return readBalancedTermAt(text, j);
|
|
11673
|
+
|
|
11674
|
+
let k = j;
|
|
11675
|
+
while (k < text.length && !/\s/.test(text[k]) && !'{}[](),;'.includes(text[k])) k += 1;
|
|
11676
|
+
if (k === j) return null;
|
|
11677
|
+
const value = text.slice(j, k);
|
|
11678
|
+
if (!value || value.startsWith('@')) return null;
|
|
11679
|
+
return { text: value, end: k };
|
|
11680
|
+
}
|
|
11681
|
+
|
|
11682
|
+
function validateSingleRdfTripleTerm(rawTriple) {
|
|
11683
|
+
const triple = rawTriple.trim();
|
|
11684
|
+
if (!triple) throw new N3SyntaxError('RDF 1.2 triple term must contain exactly one subject, predicate, and object');
|
|
11685
|
+
|
|
11686
|
+
let pos = 0;
|
|
11687
|
+
for (const label of ['subject', 'predicate', 'object']) {
|
|
11688
|
+
const term = readRdfTripleTermComponent(triple, pos);
|
|
11689
|
+
if (!term) throw new N3SyntaxError(`RDF 1.2 triple term is missing a ${label}`);
|
|
11690
|
+
pos = term.end;
|
|
11691
|
+
}
|
|
11692
|
+
|
|
11693
|
+
const rest = skipWsAndComments(triple, pos);
|
|
11694
|
+
if (rest >= triple.length) return;
|
|
11695
|
+
|
|
11696
|
+
const found = triple[rest];
|
|
11697
|
+
if (found === ',') {
|
|
11698
|
+
throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one object; object lists using ',' are not valid inside <<( ... )>>");
|
|
11699
|
+
}
|
|
11700
|
+
if (found === ';') {
|
|
11701
|
+
throw new N3SyntaxError("RDF 1.2 triple terms must contain exactly one predicate-object pair; ';' is not valid inside <<( ... )>>");
|
|
11702
|
+
}
|
|
11703
|
+
throw new N3SyntaxError(`RDF 1.2 triple term must contain exactly one subject, predicate, and object; unexpected ${JSON.stringify(triple.slice(rest, rest + 20))}`);
|
|
11704
|
+
}
|
|
11705
|
+
|
|
11143
11706
|
function graphTermFromTripleBody(rawBody, parenthesized) {
|
|
11144
11707
|
let body = rawBody.trim();
|
|
11145
11708
|
if (parenthesized && body.startsWith('(') && body.endsWith(')')) body = body.slice(1, -1).trim();
|
|
11146
11709
|
const split = splitTopLevelReifier(body);
|
|
11147
11710
|
const triple = split.triple;
|
|
11711
|
+
validateSingleRdfTripleTerm(triple);
|
|
11148
11712
|
const graph = '{ ' + triple + ' }';
|
|
11149
11713
|
if (split.reifier) {
|
|
11150
11714
|
const reifier = firstTerm(split.reifier);
|
|
@@ -12450,6 +13014,7 @@ module.exports = { Token, N3SyntaxError, lex, normalizeRdfCompatibility, decodeN
|
|
|
12450
13014
|
|
|
12451
13015
|
const { lex } = require('./lexer');
|
|
12452
13016
|
const { Parser } = require('./parser');
|
|
13017
|
+
const { tryParseFastRdfText } = require('./fast_rdf');
|
|
12453
13018
|
const {
|
|
12454
13019
|
Blank,
|
|
12455
13020
|
ListTerm,
|
|
@@ -12612,6 +13177,16 @@ function parseN3Text(text, opts = {}) {
|
|
|
12612
13177
|
sourceLocations = false,
|
|
12613
13178
|
rdf = false,
|
|
12614
13179
|
} = opts || {};
|
|
13180
|
+
|
|
13181
|
+
if (rdf) {
|
|
13182
|
+
const fastDoc = tryParseFastRdfText(text, { baseIri, label });
|
|
13183
|
+
if (fastDoc) {
|
|
13184
|
+
if (sourceLocations) annotateParsedSourceLocations(fastDoc, text, label);
|
|
13185
|
+
if (keepSourceArtifacts) fastDoc.text = text;
|
|
13186
|
+
return fastDoc;
|
|
13187
|
+
}
|
|
13188
|
+
}
|
|
13189
|
+
|
|
12615
13190
|
const tokens = lex(text, { rdf });
|
|
12616
13191
|
const parser = new Parser(tokens);
|
|
12617
13192
|
if (baseIri) parser.prefixes.setBase(baseIri);
|