@factoidal/core 0.1.0
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/CHANGELOG.md +121 -0
- package/LICENSE +201 -0
- package/README.md +514 -0
- package/browser-wasm.js +872 -0
- package/browser.d.ts +514 -0
- package/browser.js +2276 -0
- package/factoidal-npm-entry.js +32609 -0
- package/factoidal-npm-entry.wasm.assets/code-7ac046580f1bbdda8dc6.wasm +0 -0
- package/factoidal-npm-entry.wasm.js +455 -0
- package/factoidal.js +27560 -0
- package/factoidal.wasm.assets/code-bbe6099bfb5b10c4c3ab.wasm +0 -0
- package/factoidal.wasm.js +457 -0
- package/fn.d.ts +519 -0
- package/fn.js +916 -0
- package/hacl-init.js +92 -0
- package/hacl-wasm/FStar.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum25519_51.wasm +0 -0
- package/hacl-wasm/Hacl_Bignum_Base.wasm +0 -0
- package/hacl-wasm/Hacl_Curve25519_51.wasm +0 -0
- package/hacl-wasm/Hacl_Ed25519.wasm +0 -0
- package/hacl-wasm/Hacl_Ed25519_PrecompTable.wasm +0 -0
- package/hacl-wasm/Hacl_Hash_Base.wasm +0 -0
- package/hacl-wasm/Hacl_Hash_SHA2.wasm +0 -0
- package/hacl-wasm/Hacl_IntTypes_Intrinsics.wasm +0 -0
- package/hacl-wasm/LowStar_Endianness.wasm +0 -0
- package/hacl-wasm/WasmSupport.wasm +0 -0
- package/hacl-wasm/api.js +775 -0
- package/hacl-wasm/api.json +3787 -0
- package/hacl-wasm/layouts.json +1 -0
- package/hacl-wasm/loader.js +568 -0
- package/hacl-wasm/shell.js +12 -0
- package/index.d.ts +1068 -0
- package/index.js +237 -0
- package/index.mjs +85 -0
- package/lib/api.js +2140 -0
- package/lib/engine-js.js +165 -0
- package/lib/engine-wasm.js +300 -0
- package/package.json +101 -0
- package/rdfjs.js +540 -0
- package/version.json +101 -0
- package/wasm.d.ts +130 -0
- package/wasm.js +158 -0
package/rdfjs.js
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
// factoidal — RDF/JS data model + N-Quads token converters.
|
|
2
|
+
//
|
|
3
|
+
// Implements the RDF/JS Data Model specification
|
|
4
|
+
// (https://rdf.js.org/data-model-spec/): DataFactory producing
|
|
5
|
+
// NamedNode / BlankNode / Literal / Variable / DefaultGraph / Quad
|
|
6
|
+
// terms with spec-compliant `termType` / `value` / `language` /
|
|
7
|
+
// `datatype` and `.equals()`. Self-contained — no runtime deps.
|
|
8
|
+
//
|
|
9
|
+
// Also provides converters between RDF/JS quads and the engine's
|
|
10
|
+
// N-Quads token strings, which are the package's dataset interchange
|
|
11
|
+
// handle (the F*-extracted engine parses and serializes N-Quads; the
|
|
12
|
+
// converters below only tokenize/untokenize the engine's own output —
|
|
13
|
+
// full RDF parsing of user input always goes through the engine, per
|
|
14
|
+
// repo rule #4 "parsers belong in F*").
|
|
15
|
+
//
|
|
16
|
+
// Escaping mirrors the F* serializer RDF.NQuads.Serialize.escape_char
|
|
17
|
+
// exactly: \\ \" \n \r \t (and nothing else) are escaped on output;
|
|
18
|
+
// unescaping additionally accepts the full N-Triples ECHAR + UCHAR set
|
|
19
|
+
// (\t \b \n \r \f \" \' \\ \uXXXX \UXXXXXXXX) since engine input may
|
|
20
|
+
// legally contain them.
|
|
21
|
+
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string';
|
|
25
|
+
const RDF_LANGSTRING =
|
|
26
|
+
'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString';
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------
|
|
29
|
+
// Terms
|
|
30
|
+
// ---------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
class NamedNode {
|
|
33
|
+
constructor(iri) {
|
|
34
|
+
this.termType = 'NamedNode';
|
|
35
|
+
this.value = iri;
|
|
36
|
+
Object.freeze(this);
|
|
37
|
+
}
|
|
38
|
+
equals(other) {
|
|
39
|
+
return !!other && other.termType === 'NamedNode' &&
|
|
40
|
+
other.value === this.value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class BlankNode {
|
|
45
|
+
constructor(label) {
|
|
46
|
+
this.termType = 'BlankNode';
|
|
47
|
+
this.value = label;
|
|
48
|
+
Object.freeze(this);
|
|
49
|
+
}
|
|
50
|
+
equals(other) {
|
|
51
|
+
return !!other && other.termType === 'BlankNode' &&
|
|
52
|
+
other.value === this.value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class Literal {
|
|
57
|
+
constructor(value, language, datatype) {
|
|
58
|
+
this.termType = 'Literal';
|
|
59
|
+
this.value = value;
|
|
60
|
+
this.language = language || '';
|
|
61
|
+
this.datatype = datatype ||
|
|
62
|
+
new NamedNode(language ? RDF_LANGSTRING : XSD_STRING);
|
|
63
|
+
Object.freeze(this);
|
|
64
|
+
}
|
|
65
|
+
equals(other) {
|
|
66
|
+
return !!other && other.termType === 'Literal' &&
|
|
67
|
+
other.value === this.value &&
|
|
68
|
+
other.language === this.language &&
|
|
69
|
+
!!other.datatype && other.datatype.value === this.datatype.value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class Variable {
|
|
74
|
+
constructor(name) {
|
|
75
|
+
this.termType = 'Variable';
|
|
76
|
+
this.value = name;
|
|
77
|
+
Object.freeze(this);
|
|
78
|
+
}
|
|
79
|
+
equals(other) {
|
|
80
|
+
return !!other && other.termType === 'Variable' &&
|
|
81
|
+
other.value === this.value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class DefaultGraph {
|
|
86
|
+
constructor() {
|
|
87
|
+
this.termType = 'DefaultGraph';
|
|
88
|
+
this.value = '';
|
|
89
|
+
Object.freeze(this);
|
|
90
|
+
}
|
|
91
|
+
equals(other) {
|
|
92
|
+
return !!other && other.termType === 'DefaultGraph';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const DEFAULT_GRAPH = new DefaultGraph();
|
|
97
|
+
|
|
98
|
+
class Quad {
|
|
99
|
+
constructor(subject, predicate, object, graph) {
|
|
100
|
+
// Per spec, a Quad is itself a Term with termType 'Quad', value ''.
|
|
101
|
+
this.termType = 'Quad';
|
|
102
|
+
this.value = '';
|
|
103
|
+
this.subject = subject;
|
|
104
|
+
this.predicate = predicate;
|
|
105
|
+
this.object = object;
|
|
106
|
+
this.graph = graph || DEFAULT_GRAPH;
|
|
107
|
+
Object.freeze(this);
|
|
108
|
+
}
|
|
109
|
+
equals(other) {
|
|
110
|
+
return !!other &&
|
|
111
|
+
(other.termType === 'Quad' || other.termType === undefined) &&
|
|
112
|
+
this.subject.equals(other.subject) &&
|
|
113
|
+
this.predicate.equals(other.predicate) &&
|
|
114
|
+
this.object.equals(other.object) &&
|
|
115
|
+
this.graph.equals(other.graph);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------
|
|
120
|
+
// DataFactory
|
|
121
|
+
// ---------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
let blankNodeCounter = 0;
|
|
124
|
+
|
|
125
|
+
const dataFactory = {
|
|
126
|
+
namedNode(value) {
|
|
127
|
+
return new NamedNode(String(value));
|
|
128
|
+
},
|
|
129
|
+
blankNode(label) {
|
|
130
|
+
return new BlankNode(
|
|
131
|
+
label !== undefined && label !== null
|
|
132
|
+
? String(label)
|
|
133
|
+
: 'fjs_b' + (blankNodeCounter++)
|
|
134
|
+
);
|
|
135
|
+
},
|
|
136
|
+
literal(value, languageOrDatatype) {
|
|
137
|
+
const v = String(value);
|
|
138
|
+
if (languageOrDatatype === undefined || languageOrDatatype === null) {
|
|
139
|
+
return new Literal(v, '', null);
|
|
140
|
+
}
|
|
141
|
+
if (typeof languageOrDatatype === 'string') {
|
|
142
|
+
return new Literal(v, languageOrDatatype, null);
|
|
143
|
+
}
|
|
144
|
+
// A NamedNode datatype.
|
|
145
|
+
if (languageOrDatatype.termType === 'NamedNode') {
|
|
146
|
+
if (languageOrDatatype.value === RDF_LANGSTRING) {
|
|
147
|
+
// langString without a language tag is not constructible; treat
|
|
148
|
+
// as plain string per the most defensive reading of the spec.
|
|
149
|
+
return new Literal(v, '', null);
|
|
150
|
+
}
|
|
151
|
+
return new Literal(v, '', new NamedNode(languageOrDatatype.value));
|
|
152
|
+
}
|
|
153
|
+
throw new TypeError(
|
|
154
|
+
'literal: second argument must be a language string or a NamedNode'
|
|
155
|
+
);
|
|
156
|
+
},
|
|
157
|
+
variable(name) {
|
|
158
|
+
return new Variable(String(name));
|
|
159
|
+
},
|
|
160
|
+
defaultGraph() {
|
|
161
|
+
return DEFAULT_GRAPH;
|
|
162
|
+
},
|
|
163
|
+
quad(subject, predicate, object, graph) {
|
|
164
|
+
return new Quad(subject, predicate, object, graph);
|
|
165
|
+
},
|
|
166
|
+
fromTerm(original) {
|
|
167
|
+
if (!original || typeof original.termType !== 'string') {
|
|
168
|
+
throw new TypeError('fromTerm: not a term');
|
|
169
|
+
}
|
|
170
|
+
switch (original.termType) {
|
|
171
|
+
case 'NamedNode': return new NamedNode(original.value);
|
|
172
|
+
case 'BlankNode': return new BlankNode(original.value);
|
|
173
|
+
case 'Variable': return new Variable(original.value);
|
|
174
|
+
case 'DefaultGraph': return DEFAULT_GRAPH;
|
|
175
|
+
case 'Literal':
|
|
176
|
+
return new Literal(
|
|
177
|
+
original.value,
|
|
178
|
+
original.language || '',
|
|
179
|
+
original.language
|
|
180
|
+
? null
|
|
181
|
+
: new NamedNode(
|
|
182
|
+
(original.datatype && original.datatype.value) || XSD_STRING)
|
|
183
|
+
);
|
|
184
|
+
case 'Quad': return dataFactory.fromQuad(original);
|
|
185
|
+
default:
|
|
186
|
+
throw new TypeError(`fromTerm: unknown termType '${original.termType}'`);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
fromQuad(original) {
|
|
190
|
+
if (!original || !original.subject) {
|
|
191
|
+
throw new TypeError('fromQuad: not a quad');
|
|
192
|
+
}
|
|
193
|
+
return new Quad(
|
|
194
|
+
dataFactory.fromTerm(original.subject),
|
|
195
|
+
dataFactory.fromTerm(original.predicate),
|
|
196
|
+
dataFactory.fromTerm(original.object),
|
|
197
|
+
original.graph ? dataFactory.fromTerm(original.graph) : DEFAULT_GRAPH
|
|
198
|
+
);
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------
|
|
203
|
+
// SPARQL Results JSON term -> RDF/JS term
|
|
204
|
+
// ---------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Convert a SPARQL 1.1 Results JSON term object
|
|
208
|
+
* ({type:'uri'|'bnode'|'literal'|'typed-literal', value, 'xml:lang',
|
|
209
|
+
* datatype}) into an RDF/JS term.
|
|
210
|
+
*/
|
|
211
|
+
function termFromSrj(t) {
|
|
212
|
+
if (!t || typeof t.type !== 'string') {
|
|
213
|
+
throw new TypeError('termFromSrj: not a results-JSON term');
|
|
214
|
+
}
|
|
215
|
+
switch (t.type) {
|
|
216
|
+
case 'uri':
|
|
217
|
+
return dataFactory.namedNode(t.value);
|
|
218
|
+
case 'bnode':
|
|
219
|
+
return dataFactory.blankNode(t.value);
|
|
220
|
+
case 'literal':
|
|
221
|
+
case 'typed-literal': {
|
|
222
|
+
const lang = t['xml:lang'];
|
|
223
|
+
if (lang) return dataFactory.literal(t.value, lang);
|
|
224
|
+
if (t.datatype) {
|
|
225
|
+
return dataFactory.literal(t.value, dataFactory.namedNode(t.datatype));
|
|
226
|
+
}
|
|
227
|
+
return dataFactory.literal(t.value);
|
|
228
|
+
}
|
|
229
|
+
case 'triple': {
|
|
230
|
+
// RDF 1.2 triple term in SPARQL Results JSON:
|
|
231
|
+
// {"type":"triple","value":{"subject":T,"predicate":T,"object":T}}
|
|
232
|
+
// (SPARQL_Protocol.json_term) -> an RDF/JS Quad term. Recursive:
|
|
233
|
+
// the object may itself be a triple term.
|
|
234
|
+
const v = t.value || {};
|
|
235
|
+
return dataFactory.quad(
|
|
236
|
+
termFromSrj(v.subject), termFromSrj(v.predicate), termFromSrj(v.object));
|
|
237
|
+
}
|
|
238
|
+
default:
|
|
239
|
+
throw new TypeError(`termFromSrj: unknown term type '${t.type}'`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ---------------------------------------------------------------------
|
|
244
|
+
// N-Quads token output (mirrors RDF.NQuads.Serialize.escape_char)
|
|
245
|
+
// ---------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
function escapeLiteral(s) {
|
|
248
|
+
let out = '';
|
|
249
|
+
for (const ch of s) {
|
|
250
|
+
switch (ch) {
|
|
251
|
+
case '\\': out += '\\\\'; break;
|
|
252
|
+
case '"': out += '\\"'; break;
|
|
253
|
+
case '\n': out += '\\n'; break;
|
|
254
|
+
case '\r': out += '\\r'; break;
|
|
255
|
+
case '\t': out += '\\t'; break;
|
|
256
|
+
default: out += ch;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Serialize one RDF/JS term to its N-Quads token. */
|
|
263
|
+
function termToNQuads(term) {
|
|
264
|
+
switch (term.termType) {
|
|
265
|
+
case 'NamedNode':
|
|
266
|
+
return '<' + term.value + '>';
|
|
267
|
+
case 'BlankNode':
|
|
268
|
+
return '_:' + term.value;
|
|
269
|
+
case 'Literal': {
|
|
270
|
+
const body = '"' + escapeLiteral(term.value) + '"';
|
|
271
|
+
if (term.language) return body + '@' + term.language;
|
|
272
|
+
if (term.datatype && term.datatype.value !== XSD_STRING) {
|
|
273
|
+
return body + '^^<' + term.datatype.value + '>';
|
|
274
|
+
}
|
|
275
|
+
return body;
|
|
276
|
+
}
|
|
277
|
+
case 'DefaultGraph':
|
|
278
|
+
return '';
|
|
279
|
+
case 'Quad':
|
|
280
|
+
// RDF 1.2 triple term -> <<( s p o )>> (matches the engine's
|
|
281
|
+
// Mode_12 N-Quads serializer, so it round-trips back through the
|
|
282
|
+
// *12 parse path).
|
|
283
|
+
return '<<( ' + termToNQuads(term.subject) + ' ' +
|
|
284
|
+
termToNQuads(term.predicate) + ' ' +
|
|
285
|
+
termToNQuads(term.object) + ' )>>';
|
|
286
|
+
default:
|
|
287
|
+
throw new TypeError(
|
|
288
|
+
`termToNQuads: cannot serialize termType '${term.termType}'`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Serialize one RDF/JS quad to an N-Quads line (with trailing " .\n"). */
|
|
293
|
+
function quadToNQuads(quad) {
|
|
294
|
+
const g = quad.graph && quad.graph.termType !== 'DefaultGraph'
|
|
295
|
+
? ' ' + termToNQuads(quad.graph)
|
|
296
|
+
: '';
|
|
297
|
+
return termToNQuads(quad.subject) + ' ' +
|
|
298
|
+
termToNQuads(quad.predicate) + ' ' +
|
|
299
|
+
termToNQuads(quad.object) + g + ' .\n';
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Serialize an iterable of quads to an N-Quads document. */
|
|
303
|
+
function quadsToNQuads(quads) {
|
|
304
|
+
let out = '';
|
|
305
|
+
for (const q of quads) out += quadToNQuads(q);
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ---------------------------------------------------------------------
|
|
310
|
+
// N-Quads token input.
|
|
311
|
+
//
|
|
312
|
+
// A line tokenizer for the engine's own N-Quads output (and canonical
|
|
313
|
+
// N-Quads generally): one statement per line, terms separated by
|
|
314
|
+
// whitespace, ECHAR/UCHAR escapes inside literals. This is a token
|
|
315
|
+
// reader for a machine-generated regular syntax, not a general RDF
|
|
316
|
+
// parser — user documents in any syntax go through the engine.
|
|
317
|
+
// ---------------------------------------------------------------------
|
|
318
|
+
|
|
319
|
+
const UNESCAPE_MAP = {
|
|
320
|
+
't': '\t', 'b': '\b', 'n': '\n', 'r': '\r', 'f': '\f',
|
|
321
|
+
'"': '"', "'": "'", '\\': '\\',
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
function unescapeLiteral(s, lineNo) {
|
|
325
|
+
if (s.indexOf('\\') < 0) return s;
|
|
326
|
+
let out = '';
|
|
327
|
+
for (let i = 0; i < s.length; i++) {
|
|
328
|
+
const c = s[i];
|
|
329
|
+
if (c !== '\\') { out += c; continue; }
|
|
330
|
+
const e = s[++i];
|
|
331
|
+
if (e in UNESCAPE_MAP) { out += UNESCAPE_MAP[e]; continue; }
|
|
332
|
+
if (e === 'u') {
|
|
333
|
+
out += String.fromCodePoint(parseInt(s.slice(i + 1, i + 5), 16));
|
|
334
|
+
i += 4;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (e === 'U') {
|
|
338
|
+
out += String.fromCodePoint(parseInt(s.slice(i + 1, i + 9), 16));
|
|
339
|
+
i += 8;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
throw new SyntaxError(
|
|
343
|
+
`N-Quads line ${lineNo}: bad escape '\\${e}'`);
|
|
344
|
+
}
|
|
345
|
+
return out;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Read one term starting at s[i]; returns [term, nextIndex].
|
|
349
|
+
function readTerm(s, i, lineNo, factory) {
|
|
350
|
+
const c = s[i];
|
|
351
|
+
// RDF 1.2 triple term: <<( s p o )>> -> an RDF/JS Quad term
|
|
352
|
+
// (termType 'Quad'). Checked BEFORE the '<' IRI case since a triple
|
|
353
|
+
// term also opens with '<'. This reads the engine's own Mode_12
|
|
354
|
+
// N-Quads serializer output (RDF.NQuads.Serialize) into RDF/JS
|
|
355
|
+
// objects; it is not a conformance parser (that is the F* --rdf12
|
|
356
|
+
// path) — same interop role the rest of this reader plays.
|
|
357
|
+
if (c === '<' && s[i + 1] === '<' && s[i + 2] === '(') {
|
|
358
|
+
let j = skipWs(s, i + 3);
|
|
359
|
+
const [sub, a] = readTerm(s, j, lineNo, factory); j = skipWs(s, a);
|
|
360
|
+
const [pred, b] = readTerm(s, j, lineNo, factory); j = skipWs(s, b);
|
|
361
|
+
const [obj, d] = readTerm(s, j, lineNo, factory); j = skipWs(s, d);
|
|
362
|
+
if (s[j] === ')' && s[j + 1] === '>' && s[j + 2] === '>') {
|
|
363
|
+
return [factory.quad(sub, pred, obj), j + 3];
|
|
364
|
+
}
|
|
365
|
+
throw new SyntaxError(
|
|
366
|
+
`N-Quads line ${lineNo}: unterminated triple term (expected ')>>')`);
|
|
367
|
+
}
|
|
368
|
+
if (c === '<') {
|
|
369
|
+
const end = s.indexOf('>', i + 1);
|
|
370
|
+
if (end < 0) throw new SyntaxError(`N-Quads line ${lineNo}: unclosed IRI`);
|
|
371
|
+
return [factory.namedNode(s.slice(i + 1, end)), end + 1];
|
|
372
|
+
}
|
|
373
|
+
if (c === '_' && s[i + 1] === ':') {
|
|
374
|
+
let j = i + 2;
|
|
375
|
+
while (j < s.length && !/\s/.test(s[j])) j++;
|
|
376
|
+
return [factory.blankNode(s.slice(i + 2, j)), j];
|
|
377
|
+
}
|
|
378
|
+
if (c === '"') {
|
|
379
|
+
// Scan to the closing unescaped quote.
|
|
380
|
+
let j = i + 1;
|
|
381
|
+
for (;;) {
|
|
382
|
+
if (j >= s.length) {
|
|
383
|
+
throw new SyntaxError(`N-Quads line ${lineNo}: unclosed literal`);
|
|
384
|
+
}
|
|
385
|
+
if (s[j] === '\\') { j += 2; continue; }
|
|
386
|
+
if (s[j] === '"') break;
|
|
387
|
+
j++;
|
|
388
|
+
}
|
|
389
|
+
const lex = unescapeLiteral(s.slice(i + 1, j), lineNo);
|
|
390
|
+
j++; // past closing quote
|
|
391
|
+
if (s[j] === '@') {
|
|
392
|
+
let k = j + 1;
|
|
393
|
+
while (k < s.length && !/\s/.test(s[k])) k++;
|
|
394
|
+
return [factory.literal(lex, s.slice(j + 1, k)), k];
|
|
395
|
+
}
|
|
396
|
+
if (s[j] === '^' && s[j + 1] === '^' && s[j + 2] === '<') {
|
|
397
|
+
const end = s.indexOf('>', j + 3);
|
|
398
|
+
if (end < 0) {
|
|
399
|
+
throw new SyntaxError(`N-Quads line ${lineNo}: unclosed datatype IRI`);
|
|
400
|
+
}
|
|
401
|
+
return [
|
|
402
|
+
factory.literal(lex, factory.namedNode(s.slice(j + 3, end))),
|
|
403
|
+
end + 1,
|
|
404
|
+
];
|
|
405
|
+
}
|
|
406
|
+
return [factory.literal(lex), j];
|
|
407
|
+
}
|
|
408
|
+
throw new SyntaxError(
|
|
409
|
+
`N-Quads line ${lineNo}: unexpected character '${c}' at column ${i + 1}`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function skipWs(s, i) {
|
|
413
|
+
while (i < s.length && (s[i] === ' ' || s[i] === '\t')) i++;
|
|
414
|
+
return i;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Tokenize one N-Quads statement line into an RDF/JS quad.
|
|
419
|
+
* Returns null for blank/comment lines.
|
|
420
|
+
*/
|
|
421
|
+
function nquadsLineToQuad(line, lineNo, factory) {
|
|
422
|
+
const f = factory || dataFactory;
|
|
423
|
+
let i = skipWs(line, 0);
|
|
424
|
+
if (i >= line.length || line[i] === '#') return null;
|
|
425
|
+
const terms = [];
|
|
426
|
+
while (terms.length < 4) {
|
|
427
|
+
const [term, next] = readTerm(line, i, lineNo, f);
|
|
428
|
+
terms.push(term);
|
|
429
|
+
i = skipWs(line, next);
|
|
430
|
+
if (line[i] === '.') break;
|
|
431
|
+
}
|
|
432
|
+
if (line[i] !== '.') {
|
|
433
|
+
throw new SyntaxError(`N-Quads line ${lineNo}: missing terminating '.'`);
|
|
434
|
+
}
|
|
435
|
+
if (terms.length < 3) {
|
|
436
|
+
throw new SyntaxError(`N-Quads line ${lineNo}: fewer than 3 terms`);
|
|
437
|
+
}
|
|
438
|
+
return f.quad(terms[0], terms[1], terms[2], terms[3] || f.defaultGraph());
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Tokenize an engine-emitted N-Quads document into RDF/JS quads.
|
|
443
|
+
*
|
|
444
|
+
* options.blankNodePrefix — prepended to every blank-node label, used
|
|
445
|
+
* by parse() to keep labels from separate parse calls distinct (blank
|
|
446
|
+
* node identity is document-scoped; the prefix is bookkeeping, the
|
|
447
|
+
* semantic per-document renaming lives in F*
|
|
448
|
+
* RDF.Dataset.Merge.rename_dataset_bnodes).
|
|
449
|
+
*/
|
|
450
|
+
function nquadsToQuads(text, options) {
|
|
451
|
+
const opts = options || {};
|
|
452
|
+
const factory = opts.factory || dataFactory;
|
|
453
|
+
const f = opts.blankNodePrefix
|
|
454
|
+
? {
|
|
455
|
+
...factory,
|
|
456
|
+
blankNode: (label) => factory.blankNode(
|
|
457
|
+
label === undefined || label === null
|
|
458
|
+
? undefined
|
|
459
|
+
: opts.blankNodePrefix + label),
|
|
460
|
+
}
|
|
461
|
+
: factory;
|
|
462
|
+
const quads = [];
|
|
463
|
+
const lines = String(text).split('\n');
|
|
464
|
+
for (let n = 0; n < lines.length; n++) {
|
|
465
|
+
const q = nquadsLineToQuad(lines[n].replace(/\r$/, ''), n + 1, f);
|
|
466
|
+
if (q) quads.push(q);
|
|
467
|
+
}
|
|
468
|
+
return quads;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ---------------------------------------------------------------------
|
|
472
|
+
// Dataset — a minimal RDF/JS DatasetCore over an in-memory quad array.
|
|
473
|
+
// ---------------------------------------------------------------------
|
|
474
|
+
|
|
475
|
+
class Dataset {
|
|
476
|
+
constructor(quads) {
|
|
477
|
+
this._quads = [];
|
|
478
|
+
if (quads) for (const q of quads) this.add(q);
|
|
479
|
+
}
|
|
480
|
+
get size() {
|
|
481
|
+
return this._quads.length;
|
|
482
|
+
}
|
|
483
|
+
add(quad) {
|
|
484
|
+
if (!this.has(quad)) this._quads.push(quad);
|
|
485
|
+
return this;
|
|
486
|
+
}
|
|
487
|
+
delete(quad) {
|
|
488
|
+
const ix = this._quads.findIndex((q) => q.equals(quad));
|
|
489
|
+
if (ix >= 0) this._quads.splice(ix, 1);
|
|
490
|
+
return this;
|
|
491
|
+
}
|
|
492
|
+
has(quad) {
|
|
493
|
+
return this._quads.some((q) => q.equals(quad));
|
|
494
|
+
}
|
|
495
|
+
match(subject, predicate, object, graph) {
|
|
496
|
+
const out = new Dataset();
|
|
497
|
+
for (const q of this._quads) {
|
|
498
|
+
if (subject && !subject.equals(q.subject)) continue;
|
|
499
|
+
if (predicate && !predicate.equals(q.predicate)) continue;
|
|
500
|
+
if (object && !object.equals(q.object)) continue;
|
|
501
|
+
if (graph && !graph.equals(q.graph)) continue;
|
|
502
|
+
out._quads.push(q);
|
|
503
|
+
}
|
|
504
|
+
return out;
|
|
505
|
+
}
|
|
506
|
+
[Symbol.iterator]() {
|
|
507
|
+
return this._quads[Symbol.iterator]();
|
|
508
|
+
}
|
|
509
|
+
toArray() {
|
|
510
|
+
return this._quads.slice();
|
|
511
|
+
}
|
|
512
|
+
/** N-Quads text — the engine interchange handle. */
|
|
513
|
+
toNQuads() {
|
|
514
|
+
return quadsToNQuads(this._quads);
|
|
515
|
+
}
|
|
516
|
+
toString() {
|
|
517
|
+
return this.toNQuads();
|
|
518
|
+
}
|
|
519
|
+
static fromNQuads(text, options) {
|
|
520
|
+
return new Dataset(nquadsToQuads(text, options));
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
module.exports = {
|
|
525
|
+
dataFactory,
|
|
526
|
+
NamedNode,
|
|
527
|
+
BlankNode,
|
|
528
|
+
Literal,
|
|
529
|
+
Variable,
|
|
530
|
+
DefaultGraph,
|
|
531
|
+
Quad,
|
|
532
|
+
Dataset,
|
|
533
|
+
termFromSrj,
|
|
534
|
+
termToNQuads,
|
|
535
|
+
quadToNQuads,
|
|
536
|
+
quadsToNQuads,
|
|
537
|
+
nquadsToQuads,
|
|
538
|
+
XSD_STRING,
|
|
539
|
+
RDF_LANGSTRING,
|
|
540
|
+
};
|
package/version.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"gitSha": "4944fdb1cc6d5fcc7a2db2f2a1802f276aac6bee",
|
|
4
|
+
"builtAt": "2026-08-22T05:46:06Z",
|
|
5
|
+
"claims": {
|
|
6
|
+
"schema": "1",
|
|
7
|
+
"statement": "Proved sound with respect to an independent F* formalization of the W3C RDF/RDFS/OWL semantics, under the stated fragment restrictions and the trust surface recorded in docs/theorem-registry.md — never claimed as a complete formally verified implementation of RDF semantics (docs/theorem-registry.md § Calibrated claims).",
|
|
8
|
+
"registry": "docs/theorem-registry.md",
|
|
9
|
+
"trustSurface": "docs/theorem-registry.md § Trust surface",
|
|
10
|
+
"notes": "Each item below names the exact theorem/lemma, the F* file that carries it, and the registry section a reader can open to check the claim. This is a summary, not the full registry — read docs/theorem-registry.md for the complete rule-by-rule table, hypothesis provenance, and open findings.",
|
|
11
|
+
"items": [
|
|
12
|
+
{
|
|
13
|
+
"id": "rho-df-closure-decides",
|
|
14
|
+
"claim": "The certified six-rule core-RDFS (ρdf) closure (rdfs2/3/5/7/9/11) decides entailment exactly on fragment inputs — its simple-query answers are exactly the entailed consequences.",
|
|
15
|
+
"theorem": "rho_df_closure_sound, rho_df_closure_decides",
|
|
16
|
+
"file": "formal/fstar/RDF.Entailment.RDFS.RhoDFClosure.fst",
|
|
17
|
+
"registrySection": "docs/theorem-registry.md § Layer 3 — the composed regime theorem, rho_df_closure soundness/fragment-preservation rows",
|
|
18
|
+
"npmSurface": [
|
|
19
|
+
"coreRdfsClosure",
|
|
20
|
+
"rhoDfClosure",
|
|
21
|
+
"coreRdfsCheck",
|
|
22
|
+
"rhoDfFragmentCheck"
|
|
23
|
+
],
|
|
24
|
+
"status": "proved"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "owl-rl-licensing-truth",
|
|
28
|
+
"claim": "Every OWL 2 RL/RDF engine rule classified [row] emits only input triples or a licensed W3C table-row application (licensing), and every row's conclusion is model-theoretically true given its premises (truth).",
|
|
29
|
+
"file": "formal/fstar/OWL.RL.Spec.fst, OWL.RL.Refinement.fst, OWL.Semantics.fst",
|
|
30
|
+
"registrySection": "docs/theorem-registry.md § 1 (OWL 2 RL/RDF — licensing and truth, per engine rule)",
|
|
31
|
+
"npmSurface": [
|
|
32
|
+
"owlClosure",
|
|
33
|
+
"rdfsPlusClosure",
|
|
34
|
+
"tableauMaterialise",
|
|
35
|
+
"tableauDlInconsistent",
|
|
36
|
+
"owlIsConsistent",
|
|
37
|
+
"owlEntails"
|
|
38
|
+
],
|
|
39
|
+
"status": "proved per-rule; see §1 table for per-row status and PARKED clash-row exceptions"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "rdfs-entailment-13-rows",
|
|
43
|
+
"claim": "11 of 13 RDF 1.1 Semantics §9 RDFS entailment rows have a shipping engine rule with both licensing and truth proved; the remaining 2 (rdfs6, rdfs10) are proved at the spec-predicate level but the shipping engine's reflexivity over-approximation for them is correctly flagged unsound (finding RS-1), not silently shipped as sound.",
|
|
44
|
+
"file": "formal/fstar/RDF.Entailment.RDFS.Spec.fst, RDF.Entailment.RDFS.Refinement.fst, RDF.Entailment.RDFS.ModelTheory.fst",
|
|
45
|
+
"registrySection": "docs/theorem-registry.md § 2 (RDFS entailment — 13 rows)",
|
|
46
|
+
"status": "11/13 proved sound+true; 2/13 truth-only, engine unsoundness flagged not hidden"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "sparql-rdfs-regime-exact-answer",
|
|
50
|
+
"claim": "The SPARQL evaluator's answer set over the ρdf closure IS the RDFS entailment regime's answer set, on fragment inputs, for ground answers (soundness + completeness, unconditional).",
|
|
51
|
+
"theorem": "theorem_rdfs_regime_bgp_exact_answer, theorem_rdfs_regime_ask_query_complete",
|
|
52
|
+
"file": "formal/fstar/SPARQL11.EntailmentRegime.RDFS.fst",
|
|
53
|
+
"registrySection": "docs/theorem-registry.md § 5 (SPARQL algebra refinement, the query rung) § Layer 3",
|
|
54
|
+
"npmSurface": [
|
|
55
|
+
"query (options.entail = 'RDFS')"
|
|
56
|
+
],
|
|
57
|
+
"status": "proved, unconditional"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "streaming-multichunk",
|
|
61
|
+
"claim": "N-Quads streaming parse over ANY list of input chunks equals a batch parse of their concatenation (stream_parse chunks == batch_parse (concat_all chunks)) — the machine-checked answer to whether chunked/streamed parsing of arbitrarily large files agrees with parsing the whole file at once.",
|
|
62
|
+
"theorem": "theorem_stream_eq_batch",
|
|
63
|
+
"file": "formal/fstar/RDF.NQuads.Streaming.fst",
|
|
64
|
+
"registrySection": "docs/theorem-registry.md § 7 (G4/M1 parser round-trip theorems) — Task #48/#402 MULTI-CHUNK STREAMING THEOREM, 2026-08-11",
|
|
65
|
+
"status": "proved"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "srj-n-row",
|
|
69
|
+
"claim": "SPARQL Results JSON serialisation (serialise_response_json) equals the fixed SRJ shape (head/vars block, comma-joined row texts) for ANY variable list and ANY row list of ANY length N — not just the 0/1/2-row cases spot-checked before.",
|
|
70
|
+
"theorem": "lemma_srj_n_rows",
|
|
71
|
+
"file": "formal/fstar/SPARQL.Protocol.RoundTrip.fst",
|
|
72
|
+
"registrySection": "docs/theorem-registry.md § 7 (G4/M1 parser round-trip theorems) — G4 M4 N-ROW symbolic SRJ theorem, 2026-08-11",
|
|
73
|
+
"npmSurface": [
|
|
74
|
+
"query (SELECT, output SPARQL Results JSON)"
|
|
75
|
+
],
|
|
76
|
+
"status": "proved"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "symbolic-iri-roundtrip",
|
|
80
|
+
"claim": "Parsing the printed N-Triples form of a SYMBOLIC IRI term (any ASCII codepoint list of IRI-safe characters, no escapes, no controls) recovers the original term exactly — proved against the real serializer, not a transcription.",
|
|
81
|
+
"theorem": "lemma_term_iri_round_trip_build_string",
|
|
82
|
+
"file": "formal/fstar/RDF.NTriples.RoundTrip.fst",
|
|
83
|
+
"registrySection": "docs/theorem-registry.md § 7 (G4/M1 parser round-trip theorems) — G4 M1 SYMBOLIC IRI round-trip theorem, 2026-08-11",
|
|
84
|
+
"npmSurface": [
|
|
85
|
+
"parse",
|
|
86
|
+
"serialize"
|
|
87
|
+
],
|
|
88
|
+
"status": "proved for symbolic IRIs; full well-formed-IRI coverage open (string-conversion wall, documented)"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "faststring-migration-complete",
|
|
92
|
+
"claim": "The FastString byte-primitive migration (steps 0-6) is complete: zero assume vals in the FastString family except the one documented CharBoundary primitive (unsafe_char_of_d7ff); the equivalence corpus is IDENTICAL between native OCaml and Node (js_of_ocaml) execution.",
|
|
93
|
+
"measurement": "93846 pass, 962 expected-fail, 0 unexpected fail (out of 94808), verified 3 ways (fresh Node run, fresh same-source native compile, prior recorded number)",
|
|
94
|
+
"file": "formal/fstar/Parser.FastString.fst, Parser.FastString.Spec.fst",
|
|
95
|
+
"registrySection": "docs/theorem-registry.md § 7 (G4/M1 parser round-trip theorems) — FastString migration COMPLETE, steps 0-6, 2026-08-11",
|
|
96
|
+
"status": "complete, measured equivalent native+Node"
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"notClaimed": "A complete formally verified implementation of RDF semantics. See docs/theorem-registry.md § Trust surface for the assume-val count (~146, mostly COTTAS/HDT storage I/O per iron rule #11), the extraction-step caveat (fstar.exe --codegen OCaml is not itself re-verified), and the rdf-mt / W3C conformance suites this registry relies on as the independent runtime check. CLAUDE.md's standing qualifier applies: parser and algebra spec verified in F*; the on-disk backend carries unverified OCaml-side optimization layers being migrated back to F*."
|
|
100
|
+
}
|
|
101
|
+
}
|