shex 0.6.2 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +108 -97
- data/VERSION +1 -1
- data/etc/doap.ttl +1 -1
- data/lib/shex/algebra/and.rb +3 -12
- data/lib/shex/algebra/each_of.rb +2 -12
- data/lib/shex/algebra/import.rb +6 -0
- data/lib/shex/algebra/not.rb +3 -10
- data/lib/shex/algebra/one_of.rb +2 -12
- data/lib/shex/algebra/operator.rb +28 -15
- data/lib/shex/algebra/or.rb +3 -12
- data/lib/shex/algebra/schema.rb +2 -9
- data/lib/shex/algebra/shape.rb +4 -2
- data/lib/shex/algebra/shape_expression.rb +29 -0
- data/lib/shex/algebra/start.rb +2 -10
- data/lib/shex/algebra/stem.rb +5 -1
- data/lib/shex/algebra/stem_range.rb +3 -1
- data/lib/shex/algebra/triple_constraint.rb +1 -1
- data/lib/shex/algebra/triple_expression.rb +18 -0
- data/lib/shex/algebra.rb +0 -1
- data/lib/shex/meta.rb +325 -9859
- data/lib/shex/parser.rb +559 -474
- data/lib/shex/terminals.rb +5 -25
- data/lib/shex.rb +4 -1
- metadata +57 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f23772b66ae1d2d65f82803ebc661051084b62bab305781945c3d36a1c9ff1c1
|
4
|
+
data.tar.gz: c5cba5902f5c6b2dd0bdf446ec9625dc9baa94682f5375fb3c3b89d1bd76cc91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9ad30899338103d06b8b2d89070990c81215e3bfe0ab0d649bcbb3290e5981fe7aadf15e6456358786fc4f5f68c8a28aafd44d6e440f42130f1778622ebd0dc
|
7
|
+
data.tar.gz: 7107749beac51786c8778a1f2ea8a733caf49d8b01d323d0437eecc5d17e873bc1ad083fbc146133cde250dd8705a25105c5f7522afc0eb2773f50ef996b86f5
|
data/README.md
CHANGED
@@ -18,34 +18,38 @@ This is a pure-Ruby library for working with the [Shape Expressions Language][Sh
|
|
18
18
|
|
19
19
|
The ShEx gem implements a [ShEx][ShExSpec] Shape Expression engine version 2.0.
|
20
20
|
|
21
|
-
* `ShEx::Parser` parses ShExC and ShExJ formatted documents generating executable operators which can be serialized as [S-Expressions]
|
21
|
+
* `ShEx::Parser` parses ShExC and ShExJ formatted documents generating executable operators which can be serialized as [S-Expressions][].
|
22
22
|
* `ShEx::Algebra` executes operators against Any `RDF::Graph`, including compliant [RDF.rb][].
|
23
23
|
* [Implementation Report](file.earl.html)
|
24
24
|
|
25
25
|
## Examples
|
26
26
|
### Validating a node using ShExC
|
27
27
|
|
28
|
-
require 'rubygems'
|
29
28
|
require 'rdf/turtle'
|
30
29
|
require 'shex'
|
31
30
|
|
32
|
-
shexc
|
31
|
+
shexc = %(
|
33
32
|
PREFIX doap: <http://usefulinc.com/ns/doap#>
|
34
33
|
PREFIX dc: <http://purl.org/dc/terms/>
|
35
|
-
<
|
36
|
-
|
37
|
-
|
38
|
-
doap:
|
39
|
-
doap:
|
40
|
-
|
34
|
+
PREFIX ex: <http://example.com/>
|
35
|
+
|
36
|
+
ex:TestShape EXTRA a {
|
37
|
+
a [doap:Project];
|
38
|
+
( doap:name Literal;
|
39
|
+
doap:description Literal
|
40
|
+
| dc:title Literal;
|
41
|
+
dc:description Literal)+;
|
42
|
+
doap:category IRI*;
|
43
|
+
doap:developer IRI+;
|
44
|
+
doap:implements [<http://shex.io/shex-semantics/>]
|
41
45
|
}
|
42
46
|
)
|
43
47
|
graph = RDF::Graph.load("etc/doap.ttl")
|
44
48
|
schema = ShEx.parse(shexc)
|
45
49
|
map = {
|
46
|
-
"https://rubygems.org/gems/shex" => "TestShape"
|
50
|
+
RDF::URI("https://rubygems.org/gems/shex") => RDF::URI("http://example.com/TestShape")
|
47
51
|
}
|
48
|
-
schema.satisfies?(
|
52
|
+
schema.satisfies?(graph, map)
|
49
53
|
# => true
|
50
54
|
### Validating a node using ShExJ
|
51
55
|
|
@@ -53,92 +57,99 @@ The ShEx gem implements a [ShEx][ShExSpec] Shape Expression engine version 2.0.
|
|
53
57
|
require 'rdf/turtle'
|
54
58
|
require 'shex'
|
55
59
|
|
56
|
-
shexj
|
60
|
+
shexj = %({
|
61
|
+
"@context": "http://www.w3.org/ns/shex.jsonld",
|
57
62
|
"type": "Schema",
|
58
|
-
"
|
59
|
-
"
|
60
|
-
"
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
"
|
65
|
-
|
66
|
-
|
67
|
-
"
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
"type": "EachOf",
|
82
|
-
"expressions": [
|
83
|
-
{
|
84
|
-
"type": "TripleConstraint",
|
85
|
-
"predicate": "http://usefulinc.com/ns/doap#name",
|
86
|
-
"valueExpr": {"type": "NodeConstraint", "nodeKind": "literal"}
|
87
|
-
},
|
88
|
-
{
|
89
|
-
"type": "TripleConstraint",
|
90
|
-
"predicate": "http://usefulinc.com/ns/doap#description",
|
91
|
-
"valueExpr": {"type": "NodeConstraint", "nodeKind": "literal"}
|
92
|
-
}
|
93
|
-
]
|
94
|
-
},
|
95
|
-
{
|
96
|
-
"type": "EachOf",
|
97
|
-
"expressions": [
|
98
|
-
{
|
99
|
-
"type": "TripleConstraint",
|
100
|
-
"predicate": "http://purl.org/dc/terms/title",
|
101
|
-
"valueExpr": {"type": "NodeConstraint", "nodeKind": "literal"}
|
102
|
-
},
|
103
|
-
{
|
104
|
-
"type": "TripleConstraint",
|
105
|
-
"predicate": "http://purl.org/dc/terms/description",
|
106
|
-
"valueExpr": {"type": "NodeConstraint", "nodeKind": "literal"}
|
107
|
-
}
|
108
|
-
]
|
63
|
+
"shapes": [{
|
64
|
+
"id": "http://example.com/TestShape",
|
65
|
+
"type": "Shape",
|
66
|
+
"extra": ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"],
|
67
|
+
"expression": {
|
68
|
+
"type": "EachOf",
|
69
|
+
"expressions": [{
|
70
|
+
"type": "TripleConstraint",
|
71
|
+
"predicate": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
|
72
|
+
"valueExpr": {
|
73
|
+
"type": "NodeConstraint",
|
74
|
+
"values": ["http://usefulinc.com/ns/doap#Project"]
|
75
|
+
}
|
76
|
+
}, {
|
77
|
+
"type": "OneOf",
|
78
|
+
"expressions": [{
|
79
|
+
"type": "EachOf",
|
80
|
+
"expressions": [{
|
81
|
+
"type": "TripleConstraint",
|
82
|
+
"predicate": "http://usefulinc.com/ns/doap#name",
|
83
|
+
"valueExpr": {
|
84
|
+
"type": "NodeConstraint",
|
85
|
+
"nodeKind": "literal"
|
109
86
|
}
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
87
|
+
}, {
|
88
|
+
"type": "TripleConstraint",
|
89
|
+
"predicate": "http://usefulinc.com/ns/doap#description",
|
90
|
+
"valueExpr": {
|
91
|
+
"type": "NodeConstraint",
|
92
|
+
"nodeKind": "literal"
|
93
|
+
}
|
94
|
+
}]
|
95
|
+
}, {
|
96
|
+
"type": "EachOf",
|
97
|
+
"expressions": [{
|
98
|
+
"type": "TripleConstraint",
|
99
|
+
"predicate": "http://purl.org/dc/terms/title",
|
100
|
+
"valueExpr": {
|
101
|
+
"type": "NodeConstraint",
|
102
|
+
"nodeKind": "literal"
|
103
|
+
}
|
104
|
+
}, {
|
105
|
+
"type": "TripleConstraint",
|
106
|
+
"predicate": "http://purl.org/dc/terms/description",
|
107
|
+
"valueExpr": {
|
108
|
+
"type": "NodeConstraint",
|
109
|
+
"nodeKind": "literal"
|
110
|
+
}
|
111
|
+
}]
|
112
|
+
}],
|
113
|
+
"min": 1,
|
114
|
+
"max": -1
|
115
|
+
}, {
|
116
|
+
"type": "TripleConstraint",
|
117
|
+
"predicate": "http://usefulinc.com/ns/doap#category",
|
118
|
+
"valueExpr": {
|
119
|
+
"type": "NodeConstraint",
|
120
|
+
"nodeKind": "iri"
|
118
121
|
},
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
122
|
+
"min": 0,
|
123
|
+
"max": -1
|
124
|
+
}, {
|
125
|
+
"type": "TripleConstraint",
|
126
|
+
"predicate": "http://usefulinc.com/ns/doap#developer",
|
127
|
+
"valueExpr": {
|
128
|
+
"type": "NodeConstraint",
|
129
|
+
"nodeKind": "iri"
|
124
130
|
},
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
131
|
+
"min": 1,
|
132
|
+
"max": -1
|
133
|
+
}, {
|
134
|
+
"type": "TripleConstraint",
|
135
|
+
"predicate": "http://usefulinc.com/ns/doap#implements",
|
136
|
+
"valueExpr": {
|
137
|
+
"type": "NodeConstraint",
|
138
|
+
"values": [
|
139
|
+
"http://shex.io/shex-semantics/"
|
140
|
+
]
|
132
141
|
}
|
133
|
-
|
134
|
-
|
142
|
+
}
|
143
|
+
]
|
135
144
|
}
|
136
145
|
}
|
137
|
-
})
|
146
|
+
]})
|
138
147
|
graph = RDF::Graph.load("etc/doap.ttl")
|
139
148
|
schema = ShEx.parse(shexj, format: :shexj)
|
140
|
-
map = {
|
141
|
-
|
149
|
+
map = {
|
150
|
+
RDF::URI("https://rubygems.org/gems/shex") => RDF::URI("http://example.com/TestShape")
|
151
|
+
}
|
152
|
+
schema.satisfies?(graph, map)
|
142
153
|
# => true
|
143
154
|
|
144
155
|
## Extensions
|
@@ -179,20 +190,18 @@ Example usage:
|
|
179
190
|
|
180
191
|
## Documentation
|
181
192
|
|
182
|
-
<https://
|
193
|
+
<https://ruby-rdf.github.io/shex>
|
183
194
|
|
184
195
|
|
185
196
|
## Implementation Notes
|
186
|
-
The ShExC parser uses the [EBNF][] gem to generate
|
187
|
-
|
188
|
-
The parser takes branch and follow tables generated from the [ShEx Grammar](file.shex.html) described in the [specification][ShExSpec]. Branch and Follow tables are specified in the generated {ShEx::Meta}.
|
197
|
+
The ShExC parser uses the [EBNF][] gem to generate a [PEG][] parser.
|
189
198
|
|
190
|
-
The
|
199
|
+
The parser uses the executable [S-Expressions][] generated from the EBNF ShExC grammar to create a set of executable {ShEx::Algebra} Operators which are directly executed to perform shape validation.
|
191
200
|
|
192
201
|
## Dependencies
|
193
202
|
|
194
|
-
* [Ruby](https://ruby-lang.org/) (>= 2.
|
195
|
-
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.
|
203
|
+
* [Ruby](https://ruby-lang.org/) (>= 2.6)
|
204
|
+
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2)
|
196
205
|
* [SPARQL gem](https://rubygems.org/gems/sparql) (~> 3.1)
|
197
206
|
|
198
207
|
## Installation
|
@@ -215,7 +224,7 @@ follows:
|
|
215
224
|
|
216
225
|
## Resources
|
217
226
|
|
218
|
-
* <https://
|
227
|
+
* <https://ruby-rdf.github.io/shex>
|
219
228
|
* <https://github.com/ruby-rdf/shex>
|
220
229
|
* <https://rubygems.org/gems/shex>
|
221
230
|
|
@@ -255,8 +264,10 @@ see <https://unlicense.org/> or the accompanying {file:LICENSE} file.
|
|
255
264
|
|
256
265
|
[ShExSpec]: http://shex.io/shex-semantics-20170713/
|
257
266
|
[RDF]: https://www.w3.org/RDF/
|
258
|
-
[RDF.rb]: https://
|
267
|
+
[RDF.rb]: https://ruby-rdf.github.io/rdf
|
259
268
|
[EBNF]: https://rubygems.org/gems/ebnf
|
260
269
|
[YARD]: https://yardoc.org/
|
261
270
|
[YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
|
262
271
|
[PDD]: https://unlicense.org/#unlicensing-contributions
|
272
|
+
[PEG]: https://en.wikipedia.org/wiki/Parsing_expression_grammar "Parsing Expression Grammar"
|
273
|
+
[S-Expression]: https://en.wikipedia.org/wiki/S-expression
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.1
|
data/etc/doap.ttl
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
|
11
11
|
<https://rubygems.org/gems/shex> a doap:Project, earl:TestSubject, earl:Software ;
|
12
12
|
doap:name "ShEx" ;
|
13
|
-
doap:homepage <https://ruby-rdf.github.
|
13
|
+
doap:homepage <https://ruby-rdf.github.io/shex> ;
|
14
14
|
doap:license <https://unlicense.org/1.0/> ;
|
15
15
|
doap:shortdesc "ShEx is a Shape Expression engine for Ruby RDF.rb."@en ;
|
16
16
|
doap:description "ShEx is an Shape Expression engine for the Ruby RDF.rb library suite."@en ;
|
data/lib/shex/algebra/and.rb
CHANGED
@@ -61,22 +61,13 @@ module ShEx::Algebra
|
|
61
61
|
end
|
62
62
|
|
63
63
|
##
|
64
|
-
# expressions must be ShapeExpressions
|
64
|
+
# expressions must be ShapeExpressions or references to ShapeExpressions
|
65
65
|
#
|
66
66
|
# @return [Operator] `self`
|
67
67
|
# @raise [ShEx::StructureError] if the value is invalid
|
68
68
|
def validate!
|
69
|
-
|
70
|
-
|
71
|
-
when ShapeExpression
|
72
|
-
when RDF::Resource
|
73
|
-
ref = schema.find(op)
|
74
|
-
ref.is_a?(ShapeExpression) ||
|
75
|
-
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
76
|
-
else
|
77
|
-
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
78
|
-
end
|
79
|
-
end
|
69
|
+
validate_expressions!
|
70
|
+
validate_self_references!
|
80
71
|
super
|
81
72
|
end
|
82
73
|
|
data/lib/shex/algebra/each_of.rb
CHANGED
@@ -73,22 +73,12 @@ module ShEx::Algebra
|
|
73
73
|
end
|
74
74
|
|
75
75
|
##
|
76
|
-
# expressions must be TripleExpressions
|
76
|
+
# expressions must be TripleExpressions or references to TripleExpressions
|
77
77
|
#
|
78
78
|
# @return [Operator] `self`
|
79
79
|
# @raise [ShEx::StructureError] if the value is invalid
|
80
80
|
def validate!
|
81
|
-
|
82
|
-
case op
|
83
|
-
when TripleExpression
|
84
|
-
when RDF::Resource
|
85
|
-
ref = schema.find(op)
|
86
|
-
ref.is_a?(TripleExpression) ||
|
87
|
-
structure_error("#{json_type} must reference a TripleExpression: #{ref}")
|
88
|
-
else
|
89
|
-
structure_error("#{json_type} must reference a TripleExpression: #{ref}")
|
90
|
-
end
|
91
|
-
end
|
81
|
+
validate_expressions!
|
92
82
|
super
|
93
83
|
end
|
94
84
|
end
|
data/lib/shex/algebra/not.rb
CHANGED
@@ -45,20 +45,13 @@ module ShEx::Algebra
|
|
45
45
|
end
|
46
46
|
|
47
47
|
##
|
48
|
-
#
|
48
|
+
# expressions must be ShapeExpressions or references to ShapeExpressions and must not reference itself recursively.
|
49
49
|
#
|
50
50
|
# @return [Operator] `self`
|
51
51
|
# @raise [ShEx::StructureError] if the value is invalid
|
52
52
|
def validate!
|
53
|
-
|
54
|
-
|
55
|
-
when RDF::Resource
|
56
|
-
ref = schema.find(expression)
|
57
|
-
ref.is_a?(ShapeExpression) ||
|
58
|
-
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
59
|
-
else
|
60
|
-
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
61
|
-
end
|
53
|
+
validate_expressions!
|
54
|
+
validate_self_references!
|
62
55
|
super
|
63
56
|
end
|
64
57
|
|
data/lib/shex/algebra/one_of.rb
CHANGED
@@ -65,22 +65,12 @@ module ShEx::Algebra
|
|
65
65
|
end
|
66
66
|
|
67
67
|
##
|
68
|
-
# expressions must be TripleExpressions
|
68
|
+
# expressions must be TripleExpressions or references to TripleExpressions
|
69
69
|
#
|
70
70
|
# @return [Operator] `self`
|
71
71
|
# @raise [ShEx::StructureError] if the value is invalid
|
72
72
|
def validate!
|
73
|
-
|
74
|
-
case op
|
75
|
-
when TripleExpression
|
76
|
-
when RDF::Resource
|
77
|
-
ref = schema.find(op)
|
78
|
-
ref.is_a?(TripleExpression) ||
|
79
|
-
structure_error("#{json_type} must reference a TripleExpression: #{ref}")
|
80
|
-
else
|
81
|
-
structure_error("#{json_type} must reference a TripleExpression: #{ref}")
|
82
|
-
end
|
83
|
-
end
|
73
|
+
validate_expressions!
|
84
74
|
super
|
85
75
|
end
|
86
76
|
end
|
@@ -35,8 +35,8 @@ module ShEx::Algebra
|
|
35
35
|
# @option options [RDF::Resource] :id
|
36
36
|
# Identifier of the operator
|
37
37
|
# @raise [TypeError] if any operand is invalid
|
38
|
-
def initialize(*operands)
|
39
|
-
@options =
|
38
|
+
def initialize(*operands, **options)
|
39
|
+
@options = options.dup
|
40
40
|
@operands = operands.map! do |operand|
|
41
41
|
case operand
|
42
42
|
when Array
|
@@ -221,12 +221,19 @@ module ShEx::Algebra
|
|
221
221
|
end
|
222
222
|
|
223
223
|
##
|
224
|
-
# The
|
225
|
-
# @return [
|
224
|
+
# The first expression from {#expressions}.
|
225
|
+
# @return [RDF::Resource, Operand]
|
226
226
|
def expression
|
227
227
|
expressions.first
|
228
228
|
end
|
229
229
|
|
230
|
+
##
|
231
|
+
# References are all operands which are RDF::Resource
|
232
|
+
# @return [RDF::Resource, Operand]
|
233
|
+
def references
|
234
|
+
@references = operands.select {|op| op.is_a?(RDF::Resource)}
|
235
|
+
end
|
236
|
+
|
230
237
|
##
|
231
238
|
# Returns the binary S-Expression (SXP) representation of this operator.
|
232
239
|
#
|
@@ -242,15 +249,10 @@ module ShEx::Algebra
|
|
242
249
|
# Returns an S-Expression (SXP) representation of this operator
|
243
250
|
#
|
244
251
|
# @return [String]
|
245
|
-
def to_sxp
|
246
|
-
begin
|
247
|
-
require 'sxp' # @see https://rubygems.org/gems/sxp
|
248
|
-
rescue LoadError
|
249
|
-
abort "SPARQL::Algebra::Operator#to_sxp requires the SXP gem (hint: `gem install sxp')."
|
250
|
-
end
|
252
|
+
def to_sxp(**options)
|
251
253
|
require 'sparql/algebra/sxp_extensions'
|
252
254
|
|
253
|
-
to_sxp_bin.to_sxp
|
255
|
+
to_sxp_bin.to_sxp(**options)
|
254
256
|
end
|
255
257
|
|
256
258
|
##
|
@@ -432,6 +434,8 @@ module ShEx::Algebra
|
|
432
434
|
case self
|
433
435
|
when And, Or
|
434
436
|
(obj['shapeExprs'] ||= []) << op.to_h
|
437
|
+
when Not
|
438
|
+
obj['shapeExpr'] = op.to_h
|
435
439
|
else
|
436
440
|
obj['valueExpr'] = op.to_h
|
437
441
|
end
|
@@ -593,22 +597,23 @@ module ShEx::Algebra
|
|
593
597
|
|
594
598
|
##
|
595
599
|
# Enumerate via depth-first recursive descent over operands, yielding each operator
|
600
|
+
# @param [Boolean] include_self
|
596
601
|
# @yield operator
|
597
602
|
# @yieldparam [Object] operator
|
598
603
|
# @return [Enumerator]
|
599
|
-
def each_descendant(&block)
|
604
|
+
def each_descendant(include_self = false, &block)
|
600
605
|
if block_given?
|
601
606
|
|
602
|
-
block.call(self)
|
607
|
+
block.call(self) if include_self
|
603
608
|
|
604
609
|
operands.each do |operand|
|
605
610
|
case operand
|
606
611
|
when Array
|
607
612
|
operand.each do |op|
|
608
|
-
op.each_descendant(&block) if op.respond_to?(:each_descendant)
|
613
|
+
op.each_descendant(true, &block) if op.respond_to?(:each_descendant)
|
609
614
|
end
|
610
615
|
else
|
611
|
-
operand.each_descendant(&block) if operand.respond_to?(:each_descendant)
|
616
|
+
operand.each_descendant(true, &block) if operand.respond_to?(:each_descendant)
|
612
617
|
end
|
613
618
|
end
|
614
619
|
end
|
@@ -631,6 +636,14 @@ module ShEx::Algebra
|
|
631
636
|
@options[:parent]= operator
|
632
637
|
end
|
633
638
|
|
639
|
+
##
|
640
|
+
# Find a ShapeExpression or TripleExpression by identifier
|
641
|
+
# @param [#to_s] id
|
642
|
+
# @return [TripleExpression, ShapeExpression]
|
643
|
+
def find(id)
|
644
|
+
each_descendant(false).detect {|op| op.id == id}
|
645
|
+
end
|
646
|
+
|
634
647
|
##
|
635
648
|
# Ancestors of this Operator
|
636
649
|
# @return [Array<Operator>]
|
data/lib/shex/algebra/or.rb
CHANGED
@@ -67,22 +67,13 @@ module ShEx::Algebra
|
|
67
67
|
end
|
68
68
|
|
69
69
|
##
|
70
|
-
# expressions must be ShapeExpressions
|
70
|
+
# expressions must be ShapeExpressions or references to ShapeExpressions
|
71
71
|
#
|
72
72
|
# @return [Operator] `self`
|
73
73
|
# @raise [ShEx::StructureError] if the value is invalid
|
74
74
|
def validate!
|
75
|
-
|
76
|
-
|
77
|
-
when ShapeExpression
|
78
|
-
when RDF::Resource
|
79
|
-
ref = schema.find(op)
|
80
|
-
ref.is_a?(ShapeExpression) ||
|
81
|
-
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
82
|
-
else
|
83
|
-
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
84
|
-
end
|
85
|
-
end
|
75
|
+
validate_expressions!
|
76
|
+
validate_self_references!
|
86
77
|
super
|
87
78
|
end
|
88
79
|
|
data/lib/shex/algebra/schema.rb
CHANGED
@@ -25,8 +25,9 @@ module ShEx::Algebra
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# (see Operator#initialize)
|
28
|
-
def initialize(*operands)
|
28
|
+
def initialize(*operands, **options)
|
29
29
|
super
|
30
|
+
schema = self
|
30
31
|
each_descendant do |op|
|
31
32
|
# Set schema everywhere
|
32
33
|
op.schema = self
|
@@ -211,14 +212,6 @@ module ShEx::Algebra
|
|
211
212
|
@start ||= operands.detect {|op| op.is_a?(Start)}
|
212
213
|
end
|
213
214
|
|
214
|
-
##
|
215
|
-
# Find a ShapeExpression or TripleExpression by identifier
|
216
|
-
# @param [#to_s] id
|
217
|
-
# @return [TripleExpression, ShapeExpression]
|
218
|
-
def find(id)
|
219
|
-
each_descendant.detect {|op| op.id == id}
|
220
|
-
end
|
221
|
-
|
222
215
|
##
|
223
216
|
# Validate shapes, in addition to other operands
|
224
217
|
# @return [Operator] `self`
|
data/lib/shex/algebra/shape.rb
CHANGED
@@ -106,7 +106,7 @@ module ShEx::Algebra
|
|
106
106
|
end
|
107
107
|
|
108
108
|
##
|
109
|
-
# expression must be a TripleExpression
|
109
|
+
# expression must be a TripleExpression and must not reference itself recursively.
|
110
110
|
#
|
111
111
|
# @return [Operator] `self`
|
112
112
|
# @raise [ShEx::StructureError] if the value is invalid
|
@@ -118,8 +118,10 @@ module ShEx::Algebra
|
|
118
118
|
ref.is_a?(TripleExpression) ||
|
119
119
|
structure_error("#{json_type} must reference a TripleExpression: #{ref}")
|
120
120
|
else
|
121
|
-
structure_error("#{json_type} must
|
121
|
+
structure_error("#{json_type} must be a TripleExpression or reference: #{expression.to_sxp}")
|
122
122
|
end
|
123
|
+
# FIXME: this runs afoul of otherwise legitamate self-references, through a TripleExpression.
|
124
|
+
#!validate_self_references!
|
123
125
|
super
|
124
126
|
end
|
125
127
|
|
@@ -15,5 +15,34 @@ module ShEx::Algebra
|
|
15
15
|
def satisfies?(focus, depth: 0, **options)
|
16
16
|
raise NotImplementedError, "#satisfies? Not implemented in #{self.class}"
|
17
17
|
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# expressions must be ShapeExpressions or references.
|
21
|
+
#
|
22
|
+
# @raise [ShEx::StructureError] if the value is invalid
|
23
|
+
def validate_expressions!
|
24
|
+
expressions.each do |op|
|
25
|
+
case op
|
26
|
+
when ShapeExpression
|
27
|
+
when RDF::Resource
|
28
|
+
ref = schema.find(op)
|
29
|
+
ref.is_a?(ShapeExpression) ||
|
30
|
+
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
31
|
+
else
|
32
|
+
structure_error("#{json_type} must be a ShapeExpression or reference: #{op.to_sxp}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# An Operator with a label must contain a reference to itself.
|
39
|
+
#
|
40
|
+
# @raise [ShEx::StructureError] if the shape is invalid
|
41
|
+
def validate_self_references!
|
42
|
+
return # FIXME: needs to stop at a TripleConstraint
|
43
|
+
each_descendant do |op|
|
44
|
+
structure_error("#{json_type} must not reference itself (#{id}): #{op.to_sxp}") if op.references.include?(id)
|
45
|
+
end
|
46
|
+
end
|
18
47
|
end
|
19
48
|
end
|
data/lib/shex/algebra/start.rb
CHANGED
@@ -30,20 +30,12 @@ module ShEx::Algebra
|
|
30
30
|
end
|
31
31
|
|
32
32
|
##
|
33
|
-
#
|
33
|
+
# expressions must be ShapeExpressions or references to ShapeExpressions
|
34
34
|
#
|
35
35
|
# @return [Operator] `self`
|
36
36
|
# @raise [ShEx::StructureError] if the value is invalid
|
37
37
|
def validate!
|
38
|
-
|
39
|
-
when ShapeExpression
|
40
|
-
when RDF::Resource
|
41
|
-
ref = schema.find(expression)
|
42
|
-
ref.is_a?(ShapeExpression) ||
|
43
|
-
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
44
|
-
else
|
45
|
-
structure_error("#{json_type} must reference a ShapeExpression: #{ref}")
|
46
|
-
end
|
38
|
+
validate_expressions!
|
47
39
|
super
|
48
40
|
end
|
49
41
|
end
|
data/lib/shex/algebra/stem.rb
CHANGED
@@ -66,8 +66,12 @@ module ShEx::Algebra
|
|
66
66
|
NAME = :languageStem
|
67
67
|
|
68
68
|
# (see Stem#match?)
|
69
|
+
# If the operand is empty, than any language will do,
|
70
|
+
# otherwise, it matches the substring up to that first '-', if any.
|
69
71
|
def match?(value, depth: 0)
|
70
|
-
if value.literal? &&
|
72
|
+
if value.literal? &&
|
73
|
+
value.language? &&
|
74
|
+
(operands.first.to_s.empty? || value.language.to_s.match?(%r(^#{operands.first}((-.*)?)$)))
|
71
75
|
status "matched #{value}", depth: depth
|
72
76
|
true
|
73
77
|
else
|