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/eyeleng.js CHANGED
@@ -1136,7 +1136,7 @@
1136
1136
  peekN(n) { return this.tokens[this.pos + n] || this.tokens[this.tokens.length - 1]; }
1137
1137
  previous() { return this.tokens[this.pos - 1]; }
1138
1138
  strictGrammar() { return !!this.options.strictGrammar; }
1139
- error(message, token = this.peek()) { return new SyntaxErrorWithLocation(message, token); }
1139
+ error(message, token = this.peek()) { return new SyntaxErrorWithLocation(message, token && token.filename ? token : { ...token, filename: this.options.filename || '<input>' }); }
1140
1140
  }
1141
1141
 
1142
1142
 
@@ -1285,7 +1285,7 @@
1285
1285
 
1286
1286
  function current() { return source[i]; }
1287
1287
  function peek(n = 1) { return source[i + n]; }
1288
- function startsWith(text) { return source.slice(i, i + text.length) === text; }
1288
+ function startsWith(text) { return source.startsWith(text, i); }
1289
1289
  function advance() {
1290
1290
  const ch = source[i++];
1291
1291
  if (ch === '\n') { line += 1; column = 1; }
@@ -1293,7 +1293,7 @@
1293
1293
  return ch;
1294
1294
  }
1295
1295
  function token(type, value, startLine, startColumn, extra = {}) {
1296
- tokens.push({ type, value, line: startLine, column: startColumn, filename, ...extra });
1296
+ tokens.push({ type, value, line: startLine, column: startColumn, ...extra });
1297
1297
  }
1298
1298
  function syntax(message, startLine, startColumn) {
1299
1299
  throw new SyntaxErrorWithLocation(message, { line: startLine, column: startColumn, filename });
@@ -1301,19 +1301,21 @@
1301
1301
 
1302
1302
  function readNumericLiteral() {
1303
1303
  let value = '';
1304
- while (i < source.length && /[0-9]/.test(current())) value += advance();
1305
- if (current() === '.' && /[0-9]/.test(peek())) {
1306
- value += advance();
1307
- while (i < source.length && /[0-9]/.test(current())) value += advance();
1304
+ const start = i;
1305
+ while (i < source.length && isDigitCode(source.charCodeAt(i))) { i += 1; column += 1; }
1306
+ if (source[i] === '.' && isDigitCode(source.charCodeAt(i + 1))) {
1307
+ i += 1; column += 1;
1308
+ while (i < source.length && isDigitCode(source.charCodeAt(i))) { i += 1; column += 1; }
1308
1309
  }
1310
+ value = source.slice(start, i);
1309
1311
  if (current() === 'e' || current() === 'E') {
1310
1312
  const saveI = i;
1311
1313
  const saveLine = line;
1312
1314
  const saveColumn = column;
1313
1315
  let exponent = advance();
1314
1316
  if (current() === '+' || current() === '-') exponent += advance();
1315
- if (/[0-9]/.test(current())) {
1316
- while (i < source.length && /[0-9]/.test(current())) exponent += advance();
1317
+ if (isDigitCode(source.charCodeAt(i))) {
1318
+ while (i < source.length && isDigitCode(source.charCodeAt(i))) exponent += advance();
1317
1319
  value += exponent;
1318
1320
  } else {
1319
1321
  i = saveI;
@@ -1331,7 +1333,7 @@
1331
1333
  const length = esc === 'u' ? 4 : 8;
1332
1334
  let hex = '';
1333
1335
  for (let j = 0; j < length; j += 1) {
1334
- if (!/[0-9A-Fa-f]/.test(current() || '')) syntax(`Invalid \\${esc} escape`, startLine, startColumn);
1336
+ if (!isHexCode(source.charCodeAt(i))) syntax(`Invalid \\${esc} escape`, startLine, startColumn);
1335
1337
  hex += advance();
1336
1338
  }
1337
1339
  const codePoint = Number.parseInt(hex, 16);
@@ -1350,7 +1352,7 @@
1350
1352
  const length = esc === 'u' ? 4 : 8;
1351
1353
  let hex = '';
1352
1354
  for (let j = 0; j < length; j += 1) {
1353
- if (!/[0-9A-Fa-f]/.test(current() || '')) syntax(`Invalid \\${esc} escape`, startLine, startColumn);
1355
+ if (!isHexCode(source.charCodeAt(i))) syntax(`Invalid \\${esc} escape`, startLine, startColumn);
1354
1356
  hex += advance();
1355
1357
  }
1356
1358
  const codePoint = Number.parseInt(hex, 16);
@@ -1364,7 +1366,7 @@
1364
1366
 
1365
1367
  while (i < source.length) {
1366
1368
  const ch = current();
1367
- if (/\s/.test(ch)) { advance(); continue; }
1369
+ if (isWhitespaceCode(source.charCodeAt(i))) { advance(); continue; }
1368
1370
  if (ch === '#') {
1369
1371
  while (i < source.length && current() !== '\n') advance();
1370
1372
  continue;
@@ -1450,18 +1452,21 @@
1450
1452
  }
1451
1453
 
1452
1454
  if (ch === '@') {
1453
- let value = advance();
1454
- while (i < source.length && /[A-Za-z0-9-]/.test(current())) value += advance();
1455
+ const wordStart = i;
1456
+ advance();
1457
+ while (i < source.length && isLangTagCode(source.charCodeAt(i))) { i += 1; column += 1; }
1458
+ const value = source.slice(wordStart, i);
1455
1459
  if (!/^@[A-Za-z]+(?:-[A-Za-z0-9]+)*(?:--[A-Za-z]+)?$/.test(value)) syntax(`Invalid language tag ${value}`, startLine, startColumn);
1456
1460
  token('word', value, startLine, startColumn);
1457
1461
  continue;
1458
1462
  }
1459
1463
 
1460
1464
  if (ch === '?' || ch === '$') {
1461
- let value = advance();
1462
- while (i < source.length && /[A-Za-z0-9_\-]/.test(current())) value += advance();
1463
- if (value.length === 1) syntax('Expected variable name', startLine, startColumn);
1464
- token('variable', value.slice(1), startLine, startColumn);
1465
+ const varStart = i;
1466
+ advance();
1467
+ while (i < source.length && isVarNameCode(source.charCodeAt(i))) { i += 1; column += 1; }
1468
+ if (i - varStart === 1) syntax('Expected variable name', startLine, startColumn);
1469
+ token('variable', source.slice(varStart + 1, i), startLine, startColumn);
1465
1470
  continue;
1466
1471
  }
1467
1472
 
@@ -1488,24 +1493,27 @@
1488
1493
  continue;
1489
1494
  }
1490
1495
 
1491
- let value = '';
1496
+ const wordStart = i;
1492
1497
  while (i < source.length) {
1493
- const c = current();
1494
- if (c === '\\' && peek() !== undefined) {
1495
- value += advance();
1496
- value += advance();
1498
+ const c = source[i];
1499
+ if (c === '\\' && source[i + 1] !== undefined) {
1500
+ i += 2;
1501
+ column += 2;
1497
1502
  continue;
1498
1503
  }
1499
- if (/\s/.test(c) || '{}()[],;|'.includes(c) || '=<>+-*/!^~'.includes(c)) break;
1504
+ const code = source.charCodeAt(i);
1505
+ if (isWhitespaceCode(code) || '{}()[],;|'.includes(c) || '=<>+-*/!^~'.includes(c)) break;
1500
1506
  if (c === '.') {
1501
- const n = peek();
1502
- if (n === undefined || /\s/.test(n) || '{}()[],;|'.includes(n) || '=<>+-*/!^~'.includes(n)) break;
1507
+ const n = source[i + 1];
1508
+ if (n === undefined || isWhitespaceCode(n.charCodeAt(0)) || '{}()[],;|'.includes(n) || '=<>+-*/!^~'.includes(n)) break;
1503
1509
  }
1504
1510
  if (c === '#') break;
1505
- value += advance();
1511
+ i += 1;
1512
+ column += 1;
1506
1513
  }
1507
- if (value.length === 0) syntax(`Unexpected character ${JSON.stringify(ch)}`, startLine, startColumn);
1514
+ if (i === wordStart) syntax(`Unexpected character ${JSON.stringify(ch)}`, startLine, startColumn);
1508
1515
 
1516
+ const value = source.slice(wordStart, i);
1509
1517
  if (/^[+-]?(?:(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+|\d+)$/.test(value)) token('number', Number(value), startLine, startColumn);
1510
1518
  else token('word', value, startLine, startColumn);
1511
1519
  }
@@ -1514,11 +1522,32 @@
1514
1522
  return tokens;
1515
1523
  }
1516
1524
 
1525
+
1526
+ function isDigitCode(code) {
1527
+ return code >= 48 && code <= 57;
1528
+ }
1529
+
1530
+ function isHexCode(code) {
1531
+ return (code >= 48 && code <= 57) || (code >= 65 && code <= 70) || (code >= 97 && code <= 102);
1532
+ }
1533
+
1534
+ function isWhitespaceCode(code) {
1535
+ return code === 32 || code === 9 || code === 10 || code === 13 || code === 12;
1536
+ }
1537
+
1538
+ function isLangTagCode(code) {
1539
+ return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 45;
1540
+ }
1541
+
1542
+ function isVarNameCode(code) {
1543
+ return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 95 || code === 45;
1544
+ }
1545
+
1517
1546
  function startsNumericLiteral(source, i) {
1518
1547
  const ch = source[i];
1519
1548
  const next = source[i + 1];
1520
- if (/[0-9]/.test(ch)) return true;
1521
- if (ch === '.' && /[0-9]/.test(next)) return true;
1549
+ if (isDigitCode(ch.charCodeAt(0))) return true;
1550
+ if (ch === '.' && next !== undefined && isDigitCode(next.charCodeAt(0))) return true;
1522
1551
  return false;
1523
1552
  }
1524
1553
 
@@ -3148,11 +3177,13 @@
3148
3177
  }
3149
3178
 
3150
3179
  function variable(name) {
3151
- return { type: 'var', value: String(name).replace(/^[?$]/, '') };
3180
+ const value = String(name);
3181
+ return { type: 'var', value: value[0] === '?' || value[0] === '$' ? value.slice(1) : value };
3152
3182
  }
3153
3183
 
3154
3184
  function blankNode(value) {
3155
- return { type: 'blank', value: String(value).replace(/^_:/, '') };
3185
+ const label = String(value);
3186
+ return { type: 'blank', value: label.startsWith('_:') ? label.slice(2) : label };
3156
3187
  }
3157
3188
 
3158
3189
  function literal(value, datatype = null, lang = null, langDir = null) {
@@ -3407,6 +3438,8 @@
3407
3438
  const RDF_LANGSTRING = `${RDF_NS}langString`;
3408
3439
  const RDF_DIRLANGSTRING = `${RDF_NS}dirLangString`;
3409
3440
  const NUMERIC_DATATYPES = new Set([XSD_INTEGER, XSD_DECIMAL, XSD_DOUBLE]);
3441
+ const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
3442
+ const MIN_SAFE_INTEGER_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
3410
3443
 
3411
3444
  // This table is intentionally shaped by the SHACL 1.2 Rules grammar production BuiltInCall.
3412
3445
  // Keys are the canonical spellings used by the draft; lookup is case-insensitive so examples
@@ -3566,7 +3599,7 @@
3566
3599
  }
3567
3600
 
3568
3601
  function fromIntegerResult(value) {
3569
- if (value <= BigInt(Number.MAX_SAFE_INTEGER) && value >= BigInt(Number.MIN_SAFE_INTEGER)) return Number(value);
3602
+ if (value <= MAX_SAFE_INTEGER_BIGINT && value >= MIN_SAFE_INTEGER_BIGINT) return Number(value);
3570
3603
  return value;
3571
3604
  }
3572
3605
 
@@ -4277,6 +4310,18 @@
4277
4310
  layerIndexes,
4278
4311
  analysis.dependency ? analysis.dependency.edges : [],
4279
4312
  );
4313
+ const baseContext = {
4314
+ ...evalOptions,
4315
+ maxIterations,
4316
+ inputKeys,
4317
+ inferred,
4318
+ trace,
4319
+ perRule,
4320
+ layer: 0,
4321
+ iteration: 0,
4322
+ startingIterations: 0,
4323
+ recursiveLayer: false,
4324
+ };
4280
4325
 
4281
4326
  for (let layerIndex = 0; layerIndex < layerIndexes.length; layerIndex += 1) {
4282
4327
  const layer = layerIndexes[layerIndex];
@@ -4286,30 +4331,17 @@
4286
4331
  if (runOnce.length > 0) {
4287
4332
  iterations += 1;
4288
4333
  for (const ruleIndex of runOnce) {
4289
- const added = applyRuleOnce(program, store, ruleIndex, {
4290
- ...evalOptions,
4291
- inputKeys,
4292
- inferred,
4293
- trace,
4294
- perRule,
4295
- layer: layerIndex + 1,
4296
- iteration: iterations,
4297
- });
4334
+ baseContext.layer = layerIndex + 1;
4335
+ baseContext.iteration = iterations;
4336
+ const added = applyRuleOnce(program, store, ruleIndex, baseContext);
4298
4337
  ruleApplications += added.applications;
4299
4338
  }
4300
4339
  }
4301
4340
 
4302
- const ordinaryResult = runRulesToFixpoint(program, store, ordinary, {
4303
- ...evalOptions,
4304
- maxIterations,
4305
- inputKeys,
4306
- inferred,
4307
- trace,
4308
- perRule,
4309
- layer: layerIndex + 1,
4310
- startingIterations: iterations,
4311
- recursiveLayer: recursiveLayerFlags[layerIndex],
4312
- });
4341
+ baseContext.layer = layerIndex + 1;
4342
+ baseContext.startingIterations = iterations;
4343
+ baseContext.recursiveLayer = recursiveLayerFlags[layerIndex];
4344
+ const ordinaryResult = runRulesToFixpoint(program, store, ordinary, baseContext);
4313
4345
  iterations = ordinaryResult.iterations;
4314
4346
  ruleApplications += ordinaryResult.ruleApplications;
4315
4347
  }
@@ -4340,10 +4372,8 @@
4340
4372
  const iteration = context.startingIterations + 1;
4341
4373
  let ruleApplications = 0;
4342
4374
  for (const ruleIndex of ruleIndexes) {
4343
- const applied = applyRuleOnce(program, store, ruleIndex, {
4344
- ...context,
4345
- iteration,
4346
- });
4375
+ context.iteration = iteration;
4376
+ const applied = applyRuleOnce(program, store, ruleIndex, context);
4347
4377
  ruleApplications += applied.applications;
4348
4378
  }
4349
4379
  return { iterations: iteration, ruleApplications };
@@ -4359,10 +4389,8 @@
4359
4389
  let addedInIteration = 0;
4360
4390
 
4361
4391
  for (const ruleIndex of ruleIndexes) {
4362
- const applied = applyRuleOnce(program, store, ruleIndex, {
4363
- ...context,
4364
- iteration: iterations,
4365
- });
4392
+ context.iteration = iterations;
4393
+ const applied = applyRuleOnce(program, store, ruleIndex, context);
4366
4394
  addedInIteration += applied.added;
4367
4395
  ruleApplications += applied.applications;
4368
4396
  }
@@ -4391,16 +4419,24 @@
4391
4419
  return flags;
4392
4420
  }
4393
4421
 
4422
+
4394
4423
  function applyRuleOnce(program, store, ruleIndex, context) {
4395
4424
  const rule = program.rules[ruleIndex];
4396
4425
  let applications = 0;
4397
4426
  let added = 0;
4398
- const seenBindings = new Set();
4427
+ const dedupeBindings = rule.body.some((clause) => clause.type === 'path');
4428
+ const seenBindings = dedupeBindings ? new Set() : null;
4399
4429
 
4400
- for (const binding of evaluateBodyStream(rule.body, store, {}, context)) {
4401
- const key = bindingKey(binding);
4402
- if (seenBindings.has(key)) continue;
4403
- seenBindings.add(key);
4430
+ const bodyBindings = rule.body.length === 1 && rule.body[0].type === 'triple'
4431
+ ? store.match(rule.body[0].triple, {})
4432
+ : evaluateBodyStream(rule.body, store, {}, context);
4433
+
4434
+ for (const binding of bodyBindings) {
4435
+ if (seenBindings) {
4436
+ const key = bindingKey(binding);
4437
+ if (seenBindings.has(key)) continue;
4438
+ seenBindings.add(key);
4439
+ }
4404
4440
  applications += 1;
4405
4441
  context.perRule[ruleIndex].applications += 1;
4406
4442
 
@@ -4519,7 +4555,7 @@
4519
4555
  "src/store.js": function (require, module, exports) {
4520
4556
  'use strict';
4521
4557
 
4522
- const { tripleKey, termKey, termEquals, cloneTerm } = require('./term.js');
4558
+ const { tripleKey, termKey, termEquals } = require('./term.js');
4523
4559
 
4524
4560
  class TripleStore {
4525
4561
  constructor(triples = []) {
@@ -4628,7 +4664,7 @@
4628
4664
  }
4629
4665
 
4630
4666
  function normalizeTriple(triple) {
4631
- return { s: cloneTerm(triple.s), p: cloneTerm(triple.p), o: cloneTerm(triple.o) };
4667
+ return { s: triple.s, p: triple.p, o: triple.o };
4632
4668
  }
4633
4669
 
4634
4670
  function bindingKey(binding) {
@@ -4788,9 +4824,7 @@
4788
4824
 
4789
4825
  program.rules.forEach((rule, index) => {
4790
4826
  const name = ruleName(rule, index);
4791
- const label = displayRuleName(name, program.prefixes || {});
4792
4827
  const bound = boundVariables(rule.body);
4793
- const positive = positiveVariables(rule.body);
4794
4828
  const head = new Set();
4795
4829
  for (const triple of rule.head) collectTripleVars(triple, head);
4796
4830
 
@@ -4800,7 +4834,7 @@
4800
4834
  code: 'unsafe-head-variable',
4801
4835
  severity: 'error',
4802
4836
  rule: name,
4803
- message: `${label} has unbound head variable ?${variable}`,
4837
+ message: `${displayRuleName(name, program.prefixes || {})} has unbound head variable ?${variable}`,
4804
4838
  });
4805
4839
  }
4806
4840
  }
@@ -4811,19 +4845,19 @@
4811
4845
  code: 'invalid-head-predicate',
4812
4846
  severity: 'error',
4813
4847
  rule: name,
4814
- message: `${label} has a non-IRI/non-variable predicate in the head`,
4848
+ message: `${displayRuleName(name, program.prefixes || {})} has a non-IRI/non-variable predicate in the head`,
4815
4849
  });
4816
4850
  }
4817
4851
  }
4818
4852
 
4819
- diagnostics.push(...sequentialWellFormednessDiagnostics(rule.body, name, label));
4853
+ diagnostics.push(...sequentialWellFormednessDiagnostics(rule.body, name, program.prefixes || {}));
4820
4854
 
4821
4855
  if (rule.runOnce && recursiveIndexes.has(index)) {
4822
4856
  diagnostics.push({
4823
4857
  code: 'recursive-assignment-rule',
4824
4858
  severity: 'warning',
4825
4859
  rule: name,
4826
- message: `${label} is a run-once rule in a recursive dependency cycle`,
4860
+ message: `${displayRuleName(name, program.prefixes || {})} is a run-once rule in a recursive dependency cycle`,
4827
4861
  });
4828
4862
  }
4829
4863
 
@@ -5179,7 +5213,7 @@
5179
5213
  return components;
5180
5214
  }
5181
5215
 
5182
- function sequentialWellFormednessDiagnostics(clauses, ruleNameValue, label) {
5216
+ function sequentialWellFormednessDiagnostics(clauses, ruleNameValue, prefixes = {}) {
5183
5217
  const diagnostics = [];
5184
5218
 
5185
5219
  function visit(items, initialBound, scopeLabel) {
@@ -5194,7 +5228,7 @@
5194
5228
  code: 'unbound-filter-variable',
5195
5229
  severity: 'error',
5196
5230
  rule: ruleNameValue,
5197
- message: `${label} FILTER uses ?${variable} before it is bound${scopeLabel}`,
5231
+ message: `${displayRuleName(ruleNameValue, prefixes)} FILTER uses ?${variable} before it is bound${scopeLabel}`,
5198
5232
  });
5199
5233
  }
5200
5234
  }
@@ -5204,7 +5238,7 @@
5204
5238
  code: 'assignment-variable-already-bound',
5205
5239
  severity: 'error',
5206
5240
  rule: ruleNameValue,
5207
- message: `${label} SET assigns ?${clause.variable}, but that variable is already bound${scopeLabel}`,
5241
+ message: `${displayRuleName(ruleNameValue, prefixes)} SET assigns ?${clause.variable}, but that variable is already bound${scopeLabel}`,
5208
5242
  });
5209
5243
  }
5210
5244
  for (const variable of expressionVariables(clause.expr)) {
@@ -5213,7 +5247,7 @@
5213
5247
  code: 'unbound-assignment-variable',
5214
5248
  severity: 'error',
5215
5249
  rule: ruleNameValue,
5216
- message: `${label} SET expression uses ?${variable} before it is bound${scopeLabel}`,
5250
+ message: `${displayRuleName(ruleNameValue, prefixes)} SET expression uses ?${variable} before it is bound${scopeLabel}`,
5217
5251
  });
5218
5252
  }
5219
5253
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeleng",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "The EYE Logic Engine: a JavaScript implementation of SHACL Rules, including SRL and RDF Rules syntax front-ends.",
5
5
  "keywords": [
6
6
  "SRL",
@@ -37,6 +37,7 @@
37
37
  "pretest": "node tools/bundle.js",
38
38
  "test": "node test/run.js",
39
39
  "preversion": "npm test",
40
+ "version": "node tools/bundle.js",
40
41
  "postversion": "git push origin HEAD --follow-tags",
41
42
  "w3c:rules": "node tools/w3c-shacl12-rules.js",
42
43
  "w3c:rdf": "node tools/w3c-rdf.js",
@@ -44,7 +45,10 @@
44
45
  "w3c:rdf:earl": "node tools/w3c-rdf.js --earl",
45
46
  "w3c:all": "npm run w3c:rules && npm run w3c:rdf",
46
47
  "w3c:rules:json": "node tools/w3c-shacl12-rules.js --json",
47
- "w3c:rules:earl": "node tools/w3c-shacl12-rules.js --earl"
48
+ "w3c:rules:earl": "node tools/w3c-shacl12-rules.js --earl",
49
+ "bench": "node tools/perf-bench.js --check",
50
+ "bench:report": "node tools/perf-bench.js --report",
51
+ "bench:update": "node tools/perf-bench.js --update"
48
52
  },
49
53
  "publishConfig": {
50
54
  "access": "public"