eyeleng 1.0.7 → 1.0.9
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/eyeleng.browser.js +112 -78
- package/eyeleng.js +112 -78
- package/package.json +6 -2
- package/playground.html +16 -4
- package/reports/w3c-shacl12-rules-earl.ttl +1337 -1056
- package/src/analyze.js +8 -10
- package/src/builtins.js +3 -1
- package/src/engine.js +36 -33
- package/src/parser.js +1 -1
- package/src/store.js +2 -2
- package/src/term.js +4 -2
- package/src/tokenizer.js +58 -29
- package/test/browser-bundle.test.js +8 -0
- package/test/perf-baseline.json +39 -0
- package/test/shacl12-rules.test.js +0 -8
- package/test/w3c-rdf.test.js +0 -3
- package/tools/bundle.js +0 -17
- package/tools/perf-bench.js +234 -0
package/src/analyze.js
CHANGED
|
@@ -11,9 +11,7 @@ function analyze(program, options = {}) {
|
|
|
11
11
|
|
|
12
12
|
program.rules.forEach((rule, index) => {
|
|
13
13
|
const name = ruleName(rule, index);
|
|
14
|
-
const label = displayRuleName(name, program.prefixes || {});
|
|
15
14
|
const bound = boundVariables(rule.body);
|
|
16
|
-
const positive = positiveVariables(rule.body);
|
|
17
15
|
const head = new Set();
|
|
18
16
|
for (const triple of rule.head) collectTripleVars(triple, head);
|
|
19
17
|
|
|
@@ -23,7 +21,7 @@ function analyze(program, options = {}) {
|
|
|
23
21
|
code: 'unsafe-head-variable',
|
|
24
22
|
severity: 'error',
|
|
25
23
|
rule: name,
|
|
26
|
-
message: `${
|
|
24
|
+
message: `${displayRuleName(name, program.prefixes || {})} has unbound head variable ?${variable}`,
|
|
27
25
|
});
|
|
28
26
|
}
|
|
29
27
|
}
|
|
@@ -34,19 +32,19 @@ function analyze(program, options = {}) {
|
|
|
34
32
|
code: 'invalid-head-predicate',
|
|
35
33
|
severity: 'error',
|
|
36
34
|
rule: name,
|
|
37
|
-
message: `${
|
|
35
|
+
message: `${displayRuleName(name, program.prefixes || {})} has a non-IRI/non-variable predicate in the head`,
|
|
38
36
|
});
|
|
39
37
|
}
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
diagnostics.push(...sequentialWellFormednessDiagnostics(rule.body, name,
|
|
40
|
+
diagnostics.push(...sequentialWellFormednessDiagnostics(rule.body, name, program.prefixes || {}));
|
|
43
41
|
|
|
44
42
|
if (rule.runOnce && recursiveIndexes.has(index)) {
|
|
45
43
|
diagnostics.push({
|
|
46
44
|
code: 'recursive-assignment-rule',
|
|
47
45
|
severity: 'warning',
|
|
48
46
|
rule: name,
|
|
49
|
-
message: `${
|
|
47
|
+
message: `${displayRuleName(name, program.prefixes || {})} is a run-once rule in a recursive dependency cycle`,
|
|
50
48
|
});
|
|
51
49
|
}
|
|
52
50
|
|
|
@@ -402,7 +400,7 @@ function stronglyConnectedComponents(size, edges) {
|
|
|
402
400
|
return components;
|
|
403
401
|
}
|
|
404
402
|
|
|
405
|
-
function sequentialWellFormednessDiagnostics(clauses, ruleNameValue,
|
|
403
|
+
function sequentialWellFormednessDiagnostics(clauses, ruleNameValue, prefixes = {}) {
|
|
406
404
|
const diagnostics = [];
|
|
407
405
|
|
|
408
406
|
function visit(items, initialBound, scopeLabel) {
|
|
@@ -417,7 +415,7 @@ function sequentialWellFormednessDiagnostics(clauses, ruleNameValue, label) {
|
|
|
417
415
|
code: 'unbound-filter-variable',
|
|
418
416
|
severity: 'error',
|
|
419
417
|
rule: ruleNameValue,
|
|
420
|
-
message: `${
|
|
418
|
+
message: `${displayRuleName(ruleNameValue, prefixes)} FILTER uses ?${variable} before it is bound${scopeLabel}`,
|
|
421
419
|
});
|
|
422
420
|
}
|
|
423
421
|
}
|
|
@@ -427,7 +425,7 @@ function sequentialWellFormednessDiagnostics(clauses, ruleNameValue, label) {
|
|
|
427
425
|
code: 'assignment-variable-already-bound',
|
|
428
426
|
severity: 'error',
|
|
429
427
|
rule: ruleNameValue,
|
|
430
|
-
message: `${
|
|
428
|
+
message: `${displayRuleName(ruleNameValue, prefixes)} SET assigns ?${clause.variable}, but that variable is already bound${scopeLabel}`,
|
|
431
429
|
});
|
|
432
430
|
}
|
|
433
431
|
for (const variable of expressionVariables(clause.expr)) {
|
|
@@ -436,7 +434,7 @@ function sequentialWellFormednessDiagnostics(clauses, ruleNameValue, label) {
|
|
|
436
434
|
code: 'unbound-assignment-variable',
|
|
437
435
|
severity: 'error',
|
|
438
436
|
rule: ruleNameValue,
|
|
439
|
-
message: `${
|
|
437
|
+
message: `${displayRuleName(ruleNameValue, prefixes)} SET expression uses ?${variable} before it is bound${scopeLabel}`,
|
|
440
438
|
});
|
|
441
439
|
}
|
|
442
440
|
}
|
package/src/builtins.js
CHANGED
|
@@ -28,6 +28,8 @@ const XSD_DAYTIME_DURATION = 'http://www.w3.org/2001/XMLSchema#dayTimeDuration';
|
|
|
28
28
|
const RDF_LANGSTRING = `${RDF_NS}langString`;
|
|
29
29
|
const RDF_DIRLANGSTRING = `${RDF_NS}dirLangString`;
|
|
30
30
|
const NUMERIC_DATATYPES = new Set([XSD_INTEGER, XSD_DECIMAL, XSD_DOUBLE]);
|
|
31
|
+
const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
32
|
+
const MIN_SAFE_INTEGER_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
|
|
31
33
|
|
|
32
34
|
// This table is intentionally shaped by the SHACL 1.2 Rules grammar production BuiltInCall.
|
|
33
35
|
// Keys are the canonical spellings used by the draft; lookup is case-insensitive so examples
|
|
@@ -187,7 +189,7 @@ function toBigIntInteger(value) {
|
|
|
187
189
|
}
|
|
188
190
|
|
|
189
191
|
function fromIntegerResult(value) {
|
|
190
|
-
if (value <=
|
|
192
|
+
if (value <= MAX_SAFE_INTEGER_BIGINT && value >= MIN_SAFE_INTEGER_BIGINT) return Number(value);
|
|
191
193
|
return value;
|
|
192
194
|
}
|
|
193
195
|
|
package/src/engine.js
CHANGED
|
@@ -32,6 +32,18 @@ function evaluate(program, options = {}) {
|
|
|
32
32
|
layerIndexes,
|
|
33
33
|
analysis.dependency ? analysis.dependency.edges : [],
|
|
34
34
|
);
|
|
35
|
+
const baseContext = {
|
|
36
|
+
...evalOptions,
|
|
37
|
+
maxIterations,
|
|
38
|
+
inputKeys,
|
|
39
|
+
inferred,
|
|
40
|
+
trace,
|
|
41
|
+
perRule,
|
|
42
|
+
layer: 0,
|
|
43
|
+
iteration: 0,
|
|
44
|
+
startingIterations: 0,
|
|
45
|
+
recursiveLayer: false,
|
|
46
|
+
};
|
|
35
47
|
|
|
36
48
|
for (let layerIndex = 0; layerIndex < layerIndexes.length; layerIndex += 1) {
|
|
37
49
|
const layer = layerIndexes[layerIndex];
|
|
@@ -41,30 +53,17 @@ function evaluate(program, options = {}) {
|
|
|
41
53
|
if (runOnce.length > 0) {
|
|
42
54
|
iterations += 1;
|
|
43
55
|
for (const ruleIndex of runOnce) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
inferred,
|
|
48
|
-
trace,
|
|
49
|
-
perRule,
|
|
50
|
-
layer: layerIndex + 1,
|
|
51
|
-
iteration: iterations,
|
|
52
|
-
});
|
|
56
|
+
baseContext.layer = layerIndex + 1;
|
|
57
|
+
baseContext.iteration = iterations;
|
|
58
|
+
const added = applyRuleOnce(program, store, ruleIndex, baseContext);
|
|
53
59
|
ruleApplications += added.applications;
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
inferred,
|
|
62
|
-
trace,
|
|
63
|
-
perRule,
|
|
64
|
-
layer: layerIndex + 1,
|
|
65
|
-
startingIterations: iterations,
|
|
66
|
-
recursiveLayer: recursiveLayerFlags[layerIndex],
|
|
67
|
-
});
|
|
63
|
+
baseContext.layer = layerIndex + 1;
|
|
64
|
+
baseContext.startingIterations = iterations;
|
|
65
|
+
baseContext.recursiveLayer = recursiveLayerFlags[layerIndex];
|
|
66
|
+
const ordinaryResult = runRulesToFixpoint(program, store, ordinary, baseContext);
|
|
68
67
|
iterations = ordinaryResult.iterations;
|
|
69
68
|
ruleApplications += ordinaryResult.ruleApplications;
|
|
70
69
|
}
|
|
@@ -95,10 +94,8 @@ function runRulesToFixpoint(program, store, ruleIndexes, context) {
|
|
|
95
94
|
const iteration = context.startingIterations + 1;
|
|
96
95
|
let ruleApplications = 0;
|
|
97
96
|
for (const ruleIndex of ruleIndexes) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
iteration,
|
|
101
|
-
});
|
|
97
|
+
context.iteration = iteration;
|
|
98
|
+
const applied = applyRuleOnce(program, store, ruleIndex, context);
|
|
102
99
|
ruleApplications += applied.applications;
|
|
103
100
|
}
|
|
104
101
|
return { iterations: iteration, ruleApplications };
|
|
@@ -114,10 +111,8 @@ function runRulesToFixpoint(program, store, ruleIndexes, context) {
|
|
|
114
111
|
let addedInIteration = 0;
|
|
115
112
|
|
|
116
113
|
for (const ruleIndex of ruleIndexes) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
iteration: iterations,
|
|
120
|
-
});
|
|
114
|
+
context.iteration = iterations;
|
|
115
|
+
const applied = applyRuleOnce(program, store, ruleIndex, context);
|
|
121
116
|
addedInIteration += applied.added;
|
|
122
117
|
ruleApplications += applied.applications;
|
|
123
118
|
}
|
|
@@ -146,16 +141,24 @@ function computeRecursiveLayerFlags(layerIndexes, edges = []) {
|
|
|
146
141
|
return flags;
|
|
147
142
|
}
|
|
148
143
|
|
|
144
|
+
|
|
149
145
|
function applyRuleOnce(program, store, ruleIndex, context) {
|
|
150
146
|
const rule = program.rules[ruleIndex];
|
|
151
147
|
let applications = 0;
|
|
152
148
|
let added = 0;
|
|
153
|
-
const
|
|
149
|
+
const dedupeBindings = rule.body.some((clause) => clause.type === 'path');
|
|
150
|
+
const seenBindings = dedupeBindings ? new Set() : null;
|
|
154
151
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
152
|
+
const bodyBindings = rule.body.length === 1 && rule.body[0].type === 'triple'
|
|
153
|
+
? store.match(rule.body[0].triple, {})
|
|
154
|
+
: evaluateBodyStream(rule.body, store, {}, context);
|
|
155
|
+
|
|
156
|
+
for (const binding of bodyBindings) {
|
|
157
|
+
if (seenBindings) {
|
|
158
|
+
const key = bindingKey(binding);
|
|
159
|
+
if (seenBindings.has(key)) continue;
|
|
160
|
+
seenBindings.add(key);
|
|
161
|
+
}
|
|
159
162
|
applications += 1;
|
|
160
163
|
context.perRule[ruleIndex].applications += 1;
|
|
161
164
|
|
package/src/parser.js
CHANGED
|
@@ -764,7 +764,7 @@ class Parser {
|
|
|
764
764
|
peekN(n) { return this.tokens[this.pos + n] || this.tokens[this.tokens.length - 1]; }
|
|
765
765
|
previous() { return this.tokens[this.pos - 1]; }
|
|
766
766
|
strictGrammar() { return !!this.options.strictGrammar; }
|
|
767
|
-
error(message, token = this.peek()) { return new SyntaxErrorWithLocation(message, token); }
|
|
767
|
+
error(message, token = this.peek()) { return new SyntaxErrorWithLocation(message, token && token.filename ? token : { ...token, filename: this.options.filename || '<input>' }); }
|
|
768
768
|
}
|
|
769
769
|
|
|
770
770
|
|
package/src/store.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { tripleKey, termKey, termEquals
|
|
3
|
+
const { tripleKey, termKey, termEquals } = require('./term.js');
|
|
4
4
|
|
|
5
5
|
class TripleStore {
|
|
6
6
|
constructor(triples = []) {
|
|
@@ -109,7 +109,7 @@ function smallerValues(left, right) {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
function normalizeTriple(triple) {
|
|
112
|
-
return { s:
|
|
112
|
+
return { s: triple.s, p: triple.p, o: triple.o };
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
function bindingKey(binding) {
|
package/src/term.js
CHANGED
|
@@ -17,11 +17,13 @@ function iri(value) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function variable(name) {
|
|
20
|
-
|
|
20
|
+
const value = String(name);
|
|
21
|
+
return { type: 'var', value: value[0] === '?' || value[0] === '$' ? value.slice(1) : value };
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
function blankNode(value) {
|
|
24
|
-
|
|
25
|
+
const label = String(value);
|
|
26
|
+
return { type: 'blank', value: label.startsWith('_:') ? label.slice(2) : label };
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
function literal(value, datatype = null, lang = null, langDir = null) {
|
package/src/tokenizer.js
CHANGED
|
@@ -20,7 +20,7 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
20
20
|
|
|
21
21
|
function current() { return source[i]; }
|
|
22
22
|
function peek(n = 1) { return source[i + n]; }
|
|
23
|
-
function startsWith(text) { return source.
|
|
23
|
+
function startsWith(text) { return source.startsWith(text, i); }
|
|
24
24
|
function advance() {
|
|
25
25
|
const ch = source[i++];
|
|
26
26
|
if (ch === '\n') { line += 1; column = 1; }
|
|
@@ -28,7 +28,7 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
28
28
|
return ch;
|
|
29
29
|
}
|
|
30
30
|
function token(type, value, startLine, startColumn, extra = {}) {
|
|
31
|
-
tokens.push({ type, value, line: startLine, column: startColumn,
|
|
31
|
+
tokens.push({ type, value, line: startLine, column: startColumn, ...extra });
|
|
32
32
|
}
|
|
33
33
|
function syntax(message, startLine, startColumn) {
|
|
34
34
|
throw new SyntaxErrorWithLocation(message, { line: startLine, column: startColumn, filename });
|
|
@@ -36,19 +36,21 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
36
36
|
|
|
37
37
|
function readNumericLiteral() {
|
|
38
38
|
let value = '';
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const start = i;
|
|
40
|
+
while (i < source.length && isDigitCode(source.charCodeAt(i))) { i += 1; column += 1; }
|
|
41
|
+
if (source[i] === '.' && isDigitCode(source.charCodeAt(i + 1))) {
|
|
42
|
+
i += 1; column += 1;
|
|
43
|
+
while (i < source.length && isDigitCode(source.charCodeAt(i))) { i += 1; column += 1; }
|
|
43
44
|
}
|
|
45
|
+
value = source.slice(start, i);
|
|
44
46
|
if (current() === 'e' || current() === 'E') {
|
|
45
47
|
const saveI = i;
|
|
46
48
|
const saveLine = line;
|
|
47
49
|
const saveColumn = column;
|
|
48
50
|
let exponent = advance();
|
|
49
51
|
if (current() === '+' || current() === '-') exponent += advance();
|
|
50
|
-
if (
|
|
51
|
-
while (i < source.length &&
|
|
52
|
+
if (isDigitCode(source.charCodeAt(i))) {
|
|
53
|
+
while (i < source.length && isDigitCode(source.charCodeAt(i))) exponent += advance();
|
|
52
54
|
value += exponent;
|
|
53
55
|
} else {
|
|
54
56
|
i = saveI;
|
|
@@ -66,7 +68,7 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
66
68
|
const length = esc === 'u' ? 4 : 8;
|
|
67
69
|
let hex = '';
|
|
68
70
|
for (let j = 0; j < length; j += 1) {
|
|
69
|
-
if (
|
|
71
|
+
if (!isHexCode(source.charCodeAt(i))) syntax(`Invalid \\${esc} escape`, startLine, startColumn);
|
|
70
72
|
hex += advance();
|
|
71
73
|
}
|
|
72
74
|
const codePoint = Number.parseInt(hex, 16);
|
|
@@ -85,7 +87,7 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
85
87
|
const length = esc === 'u' ? 4 : 8;
|
|
86
88
|
let hex = '';
|
|
87
89
|
for (let j = 0; j < length; j += 1) {
|
|
88
|
-
if (
|
|
90
|
+
if (!isHexCode(source.charCodeAt(i))) syntax(`Invalid \\${esc} escape`, startLine, startColumn);
|
|
89
91
|
hex += advance();
|
|
90
92
|
}
|
|
91
93
|
const codePoint = Number.parseInt(hex, 16);
|
|
@@ -99,7 +101,7 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
99
101
|
|
|
100
102
|
while (i < source.length) {
|
|
101
103
|
const ch = current();
|
|
102
|
-
if (
|
|
104
|
+
if (isWhitespaceCode(source.charCodeAt(i))) { advance(); continue; }
|
|
103
105
|
if (ch === '#') {
|
|
104
106
|
while (i < source.length && current() !== '\n') advance();
|
|
105
107
|
continue;
|
|
@@ -185,18 +187,21 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
if (ch === '@') {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
const wordStart = i;
|
|
191
|
+
advance();
|
|
192
|
+
while (i < source.length && isLangTagCode(source.charCodeAt(i))) { i += 1; column += 1; }
|
|
193
|
+
const value = source.slice(wordStart, i);
|
|
190
194
|
if (!/^@[A-Za-z]+(?:-[A-Za-z0-9]+)*(?:--[A-Za-z]+)?$/.test(value)) syntax(`Invalid language tag ${value}`, startLine, startColumn);
|
|
191
195
|
token('word', value, startLine, startColumn);
|
|
192
196
|
continue;
|
|
193
197
|
}
|
|
194
198
|
|
|
195
199
|
if (ch === '?' || ch === '$') {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
+
const varStart = i;
|
|
201
|
+
advance();
|
|
202
|
+
while (i < source.length && isVarNameCode(source.charCodeAt(i))) { i += 1; column += 1; }
|
|
203
|
+
if (i - varStart === 1) syntax('Expected variable name', startLine, startColumn);
|
|
204
|
+
token('variable', source.slice(varStart + 1, i), startLine, startColumn);
|
|
200
205
|
continue;
|
|
201
206
|
}
|
|
202
207
|
|
|
@@ -223,24 +228,27 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
223
228
|
continue;
|
|
224
229
|
}
|
|
225
230
|
|
|
226
|
-
|
|
231
|
+
const wordStart = i;
|
|
227
232
|
while (i < source.length) {
|
|
228
|
-
const c =
|
|
229
|
-
if (c === '\\' &&
|
|
230
|
-
|
|
231
|
-
|
|
233
|
+
const c = source[i];
|
|
234
|
+
if (c === '\\' && source[i + 1] !== undefined) {
|
|
235
|
+
i += 2;
|
|
236
|
+
column += 2;
|
|
232
237
|
continue;
|
|
233
238
|
}
|
|
234
|
-
|
|
239
|
+
const code = source.charCodeAt(i);
|
|
240
|
+
if (isWhitespaceCode(code) || '{}()[],;|'.includes(c) || '=<>+-*/!^~'.includes(c)) break;
|
|
235
241
|
if (c === '.') {
|
|
236
|
-
const n =
|
|
237
|
-
if (n === undefined ||
|
|
242
|
+
const n = source[i + 1];
|
|
243
|
+
if (n === undefined || isWhitespaceCode(n.charCodeAt(0)) || '{}()[],;|'.includes(n) || '=<>+-*/!^~'.includes(n)) break;
|
|
238
244
|
}
|
|
239
245
|
if (c === '#') break;
|
|
240
|
-
|
|
246
|
+
i += 1;
|
|
247
|
+
column += 1;
|
|
241
248
|
}
|
|
242
|
-
if (
|
|
249
|
+
if (i === wordStart) syntax(`Unexpected character ${JSON.stringify(ch)}`, startLine, startColumn);
|
|
243
250
|
|
|
251
|
+
const value = source.slice(wordStart, i);
|
|
244
252
|
if (/^[+-]?(?:(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+|\d+)$/.test(value)) token('number', Number(value), startLine, startColumn);
|
|
245
253
|
else token('word', value, startLine, startColumn);
|
|
246
254
|
}
|
|
@@ -249,11 +257,32 @@ function tokenize(source, filenameOrOptions = '<input>') {
|
|
|
249
257
|
return tokens;
|
|
250
258
|
}
|
|
251
259
|
|
|
260
|
+
|
|
261
|
+
function isDigitCode(code) {
|
|
262
|
+
return code >= 48 && code <= 57;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function isHexCode(code) {
|
|
266
|
+
return (code >= 48 && code <= 57) || (code >= 65 && code <= 70) || (code >= 97 && code <= 102);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function isWhitespaceCode(code) {
|
|
270
|
+
return code === 32 || code === 9 || code === 10 || code === 13 || code === 12;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function isLangTagCode(code) {
|
|
274
|
+
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 45;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function isVarNameCode(code) {
|
|
278
|
+
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 95 || code === 45;
|
|
279
|
+
}
|
|
280
|
+
|
|
252
281
|
function startsNumericLiteral(source, i) {
|
|
253
282
|
const ch = source[i];
|
|
254
283
|
const next = source[i + 1];
|
|
255
|
-
if (
|
|
256
|
-
if (ch === '.' &&
|
|
284
|
+
if (isDigitCode(ch.charCodeAt(0))) return true;
|
|
285
|
+
if (ch === '.' && next !== undefined && isDigitCode(next.charCodeAt(0))) return true;
|
|
257
286
|
return false;
|
|
258
287
|
}
|
|
259
288
|
|
|
@@ -44,4 +44,12 @@ test('playground inline scripts are syntactically valid', () => {
|
|
|
44
44
|
assert.ok(checked > 0, 'expected at least one inline playground script');
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
|
|
48
|
+
test('playground loads version from package.json at runtime', () => {
|
|
49
|
+
const html = fs.readFileSync(path.join(root, 'playground.html'), 'utf8');
|
|
50
|
+
assert.equal(/window\.__EYELENG_VERSION__/.test(html), false, 'playground.html must not hard-code the package version');
|
|
51
|
+
assert.match(html, /fetch\(new URL\(['"]package\.json['"],\s*window\.location\.href\)/);
|
|
52
|
+
assert.match(html, /id=["']version-label["'][^>]*>v…<\/span>/);
|
|
53
|
+
});
|
|
54
|
+
|
|
47
55
|
main();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"generatedBy": "npm run bench:update",
|
|
4
|
+
"note": "Machine-dependent performance baseline. Use npm run bench:update to refresh intentionally.",
|
|
5
|
+
"tolerance": {
|
|
6
|
+
"relative": 1.35,
|
|
7
|
+
"absoluteMs": 1000
|
|
8
|
+
},
|
|
9
|
+
"cases": [
|
|
10
|
+
{
|
|
11
|
+
"name": "deep-taxonomy-100000.srl",
|
|
12
|
+
"file": "examples/deep-taxonomy-100000.srl",
|
|
13
|
+
"repeat": 1,
|
|
14
|
+
"baselineMs": 5427.8,
|
|
15
|
+
"outputBytes": 5068161
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "fibonacci.srl",
|
|
19
|
+
"file": "examples/fibonacci.srl",
|
|
20
|
+
"repeat": 1,
|
|
21
|
+
"baselineMs": 1409.4,
|
|
22
|
+
"outputBytes": 10810713
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "fft32-numeric.srl",
|
|
26
|
+
"file": "examples/fft32-numeric.srl",
|
|
27
|
+
"repeat": 3,
|
|
28
|
+
"baselineMs": 182.2,
|
|
29
|
+
"outputBytes": 466351
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "path-discovery.srl",
|
|
33
|
+
"file": "examples/path-discovery.srl",
|
|
34
|
+
"repeat": 5,
|
|
35
|
+
"baselineMs": 1317.7,
|
|
36
|
+
"outputBytes": 736870
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
@@ -10,7 +10,6 @@ const {
|
|
|
10
10
|
isW3cRequired,
|
|
11
11
|
runShacl12RulesManifest,
|
|
12
12
|
formatShacl12RulesProgressLine,
|
|
13
|
-
writeShacl12RulesEarlReport,
|
|
14
13
|
} = require('../src/shacl12RulesManifest.js');
|
|
15
14
|
|
|
16
15
|
const rootManifestUrl = process.env.EYELENG_SHACL12_RULES_MANIFEST || defaultShacl12RulesManifestUrl;
|
|
@@ -46,13 +45,6 @@ async function main() {
|
|
|
46
45
|
return;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
try {
|
|
50
|
-
const reportPath = writeShacl12RulesEarlReport(result);
|
|
51
|
-
console.log(`${C.dim}EARL report: ${path.relative(path.join(__dirname, '..'), reportPath)}${C.n}`);
|
|
52
|
-
} catch (err) {
|
|
53
|
-
console.error(`Failed to write SHACL Rules EARL report: ${err.message}`);
|
|
54
|
-
result.counts.fail += 1;
|
|
55
|
-
}
|
|
56
48
|
|
|
57
49
|
for (const section of result.bySection) {
|
|
58
50
|
summaryLine(section.failed === 0 ? 'ok' : 'fail', section.passed, section.total, null, { label: section.section });
|
package/test/w3c-rdf.test.js
CHANGED
|
@@ -9,7 +9,6 @@ const {
|
|
|
9
9
|
defaultW3cRdfManifestUrls,
|
|
10
10
|
runW3cRdfManifests,
|
|
11
11
|
formatW3cRdfProgressLine,
|
|
12
|
-
writeRdfEarlReport,
|
|
13
12
|
} = require('../src/rdfManifest.js');
|
|
14
13
|
const { parseNQuads, parseN3 } = require('../src/rdfSyntax.js');
|
|
15
14
|
const { evaluateEntailmentTest, entails } = require('../src/rdfEntailment.js');
|
|
@@ -186,8 +185,6 @@ test('official W3C RDF manifests run with streaming progress when reachable', as
|
|
|
186
185
|
}
|
|
187
186
|
assert.equal(result.counts.fail, 0, `${result.counts.fail} W3C RDF failure(s)`);
|
|
188
187
|
assert.ok(result.counts.pass > 0, 'expected at least one W3C RDF parser test to pass');
|
|
189
|
-
const reportPath = writeRdfEarlReport(result);
|
|
190
|
-
console.log(`${C.dim}EARL report: ${path.relative(path.join(__dirname, '..'), reportPath)}${C.n}`);
|
|
191
188
|
summaryLine('ok', result.counts.pass, result.counts.total, result.durationMs, {
|
|
192
189
|
skipped: result.counts.skip,
|
|
193
190
|
label: 'W3C RDF manifests',
|
package/tools/bundle.js
CHANGED
|
@@ -57,22 +57,6 @@ function js(value) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
function packageVersion() {
|
|
61
|
-
const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
62
|
-
if (!packageJson.version) throw new Error('package.json is missing a version');
|
|
63
|
-
return packageJson.version;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function syncPlaygroundVersion() {
|
|
67
|
-
const version = packageVersion();
|
|
68
|
-
const outPath = path.join(root, playgroundOutput);
|
|
69
|
-
const html = fs.readFileSync(outPath, 'utf8');
|
|
70
|
-
const pattern = /(window\.__EYELENG_VERSION__\s*=\s*)["'][^"']*["']\s*;/;
|
|
71
|
-
if (!pattern.test(html)) throw new Error('Could not find window.__EYELENG_VERSION__ in playground.html');
|
|
72
|
-
const next = html.replace(pattern, `$1${js(version)};`);
|
|
73
|
-
fs.writeFileSync(outPath, next, 'utf8');
|
|
74
|
-
console.log(`wrote ${playgroundOutput} version ${version}`);
|
|
75
|
-
}
|
|
76
60
|
|
|
77
61
|
function ensureParentDir(filename) {
|
|
78
62
|
fs.mkdirSync(path.dirname(filename), { recursive: true });
|
|
@@ -172,4 +156,3 @@ function indent(source, spaces) {
|
|
|
172
156
|
|
|
173
157
|
buildCli();
|
|
174
158
|
buildBrowser();
|
|
175
|
-
syncPlaygroundVersion();
|