shex 0.2.0 → 0.3.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 +119 -2
- data/VERSION +1 -1
- data/etc/doap.ttl +2 -2
- data/lib/shex.rb +21 -2
- data/lib/shex/algebra.rb +41 -3
- data/lib/shex/algebra/and.rb +27 -6
- data/lib/shex/algebra/annotation.rb +19 -0
- data/lib/shex/algebra/each_of.rb +32 -19
- data/lib/shex/algebra/external.rb +9 -6
- data/lib/shex/algebra/inclusion.rb +29 -18
- data/lib/shex/algebra/node_constraint.rb +45 -36
- data/lib/shex/algebra/not.rb +19 -4
- data/lib/shex/algebra/one_of.rb +26 -16
- data/lib/shex/algebra/operator.rb +350 -34
- data/lib/shex/algebra/or.rb +26 -9
- data/lib/shex/algebra/satisfiable.rb +5 -9
- data/lib/shex/algebra/schema.rb +87 -75
- data/lib/shex/algebra/semact.rb +69 -19
- data/lib/shex/algebra/shape.rb +28 -19
- data/lib/shex/algebra/shape_ref.rb +36 -10
- data/lib/shex/algebra/start.rb +5 -5
- data/lib/shex/algebra/stem.rb +18 -3
- data/lib/shex/algebra/stem_range.rb +24 -5
- data/lib/shex/algebra/triple_constraint.rb +26 -13
- data/lib/shex/algebra/triple_expression.rb +3 -2
- data/lib/shex/algebra/value.rb +5 -5
- data/lib/shex/extensions/extension.rb +160 -0
- data/lib/shex/extensions/test.rb +26 -0
- data/lib/shex/parser.rb +12 -25
- data/lib/shex/shex_context.rb +85 -0
- data/lib/shex/version.rb +19 -0
- metadata +35 -11
- data/lib/shex/algebra/base.rb +0 -6
- data/lib/shex/algebra/prefix.rb +0 -6
- data/lib/shex/algebra/unary_shape.rb +0 -6
data/lib/shex/algebra/or.rb
CHANGED
@@ -6,9 +6,22 @@ module ShEx::Algebra
|
|
6
6
|
|
7
7
|
def initialize(*args, **options)
|
8
8
|
case
|
9
|
-
when args.length
|
10
|
-
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected
|
9
|
+
when args.length < 2
|
10
|
+
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 2..)"
|
11
11
|
end
|
12
|
+
|
13
|
+
# All arguments must be Satisfiable
|
14
|
+
raise ArgumentError, "All operands must be Shape operands" unless args.all? {|o| o.is_a?(Satisfiable)}
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Creates an operator instance from a parsed ShExJ representation
|
20
|
+
# @param (see Operator#from_shexj)
|
21
|
+
# @return [Operator]
|
22
|
+
def self.from_shexj(operator, options = {})
|
23
|
+
raise ArgumentError unless operator.is_a?(Hash) && operator['type'] == 'ShapeOr'
|
24
|
+
raise ArgumentError, "missing shapeExprs in #{operator.inspect}" unless operator.is_a?(Hash) && operator.has_key?('shapeExprs')
|
12
25
|
super
|
13
26
|
end
|
14
27
|
|
@@ -17,27 +30,31 @@ module ShEx::Algebra
|
|
17
30
|
# @param (see Satisfiable#satisfies?)
|
18
31
|
# @return (see Satisfiable#satisfies?)
|
19
32
|
# @raise (see Satisfiable#satisfies?)
|
20
|
-
def satisfies?(focus)
|
21
|
-
status ""
|
33
|
+
def satisfies?(focus, depth: 0)
|
34
|
+
status "", depth: depth
|
22
35
|
expressions = operands.select {|o| o.is_a?(Satisfiable)}
|
23
36
|
unsatisfied = []
|
24
37
|
expressions.any? do |op|
|
25
38
|
begin
|
26
|
-
matched_op = op.satisfies?(focus)
|
27
|
-
return satisfy satisfied: matched_op,
|
39
|
+
matched_op = op.satisfies?(focus, depth: depth + 1)
|
40
|
+
return satisfy focus: focus, satisfied: matched_op, depth: depth
|
28
41
|
rescue ShEx::NotSatisfied => e
|
29
|
-
status "unsatisfied #{focus}"
|
42
|
+
status "unsatisfied #{focus}", depth: depth
|
30
43
|
op = op.dup
|
31
44
|
op.satisfied = e.expression.satisfied
|
32
45
|
op.unsatisfied = e.expression.unsatisfied
|
33
46
|
unsatisfied << op
|
34
|
-
status
|
47
|
+
status "unsatisfied: #{e.message}", depth: depth
|
35
48
|
false
|
36
49
|
end
|
37
50
|
end
|
38
51
|
|
39
52
|
not_satisfied "Expected some expression to be satisfied",
|
40
|
-
unsatisfied: unsatisfied
|
53
|
+
focus: focus, unsatisfied: unsatisfied, depth: depth
|
54
|
+
end
|
55
|
+
|
56
|
+
def json_type
|
57
|
+
"ShapeOr"
|
41
58
|
end
|
42
59
|
end
|
43
60
|
end
|
@@ -6,20 +6,16 @@ module ShEx::Algebra
|
|
6
6
|
##
|
7
7
|
# Satisfies method
|
8
8
|
# @param [RDF::Resource] focus
|
9
|
-
# @
|
9
|
+
# @param [Integer] depth for logging
|
10
|
+
# @param [Hash{Symbol => Object}] options
|
11
|
+
# Other, operand-specific options
|
12
|
+
# @return [Operator] with `matched` and `satisfied` accessors for matched triples and sub-expressions
|
10
13
|
# @raise [ShEx::NotMatched] with `expression` accessor to access `matched` and `unmatched` statements along with `satisfied` and `unsatisfied` operations.
|
11
14
|
# @see [https://shexspec.github.io/spec/#shape-expression-semantics]
|
12
|
-
def satisfies?(focus)
|
15
|
+
def satisfies?(focus, depth: 0, **options)
|
13
16
|
raise NotImplementedError, "#satisfies? Not implemented in #{self.class}"
|
14
17
|
end
|
15
18
|
|
16
|
-
##
|
17
|
-
# Included TripleExpressions
|
18
|
-
# @return [Array<TripleExpressions>]
|
19
|
-
def triple_expressions
|
20
|
-
operands.select {|o| o.is_a?(Satisfiable)}.map(&:triple_expressions).flatten.uniq
|
21
|
-
end
|
22
|
-
|
23
19
|
# This operator includes Satisfiable
|
24
20
|
def satisfiable?; true; end
|
25
21
|
end
|
data/lib/shex/algebra/schema.rb
CHANGED
@@ -11,55 +11,95 @@ module ShEx::Algebra
|
|
11
11
|
# @return [Hash{RDF::Resource => RDF::Resource}]
|
12
12
|
attr_reader :map
|
13
13
|
|
14
|
+
# Map of Semantic Action instances
|
15
|
+
# @return [Hash{String => ShEx::Extension}]
|
16
|
+
attr_reader :extensions
|
17
|
+
|
18
|
+
##
|
19
|
+
# Creates an operator instance from a parsed ShExJ representation
|
20
|
+
# @param (see Operator#from_shexj)
|
21
|
+
# @return [Operator]
|
22
|
+
def self.from_shexj(operator, options = {})
|
23
|
+
raise ArgumentError unless operator.is_a?(Hash) && operator['type'] == "Schema"
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# (see Operator#initialize)
|
28
|
+
def initialize(*operands)
|
29
|
+
super
|
30
|
+
each_descendant do |op|
|
31
|
+
# Set schema everywhere
|
32
|
+
op.schema = self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
14
36
|
##
|
15
37
|
# Match on schema. Finds appropriate shape for node, and matches that shape.
|
16
38
|
#
|
17
|
-
# @param [RDF::
|
39
|
+
# @param [RDF::Term] focus
|
18
40
|
# @param [RDF::Queryable] graph
|
19
41
|
# @param [Hash{RDF::Resource => RDF::Resource}] map
|
20
42
|
# @param [Array<Schema, String>] shapeExterns ([])
|
21
43
|
# One or more schemas, or paths to ShEx schema resources used for finding external shapes.
|
22
44
|
# @return [Operand] Returns operand graph annotated with satisfied and unsatisfied operations.
|
45
|
+
# @param [Hash{Symbol => Object}] options
|
46
|
+
# @option options [String] :base_uri
|
23
47
|
# @raise [ShEx::NotSatisfied] along with operand graph described for return
|
24
|
-
def execute(focus, graph, map, shapeExterns: [], **options)
|
25
|
-
@graph = graph
|
48
|
+
def execute(focus, graph, map, shapeExterns: [], depth: 0, **options)
|
49
|
+
@graph, @shapes_entered = graph, {}
|
26
50
|
@external_schemas = shapeExterns
|
27
|
-
focus =
|
51
|
+
focus = value(focus)
|
52
|
+
|
53
|
+
logger = options[:logger] || @options[:logger]
|
54
|
+
each_descendant do |op|
|
55
|
+
# Set logging everywhere
|
56
|
+
op.logger = logger
|
57
|
+
end
|
58
|
+
|
59
|
+
# Initialize Extensions
|
60
|
+
@extensions = {}
|
61
|
+
each_descendant do |op|
|
62
|
+
next unless op.is_a?(SemAct)
|
63
|
+
name = op.operands.first.to_s
|
64
|
+
if ext_class = ShEx::Extension.find(name)
|
65
|
+
@extensions[name] ||= ext_class.new(schema: self, depth: depth, **options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# If `n` is a Blank Node, we won't find it through normal matching, find an equivalent node in the graph having the same label
|
70
|
+
graph_focus = graph.enum_term.detect {|t| t.node? && t.id == focus.id} if focus.is_a?(RDF::Node)
|
71
|
+
graph_focus ||= focus
|
72
|
+
|
28
73
|
# Make sure they're URIs
|
29
|
-
@map = (map || {}).inject({}) {|memo, (k,v)| memo.merge(
|
74
|
+
@map = (map || {}).inject({}) {|memo, (k,v)| memo.merge(value(k) => iri(v))}
|
30
75
|
|
31
76
|
# First, evaluate semantic acts
|
32
77
|
semantic_actions.all? do |op|
|
33
|
-
op.satisfies?([])
|
78
|
+
op.satisfies?([], depth: depth + 1)
|
34
79
|
end
|
35
80
|
|
36
81
|
# Keep a new Schema, specifically for recording actions
|
37
82
|
satisfied_schema = Schema.new
|
38
83
|
# Next run any start expression
|
39
84
|
if start
|
40
|
-
|
41
|
-
satisfied_schema.operands << start.satisfies?(focus)
|
85
|
+
satisfied_schema.operands << start.satisfies?(focus, depth: depth + 1)
|
42
86
|
end
|
43
87
|
|
44
88
|
# Add shape result(s)
|
45
89
|
satisfied_shapes = {}
|
46
90
|
satisfied_schema.operands << [:shapes, satisfied_shapes] unless shapes.empty?
|
47
91
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# If `n` is a Blank Node, we won't find it through normal matching, find an equivalent node in the graph having the same label
|
54
|
-
if focus.is_a?(RDF::Node)
|
55
|
-
n = graph.enum_term.detect {|t| t.id == focus.id}
|
56
|
-
focus = n if n
|
92
|
+
# Match against all shapes associated with the labels for focus
|
93
|
+
Array(@map[focus]).each do |label|
|
94
|
+
enter_shape(label, focus) do |shape|
|
95
|
+
satisfied_shapes[label] = shape.satisfies?(graph_focus, depth: depth + 1)
|
57
96
|
end
|
58
|
-
|
59
|
-
satisfied_shapes[label] = shape.satisfies?(focus)
|
60
97
|
end
|
61
|
-
status "schema satisfied"
|
98
|
+
status "schema satisfied", depth: depth
|
62
99
|
satisfied_schema
|
100
|
+
ensure
|
101
|
+
# Close Semantic Action extensions
|
102
|
+
@extensions.values.each {|ext| ext.close(schema: self, depth: depth, **options)}
|
63
103
|
end
|
64
104
|
|
65
105
|
##
|
@@ -81,17 +121,37 @@ module ShEx::Algebra
|
|
81
121
|
|
82
122
|
##
|
83
123
|
# Shapes as a hash
|
84
|
-
# @return [
|
124
|
+
# @return [Array<Operator>]
|
85
125
|
def shapes
|
86
126
|
@shapes ||= begin
|
87
|
-
shapes = operands.detect {|op| op.is_a?(Array) && op.first == :shapes}
|
88
|
-
shapes
|
89
|
-
shapes.inject({}) do |memo, (label, operand)|
|
90
|
-
memo.merge(label.to_s => operand)
|
91
|
-
end
|
127
|
+
shapes = Array(operands.detect {|op| op.is_a?(Array) && op.first == :shapes})
|
128
|
+
Array(shapes[1..-1])
|
92
129
|
end
|
93
130
|
end
|
94
131
|
|
132
|
+
##
|
133
|
+
# Indicate that a shape has been entered with a specific focus node. Any future attempt to enter the same shape with the same node raises an exception.
|
134
|
+
# @param [RDF::Resource] label
|
135
|
+
# @param [RDF::Resource] node
|
136
|
+
# @yield :shape
|
137
|
+
# @yieldparam [Satisfiable] shape, or `nil` if shape already entered
|
138
|
+
# @return [Satisfiable]
|
139
|
+
def enter_shape(label, node, &block)
|
140
|
+
shape = shapes.detect {|s| s.label == label}
|
141
|
+
structure_error("No shape found for #{label}") unless shape
|
142
|
+
@shapes_entered[label] ||= {}
|
143
|
+
if @shapes_entered[label][node]
|
144
|
+
block.call(false)
|
145
|
+
else
|
146
|
+
@shapes_entered[label][node] = self
|
147
|
+
begin
|
148
|
+
block.call(shape)
|
149
|
+
ensure
|
150
|
+
@shapes_entered[label].delete(node)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
95
155
|
##
|
96
156
|
# Externally loaded schemas, lazily evaluated
|
97
157
|
# @return [Array<Schema>]
|
@@ -108,54 +168,6 @@ module ShEx::Algebra
|
|
108
168
|
end
|
109
169
|
end
|
110
170
|
|
111
|
-
##
|
112
|
-
# Enumerate via depth-first recursive descent over operands, yielding each operator
|
113
|
-
# @yield operator
|
114
|
-
# @yieldparam [Object] operator
|
115
|
-
# @return [Enumerator]
|
116
|
-
def each_descendant(depth = 0, &block)
|
117
|
-
if block_given?
|
118
|
-
super(depth + 1, &block)
|
119
|
-
shapes.values.each do |op|
|
120
|
-
op.each_descendant(depth + 1, &block) if op.respond_to?(:each_descendant)
|
121
|
-
|
122
|
-
case block.arity
|
123
|
-
when 1 then block.call(op)
|
124
|
-
else block.call(depth, op)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
enum_for(:each_descendant)
|
129
|
-
end
|
130
|
-
|
131
|
-
##
|
132
|
-
# Returns the Base URI defined for the parser,
|
133
|
-
# as specified or when parsing a BASE prologue element.
|
134
|
-
#
|
135
|
-
# @example
|
136
|
-
# base #=> RDF::URI('http://example.com/')
|
137
|
-
#
|
138
|
-
# @return [HRDF::URI]
|
139
|
-
def base_uri
|
140
|
-
RDF::URI(@options[:base_uri]) if @options[:base_uri]
|
141
|
-
end
|
142
|
-
|
143
|
-
# Create URIs
|
144
|
-
def iri(value)
|
145
|
-
# If we have a base URI, use that when constructing a new URI
|
146
|
-
case value
|
147
|
-
when RDF::Value then value
|
148
|
-
when /^_:/ then RDF::Node(value[2..-1].to_s)
|
149
|
-
else
|
150
|
-
value = RDF::URI(value)
|
151
|
-
if base_uri && value.relative?
|
152
|
-
base_uri.join(value)
|
153
|
-
else
|
154
|
-
value
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
171
|
##
|
160
172
|
# Start action, if any
|
161
173
|
def start
|
@@ -167,7 +179,7 @@ module ShEx::Algebra
|
|
167
179
|
# @return [SPARQL::Algebra::Expression] `self`
|
168
180
|
# @raise [ArgumentError] if the value is invalid
|
169
181
|
def validate!
|
170
|
-
shapes.
|
182
|
+
shapes.each {|op| op.validate! if op.respond_to?(:validate!)}
|
171
183
|
super
|
172
184
|
end
|
173
185
|
end
|
data/lib/shex/algebra/semact.rb
CHANGED
@@ -3,35 +3,85 @@ module ShEx::Algebra
|
|
3
3
|
class SemAct < Operator
|
4
4
|
NAME = :semact
|
5
5
|
|
6
|
+
##
|
7
|
+
# Creates an operator instance from a parsed ShExJ representation
|
8
|
+
# @param (see Operator#from_shexj)
|
9
|
+
# @return [Operator]
|
10
|
+
def self.from_shexj(operator, options = {})
|
11
|
+
raise ArgumentError unless operator.is_a?(Hash) && operator['type'] == "SemAct"
|
12
|
+
raise ArgumentError, "missing name in #{operator.inspect}" unless operator.has_key?('name')
|
13
|
+
code = operator.delete('code')
|
14
|
+
operator['code'] = code if code # Reorders operands appropriately
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Called on entry
|
20
|
+
#
|
21
|
+
# @param [String] code
|
22
|
+
# @param [Array<RDF::Statement>] arcs_in available statements to be matched having `focus` as an object
|
23
|
+
# @param [Array<RDF::Statement>] arcs_out available statements to be matched having `focus` as a subject
|
24
|
+
# @param [Integer] depth for logging
|
25
|
+
# @param [Hash{Symbol => Object}] options
|
26
|
+
# Other, operand-specific options
|
27
|
+
# @return [Boolean] Returning `false` results in {ShEx::NotSatisfied} exception
|
28
|
+
def enter(**options)
|
29
|
+
if implementation = schema.extensions[operands.first.to_s]
|
30
|
+
implementation.enter(code: operands[0], expression: parent, **options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
6
34
|
#
|
7
35
|
# The evaluation semActsSatisfied on a list of SemActs returns success or failure. The evaluation of an individual SemAct is implementation-dependent.
|
8
|
-
#
|
36
|
+
#
|
37
|
+
# In addition to standard arguments `satsisfies` arguments, the current `matched` and `unmatched` statements may be passed. Additionally, all sub-classes of `Operator` have available `parent`, and `schema` accessors, which allows access to the operands of the parent, for example.
|
38
|
+
#
|
39
|
+
# @param [Object] focus (ignored)
|
40
|
+
# @param [Array<RDF::Statement>] matched matched statements
|
41
|
+
# @param [Array<RDF::Statement>] unmatched unmatched statements
|
9
42
|
# @return [Boolean] `true` if satisfied, `false` if it does not apply
|
10
43
|
# @raise [ShEx::NotSatisfied] if not satisfied
|
11
|
-
def satisfies?(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
when 'p' then statements.first.predicate
|
19
|
-
when 'o' then statements.first.object
|
20
|
-
else statements.first.to_sxp
|
21
|
-
end.to_s
|
22
|
-
else
|
23
|
-
statements.empty? ? 'no statement' : statements.first.to_sxp
|
44
|
+
def satisfies?(focus, matched: [], unmatched: [], depth: 0)
|
45
|
+
if implementation = schema.extensions[operands.first.to_s]
|
46
|
+
if matched.empty?
|
47
|
+
implementation.visit(code: operands[1],
|
48
|
+
expression: parent,
|
49
|
+
depth: depth) ||
|
50
|
+
not_satisfied("SemAct failed", unmatched: unmatched)
|
24
51
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
52
|
+
matched.all? do |statement|
|
53
|
+
implementation.visit(code: operands[1],
|
54
|
+
matched: statement,
|
55
|
+
expression: parent,
|
56
|
+
depth: depth)
|
57
|
+
end || not_satisfied("SemAct failed", matched: matched, unmatched: unmatched)
|
29
58
|
else
|
30
|
-
status("unknown SemAct name #{operands.first}") {"expression: #{self.to_sxp}"}
|
59
|
+
status("unknown SemAct name #{operands.first}", depth: depth) {"expression: #{self.to_sxp}"}
|
31
60
|
false
|
32
61
|
end
|
33
62
|
end
|
34
63
|
|
64
|
+
##
|
65
|
+
# Called on exit from containing {ShEx::TripleExpression}
|
66
|
+
#
|
67
|
+
# @param [String] code
|
68
|
+
# @param [Array<RDF::Statement>] matched statements matched by this expression
|
69
|
+
# @param [Array<RDF::Statement>] unmatched statements considered, but not matched by this expression
|
70
|
+
# @param [ShEx::Algebra::TripleExpression] expression containing this semantic act
|
71
|
+
# @param [Integer] depth for logging
|
72
|
+
# @param [Hash{Symbol => Object}] options
|
73
|
+
# Other, operand-specific options
|
74
|
+
# @return [void]
|
75
|
+
def exit(code: nil, matched: [], unmatched: [], depth: 0, **options)
|
76
|
+
if implementation = schema.extensions[operands.first.to_s]
|
77
|
+
implementation.exit(code: operands[1],
|
78
|
+
matched: matched,
|
79
|
+
unmatched: unmatched,
|
80
|
+
expresssion: parent,
|
81
|
+
depth: depth)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
35
85
|
# Does This operator is SemAct
|
36
86
|
def semact?; true; end
|
37
87
|
end
|
data/lib/shex/algebra/shape.rb
CHANGED
@@ -19,13 +19,21 @@ module ShEx::Algebra
|
|
19
19
|
# @return [Array<RDF::Statement>]
|
20
20
|
attr_accessor :unmatchables
|
21
21
|
|
22
|
+
##
|
23
|
+
# Creates an operator instance from a parsed ShExJ representation
|
24
|
+
# @param (see Operator#from_shexj)
|
25
|
+
# @return [Operator]
|
26
|
+
def self.from_shexj(operator, options = {})
|
27
|
+
raise ArgumentError unless operator.is_a?(Hash) && operator['type'] == "Shape"
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
22
31
|
# The `satisfies` semantics for a `Shape` depend on a matches function defined below. For a node `n`, shape `S`, graph `G`, and shapeMap `m`, `satisfies(n, S, G, m)`.
|
23
32
|
# @param (see Satisfiable#satisfies?)
|
24
33
|
# @return (see Satisfiable#satisfies?)
|
25
34
|
# @raise (see Satisfiable#satisfies?)
|
26
|
-
def satisfies?(focus)
|
27
|
-
expression =
|
28
|
-
|
35
|
+
def satisfies?(focus, depth: 0)
|
36
|
+
expression = self.expression
|
29
37
|
# neigh(G, n) is the neighbourhood of the node n in the graph G.
|
30
38
|
#
|
31
39
|
# neigh(G, n) = arcsOut(G, n) ∪ arcsIn(G, n)
|
@@ -34,8 +42,8 @@ module ShEx::Algebra
|
|
34
42
|
neigh = (arcs_in + arcs_out).uniq
|
35
43
|
|
36
44
|
# `matched` is the subset of statements which match `expression`.
|
37
|
-
status("arcsIn: #{arcs_in.count}, arcsOut: #{arcs_out.count}")
|
38
|
-
matched_expression = expression.matches(
|
45
|
+
status("arcsIn: #{arcs_in.count}, arcsOut: #{arcs_out.count}", depth: depth)
|
46
|
+
matched_expression = expression.matches(arcs_in, arcs_out, depth: depth + 1) if expression
|
39
47
|
matched = Array(matched_expression && matched_expression.matched)
|
40
48
|
|
41
49
|
# `remainder` is the set of unmatched statements
|
@@ -55,7 +63,7 @@ module ShEx::Algebra
|
|
55
63
|
unmatched = matchables.select do |statement|
|
56
64
|
expression.triple_constraints.any? do |expr|
|
57
65
|
begin
|
58
|
-
statement.predicate == expr.predicate && expr.matches([statement])
|
66
|
+
statement.predicate == expr.predicate && expr.matches([], [statement], depth: depth + 1)
|
59
67
|
rescue ShEx::NotMatched
|
60
68
|
false # Expected not to match
|
61
69
|
end
|
@@ -65,7 +73,8 @@ module ShEx::Algebra
|
|
65
73
|
not_satisfied "Statements remain matching TripleConstraints",
|
66
74
|
matched: matched,
|
67
75
|
unmatched: unmatched,
|
68
|
-
satisfied: expression
|
76
|
+
satisfied: expression,
|
77
|
+
depth: depth
|
69
78
|
end
|
70
79
|
|
71
80
|
# There is no triple in matchables whose predicate does not appear in extra.
|
@@ -74,30 +83,30 @@ module ShEx::Algebra
|
|
74
83
|
not_satisfied "Statements remains with predicate #{unmatched.map(&:predicate).compact.join(',')} not in extra",
|
75
84
|
matched: matched,
|
76
85
|
unmatched: unmatched,
|
77
|
-
satisfied: expression
|
86
|
+
satisfied: expression,
|
87
|
+
depth: depth
|
78
88
|
end
|
79
89
|
|
80
90
|
# closed is false or unmatchables is empty.
|
81
|
-
not_satisfied "Unmatchables remain on a closed shape" unless !closed? || unmatchables.empty?
|
91
|
+
not_satisfied "Unmatchables remain on a closed shape", depth: depth unless !closed? || unmatchables.empty?
|
82
92
|
|
83
93
|
# Presumably, to be satisfied, there must be some triples in matches
|
84
|
-
|
85
|
-
|
86
|
-
# FIXME: what triples to run against satisfies?
|
87
|
-
op.satisfies?(matched)
|
94
|
+
semantic_actions.each do |op|
|
95
|
+
op.satisfies?(matched, matched: matched, depth: depth + 1)
|
88
96
|
end unless matched.empty?
|
89
97
|
|
90
98
|
# FIXME: also record matchables, outs and others?
|
91
|
-
satisfy matched: matched
|
99
|
+
satisfy focus: focus, matched: matched, depth: depth
|
92
100
|
rescue ShEx::NotMatched => e
|
93
|
-
not_satisfied e.message, unsatisfied: e.expression
|
101
|
+
not_satisfied e.message, focus: focus, unsatisfied: e.expression, depth: depth
|
94
102
|
end
|
95
103
|
|
104
|
+
|
96
105
|
##
|
97
|
-
#
|
98
|
-
# @return [
|
99
|
-
def
|
100
|
-
operands.
|
106
|
+
# The optional TripleExpression for this Shape.
|
107
|
+
# @return [TripleExpression]
|
108
|
+
def expression
|
109
|
+
operands.detect {|op| op.is_a?(TripleExpression)}
|
101
110
|
end
|
102
111
|
|
103
112
|
private
|