shex 0.3.0 → 0.4.0
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.
- 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
|
##
|