eyeleng 1.0.11 → 1.0.13
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/README.md +4 -2
- package/dist/browser/eyeleng.browser.js +159 -22
- package/examples/assignment.srl +1 -1
- package/examples/base-and-literals.srl +1 -1
- package/examples/output/cat-koko.trig +2 -2
- package/examples/path-discovery.srl +5 -5
- package/eyeleng.js +161 -24
- package/package.json +5 -4
- package/reports/w3c-shacl12-rules-earl.ttl +1055 -1336
- package/src/analyze.js +59 -16
- package/src/cli.js +2 -2
- package/src/engine.js +100 -6
- package/src/shacl12RulesManifest.js +5 -0
- package/test/api.test.js +82 -1
- package/test/run.js +0 -1
- package/tools/w3c-rdf.js +11 -7
- package/tools/w3c-shacl12-rules.js +11 -6
package/eyeleng.js
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
const VERSION = readPackageVersion();
|
|
41
41
|
|
|
42
42
|
function help() {
|
|
43
|
-
return `eyeleng ${VERSION}\n\nA dependency-free JavaScript implementation experiment for the SHACL 1.2 Rules draft, including SRL and RDF Rules syntax front-ends.\n\nUsage:\n eyeleng [options] [file ...]\n\nOptions:\n --all Print the full closure, including input facts\n --json Print JSON instead of compact triples/bindings\n --trace Print derivation trace to stderr, or include it in JSON\n --stats Print iteration and triple counts to stderr\n --check Parse and analyze only; do not run rules\n --strict Treat static warnings as errors\n --deps Print rule dependency edges during --check\n --query TEXT Run a raw SRL body pattern over the closure\n --query-file FILE Read a raw SRL body pattern from a file\n --max-iterations N Stop after N fixpoint iterations within a recursive layer\n --no-imports Parse IMPORTS/owl:imports but do not load imported rule sets\n --rdf-messages Parse input as an RDF Message Log\n --stream-messages Replay RDF Message Log envelopes\n --include-message-facts Include payload facts while parsing RDF Message Logs\n --syntax MODE Use srl, rdf, or auto syntax detection (default auto)\n --ruleset TERM In RDF syntax, run only the selected srl:RuleSet\n --version Print version\n -h, --help Print this help\n\nWith no file arguments, eyeleng reads from stdin.\n`;
|
|
43
|
+
return `eyeleng ${VERSION}\n\nA dependency-free JavaScript implementation experiment for the SHACL 1.2 Rules draft, including SRL and RDF Rules syntax front-ends.\n\nUsage:\n eyeleng [options] [file ...]\n\nOptions:\n --all Print the full closure, including input facts\n --json Print JSON instead of compact triples/bindings\n --trace Print derivation trace to stderr, or include it in JSON\n --stats Print iteration and triple counts to stderr\n --check Parse and analyze only; do not run rules\n --strict Treat static warnings as errors, including recursive term generation\n --deps Print rule dependency edges during --check\n --query TEXT Run a raw SRL body pattern over the closure\n --query-file FILE Read a raw SRL body pattern from a file\n --max-iterations N Stop after N fixpoint iterations within a recursive layer\n --no-imports Parse IMPORTS/owl:imports but do not load imported rule sets\n --rdf-messages Parse input as an RDF Message Log\n --stream-messages Replay RDF Message Log envelopes\n --include-message-facts Include payload facts while parsing RDF Message Logs\n --syntax MODE Use srl, rdf, or auto syntax detection (default auto)\n --ruleset TERM In RDF syntax, run only the selected srl:RuleSet\n --version Print version\n -h, --help Print this help\n\nWith no file arguments, eyeleng reads from stdin.\n`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function parseArgs(argv) {
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
for (const edge of edges) {
|
|
149
149
|
const from = formatRuleName(analysis.dependency.rules[edge.from].name, prefixes);
|
|
150
150
|
const to = formatRuleName(analysis.dependency.rules[edge.to].name, prefixes);
|
|
151
|
-
const kind = edge.
|
|
151
|
+
const kind = edge.negated ? 'NOT' : (edge.termGeneration ? 'generates' : 'uses');
|
|
152
152
|
stderr.write(`eyeleng: deps: ${from} --${kind} ${edge.predicate ? compactIRI(edge.predicate, prefixes) : '*'}--> ${to}\n`);
|
|
153
153
|
}
|
|
154
154
|
if (analysis.dependency.layers && analysis.dependency.layers.length > 0) {
|
|
@@ -4278,8 +4278,8 @@
|
|
|
4278
4278
|
"src/engine.js": function (require, module, exports) {
|
|
4279
4279
|
'use strict';
|
|
4280
4280
|
|
|
4281
|
-
const { TripleStore, bindingKey
|
|
4282
|
-
const { tripleKey, termEquals } = require('./term.js');
|
|
4281
|
+
const { TripleStore, bindingKey } = require('./store.js');
|
|
4282
|
+
const { tripleKey, termKey, termEquals, blankNode, tripleTerm } = require('./term.js');
|
|
4283
4283
|
const { evalExpression, booleanValue, asTerm } = require('./builtins.js');
|
|
4284
4284
|
const { analyze } = require('./analyze.js');
|
|
4285
4285
|
|
|
@@ -4299,7 +4299,7 @@
|
|
|
4299
4299
|
runOnce: !!rule.runOnce,
|
|
4300
4300
|
}));
|
|
4301
4301
|
|
|
4302
|
-
const analysis = options.analysis || analyze(program);
|
|
4302
|
+
const analysis = options.analysis || analyze(program, options);
|
|
4303
4303
|
if (analysis.errors && analysis.errors.length > 0 && !options.ignoreAnalysisErrors) {
|
|
4304
4304
|
throw new Error(`Analysis failed: ${analysis.errors.map((error) => error.message).join('; ')}`);
|
|
4305
4305
|
}
|
|
@@ -4310,6 +4310,9 @@
|
|
|
4310
4310
|
layerIndexes,
|
|
4311
4311
|
analysis.dependency ? analysis.dependency.edges : [],
|
|
4312
4312
|
);
|
|
4313
|
+
const relaxedRecursiveRunOnce = options.relaxedRecursion === false
|
|
4314
|
+
? new Set()
|
|
4315
|
+
: recursiveTermGenerationRuleIndexes(analysis);
|
|
4313
4316
|
const baseContext = {
|
|
4314
4317
|
...evalOptions,
|
|
4315
4318
|
maxIterations,
|
|
@@ -4325,8 +4328,8 @@
|
|
|
4325
4328
|
|
|
4326
4329
|
for (let layerIndex = 0; layerIndex < layerIndexes.length; layerIndex += 1) {
|
|
4327
4330
|
const layer = layerIndexes[layerIndex];
|
|
4328
|
-
const ordinary = layer.filter((ruleIndex) => !program.rules[ruleIndex].runOnce);
|
|
4329
|
-
const runOnce = layer.filter((ruleIndex) => program.rules[ruleIndex].runOnce);
|
|
4331
|
+
const ordinary = layer.filter((ruleIndex) => !program.rules[ruleIndex].runOnce || relaxedRecursiveRunOnce.has(ruleIndex));
|
|
4332
|
+
const runOnce = layer.filter((ruleIndex) => program.rules[ruleIndex].runOnce && !relaxedRecursiveRunOnce.has(ruleIndex));
|
|
4330
4333
|
|
|
4331
4334
|
if (runOnce.length > 0) {
|
|
4332
4335
|
iterations += 1;
|
|
@@ -4426,6 +4429,7 @@
|
|
|
4426
4429
|
let added = 0;
|
|
4427
4430
|
const dedupeBindings = rule.body.some((clause) => clause.type === 'path');
|
|
4428
4431
|
const seenBindings = dedupeBindings ? new Set() : null;
|
|
4432
|
+
const headBlankLabels = collectHeadBlankLabels(rule.head);
|
|
4429
4433
|
|
|
4430
4434
|
const bodyBindings = rule.body.length === 1 && rule.body[0].type === 'triple'
|
|
4431
4435
|
? store.match(rule.body[0].triple, {})
|
|
@@ -4440,8 +4444,10 @@
|
|
|
4440
4444
|
applications += 1;
|
|
4441
4445
|
context.perRule[ruleIndex].applications += 1;
|
|
4442
4446
|
|
|
4447
|
+
const headBlankMap = headBlankLabels.size > 0 ? new Map() : null;
|
|
4448
|
+
const skolemKey = headBlankMap ? skolemizationKey(ruleIndex, binding) : null;
|
|
4443
4449
|
for (const head of rule.head) {
|
|
4444
|
-
const triple =
|
|
4450
|
+
const triple = instantiateHeadTriple(head, binding, headBlankLabels, headBlankMap, skolemKey);
|
|
4445
4451
|
if (!triple) continue;
|
|
4446
4452
|
if (store.add(triple)) {
|
|
4447
4453
|
added += 1;
|
|
@@ -4463,6 +4469,94 @@
|
|
|
4463
4469
|
return { applications, added };
|
|
4464
4470
|
}
|
|
4465
4471
|
|
|
4472
|
+
function recursiveTermGenerationRuleIndexes(analysis) {
|
|
4473
|
+
const out = new Set();
|
|
4474
|
+
if (!analysis || !analysis.dependency || !analysis.diagnostics) return out;
|
|
4475
|
+
const byName = new Map((analysis.dependency.rules || []).map((rule) => [rule.name, rule.index]));
|
|
4476
|
+
for (const diagnostic of analysis.diagnostics) {
|
|
4477
|
+
if (diagnostic.code !== 'recursive-assignment-rule') continue;
|
|
4478
|
+
if (byName.has(diagnostic.rule)) out.add(byName.get(diagnostic.rule));
|
|
4479
|
+
}
|
|
4480
|
+
return out;
|
|
4481
|
+
}
|
|
4482
|
+
|
|
4483
|
+
function instantiateHeadTriple(pattern, binding, headBlankLabels, headBlankMap, skolemKey) {
|
|
4484
|
+
const s = instantiateHeadTerm(pattern.s, binding, headBlankLabels, headBlankMap, skolemKey);
|
|
4485
|
+
const p = instantiateHeadTerm(pattern.p, binding, headBlankLabels, headBlankMap, skolemKey);
|
|
4486
|
+
const o = instantiateHeadTerm(pattern.o, binding, headBlankLabels, headBlankMap, skolemKey);
|
|
4487
|
+
if (!s || !p || !o) return null;
|
|
4488
|
+
if (p.type !== 'iri') return null;
|
|
4489
|
+
return { s, p, o };
|
|
4490
|
+
}
|
|
4491
|
+
|
|
4492
|
+
function instantiateHeadTerm(term, binding, headBlankLabels, headBlankMap, skolemKey) {
|
|
4493
|
+
if (term.type === 'var') return binding[term.value] || null;
|
|
4494
|
+
if (term.type === 'blank' && headBlankLabels.has(term.value)) {
|
|
4495
|
+
let label = headBlankMap.get(term.value);
|
|
4496
|
+
if (!label) {
|
|
4497
|
+
label = `sk_${deterministicSkolemIdFromKey(`${skolemKey}|${term.value}`).replace(/-/g, '_')}`;
|
|
4498
|
+
headBlankMap.set(term.value, label);
|
|
4499
|
+
}
|
|
4500
|
+
return blankNode(label);
|
|
4501
|
+
}
|
|
4502
|
+
if (term.type === 'triple') {
|
|
4503
|
+
const s = instantiateHeadTerm(term.s, binding, headBlankLabels, headBlankMap, skolemKey);
|
|
4504
|
+
const p = instantiateHeadTerm(term.p, binding, headBlankLabels, headBlankMap, skolemKey);
|
|
4505
|
+
const o = instantiateHeadTerm(term.o, binding, headBlankLabels, headBlankMap, skolemKey);
|
|
4506
|
+
if (!s || !p || !o) return null;
|
|
4507
|
+
return tripleTerm(s, p, o);
|
|
4508
|
+
}
|
|
4509
|
+
return term;
|
|
4510
|
+
}
|
|
4511
|
+
|
|
4512
|
+
function collectHeadBlankLabels(head) {
|
|
4513
|
+
const labels = new Set();
|
|
4514
|
+
for (const triple of head || []) {
|
|
4515
|
+
collectBlankLabelsFromTerm(triple.s, labels);
|
|
4516
|
+
collectBlankLabelsFromTerm(triple.p, labels);
|
|
4517
|
+
collectBlankLabelsFromTerm(triple.o, labels);
|
|
4518
|
+
}
|
|
4519
|
+
return labels;
|
|
4520
|
+
}
|
|
4521
|
+
|
|
4522
|
+
function collectBlankLabelsFromTerm(term, labels) {
|
|
4523
|
+
if (!term) return;
|
|
4524
|
+
if (term.type === 'blank') {
|
|
4525
|
+
labels.add(term.value);
|
|
4526
|
+
return;
|
|
4527
|
+
}
|
|
4528
|
+
if (term.type === 'triple') {
|
|
4529
|
+
collectBlankLabelsFromTerm(term.s, labels);
|
|
4530
|
+
collectBlankLabelsFromTerm(term.p, labels);
|
|
4531
|
+
collectBlankLabelsFromTerm(term.o, labels);
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
function skolemizationKey(ruleIndex, binding) {
|
|
4536
|
+
let out = `rule:${ruleIndex}`;
|
|
4537
|
+
for (const name of Object.keys(binding).sort()) {
|
|
4538
|
+
const value = binding[name];
|
|
4539
|
+
out += `|${name}=${value ? termKey(value) : 'unbound'}`;
|
|
4540
|
+
}
|
|
4541
|
+
return out;
|
|
4542
|
+
}
|
|
4543
|
+
|
|
4544
|
+
function deterministicSkolemIdFromKey(key) {
|
|
4545
|
+
let h1 = 0x811c9dc5;
|
|
4546
|
+
let h2 = 0x811c9dc5;
|
|
4547
|
+
let h3 = 0x811c9dc5;
|
|
4548
|
+
let h4 = 0x811c9dc5;
|
|
4549
|
+
for (let i = 0; i < key.length; i += 1) {
|
|
4550
|
+
const c = key.charCodeAt(i);
|
|
4551
|
+
h1 ^= c; h1 = Math.imul(h1, 0x01000193) >>> 0;
|
|
4552
|
+
h2 ^= c + 1; h2 = Math.imul(h2, 0x01000193) >>> 0;
|
|
4553
|
+
h3 ^= c + 2; h3 = Math.imul(h3, 0x01000193) >>> 0;
|
|
4554
|
+
h4 ^= c + 3; h4 = Math.imul(h4, 0x01000193) >>> 0;
|
|
4555
|
+
}
|
|
4556
|
+
return [h1, h2, h3, h4].map((h) => h.toString(16).padStart(8, '0')).join('');
|
|
4557
|
+
}
|
|
4558
|
+
|
|
4559
|
+
|
|
4466
4560
|
function evaluateBody(clauses, store, initialBinding = {}, options = {}) {
|
|
4467
4561
|
const bindings = [];
|
|
4468
4562
|
const seen = new Set();
|
|
@@ -4819,8 +4913,8 @@
|
|
|
4819
4913
|
function analyze(program, options = {}) {
|
|
4820
4914
|
const diagnostics = [];
|
|
4821
4915
|
const dependency = dependencyGraph(program, options);
|
|
4822
|
-
const
|
|
4823
|
-
const recursiveIndexes =
|
|
4916
|
+
const hasTermGeneratingRules = dependency.rules.some((rule) => rule.createsTerms);
|
|
4917
|
+
const recursiveIndexes = hasTermGeneratingRules ? recursiveRuleIndexes(dependency) : new Set();
|
|
4824
4918
|
|
|
4825
4919
|
program.rules.forEach((rule, index) => {
|
|
4826
4920
|
const name = ruleName(rule, index);
|
|
@@ -4852,12 +4946,13 @@
|
|
|
4852
4946
|
|
|
4853
4947
|
diagnostics.push(...sequentialWellFormednessDiagnostics(rule.body, name, program.prefixes || {}));
|
|
4854
4948
|
|
|
4855
|
-
|
|
4949
|
+
const depRule = dependency.rules[index] || {};
|
|
4950
|
+
if (depRule.createsTerms && recursiveIndexes.has(index)) {
|
|
4856
4951
|
diagnostics.push({
|
|
4857
4952
|
code: 'recursive-assignment-rule',
|
|
4858
4953
|
severity: 'warning',
|
|
4859
4954
|
rule: name,
|
|
4860
|
-
message: `${displayRuleName(name, program.prefixes || {})}
|
|
4955
|
+
message: `${displayRuleName(name, program.prefixes || {})} creates terms in a recursive dependency cycle; relaxed mode allows this but termination is not guaranteed (use --strict to reject it)`,
|
|
4861
4956
|
});
|
|
4862
4957
|
}
|
|
4863
4958
|
|
|
@@ -4904,34 +4999,47 @@
|
|
|
4904
4999
|
negativePredicates: new Set(negativePatterns.flatMap((triple) => predicateIRIs(triple))),
|
|
4905
5000
|
runOnce: !!rule.runOnce,
|
|
4906
5001
|
hasAssignment: ruleHasAssignment(rule, options),
|
|
5002
|
+
hasTermGeneratingAssignment: ruleHasTermGeneratingAssignment(rule, options),
|
|
4907
5003
|
headHasBlankNode: ruleHeadHasBlankNode(rule),
|
|
5004
|
+
createsTerms: ruleCreatesTerms(rule, options),
|
|
4908
5005
|
};
|
|
4909
5006
|
});
|
|
4910
5007
|
|
|
4911
5008
|
const edgeMap = new Map();
|
|
4912
|
-
function addEdge(from, to,
|
|
5009
|
+
function addEdge(from, to, kind, predicate) {
|
|
4913
5010
|
const label = predicate || '*';
|
|
4914
5011
|
const key = `${from.index}->${to.index}:${label}`;
|
|
5012
|
+
const negated = kind === 'negated';
|
|
5013
|
+
const termGeneration = kind === 'term-generation';
|
|
4915
5014
|
const existing = edgeMap.get(key);
|
|
4916
5015
|
if (existing) {
|
|
4917
|
-
existing.
|
|
5016
|
+
existing.negated = existing.negated || negated;
|
|
5017
|
+
existing.termGeneration = existing.termGeneration || termGeneration;
|
|
5018
|
+
existing.negative = existing.negated || existing.termGeneration;
|
|
4918
5019
|
return;
|
|
4919
5020
|
}
|
|
4920
|
-
edgeMap.set(key, {
|
|
5021
|
+
edgeMap.set(key, {
|
|
5022
|
+
from: from.index,
|
|
5023
|
+
to: to.index,
|
|
5024
|
+
negative: negated || termGeneration,
|
|
5025
|
+
negated,
|
|
5026
|
+
termGeneration,
|
|
5027
|
+
predicate,
|
|
5028
|
+
});
|
|
4921
5029
|
}
|
|
4922
5030
|
|
|
4923
5031
|
const headIndex = buildHeadTemplateIndex(rules);
|
|
4924
5032
|
|
|
4925
5033
|
for (const from of rules) {
|
|
4926
|
-
const forceClosed = from.
|
|
5034
|
+
const forceClosed = from.createsTerms;
|
|
4927
5035
|
for (const pattern of from.positivePatterns) {
|
|
4928
5036
|
for (const candidate of candidateHeadTemplates(headIndex, pattern)) {
|
|
4929
|
-
if (canPossiblyGenerate(candidate.template, pattern)) addEdge(from, rules[candidate.ruleIndex], forceClosed, dependencyPredicateLabel(pattern));
|
|
5037
|
+
if (canPossiblyGenerate(candidate.template, pattern)) addEdge(from, rules[candidate.ruleIndex], forceClosed ? 'term-generation' : 'positive', dependencyPredicateLabel(pattern));
|
|
4930
5038
|
}
|
|
4931
5039
|
}
|
|
4932
5040
|
for (const pattern of from.negativePatterns) {
|
|
4933
5041
|
for (const candidate of candidateHeadTemplates(headIndex, pattern)) {
|
|
4934
|
-
if (canPossiblyGenerate(candidate.template, pattern)) addEdge(from, rules[candidate.ruleIndex],
|
|
5042
|
+
if (canPossiblyGenerate(candidate.template, pattern)) addEdge(from, rules[candidate.ruleIndex], 'negated', dependencyPredicateLabel(pattern));
|
|
4935
5043
|
}
|
|
4936
5044
|
}
|
|
4937
5045
|
}
|
|
@@ -4947,7 +5055,7 @@
|
|
|
4947
5055
|
const unstratifiedCycles = [];
|
|
4948
5056
|
const seen = new Set();
|
|
4949
5057
|
for (const edge of edges) {
|
|
4950
|
-
if (!edge.
|
|
5058
|
+
if (!edge.negated) continue;
|
|
4951
5059
|
if (edge.from === edge.to && rules[edge.from].runOnce && !rules[edge.from].headHasBlankNode) continue;
|
|
4952
5060
|
if (componentOf.get(edge.from) !== componentOf.get(edge.to)) continue;
|
|
4953
5061
|
const component = components[componentOf.get(edge.from)];
|
|
@@ -4970,7 +5078,10 @@
|
|
|
4970
5078
|
positivePredicates: Array.from(rule.positivePredicates),
|
|
4971
5079
|
negativePredicates: Array.from(rule.negativePredicates),
|
|
4972
5080
|
runOnce: rule.runOnce,
|
|
5081
|
+
hasAssignment: rule.hasAssignment,
|
|
5082
|
+
hasTermGeneratingAssignment: rule.hasTermGeneratingAssignment,
|
|
4973
5083
|
headHasBlankNode: rule.headHasBlankNode,
|
|
5084
|
+
createsTerms: rule.createsTerms,
|
|
4974
5085
|
})),
|
|
4975
5086
|
edges,
|
|
4976
5087
|
components: components.map((component) => component.map((ruleIndex) => rules[ruleIndex].name)),
|
|
@@ -5140,7 +5251,14 @@
|
|
|
5140
5251
|
function recursiveRuleIndexes(dependency) {
|
|
5141
5252
|
const out = new Set();
|
|
5142
5253
|
const ruleByName = new Map(dependency.rules.map((rule) => [rule.name, rule]));
|
|
5143
|
-
const
|
|
5254
|
+
const componentOf = new Map();
|
|
5255
|
+
|
|
5256
|
+
dependency.components.forEach((component, componentIndex) => {
|
|
5257
|
+
for (const name of component) {
|
|
5258
|
+
const rule = ruleByName.get(name);
|
|
5259
|
+
if (rule) componentOf.set(rule.index, componentIndex);
|
|
5260
|
+
}
|
|
5261
|
+
});
|
|
5144
5262
|
|
|
5145
5263
|
for (const component of dependency.components) {
|
|
5146
5264
|
if (component.length <= 1) continue;
|
|
@@ -5151,9 +5269,9 @@
|
|
|
5151
5269
|
}
|
|
5152
5270
|
|
|
5153
5271
|
for (const edge of dependency.edges) {
|
|
5154
|
-
const rule =
|
|
5155
|
-
if (edge.from === edge.to && edge.
|
|
5156
|
-
out.add(edge.from);
|
|
5272
|
+
const rule = (dependency.rules || []).find((candidate) => candidate.index === edge.from);
|
|
5273
|
+
if (edge.from === edge.to && edge.negated && rule && rule.runOnce && !rule.headHasBlankNode) continue;
|
|
5274
|
+
if (edge.from === edge.to || componentOf.get(edge.from) === componentOf.get(edge.to)) out.add(edge.from);
|
|
5157
5275
|
}
|
|
5158
5276
|
return out;
|
|
5159
5277
|
}
|
|
@@ -5426,7 +5544,26 @@
|
|
|
5426
5544
|
}
|
|
5427
5545
|
|
|
5428
5546
|
function ruleHasAssignment(rule, options = {}) {
|
|
5429
|
-
return
|
|
5547
|
+
return (rule.body || []).some((clause) => clause.type === 'set' || clause.type === 'bind');
|
|
5548
|
+
}
|
|
5549
|
+
|
|
5550
|
+
function ruleHasTermGeneratingAssignment(rule, options = {}) {
|
|
5551
|
+
return (rule.body || []).some((clause) => (clause.type === 'set' || clause.type === 'bind') && assignmentMayCreateNewTerm(clause.expr));
|
|
5552
|
+
}
|
|
5553
|
+
|
|
5554
|
+
function ruleCreatesTerms(rule, options = {}) {
|
|
5555
|
+
return ruleHeadHasBlankNode(rule) || ruleHasTermGeneratingAssignment(rule, options);
|
|
5556
|
+
}
|
|
5557
|
+
|
|
5558
|
+
function assignmentMayCreateNewTerm(expr) {
|
|
5559
|
+
// Simple aliases and constants are not term generators: they select from a
|
|
5560
|
+
// fixed term or from already-bound terms. Expressions that compute values
|
|
5561
|
+
// through operators or functions may create an unbounded sequence when used
|
|
5562
|
+
// recursively, e.g. SET(?v1 := ?v + 1), so they remain part of the strict
|
|
5563
|
+
// recursive-new-terms check.
|
|
5564
|
+
if (!expr) return false;
|
|
5565
|
+
if (expr.type === 'var' || expr.type === 'term' || expr.type === 'literal') return false;
|
|
5566
|
+
return true;
|
|
5430
5567
|
}
|
|
5431
5568
|
|
|
5432
5569
|
function ruleHeadHasBlankNode(rule) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyeleng",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
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",
|
|
@@ -36,16 +36,17 @@
|
|
|
36
36
|
"build": "node tools/bundle.js",
|
|
37
37
|
"pretest": "node tools/bundle.js",
|
|
38
38
|
"test": "node test/run.js",
|
|
39
|
+
"test:api": "node test/api.test.js",
|
|
40
|
+
"test:examples": "node test/examples.test.js",
|
|
39
41
|
"preversion": "npm test",
|
|
40
42
|
"version": "node tools/bundle.js",
|
|
41
43
|
"postversion": "git push origin HEAD --follow-tags",
|
|
42
44
|
"w3c:rules": "node tools/w3c-shacl12-rules.js",
|
|
45
|
+
"w3c:rules:json": "node tools/w3c-shacl12-rules.js --json",
|
|
46
|
+
"w3c:rules:earl": "node tools/w3c-shacl12-rules.js --earl",
|
|
43
47
|
"w3c:rdf": "node tools/w3c-rdf.js",
|
|
44
48
|
"w3c:rdf:json": "node tools/w3c-rdf.js --json",
|
|
45
49
|
"w3c:rdf:earl": "node tools/w3c-rdf.js --earl",
|
|
46
|
-
"w3c:all": "npm run w3c:rules && npm run w3c:rdf",
|
|
47
|
-
"w3c:rules:json": "node tools/w3c-shacl12-rules.js --json",
|
|
48
|
-
"w3c:rules:earl": "node tools/w3c-shacl12-rules.js --earl",
|
|
49
50
|
"bench": "node tools/perf-bench.js --check",
|
|
50
51
|
"bench:report": "node tools/perf-bench.js --report",
|
|
51
52
|
"bench:update": "node tools/perf-bench.js --update"
|