shex 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -3
- data/VERSION +1 -1
- data/lib/shex.rb +2 -2
- data/lib/shex/algebra.rb +3 -6
- data/lib/shex/algebra/and.rb +38 -8
- data/lib/shex/algebra/annotation.rb +1 -1
- data/lib/shex/algebra/each_of.rb +22 -1
- data/lib/shex/algebra/external.rb +4 -4
- data/lib/shex/algebra/node_constraint.rb +4 -4
- data/lib/shex/algebra/not.rb +36 -5
- data/lib/shex/algebra/one_of.rb +22 -1
- data/lib/shex/algebra/operator.rb +90 -62
- data/lib/shex/algebra/or.rb +43 -10
- data/lib/shex/algebra/schema.rb +34 -19
- data/lib/shex/algebra/shape.rb +25 -11
- data/lib/shex/algebra/{satisfiable.rb → shape_expression.rb} +2 -5
- data/lib/shex/algebra/start.rb +35 -5
- data/lib/shex/algebra/triple_constraint.rb +33 -8
- data/lib/shex/algebra/triple_expression.rb +1 -1
- data/lib/shex/parser.rb +25 -22
- data/lib/shex/shex_context.rb +1 -2
- metadata +5 -7
- data/lib/shex/algebra/inclusion.rb +0 -67
- data/lib/shex/algebra/shape_ref.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96423cdff8bd77f2573ab32cd954d5217b2dfe20
|
4
|
+
data.tar.gz: 510d72ce00605e864b37af848f39c54f19de96b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7af06418271fa283bf833b837b546a10870af1e6f60fea90099d8a75d8dc4097f1fce6c7800b0874b50efcddbafe97abe5e30888c51f1d97f62e46298b2c5e86
|
7
|
+
data.tar.gz: 5c9a7e9c9f7373a64a4c0722edefb6d2c9c20e5a9aaad8920f539641ddca97d5499dec2d3c2f1aa719122405b381a2d52bf536f4bcabb71ea41702eafeb4aabc
|
data/README.md
CHANGED
@@ -48,7 +48,7 @@ The ShEx gem implements a [ShEx][ShExSpec] Shape Expression engine.
|
|
48
48
|
}
|
49
49
|
schema.satisfies?("http://rubygems.org/gems/shex", graph, map)
|
50
50
|
# => true
|
51
|
-
### Validating a node using
|
51
|
+
### Validating a node using ShExJ
|
52
52
|
|
53
53
|
require 'rubygems'
|
54
54
|
require 'rdf/turtle'
|
@@ -174,10 +174,12 @@ The ShExC parser uses the [EBNF][] gem to generate first, follow and branch tabl
|
|
174
174
|
|
175
175
|
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}.
|
176
176
|
|
177
|
+
The result of parsing either ShExC or ShExJ is the creation of a set of executable {ShEx::Algebra} Operators which are directly executed to perform shape validation.
|
178
|
+
|
177
179
|
## Dependencies
|
178
180
|
|
179
|
-
* [Ruby](http://ruby-lang.org/) (>= 2.
|
180
|
-
* [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.
|
181
|
+
* [Ruby](http://ruby-lang.org/) (>= 2.2.2)
|
182
|
+
* [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.2)
|
181
183
|
|
182
184
|
## Installation
|
183
185
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/shex.rb
CHANGED
@@ -35,10 +35,10 @@ module ShEx
|
|
35
35
|
when 'shexc' then Parser.new(expression, options).parse
|
36
36
|
when 'shexj'
|
37
37
|
expression = expression.read if expression.respond_to?(:read)
|
38
|
-
Algebra.from_shexj(JSON.parse
|
38
|
+
Algebra.from_shexj(JSON.parse(expression), options)
|
39
39
|
when 'sxp'
|
40
40
|
expression = expression.read if expression.respond_to?(:read)
|
41
|
-
Algebra.from_sxp(
|
41
|
+
Algebra.from_sxp(expression, options)
|
42
42
|
else raise "Unknown expression format: #{format.inspect}"
|
43
43
|
end
|
44
44
|
end
|
data/lib/shex/algebra.rb
CHANGED
@@ -10,17 +10,15 @@ module ShEx
|
|
10
10
|
autoload :And, 'shex/algebra/and'
|
11
11
|
autoload :Annotation, 'shex/algebra/annotation'
|
12
12
|
autoload :EachOf, 'shex/algebra/each_of'
|
13
|
-
autoload :
|
13
|
+
autoload :External, 'shex/algebra/external'
|
14
14
|
autoload :Not, 'shex/algebra/not'
|
15
15
|
autoload :NodeConstraint, 'shex/algebra/node_constraint'
|
16
16
|
autoload :OneOf, 'shex/algebra/one_of'
|
17
17
|
autoload :Operator, 'shex/algebra/operator'
|
18
18
|
autoload :Or, 'shex/algebra/or'
|
19
|
-
autoload :Satisfiable, 'shex/algebra/satisfiable'
|
20
19
|
autoload :Schema, 'shex/algebra/schema'
|
21
20
|
autoload :SemAct, 'shex/algebra/semact'
|
22
|
-
autoload :
|
23
|
-
autoload :ShapeRef, 'shex/algebra/shape_ref'
|
21
|
+
autoload :ShapeExpression, 'shex/algebra/shape_expression'
|
24
22
|
autoload :Shape, 'shex/algebra/shape'
|
25
23
|
autoload :Start, 'shex/algebra/start'
|
26
24
|
autoload :Stem, 'shex/algebra/stem'
|
@@ -50,16 +48,15 @@ module ShEx
|
|
50
48
|
klass = case operator['type']
|
51
49
|
when 'Annotation' then Annotation
|
52
50
|
when 'EachOf' then EachOf
|
53
|
-
when 'Inclusion' then Inclusion
|
54
51
|
when 'NodeConstraint' then NodeConstraint
|
55
52
|
when 'OneOf' then OneOf
|
56
53
|
when 'Schema' then Schema
|
57
54
|
when 'SemAct' then SemAct
|
58
55
|
when 'Shape' then Shape
|
59
56
|
when 'ShapeAnd' then And
|
57
|
+
when 'ShapeExternal' then External
|
60
58
|
when 'ShapeNot' then Not
|
61
59
|
when 'ShapeOr' then Or
|
62
|
-
when 'ShapeRef' then ShapeRef
|
63
60
|
when 'Stem' then Stem
|
64
61
|
when 'StemRange' then StemRange
|
65
62
|
when 'TripleConstraint' then TripleConstraint
|
data/lib/shex/algebra/and.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module ShEx::Algebra
|
2
2
|
##
|
3
3
|
class And < Operator
|
4
|
-
include
|
4
|
+
include ShapeExpression
|
5
5
|
NAME = :and
|
6
6
|
|
7
7
|
def initialize(*args, **options)
|
@@ -10,8 +10,8 @@ module ShEx::Algebra
|
|
10
10
|
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 2..)"
|
11
11
|
end
|
12
12
|
|
13
|
-
# All arguments must be
|
14
|
-
raise ArgumentError, "All operands must be Shape operands" unless args.all? {|o| o.is_a?(
|
13
|
+
# All arguments must be ShapeExpression
|
14
|
+
raise ArgumentError, "All operands must be Shape operands or resource" unless args.all? {|o| o.is_a?(ShapeExpression) || o.is_a?(RDF::Resource)}
|
15
15
|
super
|
16
16
|
end
|
17
17
|
|
@@ -27,18 +27,28 @@ module ShEx::Algebra
|
|
27
27
|
|
28
28
|
#
|
29
29
|
# S is a ShapeAnd and for every shape expression se2 in shapeExprs, satisfies(n, se2, G, m).
|
30
|
-
# @param (see
|
31
|
-
# @return (see
|
32
|
-
# @raise (see
|
30
|
+
# @param (see ShapeExpression#satisfies?)
|
31
|
+
# @return (see ShapeExpression#satisfies?)
|
32
|
+
# @raise (see ShapeExpression#satisfies?)
|
33
33
|
def satisfies?(focus, depth: 0)
|
34
34
|
status ""
|
35
|
-
expressions = operands.select {|o| o.is_a?(Satisfiable)}
|
36
35
|
satisfied = []
|
37
36
|
unsatisfied = expressions.dup
|
38
37
|
|
39
38
|
# Operand raises NotSatisfied, so no need to check here.
|
40
39
|
expressions.each do |op|
|
41
|
-
satisfied << op
|
40
|
+
satisfied << case op
|
41
|
+
when RDF::Resource
|
42
|
+
schema.enter_shape(op, focus) do |shape|
|
43
|
+
if shape
|
44
|
+
shape.satisfies?(focus, depth: depth + 1)
|
45
|
+
else
|
46
|
+
status "Satisfy as #{op} was re-entered for #{focus}", depth: depth
|
47
|
+
end
|
48
|
+
end
|
49
|
+
when ShapeExpression
|
50
|
+
op.satisfies?(focus, depth: depth + 1)
|
51
|
+
end
|
42
52
|
unsatisfied.shift
|
43
53
|
end
|
44
54
|
satisfy focus: focus, satisfied: satisfied, depth: depth
|
@@ -50,6 +60,26 @@ module ShEx::Algebra
|
|
50
60
|
depth: depth
|
51
61
|
end
|
52
62
|
|
63
|
+
##
|
64
|
+
# expressions must be ShapeExpressions
|
65
|
+
#
|
66
|
+
# @return [Operator] `self`
|
67
|
+
# @raise [ShEx::StructureError] if the value is invalid
|
68
|
+
def validate!
|
69
|
+
expressions.each do |op|
|
70
|
+
case op
|
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
|
80
|
+
super
|
81
|
+
end
|
82
|
+
|
53
83
|
def json_type
|
54
84
|
"ShapeAnd"
|
55
85
|
end
|
data/lib/shex/algebra/each_of.rb
CHANGED
@@ -31,8 +31,9 @@ module ShEx::Algebra
|
|
31
31
|
while num_iters < max
|
32
32
|
begin
|
33
33
|
matched_this_iter = []
|
34
|
-
|
34
|
+
expressions.select {|o| o.is_a?(TripleExpression) || o.is_a?(RDF::Resource)}.all? do |op|
|
35
35
|
begin
|
36
|
+
op = schema.find(op) if op.is_a?(RDF::Resource)
|
36
37
|
matched_op = op.matches(arcs_in - matched_this_iter, arcs_out - matched_this_iter, depth: depth + 1)
|
37
38
|
satisfied << matched_op
|
38
39
|
matched_this_iter += matched_op.matched
|
@@ -70,5 +71,25 @@ module ShEx::Algebra
|
|
70
71
|
ensure
|
71
72
|
semantic_actions.each {|op| op.exit(matched: matched, depth: depth + 1)}
|
72
73
|
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# expressions must be TripleExpressions
|
77
|
+
#
|
78
|
+
# @return [Operator] `self`
|
79
|
+
# @raise [ShEx::StructureError] if the value is invalid
|
80
|
+
def validate!
|
81
|
+
expressions.each do |op|
|
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
|
92
|
+
super
|
93
|
+
end
|
73
94
|
end
|
74
95
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ShEx::Algebra
|
2
2
|
##
|
3
3
|
class External < Operator
|
4
|
-
include
|
4
|
+
include ShapeExpression
|
5
5
|
NAME = :external
|
6
6
|
|
7
7
|
#
|
@@ -9,11 +9,11 @@ module ShEx::Algebra
|
|
9
9
|
def satisfies?(focus, depth: 0)
|
10
10
|
extern_shape = nil
|
11
11
|
|
12
|
-
# Find the
|
13
|
-
not_satisfied("Can't find
|
12
|
+
# Find the id for this external
|
13
|
+
not_satisfied("Can't find id for this extern", depth: depth) unless id
|
14
14
|
|
15
15
|
schema.external_schemas.each do |schema|
|
16
|
-
extern_shape ||= schema.shapes.detect {|s| s.
|
16
|
+
extern_shape ||= schema.shapes.detect {|s| s.id == id}
|
17
17
|
end
|
18
18
|
|
19
19
|
not_satisfied("External not configured for this shape", depth: depth) unless extern_shape
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ShEx::Algebra
|
2
2
|
##
|
3
3
|
class NodeConstraint < Operator
|
4
|
-
include
|
4
|
+
include ShapeExpression
|
5
5
|
NAME = :nodeConstraint
|
6
6
|
|
7
7
|
##
|
@@ -15,9 +15,9 @@ module ShEx::Algebra
|
|
15
15
|
|
16
16
|
#
|
17
17
|
# S is a NodeConstraint and satisfies2(focus, se) as described below in Node Constraints. Note that testing if a node satisfies a node constraint does not require a graph or shapeMap.
|
18
|
-
# @param (see
|
19
|
-
# @return (see
|
20
|
-
# @raise (see
|
18
|
+
# @param (see ShapeExpression#satisfies?)
|
19
|
+
# @return (see ShapeExpression#satisfies?)
|
20
|
+
# @raise (see ShapeExpression#satisfies?)
|
21
21
|
def satisfies?(focus, depth: 0)
|
22
22
|
status "", depth: depth
|
23
23
|
satisfies_node_kind?(focus, depth: depth + 1) &&
|
data/lib/shex/algebra/not.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module ShEx::Algebra
|
2
2
|
##
|
3
3
|
class Not < Operator::Unary
|
4
|
-
include
|
4
|
+
include ShapeExpression
|
5
5
|
NAME = :not
|
6
6
|
|
7
7
|
##
|
@@ -16,14 +16,27 @@ module ShEx::Algebra
|
|
16
16
|
|
17
17
|
#
|
18
18
|
# S is a ShapeNot and for the shape expression se2 at shapeExpr, notSatisfies(n, se2, G, m).
|
19
|
-
# @param (see
|
20
|
-
# @return (see
|
21
|
-
# @raise (see
|
19
|
+
# @param (see ShapeExpression#satisfies?)
|
20
|
+
# @return (see ShapeExpression#satisfies?)
|
21
|
+
# @raise (see ShapeExpression#satisfies?)
|
22
22
|
# @see [https://shexspec.github.io/spec/#shape-expression-semantics]
|
23
23
|
def satisfies?(focus, depth: 0)
|
24
24
|
status ""
|
25
|
+
op = expressions.last
|
25
26
|
satisfied_op = begin
|
26
|
-
|
27
|
+
case op
|
28
|
+
when RDF::Resource
|
29
|
+
schema.enter_shape(op, focus) do |shape|
|
30
|
+
if shape
|
31
|
+
shape.satisfies?(focus, depth: depth + 1)
|
32
|
+
else
|
33
|
+
status "Satisfy as #{op} was re-entered for #{focus}", depth: depth
|
34
|
+
shape
|
35
|
+
end
|
36
|
+
end
|
37
|
+
when ShapeExpression
|
38
|
+
op.satisfies?(focus, depth: depth + 1)
|
39
|
+
end
|
27
40
|
rescue ShEx::NotSatisfied => e
|
28
41
|
return satisfy focus: focus, satisfied: e.expression.unsatisfied, depth: depth
|
29
42
|
end
|
@@ -31,6 +44,24 @@ module ShEx::Algebra
|
|
31
44
|
focus: focus, unsatisfied: satisfied_op, depth: depth
|
32
45
|
end
|
33
46
|
|
47
|
+
##
|
48
|
+
# expression must be a ShapeExpression
|
49
|
+
#
|
50
|
+
# @return [Operator] `self`
|
51
|
+
# @raise [ShEx::StructureError] if the value is invalid
|
52
|
+
def validate!
|
53
|
+
case expression
|
54
|
+
when ShapeExpression
|
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
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
34
65
|
def json_type
|
35
66
|
"ShapeNot"
|
36
67
|
end
|
data/lib/shex/algebra/one_of.rb
CHANGED
@@ -27,8 +27,9 @@ module ShEx::Algebra
|
|
27
27
|
# OneOf is greedy, and consumes triples from every sub-expression, although only one is requred it succeed. Cardinality is somewhat complicated, as if two expressions match, this works for either a cardinality of one or two. Or two passes with just one match on each pass.
|
28
28
|
status ""
|
29
29
|
while num_iters < max
|
30
|
-
matched_something =
|
30
|
+
matched_something = expressions.select {|o| o.is_a?(TripleExpression) || o.is_a?(RDF::Resource)}.any? do |op|
|
31
31
|
begin
|
32
|
+
op = schema.find(op) if op.is_a?(RDF::Resource)
|
32
33
|
matched_op = op.matches(arcs_in, arcs_out, depth: depth + 1)
|
33
34
|
satisfied << matched_op
|
34
35
|
results += matched_op.matched
|
@@ -62,5 +63,25 @@ module ShEx::Algebra
|
|
62
63
|
matched: results, unmatched: ((arcs_in + arcs_out).uniq - results),
|
63
64
|
satisfied: satisfied, unsatisfied: unsatisfied, depth: depth
|
64
65
|
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# expressions must be TripleExpressions
|
69
|
+
#
|
70
|
+
# @return [Operator] `self`
|
71
|
+
# @raise [ShEx::StructureError] if the value is invalid
|
72
|
+
def validate!
|
73
|
+
expressions.each do |op|
|
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
|
84
|
+
super
|
85
|
+
end
|
65
86
|
end
|
66
87
|
end
|
@@ -32,7 +32,7 @@ module ShEx::Algebra
|
|
32
32
|
# any additional options
|
33
33
|
# @option options [Boolean] :memoize (false)
|
34
34
|
# whether to memoize results for particular operands
|
35
|
-
# @option options [RDF::Resource] :
|
35
|
+
# @option options [RDF::Resource] :id
|
36
36
|
# Identifier of the operator
|
37
37
|
# @raise [TypeError] if any operand is invalid
|
38
38
|
def initialize(*operands)
|
@@ -55,7 +55,7 @@ module ShEx::Algebra
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
@
|
58
|
+
@id = options[:id]
|
59
59
|
end
|
60
60
|
|
61
61
|
##
|
@@ -72,9 +72,6 @@ module ShEx::Algebra
|
|
72
72
|
operands.select {|o| o.is_a?(SemAct)}
|
73
73
|
end
|
74
74
|
|
75
|
-
# Does this operator include Satisfiable?
|
76
|
-
def satisfiable?; false; end
|
77
|
-
|
78
75
|
# Does this operator include TripleExpression?
|
79
76
|
def triple_expression?; false; end
|
80
77
|
|
@@ -185,7 +182,7 @@ module ShEx::Algebra
|
|
185
182
|
end
|
186
183
|
|
187
184
|
def status(message, **opts, &block)
|
188
|
-
log_debug(self.class.const_get(:NAME).to_s + (@
|
185
|
+
log_debug(self.class.const_get(:NAME).to_s + (@id ? "(#{@id})" : ""), message, **opts, &block)
|
189
186
|
true
|
190
187
|
end
|
191
188
|
|
@@ -196,9 +193,9 @@ module ShEx::Algebra
|
|
196
193
|
attr_reader :operands
|
197
194
|
|
198
195
|
##
|
199
|
-
# The
|
196
|
+
# The id (or subject) of this operand
|
200
197
|
# @return [RDF::Resource]
|
201
|
-
attr_accessor :
|
198
|
+
attr_accessor :id
|
202
199
|
|
203
200
|
##
|
204
201
|
# Logging support (reader is in RDF::Util::Logger)
|
@@ -215,6 +212,21 @@ module ShEx::Algebra
|
|
215
212
|
operands[index]
|
216
213
|
end
|
217
214
|
|
215
|
+
##
|
216
|
+
# Expressions are all operands which are Operators or RDF::Resource
|
217
|
+
# @return [RDF::Resource, Operand]
|
218
|
+
def expressions
|
219
|
+
@expressions = operands.
|
220
|
+
select {|op| op.is_a?(RDF::Resource) || op.is_a?(ShapeExpression) || op.is_a?(TripleExpression)}
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# The optional TripleExpression for this Shape.
|
225
|
+
# @return [TripleExpression]
|
226
|
+
def expression
|
227
|
+
expressions.first
|
228
|
+
end
|
229
|
+
|
218
230
|
##
|
219
231
|
# Returns the binary S-Expression (SXP) representation of this operator.
|
220
232
|
#
|
@@ -222,7 +234,7 @@ module ShEx::Algebra
|
|
222
234
|
# @see https://en.wikipedia.org/wiki/S-expression
|
223
235
|
def to_sxp_bin
|
224
236
|
[self.class.const_get(:NAME)] +
|
225
|
-
(
|
237
|
+
(id ? [[:id, id]] : []) +
|
226
238
|
(operands || []).map(&:to_sxp_bin)
|
227
239
|
end
|
228
240
|
|
@@ -251,19 +263,25 @@ module ShEx::Algebra
|
|
251
263
|
def self.from_shexj(operator, options = {})
|
252
264
|
options[:context] ||= JSON::LD::Context.parse(ShEx::CONTEXT)
|
253
265
|
operands = []
|
254
|
-
|
266
|
+
id = nil
|
255
267
|
|
256
268
|
operator.each do |k, v|
|
257
269
|
case k
|
258
|
-
when /length|pattern|clusive/
|
259
|
-
when '
|
260
|
-
when 'min', 'max'
|
270
|
+
when /length|pattern|clusive|digits/ then operands << [k.to_sym, RDF::Literal(v)]
|
271
|
+
when 'id' then id = iri(v, options)
|
272
|
+
when 'min', 'max' then operands << [k.to_sym, v]
|
273
|
+
when 'inverse', 'closed' then operands << k.to_sym
|
261
274
|
when 'nodeKind' then operands << v.to_sym
|
262
275
|
when 'object' then operands << value(v, options)
|
263
|
-
when 'start'
|
276
|
+
when 'start'
|
277
|
+
if v.is_a?(String)
|
278
|
+
operands << Start.new(iri(v, options))
|
279
|
+
else
|
280
|
+
operands << Start.new(ShEx::Algebra.from_shexj(v, options))
|
281
|
+
end
|
264
282
|
when '@context' then
|
265
283
|
options[:context] = JSON::LD::Context.parse(v)
|
266
|
-
options[:base_uri]
|
284
|
+
options[:base_uri] ||= options[:context].base
|
267
285
|
when 'shapes'
|
268
286
|
operands << case v
|
269
287
|
when Array
|
@@ -271,10 +289,10 @@ module ShEx::Algebra
|
|
271
289
|
else
|
272
290
|
raise "Expected value of shapes #{v.inspect}"
|
273
291
|
end
|
274
|
-
when '
|
292
|
+
when 'stem', 'name'
|
275
293
|
# Value may be :wildcard for stem
|
276
294
|
operands << (v.is_a?(Symbol) ? v : iri(v, options))
|
277
|
-
when 'predicate' then operands << iri(v, options)
|
295
|
+
when 'predicate' then operands << [:predicate, iri(v, options)]
|
278
296
|
when 'extra', 'datatype'
|
279
297
|
v = [v] unless v.is_a?(Array)
|
280
298
|
operands << (v.map {|op| iri(op, options)}).unshift(k.to_sym)
|
@@ -285,11 +303,15 @@ module ShEx::Algebra
|
|
285
303
|
ShEx::Algebra.from_shexj(op, options) :
|
286
304
|
value(op, options)
|
287
305
|
end.unshift(:exclusions)
|
288
|
-
when '
|
289
|
-
'shapeExpr', 'shapeExprs', 'startActs', 'expression',
|
290
|
-
'expressions', 'annotations'
|
306
|
+
when 'semActs', 'startActs', 'annotations'
|
291
307
|
v = [v] unless v.is_a?(Array)
|
292
308
|
operands += v.map {|op| ShEx::Algebra.from_shexj(op, options)}
|
309
|
+
when 'expression', 'expressions', 'shapeExpr', 'shapeExprs', 'valueExpr'
|
310
|
+
v = [v] unless v.is_a?(Array)
|
311
|
+
operands += v.map do |op|
|
312
|
+
# It's a URI reference to a Shape
|
313
|
+
op.is_a?(String) ? iri(op, options) : ShEx::Algebra.from_shexj(op, options)
|
314
|
+
end
|
293
315
|
when 'code'
|
294
316
|
operands << v
|
295
317
|
when 'values'
|
@@ -300,7 +322,7 @@ module ShEx::Algebra
|
|
300
322
|
end
|
301
323
|
end
|
302
324
|
|
303
|
-
new(*operands,
|
325
|
+
new(*operands, options.merge(id: id))
|
304
326
|
end
|
305
327
|
|
306
328
|
def json_type
|
@@ -315,10 +337,9 @@ module ShEx::Algebra
|
|
315
337
|
# Create a hash version of the operator, suitable for turning into JSON.
|
316
338
|
# @return [Hash]
|
317
339
|
def to_h
|
318
|
-
obj = json_type == 'Schema' ?
|
319
|
-
|
320
|
-
|
321
|
-
obj['label'] = label.to_s if label
|
340
|
+
obj = json_type == 'Schema' ? {'@context' => ShEx::CONTEXT} : {}
|
341
|
+
obj['id'] = id.to_s if id
|
342
|
+
obj['type'] = json_type
|
322
343
|
operands.each do |op|
|
323
344
|
case op
|
324
345
|
when Array
|
@@ -340,6 +361,7 @@ module ShEx::Algebra
|
|
340
361
|
:totaldigits,
|
341
362
|
:fractiondigits then obj[op.first.to_s] = op.last.object
|
342
363
|
when :min, :max then obj[op.first.to_s] = op.last
|
364
|
+
when :predicate then obj[op.first.to_s] = op.last.to_s
|
343
365
|
when :base, :prefix
|
344
366
|
# Ignore base and prefix
|
345
367
|
when Symbol then obj[sym.to_s] = Array(op[1..-1]).map(&:to_h)
|
@@ -349,14 +371,20 @@ module ShEx::Algebra
|
|
349
371
|
when :wildcard then obj['stem'] = {'type' => 'Wildcard'}
|
350
372
|
when Annotation then (obj['annotations'] ||= []) << op.to_h
|
351
373
|
when SemAct then (obj[is_a?(Schema) ? 'startActs' : 'semActs'] ||= []) << op.to_h
|
352
|
-
when Start
|
374
|
+
when Start
|
375
|
+
obj['start'] = case op.operands.first
|
376
|
+
when RDF::Resource then op.operands.first.to_s
|
377
|
+
else op.operands.first.to_h
|
378
|
+
end
|
353
379
|
when RDF::Value
|
354
380
|
case self
|
355
|
-
when TripleConstraint then obj['predicate'] = op.to_s
|
356
381
|
when Stem, StemRange then obj['stem'] = op.to_s
|
357
|
-
when Inclusion then obj['include'] = op.to_s
|
358
|
-
when ShapeRef then obj['reference'] = op.to_s
|
359
382
|
when SemAct then obj[op.is_a?(RDF::URI) ? 'name' : 'code'] = op.to_s
|
383
|
+
when TripleConstraint then obj['valueExpr'] = op.to_s
|
384
|
+
when Shape then obj['expression'] = op.to_s
|
385
|
+
when EachOf, OneOf then (obj['expressions'] ||= []) << op.to_s
|
386
|
+
when And, Or then (obj['shapeExprs'] ||= []) << op.to_s
|
387
|
+
when Not then obj['shapeExpr'] = op.to_s
|
360
388
|
else
|
361
389
|
raise "How to serialize Value #{op.inspect} to json for #{self}"
|
362
390
|
end
|
@@ -368,7 +396,7 @@ module ShEx::Algebra
|
|
368
396
|
else
|
369
397
|
raise "How to serialize Symbol #{op.inspect} to json for #{self}"
|
370
398
|
end
|
371
|
-
when TripleConstraint, EachOf, OneOf
|
399
|
+
when TripleConstraint, EachOf, OneOf
|
372
400
|
case self
|
373
401
|
when EachOf, OneOf
|
374
402
|
(obj['expressions'] ||= []) << op.to_h
|
@@ -382,7 +410,7 @@ module ShEx::Algebra
|
|
382
410
|
else
|
383
411
|
obj['valueExpr'] = op.to_h
|
384
412
|
end
|
385
|
-
when And, Or, Shape, Not
|
413
|
+
when And, Or, Shape, Not
|
386
414
|
case self
|
387
415
|
when And, Or
|
388
416
|
(obj['shapeExprs'] ||= []) << op.to_h
|
@@ -462,11 +490,7 @@ module ShEx::Algebra
|
|
462
490
|
RDF::URI(value)
|
463
491
|
end
|
464
492
|
else
|
465
|
-
if
|
466
|
-
options[:context].expand_iri(value, document: true)
|
467
|
-
elsif base_uri
|
468
|
-
base_uri.join(value)
|
469
|
-
elsif base_uri
|
493
|
+
if base_uri
|
470
494
|
base_uri.join(value)
|
471
495
|
else
|
472
496
|
RDF::URI(value)
|
@@ -526,14 +550,17 @@ module ShEx::Algebra
|
|
526
550
|
#
|
527
551
|
# @return [String]
|
528
552
|
def inspect
|
529
|
-
sprintf("#<%s:%#0x(%s)>", self.class.name, __id__,
|
553
|
+
sprintf("#<%s:%#0x(%s%s)>", self.class.name, __id__, ("id: #{id} " if id), operands.inspect)
|
530
554
|
end
|
531
555
|
|
532
556
|
##
|
557
|
+
# Comparison does not consider operand order
|
533
558
|
# @param [Statement] other
|
534
559
|
# @return [Boolean]
|
535
560
|
def eql?(other)
|
536
|
-
other.class == self.class &&
|
561
|
+
other.class == self.class &&
|
562
|
+
other.id == self.id &&
|
563
|
+
other.operands.sort_by(&:to_s) == self.operands.sort_by(&:to_s)
|
537
564
|
end
|
538
565
|
alias_method :==, :eql?
|
539
566
|
|
@@ -543,22 +570,19 @@ module ShEx::Algebra
|
|
543
570
|
# @yield operator
|
544
571
|
# @yieldparam [Object] operator
|
545
572
|
# @return [Enumerator]
|
546
|
-
def each_descendant(
|
573
|
+
def each_descendant(&block)
|
547
574
|
if block_given?
|
548
575
|
|
549
|
-
|
550
|
-
when 1 then block.call(self)
|
551
|
-
else block.call(depth, self)
|
552
|
-
end
|
576
|
+
block.call(self)
|
553
577
|
|
554
578
|
operands.each do |operand|
|
555
579
|
case operand
|
556
580
|
when Array
|
557
581
|
operand.each do |op|
|
558
|
-
op.each_descendant(
|
582
|
+
op.each_descendant(&block) if op.respond_to?(:each_descendant)
|
559
583
|
end
|
560
584
|
else
|
561
|
-
operand.each_descendant(
|
585
|
+
operand.each_descendant(&block) if operand.respond_to?(:each_descendant)
|
562
586
|
end
|
563
587
|
end
|
564
588
|
end
|
@@ -582,34 +606,38 @@ module ShEx::Algebra
|
|
582
606
|
end
|
583
607
|
|
584
608
|
##
|
585
|
-
#
|
586
|
-
#
|
587
|
-
#
|
588
|
-
#
|
589
|
-
|
590
|
-
parent.is_a?(klass) ? parent : parent.first_ancestor(klass) if parent
|
591
|
-
end
|
609
|
+
# Ancestors of this Operator
|
610
|
+
# @return [Array<Operator>]
|
611
|
+
#def ancestors
|
612
|
+
# parent.is_a?(Operator) ? ([parent] + parent.ancestors) : []
|
613
|
+
#end
|
592
614
|
|
593
615
|
##
|
594
|
-
# Validate all operands, operator specific classes should override for operator-specific validation
|
616
|
+
# Validate all operands, operator specific classes should override for operator-specific validation.
|
617
|
+
#
|
618
|
+
# A schema **must not** contain any shape expression `S` with negated references, either directly or transitively, to `S`.
|
619
|
+
#
|
595
620
|
# @return [SPARQL::Algebra::Expression] `self`
|
596
621
|
# @raise [ShEx::StructureError] if the value is invalid
|
597
622
|
def validate!
|
598
|
-
operands.each
|
623
|
+
operands.each do |op|
|
624
|
+
op.validate! if op.respond_to?(:validate!)
|
625
|
+
if op.is_a?(RDF::Resource) && (is_a?(ShapeExpression) || is_a?(TripleExpression))
|
626
|
+
ref = schema.find(op)
|
627
|
+
structure_error("Missing reference: #{op}") if ref.nil?
|
628
|
+
#if ancestors.unshift(self).include?(ref)
|
629
|
+
# structure_error("Self-recursive reference to #{op}")
|
630
|
+
#end
|
631
|
+
structure_error("Self referencing shape: #{operands.first}") if ref == self
|
632
|
+
end
|
633
|
+
end
|
599
634
|
self
|
600
635
|
end
|
601
636
|
|
602
637
|
protected
|
603
638
|
def dup
|
604
639
|
operands = @operands.map {|o| o.dup rescue o}
|
605
|
-
self.class.new(*operands,
|
606
|
-
end
|
607
|
-
|
608
|
-
##
|
609
|
-
# Implement `to_hash` only if accessed; otherwise, it becomes an _Implicit Accessor_ which will cause problems with splat arguments, which causes the last to be turned into a hash for extracting keyword aruments.
|
610
|
-
def method_missing(method, *args)
|
611
|
-
return to_h(*args) if method == :hash
|
612
|
-
super
|
640
|
+
self.class.new(*operands, id: @id)
|
613
641
|
end
|
614
642
|
|
615
643
|
##
|