eyeling 1.7.9 → 1.7.10
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 +2 -2
- package/examples/output/time.n3 +8 -0
- package/examples/time.n3 +29 -0
- package/eyeling.js +208 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -216,10 +216,10 @@ Commonly used N3/Turtle features:
|
|
|
216
216
|
|
|
217
217
|
- **crypto**: `crypto:md5` `crypto:sha` `crypto:sha256` `crypto:sha512`
|
|
218
218
|
- **list**: `list:append` `list:first` `list:firstRest` `list:in` `list:iterate` `list:last` `list:length` `list:map` `list:member` `list:memberAt` `list:notMember` `list:remove` `list:rest` `list:reverse` `list:sort`
|
|
219
|
-
- **log**: `log:collectAllIn` `log:content` `log:equalTo` `log:forAllIn` `log:impliedBy` `log:implies` `log:notEqualTo` `log:notIncludes` `log:semantics` `log:skolem` `log:uri`
|
|
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:localTime`
|
|
222
|
+
- **time**: `time:day` `time:localTime` `time:minute` `time:month` `time:second` `time:timeZone` `time:year`
|
|
223
223
|
|
|
224
224
|
## License
|
|
225
225
|
|
package/examples/time.n3
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# =======================
|
|
2
|
+
# time: builtins examples
|
|
3
|
+
# =======================
|
|
4
|
+
|
|
5
|
+
@prefix : <http://example.org/> .
|
|
6
|
+
@prefix time: <http://www.w3.org/2000/10/swap/time#> .
|
|
7
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
8
|
+
|
|
9
|
+
:Let :dt "2023-04-01T18:06:04Z"^^xsd:dateTime .
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
:Let :dt ?d .
|
|
13
|
+
?d time:year ?y ;
|
|
14
|
+
time:month ?m ;
|
|
15
|
+
time:day ?day ;
|
|
16
|
+
time:minute ?min ;
|
|
17
|
+
time:second ?sec ;
|
|
18
|
+
time:timeZone ?tz .
|
|
19
|
+
}
|
|
20
|
+
=>
|
|
21
|
+
{
|
|
22
|
+
:out :year ?y ;
|
|
23
|
+
:month ?m ;
|
|
24
|
+
:day ?day ;
|
|
25
|
+
:minute ?min ;
|
|
26
|
+
:second ?sec ;
|
|
27
|
+
:tz ?tz .
|
|
28
|
+
} .
|
|
29
|
+
|
package/eyeling.js
CHANGED
|
@@ -95,9 +95,7 @@ const __logConclusionCache = new WeakMap(); // GraphTerm -> GraphTerm (deductive
|
|
|
95
95
|
// Environment detection (Node vs Browser/Worker).
|
|
96
96
|
// Eyeling is primarily synchronous, so we use sync XHR in browsers for log:content/log:semantics.
|
|
97
97
|
// Note: Browser fetches are subject to CORS; use CORS-enabled resources or a proxy.
|
|
98
|
-
const __IS_NODE =
|
|
99
|
-
typeof process !== 'undefined' &&
|
|
100
|
-
!!(process.versions && process.versions.node);
|
|
98
|
+
const __IS_NODE = typeof process !== 'undefined' && !!(process.versions && process.versions.node);
|
|
101
99
|
|
|
102
100
|
function __hasXmlHttpRequest() {
|
|
103
101
|
return typeof XMLHttpRequest !== 'undefined';
|
|
@@ -107,10 +105,7 @@ function __resolveBrowserUrl(ref) {
|
|
|
107
105
|
if (!ref) return ref;
|
|
108
106
|
// If already absolute, keep as-is.
|
|
109
107
|
if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(ref)) return ref;
|
|
110
|
-
const base =
|
|
111
|
-
(typeof document !== 'undefined' && document.baseURI) ||
|
|
112
|
-
(typeof location !== 'undefined' && location.href) ||
|
|
113
|
-
'';
|
|
108
|
+
const base = (typeof document !== 'undefined' && document.baseURI) || (typeof location !== 'undefined' && location.href) || '';
|
|
114
109
|
try {
|
|
115
110
|
return new URL(ref, base).toString();
|
|
116
111
|
} catch {
|
|
@@ -124,10 +119,7 @@ function __fetchHttpTextSyncBrowser(url) {
|
|
|
124
119
|
const xhr = new XMLHttpRequest();
|
|
125
120
|
xhr.open('GET', url, false); // synchronous
|
|
126
121
|
try {
|
|
127
|
-
xhr.setRequestHeader(
|
|
128
|
-
'Accept',
|
|
129
|
-
'text/n3, text/turtle, application/n-triples, application/n-quads, text/plain;q=0.1, */*;q=0.01'
|
|
130
|
-
);
|
|
122
|
+
xhr.setRequestHeader('Accept', 'text/n3, text/turtle, application/n-triples, application/n-quads, text/plain;q=0.1, */*;q=0.01');
|
|
131
123
|
} catch {
|
|
132
124
|
// Some environments restrict setting headers (ignore).
|
|
133
125
|
}
|
|
@@ -232,7 +224,7 @@ function __fetchHttpTextViaSubprocess(url) {
|
|
|
232
224
|
`;
|
|
233
225
|
const r = cp.spawnSync(process.execPath, ['-e', script, url], {
|
|
234
226
|
encoding: 'utf8',
|
|
235
|
-
maxBuffer: 32 * 1024 * 1024
|
|
227
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
236
228
|
});
|
|
237
229
|
if (r.status !== 0) return null;
|
|
238
230
|
return r.stdout;
|
|
@@ -297,8 +289,8 @@ function __derefSemanticsSync(iriNoFrag) {
|
|
|
297
289
|
return null;
|
|
298
290
|
}
|
|
299
291
|
try {
|
|
300
|
-
const baseIri =
|
|
301
|
-
|
|
292
|
+
const baseIri = typeof key === 'string' && key ? key : iriNoFrag;
|
|
293
|
+
const formula = __parseSemanticsToFormula(text, baseIri);
|
|
302
294
|
__logSemanticsCache.set(key, formula);
|
|
303
295
|
return formula;
|
|
304
296
|
} catch {
|
|
@@ -387,8 +379,6 @@ function __computeConclusionFromFormula(formula) {
|
|
|
387
379
|
return out;
|
|
388
380
|
}
|
|
389
381
|
|
|
390
|
-
|
|
391
|
-
|
|
392
382
|
// Controls whether human-readable proof comments are printed.
|
|
393
383
|
let proofCommentsEnabled = false;
|
|
394
384
|
// Super restricted mode: disable *all* builtins except => / <= (log:implies / log:impliedBy)
|
|
@@ -3489,6 +3479,33 @@ function parseXsdDatetimeTerm(t) {
|
|
|
3489
3479
|
return d; // Date in local/UTC, we only use timestamp
|
|
3490
3480
|
}
|
|
3491
3481
|
|
|
3482
|
+
function parseXsdDateTimeLexParts(t) {
|
|
3483
|
+
// Parse *lexical* components of an xsd:dateTime literal without timezone normalization.
|
|
3484
|
+
// Returns { yearStr, month, day, minute, second, tz } or null.
|
|
3485
|
+
if (!(t instanceof Literal)) return null;
|
|
3486
|
+
const [lex, dt] = literalParts(t.value);
|
|
3487
|
+
if (dt !== XSD_NS + 'dateTime') return null;
|
|
3488
|
+
const val = stripQuotes(lex);
|
|
3489
|
+
|
|
3490
|
+
// xsd:dateTime lexical: YYYY-MM-DDThh:mm:ss(.s+)?(Z|(+|-)hh:mm)?
|
|
3491
|
+
const m = /^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(Z|[+-]\d{2}:\d{2})?$/.exec(val);
|
|
3492
|
+
if (!m) return null;
|
|
3493
|
+
|
|
3494
|
+
const yearStr = m[1];
|
|
3495
|
+
const month = parseInt(m[2], 10);
|
|
3496
|
+
const day = parseInt(m[3], 10);
|
|
3497
|
+
const minute = parseInt(m[5], 10);
|
|
3498
|
+
const second = parseInt(m[6], 10);
|
|
3499
|
+
const tz = m[7] || null;
|
|
3500
|
+
|
|
3501
|
+
if (!(month >= 1 && month <= 12)) return null;
|
|
3502
|
+
if (!(day >= 1 && day <= 31)) return null;
|
|
3503
|
+
if (!(minute >= 0 && minute <= 59)) return null;
|
|
3504
|
+
if (!(second >= 0 && second <= 59)) return null;
|
|
3505
|
+
|
|
3506
|
+
return { yearStr, month, day, minute, second, tz };
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3492
3509
|
function parseDatetimeLike(t) {
|
|
3493
3510
|
const d = parseXsdDateTerm(t);
|
|
3494
3511
|
if (d !== null) return d;
|
|
@@ -4579,6 +4596,162 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
4579
4596
|
// 4.3 time: builtins
|
|
4580
4597
|
// -----------------------------------------------------------------
|
|
4581
4598
|
|
|
4599
|
+
// time:day
|
|
4600
|
+
// Gets as object the integer day component of the subject xsd:dateTime.
|
|
4601
|
+
// Schema: $s+ time:day $o-
|
|
4602
|
+
if (pv === TIME_NS + 'day') {
|
|
4603
|
+
const parts = parseXsdDateTimeLexParts(g.s);
|
|
4604
|
+
if (!parts) return [];
|
|
4605
|
+
const out = internLiteral(String(parts.day));
|
|
4606
|
+
|
|
4607
|
+
if (g.o instanceof Var) {
|
|
4608
|
+
const s2 = { ...subst };
|
|
4609
|
+
s2[g.o.name] = out;
|
|
4610
|
+
return [s2];
|
|
4611
|
+
}
|
|
4612
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4613
|
+
|
|
4614
|
+
const oi = parseIntLiteral(g.o);
|
|
4615
|
+
if (oi !== null) {
|
|
4616
|
+
try {
|
|
4617
|
+
if (oi === BigInt(parts.day)) return [{ ...subst }];
|
|
4618
|
+
} catch {}
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4621
|
+
const s2 = unifyTerm(g.o, out, subst);
|
|
4622
|
+
return s2 !== null ? [s2] : [];
|
|
4623
|
+
}
|
|
4624
|
+
|
|
4625
|
+
// time:minute
|
|
4626
|
+
// Gets as object the integer minutes component of the subject xsd:dateTime.
|
|
4627
|
+
// Schema: $s+ time:minute $o-
|
|
4628
|
+
if (pv === TIME_NS + 'minute') {
|
|
4629
|
+
const parts = parseXsdDateTimeLexParts(g.s);
|
|
4630
|
+
if (!parts) return [];
|
|
4631
|
+
const out = internLiteral(String(parts.minute));
|
|
4632
|
+
|
|
4633
|
+
if (g.o instanceof Var) {
|
|
4634
|
+
const s2 = { ...subst };
|
|
4635
|
+
s2[g.o.name] = out;
|
|
4636
|
+
return [s2];
|
|
4637
|
+
}
|
|
4638
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4639
|
+
|
|
4640
|
+
const oi = parseIntLiteral(g.o);
|
|
4641
|
+
if (oi !== null) {
|
|
4642
|
+
try {
|
|
4643
|
+
if (oi === BigInt(parts.minute)) return [{ ...subst }];
|
|
4644
|
+
} catch {}
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
const s2 = unifyTerm(g.o, out, subst);
|
|
4648
|
+
return s2 !== null ? [s2] : [];
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
// time:month
|
|
4652
|
+
// Gets as object the integer month component of the subject xsd:dateTime.
|
|
4653
|
+
// Schema: $s+ time:month $o-
|
|
4654
|
+
if (pv === TIME_NS + 'month') {
|
|
4655
|
+
const parts = parseXsdDateTimeLexParts(g.s);
|
|
4656
|
+
if (!parts) return [];
|
|
4657
|
+
const out = internLiteral(String(parts.month));
|
|
4658
|
+
|
|
4659
|
+
if (g.o instanceof Var) {
|
|
4660
|
+
const s2 = { ...subst };
|
|
4661
|
+
s2[g.o.name] = out;
|
|
4662
|
+
return [s2];
|
|
4663
|
+
}
|
|
4664
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4665
|
+
|
|
4666
|
+
const oi = parseIntLiteral(g.o);
|
|
4667
|
+
if (oi !== null) {
|
|
4668
|
+
try {
|
|
4669
|
+
if (oi === BigInt(parts.month)) return [{ ...subst }];
|
|
4670
|
+
} catch {}
|
|
4671
|
+
}
|
|
4672
|
+
|
|
4673
|
+
const s2 = unifyTerm(g.o, out, subst);
|
|
4674
|
+
return s2 !== null ? [s2] : [];
|
|
4675
|
+
}
|
|
4676
|
+
|
|
4677
|
+
// time:second
|
|
4678
|
+
// Gets as object the integer seconds component of the subject xsd:dateTime.
|
|
4679
|
+
// Schema: $s+ time:second $o-
|
|
4680
|
+
if (pv === TIME_NS + 'second') {
|
|
4681
|
+
const parts = parseXsdDateTimeLexParts(g.s);
|
|
4682
|
+
if (!parts) return [];
|
|
4683
|
+
const out = internLiteral(String(parts.second));
|
|
4684
|
+
|
|
4685
|
+
if (g.o instanceof Var) {
|
|
4686
|
+
const s2 = { ...subst };
|
|
4687
|
+
s2[g.o.name] = out;
|
|
4688
|
+
return [s2];
|
|
4689
|
+
}
|
|
4690
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4691
|
+
|
|
4692
|
+
const oi = parseIntLiteral(g.o);
|
|
4693
|
+
if (oi !== null) {
|
|
4694
|
+
try {
|
|
4695
|
+
if (oi === BigInt(parts.second)) return [{ ...subst }];
|
|
4696
|
+
} catch {}
|
|
4697
|
+
}
|
|
4698
|
+
|
|
4699
|
+
const s2 = unifyTerm(g.o, out, subst);
|
|
4700
|
+
return s2 !== null ? [s2] : [];
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4703
|
+
// time:timeZone
|
|
4704
|
+
// Gets as object the trailing timezone offset of the subject xsd:dateTime (e.g., "-05:00" or "Z").
|
|
4705
|
+
// Schema: $s+ time:timeZone $o-
|
|
4706
|
+
if (pv === TIME_NS + 'timeZone') {
|
|
4707
|
+
const parts = parseXsdDateTimeLexParts(g.s);
|
|
4708
|
+
if (!parts) return [];
|
|
4709
|
+
if (parts.tz === null) return [];
|
|
4710
|
+
const out = internLiteral(`"${parts.tz}"`);
|
|
4711
|
+
|
|
4712
|
+
if (g.o instanceof Var) {
|
|
4713
|
+
const s2 = { ...subst };
|
|
4714
|
+
s2[g.o.name] = out;
|
|
4715
|
+
return [s2];
|
|
4716
|
+
}
|
|
4717
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4718
|
+
|
|
4719
|
+
if (termsEqual(g.o, out)) return [{ ...subst }];
|
|
4720
|
+
|
|
4721
|
+
// Also accept explicitly typed xsd:string literals.
|
|
4722
|
+
if (g.o instanceof Literal) {
|
|
4723
|
+
const [lexO, dtO] = literalParts(g.o.value);
|
|
4724
|
+
if (dtO === XSD_NS + 'string' && stripQuotes(lexO) === parts.tz) return [{ ...subst }];
|
|
4725
|
+
}
|
|
4726
|
+
return [];
|
|
4727
|
+
}
|
|
4728
|
+
|
|
4729
|
+
// time:year
|
|
4730
|
+
// Gets as object the integer year component of the subject xsd:dateTime.
|
|
4731
|
+
// Schema: $s+ time:year $o-
|
|
4732
|
+
if (pv === TIME_NS + 'year') {
|
|
4733
|
+
const parts = parseXsdDateTimeLexParts(g.s);
|
|
4734
|
+
if (!parts) return [];
|
|
4735
|
+
const out = internLiteral(String(parts.yearStr));
|
|
4736
|
+
|
|
4737
|
+
if (g.o instanceof Var) {
|
|
4738
|
+
const s2 = { ...subst };
|
|
4739
|
+
s2[g.o.name] = out;
|
|
4740
|
+
return [s2];
|
|
4741
|
+
}
|
|
4742
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
4743
|
+
|
|
4744
|
+
const oi = parseIntLiteral(g.o);
|
|
4745
|
+
if (oi !== null) {
|
|
4746
|
+
try {
|
|
4747
|
+
if (oi === BigInt(parts.yearStr)) return [{ ...subst }];
|
|
4748
|
+
} catch {}
|
|
4749
|
+
}
|
|
4750
|
+
|
|
4751
|
+
const s2 = unifyTerm(g.o, out, subst);
|
|
4752
|
+
return s2 !== null ? [s2] : [];
|
|
4753
|
+
}
|
|
4754
|
+
|
|
4582
4755
|
// time:localTime
|
|
4583
4756
|
// "" time:localTime ?D. binds ?D to “now” as xsd:dateTime.
|
|
4584
4757
|
if (pv === TIME_NS + 'localTime') {
|
|
@@ -5021,30 +5194,29 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
5021
5194
|
return s2 !== null ? [s2] : [];
|
|
5022
5195
|
}
|
|
5023
5196
|
|
|
5197
|
+
// log:conclusion
|
|
5198
|
+
// Schema: $s+ log:conclusion $o?
|
|
5199
|
+
// $o is the deductive closure of the subject formula $s (including rule inferences).
|
|
5200
|
+
if (pv === LOG_NS + 'conclusion') {
|
|
5201
|
+
// Accept 'true' as the empty formula.
|
|
5202
|
+
let inFormula = null;
|
|
5203
|
+
if (g.s instanceof GraphTerm) inFormula = g.s;
|
|
5204
|
+
else if (g.s instanceof Literal && g.s.value === 'true') inFormula = new GraphTerm([]);
|
|
5205
|
+
else return [];
|
|
5024
5206
|
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
// $o is the deductive closure of the subject formula $s (including rule inferences).
|
|
5028
|
-
if (pv === LOG_NS + 'conclusion') {
|
|
5029
|
-
// Accept 'true' as the empty formula.
|
|
5030
|
-
let inFormula = null;
|
|
5031
|
-
if (g.s instanceof GraphTerm) inFormula = g.s;
|
|
5032
|
-
else if (g.s instanceof Literal && g.s.value === 'true') inFormula = new GraphTerm([]);
|
|
5033
|
-
else return [];
|
|
5207
|
+
const conclusion = __computeConclusionFromFormula(inFormula);
|
|
5208
|
+
if (!(conclusion instanceof GraphTerm)) return [];
|
|
5034
5209
|
|
|
5035
|
-
|
|
5036
|
-
|
|
5210
|
+
if (g.o instanceof Var) {
|
|
5211
|
+
const s2 = { ...subst };
|
|
5212
|
+
s2[g.o.name] = conclusion;
|
|
5213
|
+
return [s2];
|
|
5214
|
+
}
|
|
5215
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
5037
5216
|
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
s2[g.o.name] = conclusion;
|
|
5041
|
-
return [s2];
|
|
5217
|
+
const s2 = unifyTerm(g.o, conclusion, subst);
|
|
5218
|
+
return s2 !== null ? [s2] : [];
|
|
5042
5219
|
}
|
|
5043
|
-
if (g.o instanceof Blank) return [{ ...subst }];
|
|
5044
|
-
|
|
5045
|
-
const s2 = unifyTerm(g.o, conclusion, subst);
|
|
5046
|
-
return s2 !== null ? [s2] : [];
|
|
5047
|
-
}
|
|
5048
5220
|
|
|
5049
5221
|
// log:content
|
|
5050
5222
|
// Schema: $s+ log:content $o?
|
|
@@ -5195,8 +5367,6 @@ if (pv === LOG_NS + 'conclusion') {
|
|
|
5195
5367
|
return s2 !== null ? [s2] : [];
|
|
5196
5368
|
}
|
|
5197
5369
|
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
5370
|
// log:dtlit
|
|
5201
5371
|
// Schema: ( $s.1? $s.2? )? log:dtlit $o?
|
|
5202
5372
|
// true iff $o is a datatyped literal with string value $s.1 and datatype IRI $s.2
|
|
@@ -5436,7 +5606,6 @@ if (pv === LOG_NS + 'conclusion') {
|
|
|
5436
5606
|
return [{ ...subst }];
|
|
5437
5607
|
}
|
|
5438
5608
|
|
|
5439
|
-
|
|
5440
5609
|
// log:collectAllIn (scoped)
|
|
5441
5610
|
if (pv === LOG_NS + 'collectAllIn') {
|
|
5442
5611
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 3) return [];
|
|
@@ -6575,7 +6744,6 @@ function __collectOutputStringsFromFacts(facts, prefixes) {
|
|
|
6575
6744
|
return pairs.map((p) => p.text).join('');
|
|
6576
6745
|
}
|
|
6577
6746
|
|
|
6578
|
-
|
|
6579
6747
|
function main() {
|
|
6580
6748
|
// Drop "node" and script name; keep only user-provided args
|
|
6581
6749
|
const argv = process.argv.slice(2);
|