eyeling 1.7.13 → 1.7.15
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 +1 -1
- package/eyeling.js +81 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -219,7 +219,7 @@ Commonly used N3/Turtle features:
|
|
|
219
219
|
- **log**: `log:collectAllIn` `log:content` `log:dtlit` `log:equalTo` `log:forAllIn` `log:impliedBy` `log:implies` `log:includes` `log:langlit` `log:notEqualTo` `log:notIncludes` `log:outputString` `log:parsedAsN3` `log:rawType` `log:semantics` `log:semanticsOrError` `log:skolem` `log:uri`
|
|
220
220
|
- **math**: `math:absoluteValue` `math:acos` `math:asin` `math:atan` `math:cos` `math:cosh` `math:degrees` `math:difference` `math:equalTo` `math:exponentiation` `math:greaterThan` `math:integerQuotient` `math:lessThan` `math:negation` `math:notEqualTo` `math:notGreaterThan` `math:notLessThan` `math:product` `math:quotient` `math:remainder` `math:rounded` `math:sin` `math:sinh` `math:sum` `math:tan` `math:tanh`
|
|
221
221
|
- **string**: `string:concatenation` `string:contains` `string:containsIgnoringCase` `string:endsWith` `string:equalIgnoringCase` `string:format` `string:greaterThan` `string:jsonPointer` `string:lessThan` `string:matches` `string:notEqualIgnoringCase` `string:notGreaterThan` `string:notLessThan` `string:notMatches` `string:replace` `string:scrape` `string:startsWith`
|
|
222
|
-
- **time**: `time:day` `time:localTime` `time:minute` `time:month` `time:second` `time:timeZone` `time:year`
|
|
222
|
+
- **time**: `time:day` `time:hour` `time:localTime` `time:minute` `time:month` `time:second` `time:timeZone` `time:year`
|
|
223
223
|
|
|
224
224
|
## License
|
|
225
225
|
|
package/eyeling.js
CHANGED
|
@@ -3542,7 +3542,7 @@ function parseXsdDatetimeTerm(t) {
|
|
|
3542
3542
|
|
|
3543
3543
|
function parseXsdDateTimeLexParts(t) {
|
|
3544
3544
|
// Parse *lexical* components of an xsd:dateTime literal without timezone normalization.
|
|
3545
|
-
// Returns { yearStr, month, day, minute, second, tz } or null.
|
|
3545
|
+
// Returns { yearStr, month, day, hour, minute, second, tz } or null.
|
|
3546
3546
|
if (!(t instanceof Literal)) return null;
|
|
3547
3547
|
const [lex, dt] = literalParts(t.value);
|
|
3548
3548
|
if (dt !== XSD_NS + 'dateTime') return null;
|
|
@@ -3555,16 +3555,18 @@ function parseXsdDateTimeLexParts(t) {
|
|
|
3555
3555
|
const yearStr = m[1];
|
|
3556
3556
|
const month = parseInt(m[2], 10);
|
|
3557
3557
|
const day = parseInt(m[3], 10);
|
|
3558
|
+
const hour = parseInt(m[4], 10);
|
|
3558
3559
|
const minute = parseInt(m[5], 10);
|
|
3559
3560
|
const second = parseInt(m[6], 10);
|
|
3560
3561
|
const tz = m[7] || null;
|
|
3561
3562
|
|
|
3562
3563
|
if (!(month >= 1 && month <= 12)) return null;
|
|
3563
3564
|
if (!(day >= 1 && day <= 31)) return null;
|
|
3565
|
+
if (!(hour >= 0 && hour <= 23)) return null;
|
|
3564
3566
|
if (!(minute >= 0 && minute <= 59)) return null;
|
|
3565
3567
|
if (!(second >= 0 && second <= 59)) return null;
|
|
3566
3568
|
|
|
3567
|
-
return { yearStr, month, day, minute, second, tz };
|
|
3569
|
+
return { yearStr, month, day, hour, minute, second, tz };
|
|
3568
3570
|
}
|
|
3569
3571
|
|
|
3570
3572
|
function parseDatetimeLike(t) {
|
|
@@ -4129,7 +4131,7 @@ function evalCryptoHashBuiltin(g, subst, algo) {
|
|
|
4129
4131
|
// ===========================================================================
|
|
4130
4132
|
// Backward proof & builtins mutual recursion — declarations first
|
|
4131
4133
|
|
|
4132
|
-
function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
4134
|
+
function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
4133
4135
|
const g = applySubstTriple(goal, subst);
|
|
4134
4136
|
const pv = iriValue(g.p);
|
|
4135
4137
|
if (pv === null) return null;
|
|
@@ -4689,6 +4691,32 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4689
4691
|
return s2 !== null ? [s2] : [];
|
|
4690
4692
|
}
|
|
4691
4693
|
|
|
4694
|
+
// time:hour
|
|
4695
|
+
// Gets as object the integer hour component of the subject xsd:dateTime.
|
|
4696
|
+
// Schema: $s+ time:hour $o-
|
|
4697
|
+
if (pv === TIME_NS + 'hour') {
|
|
4698
|
+
const parts = parseXsdDateTimeLexParts(g.s);
|
|
4699
|
+
if (!parts) return [];
|
|
4700
|
+
const out = internLiteral(String(parts.hour));
|
|
4701
|
+
|
|
4702
|
+
if (g.o instanceof Var) {
|
|
4703
|
+
const s2 = { ...subst };
|
|
4704
|
+
s2[g.o.name] = out;
|
|
4705
|
+
return [s2];
|
|
4706
|
+
}
|
|
4707
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4708
|
+
|
|
4709
|
+
const oi = parseIntLiteral(g.o);
|
|
4710
|
+
if (oi !== null) {
|
|
4711
|
+
try {
|
|
4712
|
+
if (oi === BigInt(parts.hour)) return [{ ...subst }];
|
|
4713
|
+
} catch {}
|
|
4714
|
+
}
|
|
4715
|
+
|
|
4716
|
+
const s2 = unifyTerm(g.o, out, subst);
|
|
4717
|
+
return s2 !== null ? [s2] : [];
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4692
4720
|
// time:minute
|
|
4693
4721
|
// Gets as object the integer minutes component of the subject xsd:dateTime.
|
|
4694
4722
|
// Schema: $s+ time:minute $o-
|
|
@@ -5620,7 +5648,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
5620
5648
|
|
|
5621
5649
|
const visited2 = [];
|
|
5622
5650
|
// Start from the incoming substitution so bindings flow outward.
|
|
5623
|
-
return proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, [], depth + 1, visited2, varGen);
|
|
5651
|
+
return proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, [], depth + 1, visited2, varGen, maxResults);
|
|
5624
5652
|
}
|
|
5625
5653
|
|
|
5626
5654
|
// log:notIncludes
|
|
@@ -5641,7 +5669,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
5641
5669
|
});
|
|
5642
5670
|
|
|
5643
5671
|
const visited2 = [];
|
|
5644
|
-
const sols = proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, [], depth + 1, visited2, varGen);
|
|
5672
|
+
const sols = proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, [], depth + 1, visited2, varGen, 1);
|
|
5645
5673
|
return sols.length ? [] : [{ ...subst }];
|
|
5646
5674
|
}
|
|
5647
5675
|
|
|
@@ -6216,10 +6244,11 @@ function maybeCompactSubst(subst, goals, answerVars, depth) {
|
|
|
6216
6244
|
return gcCompactForGoals(subst, goals, answerVars);
|
|
6217
6245
|
}
|
|
6218
6246
|
|
|
6219
|
-
function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
|
|
6247
|
+
function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxResults) {
|
|
6220
6248
|
// Iterative DFS over proof states using an explicit stack.
|
|
6221
6249
|
// Each state carries its own substitution and remaining goals.
|
|
6222
6250
|
const results = [];
|
|
6251
|
+
const max = typeof maxResults === 'number' && maxResults > 0 ? maxResults : Infinity;
|
|
6223
6252
|
|
|
6224
6253
|
const initialGoals = Array.isArray(goals) ? goals.slice() : [];
|
|
6225
6254
|
const initialSubst = subst ? { ...subst } : {};
|
|
@@ -6230,6 +6259,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
|
|
|
6230
6259
|
gcCollectVarsInGoals(initialGoals, answerVars);
|
|
6231
6260
|
if (!initialGoals.length) {
|
|
6232
6261
|
results.push(gcCompactForGoals(initialSubst, [], answerVars));
|
|
6262
|
+
|
|
6263
|
+
if (results.length >= max) return results;
|
|
6233
6264
|
return results;
|
|
6234
6265
|
}
|
|
6235
6266
|
|
|
@@ -6247,6 +6278,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
|
|
|
6247
6278
|
|
|
6248
6279
|
if (!state.goals.length) {
|
|
6249
6280
|
results.push(gcCompactForGoals(state.subst, [], answerVars));
|
|
6281
|
+
|
|
6282
|
+
if (results.length >= max) return results;
|
|
6250
6283
|
continue;
|
|
6251
6284
|
}
|
|
6252
6285
|
|
|
@@ -6256,13 +6289,18 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
|
|
|
6256
6289
|
|
|
6257
6290
|
// 1) Builtins
|
|
6258
6291
|
if (isBuiltinPred(goal0.p)) {
|
|
6259
|
-
const
|
|
6292
|
+
const remaining = max - results.length;
|
|
6293
|
+
if (remaining <= 0) return results;
|
|
6294
|
+
const builtinMax = Number.isFinite(remaining) && !restGoals.length ? remaining : undefined;
|
|
6295
|
+
const deltas = evalBuiltin(goal0, {}, facts, backRules, state.depth, varGen, builtinMax);
|
|
6260
6296
|
const nextStates = [];
|
|
6261
6297
|
for (const delta of deltas) {
|
|
6262
6298
|
const composed = composeSubst(state.subst, delta);
|
|
6263
6299
|
if (composed === null) continue;
|
|
6264
6300
|
if (!restGoals.length) {
|
|
6265
6301
|
results.push(gcCompactForGoals(composed, [], answerVars));
|
|
6302
|
+
|
|
6303
|
+
if (results.length >= max) return results;
|
|
6266
6304
|
} else {
|
|
6267
6305
|
const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
|
|
6268
6306
|
nextStates.push({
|
|
@@ -6293,6 +6331,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
|
|
|
6293
6331
|
if (composed === null) continue;
|
|
6294
6332
|
if (!restGoals.length) {
|
|
6295
6333
|
results.push(gcCompactForGoals(composed, [], answerVars));
|
|
6334
|
+
|
|
6335
|
+
if (results.length >= max) return results;
|
|
6296
6336
|
} else {
|
|
6297
6337
|
const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
|
|
6298
6338
|
nextStates.push({
|
|
@@ -6314,6 +6354,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
|
|
|
6314
6354
|
if (composed === null) continue;
|
|
6315
6355
|
if (!restGoals.length) {
|
|
6316
6356
|
results.push(gcCompactForGoals(composed, [], answerVars));
|
|
6357
|
+
|
|
6358
|
+
if (results.length >= max) return results;
|
|
6317
6359
|
} else {
|
|
6318
6360
|
const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
|
|
6319
6361
|
nextStates.push({
|
|
@@ -6426,7 +6468,38 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */)
|
|
|
6426
6468
|
const r = forwardRules[i];
|
|
6427
6469
|
const empty = {};
|
|
6428
6470
|
const visited = [];
|
|
6429
|
-
|
|
6471
|
+
// Optimization: if the rule head is **structurally ground** (no vars anywhere, even inside
|
|
6472
|
+
// quoted formulas) and has no head blanks, then the head does not depend on which body
|
|
6473
|
+
// solution we pick. In that case, we only need *one* proof of the body, and once all head
|
|
6474
|
+
// triples are already known we can skip proving the body entirely.
|
|
6475
|
+
function isStrictGroundTerm(t) {
|
|
6476
|
+
if (t instanceof Var) return false;
|
|
6477
|
+
if (t instanceof Blank) return false;
|
|
6478
|
+
if (t instanceof OpenListTerm) return false;
|
|
6479
|
+
if (t instanceof ListTerm) return t.elems.every(isStrictGroundTerm);
|
|
6480
|
+
if (t instanceof GraphTerm) return t.triples.every(isStrictGroundTriple);
|
|
6481
|
+
return true; // Iri/Literal and any other atomic terms
|
|
6482
|
+
}
|
|
6483
|
+
function isStrictGroundTriple(tr) {
|
|
6484
|
+
return isStrictGroundTerm(tr.s) && isStrictGroundTerm(tr.p) && isStrictGroundTerm(tr.o);
|
|
6485
|
+
}
|
|
6486
|
+
|
|
6487
|
+
const headIsStrictGround =
|
|
6488
|
+
!r.isFuse && (!r.headBlankLabels || r.headBlankLabels.size === 0) && r.conclusion.every(isStrictGroundTriple);
|
|
6489
|
+
|
|
6490
|
+
if (headIsStrictGround) {
|
|
6491
|
+
let allKnown = true;
|
|
6492
|
+
for (const tr of r.conclusion) {
|
|
6493
|
+
if (!hasFactIndexed(facts, tr)) {
|
|
6494
|
+
allKnown = false;
|
|
6495
|
+
break;
|
|
6496
|
+
}
|
|
6497
|
+
}
|
|
6498
|
+
if (allKnown) continue;
|
|
6499
|
+
}
|
|
6500
|
+
|
|
6501
|
+
const maxSols = (r.isFuse || headIsStrictGround) ? 1 : undefined;
|
|
6502
|
+
const sols = proveGoals(r.premise.slice(), empty, facts, backRules, 0, visited, varGen, maxSols);
|
|
6430
6503
|
|
|
6431
6504
|
// Inference fuse
|
|
6432
6505
|
if (r.isFuse && sols.length) {
|