eyeling 1.5.30 → 1.5.32
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/json-pointer.n3 +69 -0
- package/examples/output/json-pointer.n3 +800 -0
- package/eyeling.js +236 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -239,7 +239,7 @@ As soon as the premise is provable, `eyeling` exits with status code `2`.
|
|
|
239
239
|
- **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`
|
|
240
240
|
- **log**: `log:collectAllIn` `log:equalTo` `log:forAllIn` `log:impliedBy` `log:implies` `log:notEqualTo` `log:notIncludes` `log:skolem` `log:uri`
|
|
241
241
|
- **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`
|
|
242
|
-
- **string**: `string:concatenation` `string:contains` `string:containsIgnoringCase` `string:endsWith` `string:equalIgnoringCase` `string:format` `string:greaterThan` `string:lessThan` `string:matches` `string:notEqualIgnoringCase` `string:notGreaterThan` `string:notLessThan` `string:notMatches` `string:replace` `string:scrape` `string:startsWith`
|
|
242
|
+
- **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`
|
|
243
243
|
- **time**: `time:localTime`
|
|
244
244
|
|
|
245
245
|
## License
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
|
|
2
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#> .
|
|
3
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
4
|
+
@prefix ex: <http://example.org/> .
|
|
5
|
+
|
|
6
|
+
ex:doc ex:json """{
|
|
7
|
+
"users": [
|
|
8
|
+
{ "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
9
|
+
{ "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
10
|
+
],
|
|
11
|
+
"policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
12
|
+
}""" .
|
|
13
|
+
|
|
14
|
+
# Sanity check: key contains "/" so JSON Pointer must use "~1"
|
|
15
|
+
{
|
|
16
|
+
ex:doc ex:json ?J .
|
|
17
|
+
(?J "/users/0/profile~1name") string:jsonPointer "Ada Lovelace" .
|
|
18
|
+
} => {
|
|
19
|
+
ex:checks ex:firstUserNameOk true .
|
|
20
|
+
} .
|
|
21
|
+
|
|
22
|
+
# Allowed users: email domain is in policy.allowedDomains
|
|
23
|
+
{
|
|
24
|
+
ex:doc ex:json ?J .
|
|
25
|
+
(?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
26
|
+
(?J "/users") string:jsonPointer ?Users .
|
|
27
|
+
|
|
28
|
+
?Users list:iterate (?Idx ?UserJson) .
|
|
29
|
+
(?UserJson "/id") string:jsonPointer ?Id .
|
|
30
|
+
(?UserJson "/email") string:jsonPointer ?Email .
|
|
31
|
+
(?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
32
|
+
|
|
33
|
+
(?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
34
|
+
?Allowed list:member ?EmailDomain .
|
|
35
|
+
|
|
36
|
+
("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
37
|
+
?User log:uri ?UriStr .
|
|
38
|
+
} => {
|
|
39
|
+
?User a ex:AllowedUser ;
|
|
40
|
+
ex:name ?Name ;
|
|
41
|
+
ex:email ?Email ;
|
|
42
|
+
ex:emailDomain ?EmailDomain ;
|
|
43
|
+
ex:userIndex ?Idx .
|
|
44
|
+
} .
|
|
45
|
+
|
|
46
|
+
# Blocked users: email domain is NOT in the allowlist
|
|
47
|
+
{
|
|
48
|
+
ex:doc ex:json ?J .
|
|
49
|
+
(?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
50
|
+
(?J "/users") string:jsonPointer ?Users .
|
|
51
|
+
|
|
52
|
+
?Users list:iterate (?Idx ?UserJson) .
|
|
53
|
+
(?UserJson "/id") string:jsonPointer ?Id .
|
|
54
|
+
(?UserJson "/email") string:jsonPointer ?Email .
|
|
55
|
+
(?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
56
|
+
|
|
57
|
+
(?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
58
|
+
?Allowed list:notMember ?EmailDomain .
|
|
59
|
+
|
|
60
|
+
("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
61
|
+
?User log:uri ?UriStr .
|
|
62
|
+
} => {
|
|
63
|
+
?User a ex:BlockedUser ;
|
|
64
|
+
ex:name ?Name ;
|
|
65
|
+
ex:email ?Email ;
|
|
66
|
+
ex:emailDomain ?EmailDomain ;
|
|
67
|
+
ex:userIndex ?Idx .
|
|
68
|
+
} .
|
|
69
|
+
|
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
@prefix ex: <http://example.org/> .
|
|
2
|
+
|
|
3
|
+
# ----------------------------------------------------------------------
|
|
4
|
+
# Proof for derived triple:
|
|
5
|
+
# ex:checks ex:firstUserNameOk true .
|
|
6
|
+
# It holds because the following instance of the rule body is provable:
|
|
7
|
+
# ex:doc ex:json """{
|
|
8
|
+
# "users": [
|
|
9
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
10
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
11
|
+
# ],
|
|
12
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
13
|
+
# }""" .
|
|
14
|
+
# ("""{
|
|
15
|
+
# "users": [
|
|
16
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
17
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
18
|
+
# ],
|
|
19
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
20
|
+
# }""" "/users/0/profile~1name") string:jsonPointer "Ada Lovelace" .
|
|
21
|
+
# via the schematic forward rule:
|
|
22
|
+
# {
|
|
23
|
+
# ex:doc ex:json ?J .
|
|
24
|
+
# (?J "/users/0/profile~1name") string:jsonPointer "Ada Lovelace" .
|
|
25
|
+
# } => {
|
|
26
|
+
# ex:checks ex:firstUserNameOk true .
|
|
27
|
+
# } .
|
|
28
|
+
# with substitution (on rule variables):
|
|
29
|
+
# ?J = """{
|
|
30
|
+
# "users": [
|
|
31
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
32
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
33
|
+
# ],
|
|
34
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
35
|
+
# }"""
|
|
36
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
37
|
+
# ----------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
ex:checks ex:firstUserNameOk true .
|
|
40
|
+
|
|
41
|
+
# ----------------------------------------------------------------------
|
|
42
|
+
# Proof for derived triple:
|
|
43
|
+
# <urn:example:user:u1> a ex:AllowedUser .
|
|
44
|
+
# It holds because the following instance of the rule body is provable:
|
|
45
|
+
# ex:doc ex:json """{
|
|
46
|
+
# "users": [
|
|
47
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
48
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
49
|
+
# ],
|
|
50
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
51
|
+
# }""" .
|
|
52
|
+
# ("""{
|
|
53
|
+
# "users": [
|
|
54
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
55
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
56
|
+
# ],
|
|
57
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
58
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
59
|
+
# ("""{
|
|
60
|
+
# "users": [
|
|
61
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
62
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
63
|
+
# ],
|
|
64
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
65
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
66
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (0 """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""") .
|
|
67
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/id") string:jsonPointer "u1" .
|
|
68
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/email") string:jsonPointer "ada@example.org" .
|
|
69
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/profile~1name") string:jsonPointer "Ada Lovelace" .
|
|
70
|
+
# ("ada@example.org" "@([^@]+)$") string:scrape "example.org" .
|
|
71
|
+
# ("example.org" "example.com") list:member "example.org" .
|
|
72
|
+
# ("urn:example:user:%s" "u1") string:format "urn:example:user:u1" .
|
|
73
|
+
# <urn:example:user:u1> log:uri "urn:example:user:u1" .
|
|
74
|
+
# via the schematic forward rule:
|
|
75
|
+
# {
|
|
76
|
+
# ex:doc ex:json ?J .
|
|
77
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
78
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
79
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
80
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
81
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
82
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
83
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
84
|
+
# ?Allowed list:member ?EmailDomain .
|
|
85
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
86
|
+
# ?User log:uri ?UriStr .
|
|
87
|
+
# } => {
|
|
88
|
+
# ?User a ex:AllowedUser .
|
|
89
|
+
# ?User ex:name ?Name .
|
|
90
|
+
# ?User ex:email ?Email .
|
|
91
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
92
|
+
# ?User ex:userIndex ?Idx .
|
|
93
|
+
# } .
|
|
94
|
+
# with substitution (on rule variables):
|
|
95
|
+
# ?Allowed = ("example.org" "example.com")
|
|
96
|
+
# ?Email = "ada@example.org"
|
|
97
|
+
# ?EmailDomain = "example.org"
|
|
98
|
+
# ?Id = "u1"
|
|
99
|
+
# ?Idx = 0
|
|
100
|
+
# ?J = """{
|
|
101
|
+
# "users": [
|
|
102
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
103
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
104
|
+
# ],
|
|
105
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
106
|
+
# }"""
|
|
107
|
+
# ?Name = "Ada Lovelace"
|
|
108
|
+
# ?UriStr = "urn:example:user:u1"
|
|
109
|
+
# ?User = <urn:example:user:u1>
|
|
110
|
+
# ?UserJson = """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}"""
|
|
111
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
112
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
113
|
+
# ----------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
<urn:example:user:u1> a ex:AllowedUser .
|
|
116
|
+
|
|
117
|
+
# ----------------------------------------------------------------------
|
|
118
|
+
# Proof for derived triple:
|
|
119
|
+
# <urn:example:user:u1> ex:name "Ada Lovelace" .
|
|
120
|
+
# It holds because the following instance of the rule body is provable:
|
|
121
|
+
# ex:doc ex:json """{
|
|
122
|
+
# "users": [
|
|
123
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
124
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
125
|
+
# ],
|
|
126
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
127
|
+
# }""" .
|
|
128
|
+
# ("""{
|
|
129
|
+
# "users": [
|
|
130
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
131
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
132
|
+
# ],
|
|
133
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
134
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
135
|
+
# ("""{
|
|
136
|
+
# "users": [
|
|
137
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
138
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
139
|
+
# ],
|
|
140
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
141
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
142
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (0 """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""") .
|
|
143
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/id") string:jsonPointer "u1" .
|
|
144
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/email") string:jsonPointer "ada@example.org" .
|
|
145
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/profile~1name") string:jsonPointer "Ada Lovelace" .
|
|
146
|
+
# ("ada@example.org" "@([^@]+)$") string:scrape "example.org" .
|
|
147
|
+
# ("example.org" "example.com") list:member "example.org" .
|
|
148
|
+
# ("urn:example:user:%s" "u1") string:format "urn:example:user:u1" .
|
|
149
|
+
# <urn:example:user:u1> log:uri "urn:example:user:u1" .
|
|
150
|
+
# via the schematic forward rule:
|
|
151
|
+
# {
|
|
152
|
+
# ex:doc ex:json ?J .
|
|
153
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
154
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
155
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
156
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
157
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
158
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
159
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
160
|
+
# ?Allowed list:member ?EmailDomain .
|
|
161
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
162
|
+
# ?User log:uri ?UriStr .
|
|
163
|
+
# } => {
|
|
164
|
+
# ?User a ex:AllowedUser .
|
|
165
|
+
# ?User ex:name ?Name .
|
|
166
|
+
# ?User ex:email ?Email .
|
|
167
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
168
|
+
# ?User ex:userIndex ?Idx .
|
|
169
|
+
# } .
|
|
170
|
+
# with substitution (on rule variables):
|
|
171
|
+
# ?Allowed = ("example.org" "example.com")
|
|
172
|
+
# ?Email = "ada@example.org"
|
|
173
|
+
# ?EmailDomain = "example.org"
|
|
174
|
+
# ?Id = "u1"
|
|
175
|
+
# ?Idx = 0
|
|
176
|
+
# ?J = """{
|
|
177
|
+
# "users": [
|
|
178
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
179
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
180
|
+
# ],
|
|
181
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
182
|
+
# }"""
|
|
183
|
+
# ?Name = "Ada Lovelace"
|
|
184
|
+
# ?UriStr = "urn:example:user:u1"
|
|
185
|
+
# ?User = <urn:example:user:u1>
|
|
186
|
+
# ?UserJson = """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}"""
|
|
187
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
188
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
189
|
+
# ----------------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
<urn:example:user:u1> ex:name "Ada Lovelace" .
|
|
192
|
+
|
|
193
|
+
# ----------------------------------------------------------------------
|
|
194
|
+
# Proof for derived triple:
|
|
195
|
+
# <urn:example:user:u1> ex:email "ada@example.org" .
|
|
196
|
+
# It holds because the following instance of the rule body is provable:
|
|
197
|
+
# ex:doc ex:json """{
|
|
198
|
+
# "users": [
|
|
199
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
200
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
201
|
+
# ],
|
|
202
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
203
|
+
# }""" .
|
|
204
|
+
# ("""{
|
|
205
|
+
# "users": [
|
|
206
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
207
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
208
|
+
# ],
|
|
209
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
210
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
211
|
+
# ("""{
|
|
212
|
+
# "users": [
|
|
213
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
214
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
215
|
+
# ],
|
|
216
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
217
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
218
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (0 """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""") .
|
|
219
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/id") string:jsonPointer "u1" .
|
|
220
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/email") string:jsonPointer "ada@example.org" .
|
|
221
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/profile~1name") string:jsonPointer "Ada Lovelace" .
|
|
222
|
+
# ("ada@example.org" "@([^@]+)$") string:scrape "example.org" .
|
|
223
|
+
# ("example.org" "example.com") list:member "example.org" .
|
|
224
|
+
# ("urn:example:user:%s" "u1") string:format "urn:example:user:u1" .
|
|
225
|
+
# <urn:example:user:u1> log:uri "urn:example:user:u1" .
|
|
226
|
+
# via the schematic forward rule:
|
|
227
|
+
# {
|
|
228
|
+
# ex:doc ex:json ?J .
|
|
229
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
230
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
231
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
232
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
233
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
234
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
235
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
236
|
+
# ?Allowed list:member ?EmailDomain .
|
|
237
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
238
|
+
# ?User log:uri ?UriStr .
|
|
239
|
+
# } => {
|
|
240
|
+
# ?User a ex:AllowedUser .
|
|
241
|
+
# ?User ex:name ?Name .
|
|
242
|
+
# ?User ex:email ?Email .
|
|
243
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
244
|
+
# ?User ex:userIndex ?Idx .
|
|
245
|
+
# } .
|
|
246
|
+
# with substitution (on rule variables):
|
|
247
|
+
# ?Allowed = ("example.org" "example.com")
|
|
248
|
+
# ?Email = "ada@example.org"
|
|
249
|
+
# ?EmailDomain = "example.org"
|
|
250
|
+
# ?Id = "u1"
|
|
251
|
+
# ?Idx = 0
|
|
252
|
+
# ?J = """{
|
|
253
|
+
# "users": [
|
|
254
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
255
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
256
|
+
# ],
|
|
257
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
258
|
+
# }"""
|
|
259
|
+
# ?Name = "Ada Lovelace"
|
|
260
|
+
# ?UriStr = "urn:example:user:u1"
|
|
261
|
+
# ?User = <urn:example:user:u1>
|
|
262
|
+
# ?UserJson = """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}"""
|
|
263
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
264
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
265
|
+
# ----------------------------------------------------------------------
|
|
266
|
+
|
|
267
|
+
<urn:example:user:u1> ex:email "ada@example.org" .
|
|
268
|
+
|
|
269
|
+
# ----------------------------------------------------------------------
|
|
270
|
+
# Proof for derived triple:
|
|
271
|
+
# <urn:example:user:u1> ex:emailDomain "example.org" .
|
|
272
|
+
# It holds because the following instance of the rule body is provable:
|
|
273
|
+
# ex:doc ex:json """{
|
|
274
|
+
# "users": [
|
|
275
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
276
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
277
|
+
# ],
|
|
278
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
279
|
+
# }""" .
|
|
280
|
+
# ("""{
|
|
281
|
+
# "users": [
|
|
282
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
283
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
284
|
+
# ],
|
|
285
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
286
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
287
|
+
# ("""{
|
|
288
|
+
# "users": [
|
|
289
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
290
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
291
|
+
# ],
|
|
292
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
293
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
294
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (0 """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""") .
|
|
295
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/id") string:jsonPointer "u1" .
|
|
296
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/email") string:jsonPointer "ada@example.org" .
|
|
297
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/profile~1name") string:jsonPointer "Ada Lovelace" .
|
|
298
|
+
# ("ada@example.org" "@([^@]+)$") string:scrape "example.org" .
|
|
299
|
+
# ("example.org" "example.com") list:member "example.org" .
|
|
300
|
+
# ("urn:example:user:%s" "u1") string:format "urn:example:user:u1" .
|
|
301
|
+
# <urn:example:user:u1> log:uri "urn:example:user:u1" .
|
|
302
|
+
# via the schematic forward rule:
|
|
303
|
+
# {
|
|
304
|
+
# ex:doc ex:json ?J .
|
|
305
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
306
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
307
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
308
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
309
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
310
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
311
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
312
|
+
# ?Allowed list:member ?EmailDomain .
|
|
313
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
314
|
+
# ?User log:uri ?UriStr .
|
|
315
|
+
# } => {
|
|
316
|
+
# ?User a ex:AllowedUser .
|
|
317
|
+
# ?User ex:name ?Name .
|
|
318
|
+
# ?User ex:email ?Email .
|
|
319
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
320
|
+
# ?User ex:userIndex ?Idx .
|
|
321
|
+
# } .
|
|
322
|
+
# with substitution (on rule variables):
|
|
323
|
+
# ?Allowed = ("example.org" "example.com")
|
|
324
|
+
# ?Email = "ada@example.org"
|
|
325
|
+
# ?EmailDomain = "example.org"
|
|
326
|
+
# ?Id = "u1"
|
|
327
|
+
# ?Idx = 0
|
|
328
|
+
# ?J = """{
|
|
329
|
+
# "users": [
|
|
330
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
331
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
332
|
+
# ],
|
|
333
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
334
|
+
# }"""
|
|
335
|
+
# ?Name = "Ada Lovelace"
|
|
336
|
+
# ?UriStr = "urn:example:user:u1"
|
|
337
|
+
# ?User = <urn:example:user:u1>
|
|
338
|
+
# ?UserJson = """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}"""
|
|
339
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
340
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
341
|
+
# ----------------------------------------------------------------------
|
|
342
|
+
|
|
343
|
+
<urn:example:user:u1> ex:emailDomain "example.org" .
|
|
344
|
+
|
|
345
|
+
# ----------------------------------------------------------------------
|
|
346
|
+
# Proof for derived triple:
|
|
347
|
+
# <urn:example:user:u1> ex:userIndex 0 .
|
|
348
|
+
# It holds because the following instance of the rule body is provable:
|
|
349
|
+
# ex:doc ex:json """{
|
|
350
|
+
# "users": [
|
|
351
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
352
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
353
|
+
# ],
|
|
354
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
355
|
+
# }""" .
|
|
356
|
+
# ("""{
|
|
357
|
+
# "users": [
|
|
358
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
359
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
360
|
+
# ],
|
|
361
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
362
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
363
|
+
# ("""{
|
|
364
|
+
# "users": [
|
|
365
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
366
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
367
|
+
# ],
|
|
368
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
369
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
370
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (0 """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""") .
|
|
371
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/id") string:jsonPointer "u1" .
|
|
372
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/email") string:jsonPointer "ada@example.org" .
|
|
373
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" "/profile~1name") string:jsonPointer "Ada Lovelace" .
|
|
374
|
+
# ("ada@example.org" "@([^@]+)$") string:scrape "example.org" .
|
|
375
|
+
# ("example.org" "example.com") list:member "example.org" .
|
|
376
|
+
# ("urn:example:user:%s" "u1") string:format "urn:example:user:u1" .
|
|
377
|
+
# <urn:example:user:u1> log:uri "urn:example:user:u1" .
|
|
378
|
+
# via the schematic forward rule:
|
|
379
|
+
# {
|
|
380
|
+
# ex:doc ex:json ?J .
|
|
381
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
382
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
383
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
384
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
385
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
386
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
387
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
388
|
+
# ?Allowed list:member ?EmailDomain .
|
|
389
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
390
|
+
# ?User log:uri ?UriStr .
|
|
391
|
+
# } => {
|
|
392
|
+
# ?User a ex:AllowedUser .
|
|
393
|
+
# ?User ex:name ?Name .
|
|
394
|
+
# ?User ex:email ?Email .
|
|
395
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
396
|
+
# ?User ex:userIndex ?Idx .
|
|
397
|
+
# } .
|
|
398
|
+
# with substitution (on rule variables):
|
|
399
|
+
# ?Allowed = ("example.org" "example.com")
|
|
400
|
+
# ?Email = "ada@example.org"
|
|
401
|
+
# ?EmailDomain = "example.org"
|
|
402
|
+
# ?Id = "u1"
|
|
403
|
+
# ?Idx = 0
|
|
404
|
+
# ?J = """{
|
|
405
|
+
# "users": [
|
|
406
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
407
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
408
|
+
# ],
|
|
409
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
410
|
+
# }"""
|
|
411
|
+
# ?Name = "Ada Lovelace"
|
|
412
|
+
# ?UriStr = "urn:example:user:u1"
|
|
413
|
+
# ?User = <urn:example:user:u1>
|
|
414
|
+
# ?UserJson = """{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}"""
|
|
415
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
416
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
417
|
+
# ----------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
<urn:example:user:u1> ex:userIndex 0 .
|
|
420
|
+
|
|
421
|
+
# ----------------------------------------------------------------------
|
|
422
|
+
# Proof for derived triple:
|
|
423
|
+
# <urn:example:user:u2> a ex:BlockedUser .
|
|
424
|
+
# It holds because the following instance of the rule body is provable:
|
|
425
|
+
# ex:doc ex:json """{
|
|
426
|
+
# "users": [
|
|
427
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
428
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
429
|
+
# ],
|
|
430
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
431
|
+
# }""" .
|
|
432
|
+
# ("""{
|
|
433
|
+
# "users": [
|
|
434
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
435
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
436
|
+
# ],
|
|
437
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
438
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
439
|
+
# ("""{
|
|
440
|
+
# "users": [
|
|
441
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
442
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
443
|
+
# ],
|
|
444
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
445
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
446
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (1 """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
447
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/id") string:jsonPointer "u2" .
|
|
448
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/email") string:jsonPointer "bob@evil.invalid" .
|
|
449
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/profile~1name") string:jsonPointer "Bob Mallory" .
|
|
450
|
+
# ("bob@evil.invalid" "@([^@]+)$") string:scrape "evil.invalid" .
|
|
451
|
+
# ("urn:example:user:%s" "u2") string:format "urn:example:user:u2" .
|
|
452
|
+
# <urn:example:user:u2> log:uri "urn:example:user:u2" .
|
|
453
|
+
# ("example.org" "example.com") list:notMember "evil.invalid" .
|
|
454
|
+
# via the schematic forward rule:
|
|
455
|
+
# {
|
|
456
|
+
# ex:doc ex:json ?J .
|
|
457
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
458
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
459
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
460
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
461
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
462
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
463
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
464
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
465
|
+
# ?User log:uri ?UriStr .
|
|
466
|
+
# ?Allowed list:notMember ?EmailDomain .
|
|
467
|
+
# } => {
|
|
468
|
+
# ?User a ex:BlockedUser .
|
|
469
|
+
# ?User ex:name ?Name .
|
|
470
|
+
# ?User ex:email ?Email .
|
|
471
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
472
|
+
# ?User ex:userIndex ?Idx .
|
|
473
|
+
# } .
|
|
474
|
+
# with substitution (on rule variables):
|
|
475
|
+
# ?Allowed = ("example.org" "example.com")
|
|
476
|
+
# ?Email = "bob@evil.invalid"
|
|
477
|
+
# ?EmailDomain = "evil.invalid"
|
|
478
|
+
# ?Id = "u2"
|
|
479
|
+
# ?Idx = 1
|
|
480
|
+
# ?J = """{
|
|
481
|
+
# "users": [
|
|
482
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
483
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
484
|
+
# ],
|
|
485
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
486
|
+
# }"""
|
|
487
|
+
# ?Name = "Bob Mallory"
|
|
488
|
+
# ?UriStr = "urn:example:user:u2"
|
|
489
|
+
# ?User = <urn:example:user:u2>
|
|
490
|
+
# ?UserJson = """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}"""
|
|
491
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
492
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
493
|
+
# ----------------------------------------------------------------------
|
|
494
|
+
|
|
495
|
+
<urn:example:user:u2> a ex:BlockedUser .
|
|
496
|
+
|
|
497
|
+
# ----------------------------------------------------------------------
|
|
498
|
+
# Proof for derived triple:
|
|
499
|
+
# <urn:example:user:u2> ex:name "Bob Mallory" .
|
|
500
|
+
# It holds because the following instance of the rule body is provable:
|
|
501
|
+
# ex:doc ex:json """{
|
|
502
|
+
# "users": [
|
|
503
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
504
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
505
|
+
# ],
|
|
506
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
507
|
+
# }""" .
|
|
508
|
+
# ("""{
|
|
509
|
+
# "users": [
|
|
510
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
511
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
512
|
+
# ],
|
|
513
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
514
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
515
|
+
# ("""{
|
|
516
|
+
# "users": [
|
|
517
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
518
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
519
|
+
# ],
|
|
520
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
521
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
522
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (1 """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
523
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/id") string:jsonPointer "u2" .
|
|
524
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/email") string:jsonPointer "bob@evil.invalid" .
|
|
525
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/profile~1name") string:jsonPointer "Bob Mallory" .
|
|
526
|
+
# ("bob@evil.invalid" "@([^@]+)$") string:scrape "evil.invalid" .
|
|
527
|
+
# ("urn:example:user:%s" "u2") string:format "urn:example:user:u2" .
|
|
528
|
+
# <urn:example:user:u2> log:uri "urn:example:user:u2" .
|
|
529
|
+
# ("example.org" "example.com") list:notMember "evil.invalid" .
|
|
530
|
+
# via the schematic forward rule:
|
|
531
|
+
# {
|
|
532
|
+
# ex:doc ex:json ?J .
|
|
533
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
534
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
535
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
536
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
537
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
538
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
539
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
540
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
541
|
+
# ?User log:uri ?UriStr .
|
|
542
|
+
# ?Allowed list:notMember ?EmailDomain .
|
|
543
|
+
# } => {
|
|
544
|
+
# ?User a ex:BlockedUser .
|
|
545
|
+
# ?User ex:name ?Name .
|
|
546
|
+
# ?User ex:email ?Email .
|
|
547
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
548
|
+
# ?User ex:userIndex ?Idx .
|
|
549
|
+
# } .
|
|
550
|
+
# with substitution (on rule variables):
|
|
551
|
+
# ?Allowed = ("example.org" "example.com")
|
|
552
|
+
# ?Email = "bob@evil.invalid"
|
|
553
|
+
# ?EmailDomain = "evil.invalid"
|
|
554
|
+
# ?Id = "u2"
|
|
555
|
+
# ?Idx = 1
|
|
556
|
+
# ?J = """{
|
|
557
|
+
# "users": [
|
|
558
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
559
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
560
|
+
# ],
|
|
561
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
562
|
+
# }"""
|
|
563
|
+
# ?Name = "Bob Mallory"
|
|
564
|
+
# ?UriStr = "urn:example:user:u2"
|
|
565
|
+
# ?User = <urn:example:user:u2>
|
|
566
|
+
# ?UserJson = """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}"""
|
|
567
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
568
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
569
|
+
# ----------------------------------------------------------------------
|
|
570
|
+
|
|
571
|
+
<urn:example:user:u2> ex:name "Bob Mallory" .
|
|
572
|
+
|
|
573
|
+
# ----------------------------------------------------------------------
|
|
574
|
+
# Proof for derived triple:
|
|
575
|
+
# <urn:example:user:u2> ex:email "bob@evil.invalid" .
|
|
576
|
+
# It holds because the following instance of the rule body is provable:
|
|
577
|
+
# ex:doc ex:json """{
|
|
578
|
+
# "users": [
|
|
579
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
580
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
581
|
+
# ],
|
|
582
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
583
|
+
# }""" .
|
|
584
|
+
# ("""{
|
|
585
|
+
# "users": [
|
|
586
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
587
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
588
|
+
# ],
|
|
589
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
590
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
591
|
+
# ("""{
|
|
592
|
+
# "users": [
|
|
593
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
594
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
595
|
+
# ],
|
|
596
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
597
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
598
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (1 """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
599
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/id") string:jsonPointer "u2" .
|
|
600
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/email") string:jsonPointer "bob@evil.invalid" .
|
|
601
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/profile~1name") string:jsonPointer "Bob Mallory" .
|
|
602
|
+
# ("bob@evil.invalid" "@([^@]+)$") string:scrape "evil.invalid" .
|
|
603
|
+
# ("urn:example:user:%s" "u2") string:format "urn:example:user:u2" .
|
|
604
|
+
# <urn:example:user:u2> log:uri "urn:example:user:u2" .
|
|
605
|
+
# ("example.org" "example.com") list:notMember "evil.invalid" .
|
|
606
|
+
# via the schematic forward rule:
|
|
607
|
+
# {
|
|
608
|
+
# ex:doc ex:json ?J .
|
|
609
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
610
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
611
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
612
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
613
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
614
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
615
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
616
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
617
|
+
# ?User log:uri ?UriStr .
|
|
618
|
+
# ?Allowed list:notMember ?EmailDomain .
|
|
619
|
+
# } => {
|
|
620
|
+
# ?User a ex:BlockedUser .
|
|
621
|
+
# ?User ex:name ?Name .
|
|
622
|
+
# ?User ex:email ?Email .
|
|
623
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
624
|
+
# ?User ex:userIndex ?Idx .
|
|
625
|
+
# } .
|
|
626
|
+
# with substitution (on rule variables):
|
|
627
|
+
# ?Allowed = ("example.org" "example.com")
|
|
628
|
+
# ?Email = "bob@evil.invalid"
|
|
629
|
+
# ?EmailDomain = "evil.invalid"
|
|
630
|
+
# ?Id = "u2"
|
|
631
|
+
# ?Idx = 1
|
|
632
|
+
# ?J = """{
|
|
633
|
+
# "users": [
|
|
634
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
635
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
636
|
+
# ],
|
|
637
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
638
|
+
# }"""
|
|
639
|
+
# ?Name = "Bob Mallory"
|
|
640
|
+
# ?UriStr = "urn:example:user:u2"
|
|
641
|
+
# ?User = <urn:example:user:u2>
|
|
642
|
+
# ?UserJson = """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}"""
|
|
643
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
644
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
645
|
+
# ----------------------------------------------------------------------
|
|
646
|
+
|
|
647
|
+
<urn:example:user:u2> ex:email "bob@evil.invalid" .
|
|
648
|
+
|
|
649
|
+
# ----------------------------------------------------------------------
|
|
650
|
+
# Proof for derived triple:
|
|
651
|
+
# <urn:example:user:u2> ex:emailDomain "evil.invalid" .
|
|
652
|
+
# It holds because the following instance of the rule body is provable:
|
|
653
|
+
# ex:doc ex:json """{
|
|
654
|
+
# "users": [
|
|
655
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
656
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
657
|
+
# ],
|
|
658
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
659
|
+
# }""" .
|
|
660
|
+
# ("""{
|
|
661
|
+
# "users": [
|
|
662
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
663
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
664
|
+
# ],
|
|
665
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
666
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
667
|
+
# ("""{
|
|
668
|
+
# "users": [
|
|
669
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
670
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
671
|
+
# ],
|
|
672
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
673
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
674
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (1 """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
675
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/id") string:jsonPointer "u2" .
|
|
676
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/email") string:jsonPointer "bob@evil.invalid" .
|
|
677
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/profile~1name") string:jsonPointer "Bob Mallory" .
|
|
678
|
+
# ("bob@evil.invalid" "@([^@]+)$") string:scrape "evil.invalid" .
|
|
679
|
+
# ("urn:example:user:%s" "u2") string:format "urn:example:user:u2" .
|
|
680
|
+
# <urn:example:user:u2> log:uri "urn:example:user:u2" .
|
|
681
|
+
# ("example.org" "example.com") list:notMember "evil.invalid" .
|
|
682
|
+
# via the schematic forward rule:
|
|
683
|
+
# {
|
|
684
|
+
# ex:doc ex:json ?J .
|
|
685
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
686
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
687
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
688
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
689
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
690
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
691
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
692
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
693
|
+
# ?User log:uri ?UriStr .
|
|
694
|
+
# ?Allowed list:notMember ?EmailDomain .
|
|
695
|
+
# } => {
|
|
696
|
+
# ?User a ex:BlockedUser .
|
|
697
|
+
# ?User ex:name ?Name .
|
|
698
|
+
# ?User ex:email ?Email .
|
|
699
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
700
|
+
# ?User ex:userIndex ?Idx .
|
|
701
|
+
# } .
|
|
702
|
+
# with substitution (on rule variables):
|
|
703
|
+
# ?Allowed = ("example.org" "example.com")
|
|
704
|
+
# ?Email = "bob@evil.invalid"
|
|
705
|
+
# ?EmailDomain = "evil.invalid"
|
|
706
|
+
# ?Id = "u2"
|
|
707
|
+
# ?Idx = 1
|
|
708
|
+
# ?J = """{
|
|
709
|
+
# "users": [
|
|
710
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
711
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
712
|
+
# ],
|
|
713
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
714
|
+
# }"""
|
|
715
|
+
# ?Name = "Bob Mallory"
|
|
716
|
+
# ?UriStr = "urn:example:user:u2"
|
|
717
|
+
# ?User = <urn:example:user:u2>
|
|
718
|
+
# ?UserJson = """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}"""
|
|
719
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
720
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
721
|
+
# ----------------------------------------------------------------------
|
|
722
|
+
|
|
723
|
+
<urn:example:user:u2> ex:emailDomain "evil.invalid" .
|
|
724
|
+
|
|
725
|
+
# ----------------------------------------------------------------------
|
|
726
|
+
# Proof for derived triple:
|
|
727
|
+
# <urn:example:user:u2> ex:userIndex 1 .
|
|
728
|
+
# It holds because the following instance of the rule body is provable:
|
|
729
|
+
# ex:doc ex:json """{
|
|
730
|
+
# "users": [
|
|
731
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
732
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
733
|
+
# ],
|
|
734
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
735
|
+
# }""" .
|
|
736
|
+
# ("""{
|
|
737
|
+
# "users": [
|
|
738
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
739
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
740
|
+
# ],
|
|
741
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
742
|
+
# }""" "/policy/allowedDomains") string:jsonPointer ("example.org" "example.com") .
|
|
743
|
+
# ("""{
|
|
744
|
+
# "users": [
|
|
745
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
746
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
747
|
+
# ],
|
|
748
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
749
|
+
# }""" "/users") string:jsonPointer ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
750
|
+
# ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") list:iterate (1 """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""") .
|
|
751
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/id") string:jsonPointer "u2" .
|
|
752
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/email") string:jsonPointer "bob@evil.invalid" .
|
|
753
|
+
# ("""{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""" "/profile~1name") string:jsonPointer "Bob Mallory" .
|
|
754
|
+
# ("bob@evil.invalid" "@([^@]+)$") string:scrape "evil.invalid" .
|
|
755
|
+
# ("urn:example:user:%s" "u2") string:format "urn:example:user:u2" .
|
|
756
|
+
# <urn:example:user:u2> log:uri "urn:example:user:u2" .
|
|
757
|
+
# ("example.org" "example.com") list:notMember "evil.invalid" .
|
|
758
|
+
# via the schematic forward rule:
|
|
759
|
+
# {
|
|
760
|
+
# ex:doc ex:json ?J .
|
|
761
|
+
# (?J "/policy/allowedDomains") string:jsonPointer ?Allowed .
|
|
762
|
+
# (?J "/users") string:jsonPointer ?Users .
|
|
763
|
+
# ?Users list:iterate (?Idx ?UserJson) .
|
|
764
|
+
# (?UserJson "/id") string:jsonPointer ?Id .
|
|
765
|
+
# (?UserJson "/email") string:jsonPointer ?Email .
|
|
766
|
+
# (?UserJson "/profile~1name") string:jsonPointer ?Name .
|
|
767
|
+
# (?Email "@([^@]+)$") string:scrape ?EmailDomain .
|
|
768
|
+
# ("urn:example:user:%s" ?Id) string:format ?UriStr .
|
|
769
|
+
# ?User log:uri ?UriStr .
|
|
770
|
+
# ?Allowed list:notMember ?EmailDomain .
|
|
771
|
+
# } => {
|
|
772
|
+
# ?User a ex:BlockedUser .
|
|
773
|
+
# ?User ex:name ?Name .
|
|
774
|
+
# ?User ex:email ?Email .
|
|
775
|
+
# ?User ex:emailDomain ?EmailDomain .
|
|
776
|
+
# ?User ex:userIndex ?Idx .
|
|
777
|
+
# } .
|
|
778
|
+
# with substitution (on rule variables):
|
|
779
|
+
# ?Allowed = ("example.org" "example.com")
|
|
780
|
+
# ?Email = "bob@evil.invalid"
|
|
781
|
+
# ?EmailDomain = "evil.invalid"
|
|
782
|
+
# ?Id = "u2"
|
|
783
|
+
# ?Idx = 1
|
|
784
|
+
# ?J = """{
|
|
785
|
+
# "users": [
|
|
786
|
+
# { "id": "u1", "email": "ada@example.org", "profile/name": "Ada Lovelace" },
|
|
787
|
+
# { "id": "u2", "email": "bob@evil.invalid", "profile/name": "Bob Mallory" }
|
|
788
|
+
# ],
|
|
789
|
+
# "policy": { "allowedDomains": ["example.org", "example.com"] }
|
|
790
|
+
# }"""
|
|
791
|
+
# ?Name = "Bob Mallory"
|
|
792
|
+
# ?UriStr = "urn:example:user:u2"
|
|
793
|
+
# ?User = <urn:example:user:u2>
|
|
794
|
+
# ?UserJson = """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}"""
|
|
795
|
+
# ?Users = ("""{"id":"u1","email":"ada@example.org","profile/name":"Ada Lovelace"}""" """{"id":"u2","email":"bob@evil.invalid","profile/name":"Bob Mallory"}""")
|
|
796
|
+
# Therefore the derived triple above is entailed by the rules and facts.
|
|
797
|
+
# ----------------------------------------------------------------------
|
|
798
|
+
|
|
799
|
+
<urn:example:user:u2> ex:userIndex 1 .
|
|
800
|
+
|
package/eyeling.js
CHANGED
|
@@ -39,6 +39,9 @@ const SKOLEM_NS = "https://eyereasoner.github.io/.well-known/genid/";
|
|
|
39
39
|
// of the subject term in log:skolem to a Skolem IRI.
|
|
40
40
|
const skolemCache = new Map();
|
|
41
41
|
|
|
42
|
+
// Cache for string:jsonPointer: jsonText -> { parsed: any|null, ptrCache: Map<string, Term|null> }
|
|
43
|
+
const jsonPointerCache = new Map();
|
|
44
|
+
|
|
42
45
|
// Controls whether human-readable proof comments are printed.
|
|
43
46
|
let proofCommentsEnabled = true;
|
|
44
47
|
|
|
@@ -676,8 +679,23 @@ class Parser {
|
|
|
676
679
|
this.expectDot();
|
|
677
680
|
backwardRules.push(this.makeRule(first, second, false));
|
|
678
681
|
} else {
|
|
679
|
-
|
|
680
|
-
|
|
682
|
+
let more;
|
|
683
|
+
|
|
684
|
+
if (this.peek().typ === "Dot") {
|
|
685
|
+
// Allow a bare blank-node property list statement, e.g. `[ a :Statement ].`
|
|
686
|
+
const lastTok = this.toks[this.pos - 1];
|
|
687
|
+
if (this.pendingTriples.length > 0 && lastTok && lastTok.typ === "RBracket") {
|
|
688
|
+
more = this.pendingTriples;
|
|
689
|
+
this.pendingTriples = [];
|
|
690
|
+
this.next(); // consume '.'
|
|
691
|
+
} else {
|
|
692
|
+
throw new Error(`Unexpected '.' after term; missing predicate/object list`);
|
|
693
|
+
}
|
|
694
|
+
} else {
|
|
695
|
+
more = this.parsePredicateObjectList(first);
|
|
696
|
+
this.expectDot();
|
|
697
|
+
}
|
|
698
|
+
|
|
681
699
|
// normalize explicit log:implies / log:impliedBy at top-level
|
|
682
700
|
for (const tr of more) {
|
|
683
701
|
if (isLogImplies(tr.p) && tr.s instanceof FormulaTerm && tr.o instanceof FormulaTerm) {
|
|
@@ -692,7 +710,6 @@ class Parser {
|
|
|
692
710
|
}
|
|
693
711
|
}
|
|
694
712
|
|
|
695
|
-
// console.log(JSON.stringify([this.prefixes, triples, forwardRules, backwardRules], null, 2));
|
|
696
713
|
return [this.prefixes, triples, forwardRules, backwardRules];
|
|
697
714
|
}
|
|
698
715
|
|
|
@@ -929,6 +946,17 @@ class Parser {
|
|
|
929
946
|
throw new Error(`Expected '.' or '}', got ${this.peek().toString()}`);
|
|
930
947
|
}
|
|
931
948
|
} else {
|
|
949
|
+
// Allow a bare blank-node property list statement inside a formula, e.g. `{ [ a :X ]. }`
|
|
950
|
+
if (this.peek().typ === "Dot" || this.peek().typ === "RBrace") {
|
|
951
|
+
const lastTok = this.toks[this.pos - 1];
|
|
952
|
+
if (this.pendingTriples.length > 0 && lastTok && lastTok.typ === "RBracket") {
|
|
953
|
+
triples.push(...this.pendingTriples);
|
|
954
|
+
this.pendingTriples = [];
|
|
955
|
+
if (this.peek().typ === "Dot") this.next();
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
932
960
|
triples.push(...this.parsePredicateObjectList(left));
|
|
933
961
|
if (this.peek().typ === "Dot") this.next();
|
|
934
962
|
else if (this.peek().typ === "RBrace") {
|
|
@@ -1794,6 +1822,195 @@ function makeStringLiteral(str) {
|
|
|
1794
1822
|
return new Literal(JSON.stringify(str));
|
|
1795
1823
|
}
|
|
1796
1824
|
|
|
1825
|
+
function termToJsStringDecoded(t) {
|
|
1826
|
+
// Like termToJsString, but for short literals it *also* interprets escapes
|
|
1827
|
+
// (\" \n \uXXXX …) by attempting JSON.parse on the quoted lexical form.
|
|
1828
|
+
if (!(t instanceof Literal)) return null;
|
|
1829
|
+
const [lex, _dt] = literalParts(t.value);
|
|
1830
|
+
|
|
1831
|
+
// Long strings: """ ... """ are taken verbatim.
|
|
1832
|
+
if (lex.length >= 6 && lex.startsWith('"""') && lex.endsWith('"""')) {
|
|
1833
|
+
return lex.slice(3, -3);
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
// Short strings: try to decode escapes (this makes "{\"a\":1}" usable too).
|
|
1837
|
+
if (lex.length >= 2 && lex[0] === '"' && lex[lex.length - 1] === '"') {
|
|
1838
|
+
try { return JSON.parse(lex); } catch (e) { /* fall through */ }
|
|
1839
|
+
return stripQuotes(lex);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
return stripQuotes(lex);
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
function _jsonPointerUnescape(seg) {
|
|
1846
|
+
// RFC6901: ~1 -> '/', ~0 -> '~'
|
|
1847
|
+
// Any other '~' escape is invalid.
|
|
1848
|
+
let out = "";
|
|
1849
|
+
for (let i = 0; i < seg.length; i++) {
|
|
1850
|
+
const c = seg[i];
|
|
1851
|
+
if (c !== "~") { out += c; continue; }
|
|
1852
|
+
if (i + 1 >= seg.length) return null;
|
|
1853
|
+
const n = seg[i + 1];
|
|
1854
|
+
if (n === "0") out += "~";
|
|
1855
|
+
else if (n === "1") out += "/";
|
|
1856
|
+
else return null;
|
|
1857
|
+
i++;
|
|
1858
|
+
}
|
|
1859
|
+
return out;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
function _jsonToTerm(v) {
|
|
1863
|
+
if (v === null) return makeStringLiteral("null");
|
|
1864
|
+
if (typeof v === "string") return makeStringLiteral(v);
|
|
1865
|
+
if (typeof v === "number") return new Literal(String(v));
|
|
1866
|
+
if (typeof v === "boolean") return new Literal(v ? "true" : "false");
|
|
1867
|
+
if (Array.isArray(v)) return new ListTerm(v.map(_jsonToTerm));
|
|
1868
|
+
if (typeof v === "object") return makeStringLiteral(JSON.stringify(v));
|
|
1869
|
+
return null;
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
function _jsonPointerLookup(jsonText, pointer) {
|
|
1873
|
+
// Support URI fragment form "#/a/b" (percent-decoded) as well.
|
|
1874
|
+
let ptr = pointer;
|
|
1875
|
+
if (ptr.startsWith("#")) {
|
|
1876
|
+
try { ptr = decodeURIComponent(ptr.slice(1)); } catch (e) { return null; }
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
// Cache per JSON document
|
|
1880
|
+
let entry = jsonPointerCache.get(jsonText);
|
|
1881
|
+
if (!entry) {
|
|
1882
|
+
let parsed = null;
|
|
1883
|
+
try { parsed = JSON.parse(jsonText); } catch (e) { parsed = null; }
|
|
1884
|
+
entry = { parsed, ptrCache: new Map() };
|
|
1885
|
+
jsonPointerCache.set(jsonText, entry);
|
|
1886
|
+
}
|
|
1887
|
+
if (entry.parsed === null) return null;
|
|
1888
|
+
|
|
1889
|
+
// Cache per pointer within this doc
|
|
1890
|
+
if (entry.ptrCache.has(ptr)) return entry.ptrCache.get(ptr);
|
|
1891
|
+
|
|
1892
|
+
let cur = entry.parsed;
|
|
1893
|
+
|
|
1894
|
+
if (ptr === "") {
|
|
1895
|
+
const t = _jsonToTerm(cur);
|
|
1896
|
+
entry.ptrCache.set(ptr, t);
|
|
1897
|
+
return t;
|
|
1898
|
+
}
|
|
1899
|
+
if (!ptr.startsWith("/")) { entry.ptrCache.set(ptr, null); return null; }
|
|
1900
|
+
|
|
1901
|
+
const parts = ptr.split("/").slice(1);
|
|
1902
|
+
for (const raw of parts) {
|
|
1903
|
+
const seg = _jsonPointerUnescape(raw);
|
|
1904
|
+
if (seg === null) { entry.ptrCache.set(ptr, null); return null; }
|
|
1905
|
+
|
|
1906
|
+
if (Array.isArray(cur)) {
|
|
1907
|
+
// JSON Pointer uses array indices as decimal strings
|
|
1908
|
+
if (!/^(0|[1-9]\d*)$/.test(seg)) { entry.ptrCache.set(ptr, null); return null; }
|
|
1909
|
+
const idx = Number(seg);
|
|
1910
|
+
if (!Number.isFinite(idx) || idx < 0 || idx >= cur.length) { entry.ptrCache.set(ptr, null); return null; }
|
|
1911
|
+
cur = cur[idx];
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
if (cur !== null && typeof cur === "object") {
|
|
1916
|
+
if (!Object.prototype.hasOwnProperty.call(cur, seg)) { entry.ptrCache.set(ptr, null); return null; }
|
|
1917
|
+
cur = cur[seg];
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
entry.ptrCache.set(ptr, null);
|
|
1922
|
+
return null;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
const out = _jsonToTerm(cur);
|
|
1926
|
+
entry.ptrCache.set(ptr, out);
|
|
1927
|
+
return out;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
function jsonPointerUnescape(seg) {
|
|
1931
|
+
// RFC6901: ~1 -> '/', ~0 -> '~'
|
|
1932
|
+
let out = "";
|
|
1933
|
+
for (let i = 0; i < seg.length; i++) {
|
|
1934
|
+
const c = seg[i];
|
|
1935
|
+
if (c !== "~") { out += c; continue; }
|
|
1936
|
+
if (i + 1 >= seg.length) return null;
|
|
1937
|
+
const n = seg[i + 1];
|
|
1938
|
+
if (n === "0") out += "~";
|
|
1939
|
+
else if (n === "1") out += "/";
|
|
1940
|
+
else return null;
|
|
1941
|
+
i++;
|
|
1942
|
+
}
|
|
1943
|
+
return out;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
function jsonToTerm(v) {
|
|
1947
|
+
if (v === null) return makeStringLiteral("null");
|
|
1948
|
+
if (typeof v === "string") return makeStringLiteral(v);
|
|
1949
|
+
if (typeof v === "number") return new Literal(String(v));
|
|
1950
|
+
if (typeof v === "boolean") return new Literal(v ? "true" : "false");
|
|
1951
|
+
if (Array.isArray(v)) return new ListTerm(v.map(jsonToTerm));
|
|
1952
|
+
|
|
1953
|
+
if (v && typeof v === "object") {
|
|
1954
|
+
// IMPORTANT: long literal so it can be parsed again via termToJsString()
|
|
1955
|
+
// without needing escape decoding.
|
|
1956
|
+
const raw = JSON.stringify(v);
|
|
1957
|
+
return new Literal('"""' + raw + '"""');
|
|
1958
|
+
}
|
|
1959
|
+
return null;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
function jsonPointerLookup(jsonText, pointer) {
|
|
1963
|
+
let ptr = pointer;
|
|
1964
|
+
|
|
1965
|
+
// Support URI fragment form "#/a/b"
|
|
1966
|
+
if (ptr.startsWith("#")) {
|
|
1967
|
+
try { ptr = decodeURIComponent(ptr.slice(1)); } catch { return null; }
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
let entry = jsonPointerCache.get(jsonText);
|
|
1971
|
+
if (!entry) {
|
|
1972
|
+
let parsed = null;
|
|
1973
|
+
try { parsed = JSON.parse(jsonText); } catch { parsed = null; }
|
|
1974
|
+
entry = { parsed, ptrCache: new Map() };
|
|
1975
|
+
jsonPointerCache.set(jsonText, entry);
|
|
1976
|
+
}
|
|
1977
|
+
if (entry.parsed === null) return null;
|
|
1978
|
+
|
|
1979
|
+
if (entry.ptrCache.has(ptr)) return entry.ptrCache.get(ptr);
|
|
1980
|
+
|
|
1981
|
+
let cur = entry.parsed;
|
|
1982
|
+
|
|
1983
|
+
if (ptr === "") {
|
|
1984
|
+
const t = jsonToTerm(cur);
|
|
1985
|
+
entry.ptrCache.set(ptr, t);
|
|
1986
|
+
return t;
|
|
1987
|
+
}
|
|
1988
|
+
if (!ptr.startsWith("/")) { entry.ptrCache.set(ptr, null); return null; }
|
|
1989
|
+
|
|
1990
|
+
const parts = ptr.split("/").slice(1);
|
|
1991
|
+
for (const raw of parts) {
|
|
1992
|
+
const seg = jsonPointerUnescape(raw);
|
|
1993
|
+
if (seg === null) { entry.ptrCache.set(ptr, null); return null; }
|
|
1994
|
+
|
|
1995
|
+
if (Array.isArray(cur)) {
|
|
1996
|
+
if (!/^(0|[1-9]\d*)$/.test(seg)) { entry.ptrCache.set(ptr, null); return null; }
|
|
1997
|
+
const idx = Number(seg);
|
|
1998
|
+
if (idx < 0 || idx >= cur.length) { entry.ptrCache.set(ptr, null); return null; }
|
|
1999
|
+
cur = cur[idx];
|
|
2000
|
+
} else if (cur !== null && typeof cur === "object") {
|
|
2001
|
+
if (!Object.prototype.hasOwnProperty.call(cur, seg)) { entry.ptrCache.set(ptr, null); return null; }
|
|
2002
|
+
cur = cur[seg];
|
|
2003
|
+
} else {
|
|
2004
|
+
entry.ptrCache.set(ptr, null);
|
|
2005
|
+
return null;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
const out = jsonToTerm(cur);
|
|
2010
|
+
entry.ptrCache.set(ptr, out);
|
|
2011
|
+
return out;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
1797
2014
|
// Tiny subset of sprintf: supports only %s and %%.
|
|
1798
2015
|
// Good enough for most N3 string:format use cases that just splice strings.
|
|
1799
2016
|
function simpleStringFormat(fmt, args) {
|
|
@@ -3345,6 +3562,21 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3345
3562
|
return s2 !== null ? [s2] : [];
|
|
3346
3563
|
}
|
|
3347
3564
|
|
|
3565
|
+
// string:jsonPointer
|
|
3566
|
+
// Schema: ( $jsonText $pointer ) string:jsonPointer $value
|
|
3567
|
+
if (g.p instanceof Iri && g.p.value === STRING_NS + "jsonPointer") {
|
|
3568
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3569
|
+
const jsonText = termToJsString(g.s.elems[0]);
|
|
3570
|
+
const ptr = termToJsString(g.s.elems[1]);
|
|
3571
|
+
if (jsonText === null || ptr === null) return [];
|
|
3572
|
+
|
|
3573
|
+
const valTerm = jsonPointerLookup(jsonText, ptr);
|
|
3574
|
+
if (valTerm === null) return [];
|
|
3575
|
+
|
|
3576
|
+
const s2 = unifyTerm(g.o, valTerm, subst);
|
|
3577
|
+
return s2 !== null ? [s2] : [];
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3348
3580
|
// string:greaterThan
|
|
3349
3581
|
if (g.p instanceof Iri && g.p.value === STRING_NS + "greaterThan") {
|
|
3350
3582
|
const sStr = termToJsString(g.s);
|
|
@@ -4187,6 +4419,7 @@ function main() {
|
|
|
4187
4419
|
const toks = lex(text);
|
|
4188
4420
|
const parser = new Parser(toks);
|
|
4189
4421
|
const [prefixes, triples, frules, brules] = parser.parseDocument();
|
|
4422
|
+
// console.log(JSON.stringify([prefixes, triples, frules, brules], null, 2));
|
|
4190
4423
|
|
|
4191
4424
|
const facts = triples.filter(tr => isGroundTriple(tr));
|
|
4192
4425
|
const derived = forwardChain(facts, frules, brules);
|