eyeling 1.7.18 → 1.7.20
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/examples/it-is-about-time.n3 +29 -0
- package/examples/it-is-about-time.srl +36 -0
- package/examples/output/it-is-about-time.n3 +7 -0
- package/examples/output/json-reconcile-vat.n3 +151 -0
- package/eyeling-builtins.ttl +24 -0
- package/eyeling.js +336 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Try it here:
|
|
|
21
21
|
- Load an N3 program from a URL (in the "Load N3 from URL" box or as ?url=...).
|
|
22
22
|
- Share a link with the program encoded in the URL fragment (`#...`).
|
|
23
23
|
|
|
24
|
-
-
|
|
24
|
+
- Streaming demo
|
|
25
25
|
- Browse a Wikidata entity, load its facts, and see Eyeling’s **deductive closure appear incrementally** as triples are derived.
|
|
26
26
|
- Edit **N3 rules live** and re-run to watch how different inference rules change what gets derived.
|
|
27
27
|
- Demo **CORS-safe dynamic fetching**: derived “fetch requests” can trigger extra facts (e.g., Wikiquote extracts) that are injected and re-reasoned.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@prefix ex: <http://example.org/> .
|
|
2
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
3
|
+
@prefix tfn: <https://w3id.org/time-fn#> .
|
|
4
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
|
|
5
|
+
|
|
6
|
+
ex:e1 a ex:Event ;
|
|
7
|
+
ex:when "2000-01-02T03:04:05"^^xsd:dateTime ; # floating (no TZ)
|
|
8
|
+
ex:day "2000-01-02"^^xsd:date ;
|
|
9
|
+
ex:tz "Z" .
|
|
10
|
+
ex:e2 a ex:Event ;
|
|
11
|
+
ex:when "2000-01-02T03:04:05-03:00"^^xsd:dateTime ;
|
|
12
|
+
ex:day "2000-01-02"^^xsd:date ;
|
|
13
|
+
ex:tz "-03:00" .
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
?this ex:when ?whenRaw .
|
|
17
|
+
?this ex:day ?day .
|
|
18
|
+
?this ex:tz ?tz .
|
|
19
|
+
?startInc math:notGreaterThan ?whenBound .
|
|
20
|
+
?whenBound math:notGreaterThan ?endInc .
|
|
21
|
+
(?whenRaw ?tz) tfn:bindDefaultTimezone ?whenBound .
|
|
22
|
+
(?day) tfn:periodMinInclusive ?startInc .
|
|
23
|
+
(?day) tfn:periodMaxInclusive ?endInc .
|
|
24
|
+
(?day) tfn:periodMinExclusive ?startEx .
|
|
25
|
+
(?day) tfn:periodMaxExclusive ?endEx .
|
|
26
|
+
} => {
|
|
27
|
+
?this ex:occursOn ?day .
|
|
28
|
+
?this ex:whenBound ?whenBound .
|
|
29
|
+
} .
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
PREFIX ex: <http://example.org/>
|
|
2
|
+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
|
3
|
+
PREFIX tfn: <https://w3id.org/time-fn#>
|
|
4
|
+
|
|
5
|
+
DATA {
|
|
6
|
+
ex:e1 a ex:Event ;
|
|
7
|
+
ex:when "2000-01-02T03:04:05"^^xsd:dateTime ; # floating (no TZ)
|
|
8
|
+
ex:day "2000-01-02"^^xsd:date ;
|
|
9
|
+
ex:tz "Z" .
|
|
10
|
+
|
|
11
|
+
ex:e2 a ex:Event ;
|
|
12
|
+
ex:when "2000-01-02T03:04:05-03:00"^^xsd:dateTime ;
|
|
13
|
+
ex:day "2000-01-02"^^xsd:date ;
|
|
14
|
+
ex:tz "-03:00" .
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
RULE {
|
|
18
|
+
$this ex:occursOn ?day .
|
|
19
|
+
$this ex:whenBound ?whenBound .
|
|
20
|
+
}
|
|
21
|
+
WHERE {
|
|
22
|
+
$this ex:when ?whenRaw ;
|
|
23
|
+
ex:day ?day ;
|
|
24
|
+
ex:tz ?tz .
|
|
25
|
+
|
|
26
|
+
BIND( tfn:bindDefaultTimezone(?whenRaw, ?tz) AS ?whenBound )
|
|
27
|
+
|
|
28
|
+
BIND( tfn:periodMinInclusive(?day) AS ?startInc )
|
|
29
|
+
BIND( tfn:periodMaxInclusive(?day) AS ?endInc )
|
|
30
|
+
BIND( tfn:periodMinExclusive(?day) AS ?startEx )
|
|
31
|
+
BIND( tfn:periodMaxExclusive(?day) AS ?endEx )
|
|
32
|
+
|
|
33
|
+
# Inclusive containment: start <= whenBound <= end
|
|
34
|
+
FILTER( ?startInc <= ?whenBound && ?whenBound <= ?endInc )
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
@prefix ex: <http://example.org/> .
|
|
2
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
3
|
+
|
|
4
|
+
ex:e1 ex:occursOn "2000-01-02"^^xsd:date .
|
|
5
|
+
ex:e1 ex:whenBound "2000-01-02T03:04:05Z"^^xsd:dateTime .
|
|
6
|
+
ex:e2 ex:occursOn "2000-01-02"^^xsd:date .
|
|
7
|
+
ex:e2 ex:whenBound "2000-01-02T03:04:05-03:00"^^xsd:dateTime .
|
|
@@ -73,3 +73,154 @@ genid:8e2eaee7-90fd-39b8-cf83-6d8c8e5816c8 ex:email "ada@other.example" .
|
|
|
73
73
|
<urn:example:customer:vat:NL999999999B01> a ex:CustomerEmailConflict .
|
|
74
74
|
<urn:example:customer:vat:NL999999999B01> ex:conflictingEmail "acct@company.nl" .
|
|
75
75
|
<urn:example:customer:vat:NL999999999B01> ex:conflictingEmail "billing@company.nl" .
|
|
76
|
+
<urn:example:order:A100> ex:line <urn:example:order:A100#line:0> .
|
|
77
|
+
<urn:example:order:A100#line:0> a ex:OrderLine .
|
|
78
|
+
<urn:example:order:A100#line:0> ex:lineIndex 0 .
|
|
79
|
+
<urn:example:order:A100#line:0> ex:product ex:Widget .
|
|
80
|
+
<urn:example:order:A100#line:0> ex:sku "WID-001" .
|
|
81
|
+
<urn:example:order:A100#line:0> ex:qty 2 .
|
|
82
|
+
<urn:example:order:A100#line:0> ex:unitCents 999 .
|
|
83
|
+
<urn:example:order:A100#line:0> ex:netCents 1998 .
|
|
84
|
+
<urn:example:order:A100> ex:line <urn:example:order:A100#line:1> .
|
|
85
|
+
<urn:example:order:A100#line:1> a ex:OrderLine .
|
|
86
|
+
<urn:example:order:A100#line:1> ex:lineIndex 1 .
|
|
87
|
+
<urn:example:order:A100#line:1> ex:product ex:Gadget .
|
|
88
|
+
<urn:example:order:A100#line:1> ex:sku "GAD-002" .
|
|
89
|
+
<urn:example:order:A100#line:1> ex:qty 1 .
|
|
90
|
+
<urn:example:order:A100#line:1> ex:unitCents 1500 .
|
|
91
|
+
<urn:example:order:A100#line:1> ex:netCents 1500 .
|
|
92
|
+
<urn:example:order:A200> ex:line <urn:example:order:A200#line:0> .
|
|
93
|
+
<urn:example:order:A200#line:0> a ex:OrderLine .
|
|
94
|
+
<urn:example:order:A200#line:0> ex:lineIndex 0 .
|
|
95
|
+
<urn:example:order:A200#line:0> ex:product ex:Widget .
|
|
96
|
+
<urn:example:order:A200#line:0> ex:sku "WID-001" .
|
|
97
|
+
<urn:example:order:A200#line:0> ex:qty 2 .
|
|
98
|
+
<urn:example:order:A200#line:0> ex:unitCents 999 .
|
|
99
|
+
<urn:example:order:A200#line:0> ex:netCents 1998 .
|
|
100
|
+
<urn:example:order:A200> ex:line <urn:example:order:A200#line:1> .
|
|
101
|
+
<urn:example:order:A200#line:1> a ex:OrderLine .
|
|
102
|
+
<urn:example:order:A200#line:1> ex:lineIndex 1 .
|
|
103
|
+
<urn:example:order:A200#line:1> ex:product ex:Gadget .
|
|
104
|
+
<urn:example:order:A200#line:1> ex:sku "GAD-002" .
|
|
105
|
+
<urn:example:order:A200#line:1> ex:qty 1 .
|
|
106
|
+
<urn:example:order:A200#line:1> ex:unitCents 1500 .
|
|
107
|
+
<urn:example:order:A200#line:1> ex:netCents 1500 .
|
|
108
|
+
<urn:example:order:A300> ex:line <urn:example:order:A300#line:0> .
|
|
109
|
+
<urn:example:order:A300#line:0> a ex:OrderLine .
|
|
110
|
+
<urn:example:order:A300#line:0> ex:lineIndex 0 .
|
|
111
|
+
<urn:example:order:A300#line:0> ex:product ex:Widget .
|
|
112
|
+
<urn:example:order:A300#line:0> ex:sku "WID-001" .
|
|
113
|
+
<urn:example:order:A300#line:0> ex:qty 2 .
|
|
114
|
+
<urn:example:order:A300#line:0> ex:unitCents 999 .
|
|
115
|
+
<urn:example:order:A300#line:0> ex:netCents 1998 .
|
|
116
|
+
<urn:example:order:A300> ex:line <urn:example:order:A300#line:1> .
|
|
117
|
+
<urn:example:order:A300#line:1> a ex:OrderLine .
|
|
118
|
+
<urn:example:order:A300#line:1> ex:lineIndex 1 .
|
|
119
|
+
<urn:example:order:A300#line:1> ex:product ex:Gadget .
|
|
120
|
+
<urn:example:order:A300#line:1> ex:sku "GAD-002" .
|
|
121
|
+
<urn:example:order:A300#line:1> ex:qty 1 .
|
|
122
|
+
<urn:example:order:A300#line:1> ex:unitCents 1500 .
|
|
123
|
+
<urn:example:order:A300#line:1> ex:netCents 1500 .
|
|
124
|
+
<urn:example:order:A301> ex:line <urn:example:order:A301#line:0> .
|
|
125
|
+
<urn:example:order:A301#line:0> a ex:OrderLine .
|
|
126
|
+
<urn:example:order:A301#line:0> ex:lineIndex 0 .
|
|
127
|
+
<urn:example:order:A301#line:0> ex:product ex:Widget .
|
|
128
|
+
<urn:example:order:A301#line:0> ex:sku "WID-001" .
|
|
129
|
+
<urn:example:order:A301#line:0> ex:qty 2 .
|
|
130
|
+
<urn:example:order:A301#line:0> ex:unitCents 999 .
|
|
131
|
+
<urn:example:order:A301#line:0> ex:netCents 1998 .
|
|
132
|
+
<urn:example:order:A301> ex:line <urn:example:order:A301#line:1> .
|
|
133
|
+
<urn:example:order:A301#line:1> a ex:OrderLine .
|
|
134
|
+
<urn:example:order:A301#line:1> ex:lineIndex 1 .
|
|
135
|
+
<urn:example:order:A301#line:1> ex:product ex:Gadget .
|
|
136
|
+
<urn:example:order:A301#line:1> ex:sku "GAD-002" .
|
|
137
|
+
<urn:example:order:A301#line:1> ex:qty 1 .
|
|
138
|
+
<urn:example:order:A301#line:1> ex:unitCents 1500 .
|
|
139
|
+
<urn:example:order:A301#line:1> ex:netCents 1500 .
|
|
140
|
+
<urn:example:order:A400> ex:line <urn:example:order:A400#line:0> .
|
|
141
|
+
<urn:example:order:A400#line:0> a ex:OrderLine .
|
|
142
|
+
<urn:example:order:A400#line:0> ex:lineIndex 0 .
|
|
143
|
+
<urn:example:order:A400#line:0> ex:product ex:Widget .
|
|
144
|
+
<urn:example:order:A400#line:0> ex:sku "WID-001" .
|
|
145
|
+
<urn:example:order:A400#line:0> ex:qty 2 .
|
|
146
|
+
<urn:example:order:A400#line:0> ex:unitCents 999 .
|
|
147
|
+
<urn:example:order:A400#line:0> ex:netCents 1998 .
|
|
148
|
+
<urn:example:order:A400> ex:line <urn:example:order:A400#line:1> .
|
|
149
|
+
<urn:example:order:A400#line:1> a ex:OrderLine .
|
|
150
|
+
<urn:example:order:A400#line:1> ex:lineIndex 1 .
|
|
151
|
+
<urn:example:order:A400#line:1> ex:product ex:Gadget .
|
|
152
|
+
<urn:example:order:A400#line:1> ex:sku "GAD-002" .
|
|
153
|
+
<urn:example:order:A400#line:1> ex:qty 1 .
|
|
154
|
+
<urn:example:order:A400#line:1> ex:unitCents 1500 .
|
|
155
|
+
<urn:example:order:A400#line:1> ex:netCents 1500 .
|
|
156
|
+
<urn:example:order:A200#line:0> ex:vatBps 0 .
|
|
157
|
+
<urn:example:order:A200#line:0> ex:vatCents 0 .
|
|
158
|
+
<urn:example:order:A200#line:0> ex:grossCents 1998 .
|
|
159
|
+
<urn:example:order:A200#line:0> ex:vatReason ex:Export .
|
|
160
|
+
<urn:example:order:A200#line:1> ex:vatBps 0 .
|
|
161
|
+
<urn:example:order:A200#line:1> ex:vatCents 0 .
|
|
162
|
+
<urn:example:order:A200#line:1> ex:grossCents 1500 .
|
|
163
|
+
<urn:example:order:A200#line:1> ex:vatReason ex:Export .
|
|
164
|
+
<urn:example:order:A300#line:0> ex:vatBps 0 .
|
|
165
|
+
<urn:example:order:A300#line:0> ex:vatCents 0 .
|
|
166
|
+
<urn:example:order:A300#line:0> ex:grossCents 1998 .
|
|
167
|
+
<urn:example:order:A300#line:0> ex:vatReason ex:ReverseCharge .
|
|
168
|
+
<urn:example:order:A300#line:1> ex:vatBps 0 .
|
|
169
|
+
<urn:example:order:A300#line:1> ex:vatCents 0 .
|
|
170
|
+
<urn:example:order:A300#line:1> ex:grossCents 1500 .
|
|
171
|
+
<urn:example:order:A300#line:1> ex:vatReason ex:ReverseCharge .
|
|
172
|
+
<urn:example:order:A301#line:0> ex:vatBps 0 .
|
|
173
|
+
<urn:example:order:A301#line:0> ex:vatCents 0 .
|
|
174
|
+
<urn:example:order:A301#line:0> ex:grossCents 1998 .
|
|
175
|
+
<urn:example:order:A301#line:0> ex:vatReason ex:ReverseCharge .
|
|
176
|
+
<urn:example:order:A301#line:1> ex:vatBps 0 .
|
|
177
|
+
<urn:example:order:A301#line:1> ex:vatCents 0 .
|
|
178
|
+
<urn:example:order:A301#line:1> ex:grossCents 1500 .
|
|
179
|
+
<urn:example:order:A301#line:1> ex:vatReason ex:ReverseCharge .
|
|
180
|
+
<urn:example:order:A100#line:0> ex:vatBps 2100 .
|
|
181
|
+
<urn:example:order:A100#line:0> ex:vatCents 419 .
|
|
182
|
+
<urn:example:order:A100#line:0> ex:grossCents 2417 .
|
|
183
|
+
<urn:example:order:A100#line:0> ex:vatReason ex:Domestic .
|
|
184
|
+
<urn:example:order:A100#line:1> ex:vatBps 600 .
|
|
185
|
+
<urn:example:order:A100#line:1> ex:vatCents 90 .
|
|
186
|
+
<urn:example:order:A100#line:1> ex:grossCents 1590 .
|
|
187
|
+
<urn:example:order:A100#line:1> ex:vatReason ex:Domestic .
|
|
188
|
+
<urn:example:order:A400#line:0> ex:vatBps 2100 .
|
|
189
|
+
<urn:example:order:A400#line:0> ex:vatCents 419 .
|
|
190
|
+
<urn:example:order:A400#line:0> ex:grossCents 2417 .
|
|
191
|
+
<urn:example:order:A400#line:0> ex:vatReason ex:Domestic .
|
|
192
|
+
<urn:example:order:A400#line:1> ex:vatBps 600 .
|
|
193
|
+
<urn:example:order:A400#line:1> ex:vatCents 90 .
|
|
194
|
+
<urn:example:order:A400#line:1> ex:grossCents 1590 .
|
|
195
|
+
<urn:example:order:A400#line:1> ex:vatReason ex:Domestic .
|
|
196
|
+
<urn:example:order:A100> ex:computedNetCents 3498 .
|
|
197
|
+
<urn:example:order:A100> ex:computedVatCents 509 .
|
|
198
|
+
<urn:example:order:A100> ex:computedGrossCents 4007 .
|
|
199
|
+
<urn:example:order:A200> ex:computedNetCents 3498 .
|
|
200
|
+
<urn:example:order:A200> ex:computedVatCents 0 .
|
|
201
|
+
<urn:example:order:A200> ex:computedGrossCents 3498 .
|
|
202
|
+
<urn:example:order:A300> ex:computedNetCents 3498 .
|
|
203
|
+
<urn:example:order:A300> ex:computedVatCents 0 .
|
|
204
|
+
<urn:example:order:A300> ex:computedGrossCents 3498 .
|
|
205
|
+
<urn:example:order:A301> ex:computedNetCents 3498 .
|
|
206
|
+
<urn:example:order:A301> ex:computedVatCents 0 .
|
|
207
|
+
<urn:example:order:A301> ex:computedGrossCents 3498 .
|
|
208
|
+
<urn:example:order:A400> ex:computedNetCents 3498 .
|
|
209
|
+
<urn:example:order:A400> ex:computedVatCents 509 .
|
|
210
|
+
<urn:example:order:A400> ex:computedGrossCents 4007 .
|
|
211
|
+
<urn:example:order:A100> a ex:OrderOk .
|
|
212
|
+
<urn:example:order:A200> a ex:OrderOk .
|
|
213
|
+
<urn:example:order:A300> a ex:OrderOk .
|
|
214
|
+
<urn:example:order:A301> a ex:OrderOk .
|
|
215
|
+
<urn:example:issue:A400:vat> a ex:Issue .
|
|
216
|
+
<urn:example:issue:A400:vat> ex:onOrder <urn:example:order:A400> .
|
|
217
|
+
<urn:example:issue:A400:vat> ex:field "vatCents" .
|
|
218
|
+
<urn:example:issue:A400:vat> ex:declared 508 .
|
|
219
|
+
<urn:example:issue:A400:vat> ex:computed 509 .
|
|
220
|
+
<urn:example:order:A400> ex:hasIssue <urn:example:issue:A400:vat> .
|
|
221
|
+
<urn:example:issue:A400:gross> a ex:Issue .
|
|
222
|
+
<urn:example:issue:A400:gross> ex:onOrder <urn:example:order:A400> .
|
|
223
|
+
<urn:example:issue:A400:gross> ex:field "grossCents" .
|
|
224
|
+
<urn:example:issue:A400:gross> ex:declared 4006 .
|
|
225
|
+
<urn:example:issue:A400:gross> ex:computed 4007 .
|
|
226
|
+
<urn:example:order:A400> ex:hasIssue <urn:example:issue:A400:gross> .
|
package/eyeling-builtins.ttl
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
9
9
|
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
10
10
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
11
|
+
@prefix tfn: <https://w3id.org/time-fn#> .
|
|
11
12
|
|
|
12
13
|
# ------------------------------------------------------------------------------
|
|
13
14
|
# Builtin catalog (eyeling.js)
|
|
@@ -336,3 +337,26 @@ string:format a ex:Builtin ; ex:kind ex:Function ;
|
|
|
336
337
|
|
|
337
338
|
string:jsonPointer a ex:Builtin ; ex:kind ex:Function ;
|
|
338
339
|
rdfs:comment "JSON Pointer lookup: (jsonText pointer) -> value term. Expects rdf:JSON-typed literal (or treated as rdf:JSON); caches parsed JSON and pointer lookups." .
|
|
340
|
+
|
|
341
|
+
# --- tfn: (Time Functions) -------------------------------------------
|
|
342
|
+
# Source semantics: Smessaert et al., “It’s About Time: Time Functions for Comparing Partial and Floating Time Literals in SPARQL”, §2.2.
|
|
343
|
+
|
|
344
|
+
tfn:periodMinInclusive a ex:Builtin ;
|
|
345
|
+
ex:kind ex:Function ;
|
|
346
|
+
rdfs:comment "Time Functions: inclusive lower bound of the period represented by a temporal literal (xsd:date, xsd:gYearMonth, xsd:gYear, xsd:dateTime, ...), returned as xsd:dateTime." .
|
|
347
|
+
|
|
348
|
+
tfn:periodMaxInclusive a ex:Builtin ;
|
|
349
|
+
ex:kind ex:Function ;
|
|
350
|
+
rdfs:comment "Time Functions: inclusive upper bound of the period represented by a temporal literal, returned as xsd:dateTime." .
|
|
351
|
+
|
|
352
|
+
tfn:periodMinExclusive a ex:Builtin ;
|
|
353
|
+
ex:kind ex:Function ;
|
|
354
|
+
rdfs:comment "Time Functions: exclusive lower bound of the period represented by a temporal literal, returned as xsd:dateTime." .
|
|
355
|
+
|
|
356
|
+
tfn:periodMaxExclusive a ex:Builtin ;
|
|
357
|
+
ex:kind ex:Function ;
|
|
358
|
+
rdfs:comment "Time Functions: exclusive upper bound of the period represented by a temporal literal, returned as xsd:dateTime." .
|
|
359
|
+
|
|
360
|
+
tfn:bindDefaultTimezone a ex:Builtin ;
|
|
361
|
+
ex:kind ex:Function ;
|
|
362
|
+
rdfs:comment "Time Functions: binds a default timezone to a floating time literal. Input is (timeLiteral timeZone). If the literal already has a timezone, it is returned unchanged; otherwise the specified timezone is applied." .
|
package/eyeling.js
CHANGED
|
@@ -42,6 +42,7 @@ const XSD_NS = 'http://www.w3.org/2001/XMLSchema#';
|
|
|
42
42
|
const CRYPTO_NS = 'http://www.w3.org/2000/10/swap/crypto#';
|
|
43
43
|
const MATH_NS = 'http://www.w3.org/2000/10/swap/math#';
|
|
44
44
|
const TIME_NS = 'http://www.w3.org/2000/10/swap/time#';
|
|
45
|
+
const TFN_NS = 'https://w3id.org/time-fn#';
|
|
45
46
|
const LIST_NS = 'http://www.w3.org/2000/10/swap/list#';
|
|
46
47
|
const LOG_NS = 'http://www.w3.org/2000/10/swap/log#';
|
|
47
48
|
const STRING_NS = 'http://www.w3.org/2000/10/swap/string#';
|
|
@@ -3081,19 +3082,34 @@ function termToJsXsdStringNoLang(t) {
|
|
|
3081
3082
|
}
|
|
3082
3083
|
|
|
3083
3084
|
function termToJsString(t) {
|
|
3084
|
-
//
|
|
3085
|
-
//
|
|
3086
|
-
//
|
|
3087
|
-
//
|
|
3085
|
+
// Domain is xsd:string for SWAP/N3 string builtins (string:*).
|
|
3086
|
+
//
|
|
3087
|
+
// Per the N3 Builtins spec, when the domain is xsd:string we must be able to
|
|
3088
|
+
// cast *any* IRI or literal value (incl. numeric, boolean, dateTime, anyURI,
|
|
3089
|
+
// rdf:langString, and plain literals) to a string.
|
|
3090
|
+
//
|
|
3091
|
+
// We implement this as:
|
|
3092
|
+
// - IRI -> its IRI string
|
|
3093
|
+
// - Literal:
|
|
3094
|
+
// * quoted lexical form: decode N3/Turtle escapes and strip quotes
|
|
3095
|
+
// * unquoted lexical form: use as-is (e.g., 1234, true, 1971-..., 1.23E4)
|
|
3096
|
+
// - Everything else (blank nodes, lists, formulas, vars) -> fail
|
|
3097
|
+
if (t instanceof Iri) return t.value;
|
|
3088
3098
|
if (!(t instanceof Literal)) return null;
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3099
|
+
|
|
3100
|
+
const [lex, _dt] = literalParts(t.value);
|
|
3101
|
+
|
|
3102
|
+
if (isQuotedLexical(lex)) {
|
|
3103
|
+
// Interpret N3/Turtle string escapes (\" \n \uXXXX \UXXXXXXXX …)
|
|
3104
|
+
// to obtain the actual string value.
|
|
3105
|
+
return decodeN3StringEscapes(stripQuotes(lex));
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
// Unquoted lexical (numbers/booleans/dateTimes, etc.)
|
|
3109
|
+
return typeof lex === 'string' ? lex : String(lex);
|
|
3095
3110
|
}
|
|
3096
3111
|
|
|
3112
|
+
|
|
3097
3113
|
function makeStringLiteral(str) {
|
|
3098
3114
|
// JSON.stringify gives us a valid N3/Turtle-style quoted string
|
|
3099
3115
|
// (with proper escaping for quotes, backslashes, newlines, …).
|
|
@@ -3610,6 +3626,248 @@ function parseXsdDateTimeLexParts(t) {
|
|
|
3610
3626
|
return { yearStr, month, day, hour, minute, second, tz };
|
|
3611
3627
|
}
|
|
3612
3628
|
|
|
3629
|
+
// -----------------------------------------------------------------------------
|
|
3630
|
+
// Time Functions (tfn:) helpers
|
|
3631
|
+
// -----------------------------------------------------------------------------
|
|
3632
|
+
|
|
3633
|
+
function __tfnParseTimezoneValue(t) {
|
|
3634
|
+
// Accept plain string literals and xsd:string. Return "Z" or "+hh:mm" or "-hh:mm".
|
|
3635
|
+
if (!(t instanceof Literal)) return null;
|
|
3636
|
+
const [lex, dt] = literalParts(t.value);
|
|
3637
|
+
const s = stripQuotes(lex).trim();
|
|
3638
|
+
if (!s) return null;
|
|
3639
|
+
if (s === 'Z') return 'Z';
|
|
3640
|
+
const m = /^([+-])(\d{2}):(\d{2})$/.exec(s);
|
|
3641
|
+
if (!m) return null;
|
|
3642
|
+
const hh = parseInt(m[2], 10);
|
|
3643
|
+
const mm = parseInt(m[3], 10);
|
|
3644
|
+
if (!(hh >= 0 && hh <= 14)) return null;
|
|
3645
|
+
if (!(mm >= 0 && mm <= 59)) return null;
|
|
3646
|
+
// disallow offsets beyond 14:00 (e.g., +14:30)
|
|
3647
|
+
if (hh === 14 && mm !== 0) return null;
|
|
3648
|
+
return `${m[1]}${m[2]}:${m[3]}`;
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
function __tfnHasTimezoneSuffix(s) {
|
|
3652
|
+
return /(Z|[+-]\d{2}:\d{2})$/.test(s);
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3655
|
+
function __tfnParseTemporalLiteralParts(t) {
|
|
3656
|
+
if (!(t instanceof Literal)) return null;
|
|
3657
|
+
const [lex, dt] = literalParts(t.value);
|
|
3658
|
+
if (!dt) return null;
|
|
3659
|
+
const val = stripQuotes(lex);
|
|
3660
|
+
|
|
3661
|
+
// xsd:dateTime
|
|
3662
|
+
if (dt === XSD_NS + 'dateTime') {
|
|
3663
|
+
const m = /^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(Z|[+-]\d{2}:\d{2})?$/.exec(val);
|
|
3664
|
+
if (!m) return null;
|
|
3665
|
+
const year = BigInt(m[1]);
|
|
3666
|
+
const month = parseInt(m[2], 10);
|
|
3667
|
+
const day = parseInt(m[3], 10);
|
|
3668
|
+
const hour = parseInt(m[4], 10);
|
|
3669
|
+
const minute = parseInt(m[5], 10);
|
|
3670
|
+
const second = parseInt(m[6], 10);
|
|
3671
|
+
const frac = m[7] || '';
|
|
3672
|
+
const tz = m[8] || null;
|
|
3673
|
+
|
|
3674
|
+
if (!(month >= 1 && month <= 12)) return null;
|
|
3675
|
+
if (!(day >= 1 && day <= 31)) return null;
|
|
3676
|
+
if (!(hour >= 0 && hour <= 23)) return null;
|
|
3677
|
+
if (!(minute >= 0 && minute <= 59)) return null;
|
|
3678
|
+
if (!(second >= 0 && second <= 59)) return null;
|
|
3679
|
+
|
|
3680
|
+
// Keep milliseconds precision (3 digits), truncating extra digits.
|
|
3681
|
+
const msStr = (frac + '000').slice(0, 3);
|
|
3682
|
+
const millis = parseInt(msStr, 10);
|
|
3683
|
+
|
|
3684
|
+
return { kind: 'dateTime', year, month, day, hour, minute, second, millis, tz, dt };
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
// xsd:date
|
|
3688
|
+
if (dt === XSD_NS + 'date') {
|
|
3689
|
+
const m = /^(-?\d{4,})-(\d{2})-(\d{2})(Z|[+-]\d{2}:\d{2})?$/.exec(val);
|
|
3690
|
+
if (!m) return null;
|
|
3691
|
+
const year = BigInt(m[1]);
|
|
3692
|
+
const month = parseInt(m[2], 10);
|
|
3693
|
+
const day = parseInt(m[3], 10);
|
|
3694
|
+
const tz = m[4] || null;
|
|
3695
|
+
if (!(month >= 1 && month <= 12)) return null;
|
|
3696
|
+
if (!(day >= 1 && day <= 31)) return null;
|
|
3697
|
+
return { kind: 'date', year, month, day, tz, dt };
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
// xsd:gYearMonth
|
|
3701
|
+
if (dt === XSD_NS + 'gYearMonth') {
|
|
3702
|
+
const m = /^(-?\d{4,})-(\d{2})(Z|[+-]\d{2}:\d{2})?$/.exec(val);
|
|
3703
|
+
if (!m) return null;
|
|
3704
|
+
const year = BigInt(m[1]);
|
|
3705
|
+
const month = parseInt(m[2], 10);
|
|
3706
|
+
const tz = m[3] || null;
|
|
3707
|
+
if (!(month >= 1 && month <= 12)) return null;
|
|
3708
|
+
return { kind: 'gYearMonth', year, month, tz, dt };
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
// xsd:gYear
|
|
3712
|
+
if (dt === XSD_NS + 'gYear') {
|
|
3713
|
+
const m = /^(-?\d{4,})(Z|[+-]\d{2}:\d{2})?$/.exec(val);
|
|
3714
|
+
if (!m) return null;
|
|
3715
|
+
const year = BigInt(m[1]);
|
|
3716
|
+
const tz = m[2] || null;
|
|
3717
|
+
return { kind: 'gYear', year, tz, dt };
|
|
3718
|
+
}
|
|
3719
|
+
|
|
3720
|
+
return null;
|
|
3721
|
+
}
|
|
3722
|
+
|
|
3723
|
+
function __tfnMod(a, b) {
|
|
3724
|
+
let r = a % b;
|
|
3725
|
+
if (r < 0n) r += b;
|
|
3726
|
+
return r;
|
|
3727
|
+
}
|
|
3728
|
+
|
|
3729
|
+
function __tfnIsLeapYear(y) {
|
|
3730
|
+
// Proleptic Gregorian leap-year rule.
|
|
3731
|
+
// Works for negative years using normalized modulus.
|
|
3732
|
+
if (__tfnMod(y, 4n) !== 0n) return false;
|
|
3733
|
+
if (__tfnMod(y, 100n) !== 0n) return true;
|
|
3734
|
+
return __tfnMod(y, 400n) === 0n;
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
function __tfnDaysInMonth(y, m) {
|
|
3738
|
+
if (m === 2) return __tfnIsLeapYear(y) ? 29 : 28;
|
|
3739
|
+
if (m === 4 || m === 6 || m === 9 || m === 11) return 30;
|
|
3740
|
+
return 31;
|
|
3741
|
+
}
|
|
3742
|
+
|
|
3743
|
+
function __tfnPad2(n) {
|
|
3744
|
+
return n < 10 ? '0' + String(n) : String(n);
|
|
3745
|
+
}
|
|
3746
|
+
function __tfnPad3(n) {
|
|
3747
|
+
const s = String(n);
|
|
3748
|
+
return s.length === 1 ? '00' + s : s.length === 2 ? '0' + s : s;
|
|
3749
|
+
}
|
|
3750
|
+
function __tfnFormatYear(y) {
|
|
3751
|
+
const neg = y < 0n;
|
|
3752
|
+
const abs = neg ? -y : y;
|
|
3753
|
+
let digits = abs.toString();
|
|
3754
|
+
if (digits.length < 4) digits = '0'.repeat(4 - digits.length) + digits;
|
|
3755
|
+
return neg ? '-' + digits : digits;
|
|
3756
|
+
}
|
|
3757
|
+
|
|
3758
|
+
function __tfnAdd1ms(c) {
|
|
3759
|
+
// Mutates and returns c; c: {year,month,day,hour,minute,second,millis}
|
|
3760
|
+
if (c.millis < 999) { c.millis++; return c; }
|
|
3761
|
+
c.millis = 0;
|
|
3762
|
+
if (c.second < 59) { c.second++; return c; }
|
|
3763
|
+
c.second = 0;
|
|
3764
|
+
if (c.minute < 59) { c.minute++; return c; }
|
|
3765
|
+
c.minute = 0;
|
|
3766
|
+
if (c.hour < 23) { c.hour++; return c; }
|
|
3767
|
+
c.hour = 0;
|
|
3768
|
+
|
|
3769
|
+
const dim = __tfnDaysInMonth(c.year, c.month);
|
|
3770
|
+
if (c.day < dim) { c.day++; return c; }
|
|
3771
|
+
c.day = 1;
|
|
3772
|
+
if (c.month < 12) { c.month++; return c; }
|
|
3773
|
+
c.month = 1;
|
|
3774
|
+
c.year = c.year + 1n;
|
|
3775
|
+
return c;
|
|
3776
|
+
}
|
|
3777
|
+
|
|
3778
|
+
function __tfnSub1ms(c) {
|
|
3779
|
+
// Mutates and returns c; c: {year,month,day,hour,minute,second,millis}
|
|
3780
|
+
if (c.millis > 0) { c.millis--; return c; }
|
|
3781
|
+
c.millis = 999;
|
|
3782
|
+
if (c.second > 0) { c.second--; return c; }
|
|
3783
|
+
c.second = 59;
|
|
3784
|
+
if (c.minute > 0) { c.minute--; return c; }
|
|
3785
|
+
c.minute = 59;
|
|
3786
|
+
if (c.hour > 0) { c.hour--; return c; }
|
|
3787
|
+
c.hour = 23;
|
|
3788
|
+
|
|
3789
|
+
if (c.day > 1) { c.day--; return c; }
|
|
3790
|
+
// move to previous month
|
|
3791
|
+
if (c.month > 1) {
|
|
3792
|
+
c.month--;
|
|
3793
|
+
} else {
|
|
3794
|
+
c.month = 12;
|
|
3795
|
+
c.year = c.year - 1n;
|
|
3796
|
+
}
|
|
3797
|
+
c.day = __tfnDaysInMonth(c.year, c.month);
|
|
3798
|
+
return c;
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
function __tfnMakeDateTimeLiteral(c, tz) {
|
|
3802
|
+
const y = __tfnFormatYear(c.year);
|
|
3803
|
+
const lex = `${y}-${__tfnPad2(c.month)}-${__tfnPad2(c.day)}T${__tfnPad2(c.hour)}:${__tfnPad2(c.minute)}:${__tfnPad2(c.second)}.${__tfnPad3(c.millis)}${tz}`;
|
|
3804
|
+
return internLiteral(`"${lex}"^^<${XSD_NS}dateTime>`);
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
function __tfnComputePeriodBounds(parts) {
|
|
3808
|
+
// Returns { tzMin, tzMax, startC, endC } where startC/endC are component objects.
|
|
3809
|
+
const tzMin = parts.tz || '+14:00';
|
|
3810
|
+
const tzMax = parts.tz || '-14:00';
|
|
3811
|
+
|
|
3812
|
+
if (parts.kind === 'dateTime') {
|
|
3813
|
+
const base = {
|
|
3814
|
+
year: parts.year,
|
|
3815
|
+
month: parts.month,
|
|
3816
|
+
day: parts.day,
|
|
3817
|
+
hour: parts.hour,
|
|
3818
|
+
minute: parts.minute,
|
|
3819
|
+
second: parts.second,
|
|
3820
|
+
millis: parts.millis,
|
|
3821
|
+
};
|
|
3822
|
+
return { tzMin, tzMax, startC: { ...base }, endC: { ...base } };
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
if (parts.kind === 'date') {
|
|
3826
|
+
const startC = { year: parts.year, month: parts.month, day: parts.day, hour: 0, minute: 0, second: 0, millis: 0 };
|
|
3827
|
+
const endC = { year: parts.year, month: parts.month, day: parts.day, hour: 23, minute: 59, second: 59, millis: 999 };
|
|
3828
|
+
return { tzMin, tzMax, startC, endC };
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3831
|
+
if (parts.kind === 'gYearMonth') {
|
|
3832
|
+
const dim = __tfnDaysInMonth(parts.year, parts.month);
|
|
3833
|
+
const startC = { year: parts.year, month: parts.month, day: 1, hour: 0, minute: 0, second: 0, millis: 0 };
|
|
3834
|
+
const endC = { year: parts.year, month: parts.month, day: dim, hour: 23, minute: 59, second: 59, millis: 999 };
|
|
3835
|
+
return { tzMin, tzMax, startC, endC };
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3838
|
+
if (parts.kind === 'gYear') {
|
|
3839
|
+
const startC = { year: parts.year, month: 1, day: 1, hour: 0, minute: 0, second: 0, millis: 0 };
|
|
3840
|
+
const endC = { year: parts.year, month: 12, day: 31, hour: 23, minute: 59, second: 59, millis: 999 };
|
|
3841
|
+
return { tzMin, tzMax, startC, endC };
|
|
3842
|
+
}
|
|
3843
|
+
|
|
3844
|
+
return null;
|
|
3845
|
+
}
|
|
3846
|
+
|
|
3847
|
+
function __tfnBindDefaultTimezone(timeLit, tzLit) {
|
|
3848
|
+
if (!(timeLit instanceof Literal) || !(tzLit instanceof Literal)) return null;
|
|
3849
|
+
const tz = __tfnParseTimezoneValue(tzLit);
|
|
3850
|
+
if (!tz) return null;
|
|
3851
|
+
|
|
3852
|
+
const [lex, dt] = literalParts(timeLit.value);
|
|
3853
|
+
if (!dt) return null;
|
|
3854
|
+
const v = stripQuotes(lex);
|
|
3855
|
+
|
|
3856
|
+
// If already has tz, return unchanged.
|
|
3857
|
+
if (__tfnHasTimezoneSuffix(v)) return timeLit;
|
|
3858
|
+
|
|
3859
|
+
// Only support the temporal types we parse.
|
|
3860
|
+
if (
|
|
3861
|
+
dt !== XSD_NS + 'dateTime' &&
|
|
3862
|
+
dt !== XSD_NS + 'date' &&
|
|
3863
|
+
dt !== XSD_NS + 'gYearMonth' &&
|
|
3864
|
+
dt !== XSD_NS + 'gYear'
|
|
3865
|
+
) return null;
|
|
3866
|
+
|
|
3867
|
+
const outLex = `"${v}${tz}"^^<${dt}>`;
|
|
3868
|
+
return internLiteral(outLex);
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3613
3871
|
function parseDatetimeLike(t) {
|
|
3614
3872
|
const d = parseXsdDateTerm(t);
|
|
3615
3873
|
if (d !== null) return d;
|
|
@@ -4905,6 +5163,73 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
4905
5163
|
return [];
|
|
4906
5164
|
}
|
|
4907
5165
|
|
|
5166
|
+
// -----------------------------------------------------------------
|
|
5167
|
+
// 4.3.1 tfn: Time Functions builtins
|
|
5168
|
+
// -----------------------------------------------------------------
|
|
5169
|
+
|
|
5170
|
+
// tfn:periodMinInclusive / periodMaxInclusive / periodMinExclusive / periodMaxExclusive
|
|
5171
|
+
// Schema: ( $s.1+ )+ tfn:* $o-
|
|
5172
|
+
const tfnPeriodKind =
|
|
5173
|
+
pv === TFN_NS + 'periodMinInclusive'
|
|
5174
|
+
? 'minInc'
|
|
5175
|
+
: pv === TFN_NS + 'periodMaxInclusive'
|
|
5176
|
+
? 'maxInc'
|
|
5177
|
+
: pv === TFN_NS + 'periodMinExclusive'
|
|
5178
|
+
? 'minEx'
|
|
5179
|
+
: pv === TFN_NS + 'periodMaxExclusive'
|
|
5180
|
+
? 'maxEx'
|
|
5181
|
+
: null;
|
|
5182
|
+
if (tfnPeriodKind) {
|
|
5183
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 1) return [];
|
|
5184
|
+
const arg = g.s.elems[0];
|
|
5185
|
+
const parts = __tfnParseTemporalLiteralParts(arg);
|
|
5186
|
+
if (!parts) return [];
|
|
5187
|
+
const bounds = __tfnComputePeriodBounds(parts);
|
|
5188
|
+
if (!bounds) return [];
|
|
5189
|
+
|
|
5190
|
+
let out;
|
|
5191
|
+
if (tfnPeriodKind === 'minInc') {
|
|
5192
|
+
out = __tfnMakeDateTimeLiteral({ ...bounds.startC }, bounds.tzMin);
|
|
5193
|
+
} else if (tfnPeriodKind === 'maxInc') {
|
|
5194
|
+
out = __tfnMakeDateTimeLiteral({ ...bounds.endC }, bounds.tzMax);
|
|
5195
|
+
} else if (tfnPeriodKind === 'minEx') {
|
|
5196
|
+
const c = __tfnSub1ms({ ...bounds.startC });
|
|
5197
|
+
out = __tfnMakeDateTimeLiteral(c, bounds.tzMin);
|
|
5198
|
+
} else { // maxEx
|
|
5199
|
+
const c = __tfnAdd1ms({ ...bounds.endC });
|
|
5200
|
+
out = __tfnMakeDateTimeLiteral(c, bounds.tzMax);
|
|
5201
|
+
}
|
|
5202
|
+
|
|
5203
|
+
if (g.o instanceof Var) {
|
|
5204
|
+
const s2 = { ...subst };
|
|
5205
|
+
s2[g.o.name] = out;
|
|
5206
|
+
return [s2];
|
|
5207
|
+
}
|
|
5208
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
5209
|
+
|
|
5210
|
+
const s2 = unifyTerm(g.o, out, subst);
|
|
5211
|
+
return s2 !== null ? [s2] : [];
|
|
5212
|
+
}
|
|
5213
|
+
|
|
5214
|
+
// tfn:bindDefaultTimezone
|
|
5215
|
+
// Schema: ( $s.1+ $s.2+ )+ tfn:bindDefaultTimezone $o-
|
|
5216
|
+
if (pv === TFN_NS + 'bindDefaultTimezone') {
|
|
5217
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
5218
|
+
const [timeLit, tzLit] = g.s.elems;
|
|
5219
|
+
const out = __tfnBindDefaultTimezone(timeLit, tzLit);
|
|
5220
|
+
if (!out) return [];
|
|
5221
|
+
|
|
5222
|
+
if (g.o instanceof Var) {
|
|
5223
|
+
const s2 = { ...subst };
|
|
5224
|
+
s2[g.o.name] = out;
|
|
5225
|
+
return [s2];
|
|
5226
|
+
}
|
|
5227
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
5228
|
+
|
|
5229
|
+
const s2 = unifyTerm(g.o, out, subst);
|
|
5230
|
+
return s2 !== null ? [s2] : [];
|
|
5231
|
+
}
|
|
5232
|
+
|
|
4908
5233
|
// -----------------------------------------------------------------
|
|
4909
5234
|
// 4.4 list: builtins
|
|
4910
5235
|
// -----------------------------------------------------------------
|
|
@@ -6129,6 +6454,7 @@ function isBuiltinPred(p) {
|
|
|
6129
6454
|
v.startsWith(LOG_NS) ||
|
|
6130
6455
|
v.startsWith(STRING_NS) ||
|
|
6131
6456
|
v.startsWith(TIME_NS) ||
|
|
6457
|
+
v.startsWith(TFN_NS) ||
|
|
6132
6458
|
v.startsWith(LIST_NS)
|
|
6133
6459
|
);
|
|
6134
6460
|
}
|