shex 0.4.0 → 0.5.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 +8 -8
- data/VERSION +1 -1
- data/etc/doap.ttl +1 -1
- data/lib/shex.rb +11 -11
- data/lib/shex/algebra.rb +43 -33
- data/lib/shex/algebra/node_constraint.rb +12 -3
- data/lib/shex/algebra/not.rb +1 -1
- data/lib/shex/algebra/operator.rb +20 -13
- data/lib/shex/algebra/schema.rb +106 -33
- data/lib/shex/algebra/shape_expression.rb +1 -1
- data/lib/shex/algebra/stem.rb +46 -2
- data/lib/shex/algebra/stem_range.rb +70 -2
- data/lib/shex/extensions/extension.rb +2 -2
- data/lib/shex/meta.rb +4282 -2334
- data/lib/shex/parser.rb +223 -72
- data/lib/shex/shex_context.rb +67 -68
- data/lib/shex/terminals.rb +36 -21
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d66489982d96aa02cd6be18354daaa81934af243
|
4
|
+
data.tar.gz: 7385a3e9933b1c81e09b7d0a126a1760de9aa4c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d060ca866830927b3342352249337b6c06b25060677a8fe46e3a017d9c7a269749e5e1e792928eb6c4324bbaffb3bab91da1fb560d5e97ef33df57847df281e7
|
7
|
+
data.tar.gz: 7bf7a3fb6bd9d121a00ba837a5b0725cb5b2732d04dc8c959a8ee37ff3a311340dd9ebdf4496254dc450c39ee139e92800773990a133d922ffb98e5e7f602160
|
data/README.md
CHANGED
@@ -38,7 +38,7 @@ The ShEx gem implements a [ShEx][ShExSpec] Shape Expression engine.
|
|
38
38
|
(doap:name;doap:description|dc:title;dc:description)+;
|
39
39
|
doap:category*;
|
40
40
|
doap:developer IRI;
|
41
|
-
doap:implements [<
|
41
|
+
doap:implements [<http://shex.io/shex-semantics/>]
|
42
42
|
}
|
43
43
|
)
|
44
44
|
graph = RDF::Graph.load("etc/doap.ttl")
|
@@ -109,26 +109,26 @@ The ShEx gem implements a [ShEx][ShExSpec] Shape Expression engine.
|
|
109
109
|
]
|
110
110
|
}
|
111
111
|
],
|
112
|
-
"min": 1, "max":
|
112
|
+
"min": 1, "max": -1
|
113
113
|
},
|
114
114
|
{
|
115
115
|
"type": "TripleConstraint",
|
116
116
|
"predicate": "http://usefulinc.com/ns/doap#category",
|
117
117
|
"valueExpr": {"type": "NodeConstraint", "nodeKind": "iri"},
|
118
|
-
"min": 0, "max":
|
118
|
+
"min": 0, "max": -1
|
119
119
|
},
|
120
120
|
{
|
121
121
|
"type": "TripleConstraint",
|
122
122
|
"predicate": "http://usefulinc.com/ns/doap#developer",
|
123
123
|
"valueExpr": {"type": "NodeConstraint", "nodeKind": "iri"},
|
124
|
-
"min": 1, "max":
|
124
|
+
"min": 1, "max": -1
|
125
125
|
},
|
126
126
|
{
|
127
127
|
"type": "TripleConstraint",
|
128
128
|
"predicate": "http://usefulinc.com/ns/doap#implements",
|
129
129
|
"valueExpr": {
|
130
130
|
"type": "NodeConstraint",
|
131
|
-
"values": ["
|
131
|
+
"values": ["http://shex.io/shex-semantics/"]
|
132
132
|
}
|
133
133
|
}
|
134
134
|
]
|
@@ -143,7 +143,7 @@ The ShEx gem implements a [ShEx][ShExSpec] Shape Expression engine.
|
|
143
143
|
# => true
|
144
144
|
|
145
145
|
## Extensions
|
146
|
-
ShEx has an extension mechanism using [Semantic Actions](
|
146
|
+
ShEx has an extension mechanism using [Semantic Actions](http://shex.io/shex-semantics/#semantic-actions). Extensions may be implemented in Ruby ShEx by sub-classing {ShEx::Extension} and implementing {ShEx::Extension#visit} and possibly {ShEx::Extension#initialize}, {ShEx::Extension#enter}, {ShEx::Extension#exit}, and {ShEx::Extension#close}. The `#visit` method will be called as part of the `#satisfies?` operation.
|
147
147
|
|
148
148
|
require 'shex'
|
149
149
|
class ShEx::Test < ShEx::Extension("http://shex.io/extensions/Test/")
|
@@ -179,7 +179,7 @@ The result of parsing either ShExC or ShExJ is the creation of a set of executab
|
|
179
179
|
## Dependencies
|
180
180
|
|
181
181
|
* [Ruby](http://ruby-lang.org/) (>= 2.2.2)
|
182
|
-
* [RDF.rb](http://rubygems.org/gems/rdf) (
|
182
|
+
* [RDF.rb](http://rubygems.org/gems/rdf) (~> 2.2)
|
183
183
|
|
184
184
|
## Installation
|
185
185
|
|
@@ -237,7 +237,7 @@ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange develo
|
|
237
237
|
This is free and unencumbered public domain software. For more information,
|
238
238
|
see <http://unlicense.org/> or the accompanying {file:LICENSE} file.
|
239
239
|
|
240
|
-
[ShExSpec]:
|
240
|
+
[ShExSpec]: http://shex.io/shex-semantics/
|
241
241
|
[RDF]: http://www.w3.org/RDF/
|
242
242
|
[RDF.rb]: http://rubydoc.info/github/ruby-rdf/rdf
|
243
243
|
[EBNF]: http://rubygems.org/gems/ebnf
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/etc/doap.ttl
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
doap:description "ShEx is an Shape Expression engine for the RDF.rb library suite."@en ;
|
16
16
|
doap:created "2016-12-09"^^xsd:date ;
|
17
17
|
doap:programming-language "Ruby" ;
|
18
|
-
doap:implements <
|
18
|
+
doap:implements <http://shex.io/shex-semantics/> ;
|
19
19
|
doap:category <http://dbpedia.org/resource/Resource_Description_Framework>,
|
20
20
|
<http://dbpedia.org/resource/Ruby_(programming_language)> ;
|
21
21
|
doap:download-page <http://rubygems.org/gems/shex> ;
|
data/lib/shex.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
##
|
2
2
|
# A ShEx runtime for RDF.rb.
|
3
3
|
#
|
4
|
-
# @see
|
4
|
+
# @see http://shex.io/shex-semantics/#shexc
|
5
5
|
module ShEx
|
6
6
|
autoload :Algebra, 'shex/algebra'
|
7
7
|
autoload :Meta, 'shex/meta'
|
@@ -11,7 +11,7 @@ module ShEx
|
|
11
11
|
autoload :VERSION, 'shex/version'
|
12
12
|
|
13
13
|
# Location of the ShEx JSON-LD context
|
14
|
-
CONTEXT = "
|
14
|
+
CONTEXT = "http://www.w3.org/ns/shex.jsonld"
|
15
15
|
|
16
16
|
# Extensions defined in this gem
|
17
17
|
EXTENSIONS = %w{test}
|
@@ -71,11 +71,11 @@ module ShEx
|
|
71
71
|
# @param (see ShEx::Algebra::Schema#execute)
|
72
72
|
# @return (see ShEx::Algebra::Schema#execute)
|
73
73
|
# @raise (see ShEx::Algebra::Schema#execute)
|
74
|
-
def self.execute(expression, queryable,
|
74
|
+
def self.execute(expression, queryable, map, format: 'shexc', **options)
|
75
75
|
shex = self.parse(expression, options.merge(format: format))
|
76
76
|
queryable = queryable || RDF::Graph.new
|
77
77
|
|
78
|
-
shex.execute(
|
78
|
+
shex.execute(queryable, map, options)
|
79
79
|
end
|
80
80
|
|
81
81
|
##
|
@@ -89,11 +89,11 @@ module ShEx
|
|
89
89
|
# @param (see ShEx::Algebra::Schema#satisfies?)
|
90
90
|
# @return (see ShEx::Algebra::Schema#satisfies?)
|
91
91
|
# @raise (see ShEx::Algebra::Schema#satisfies?)
|
92
|
-
def self.satisfies?(expression, queryable,
|
92
|
+
def self.satisfies?(expression, queryable, map, format: 'shexc', **options)
|
93
93
|
shex = self.parse(expression, options.merge(format: format))
|
94
94
|
queryable = queryable || RDF::Graph.new
|
95
95
|
|
96
|
-
shex.satisfies?(
|
96
|
+
shex.satisfies?(queryable, map, options)
|
97
97
|
end
|
98
98
|
|
99
99
|
##
|
@@ -129,14 +129,14 @@ module ShEx
|
|
129
129
|
class NotSatisfied < Error
|
130
130
|
##
|
131
131
|
# The expression which was not satified
|
132
|
-
# @return [ShEx::
|
132
|
+
# @return [ShEx::Algebra::ShapeExpression]
|
133
133
|
attr_reader :expression
|
134
134
|
|
135
135
|
##
|
136
136
|
# Initializes a new parser error instance.
|
137
137
|
#
|
138
|
-
# @param [String, #to_s]
|
139
|
-
# @param [
|
138
|
+
# @param [String, #to_s] message
|
139
|
+
# @param [ShEx::Algebra::ShapeExpression] expression
|
140
140
|
def initialize(message, expression: self)
|
141
141
|
@expression = expression
|
142
142
|
super(message.to_s)
|
@@ -157,8 +157,8 @@ module ShEx
|
|
157
157
|
##
|
158
158
|
# Initializes a new parser error instance.
|
159
159
|
#
|
160
|
-
# @param [String, #to_s]
|
161
|
-
# @param [
|
160
|
+
# @param [String, #to_s] message
|
161
|
+
# @param [ShEx::Algebra::TripleExpression] expression
|
162
162
|
def initialize(message, expression: self)
|
163
163
|
@expression = expression
|
164
164
|
super(message.to_s)
|
data/lib/shex/algebra.rb
CHANGED
@@ -7,25 +7,31 @@ module ShEx
|
|
7
7
|
#
|
8
8
|
# @author [Gregg Kellogg](http://greggkellogg.net/)
|
9
9
|
module Algebra
|
10
|
-
autoload :And,
|
11
|
-
autoload :Annotation,
|
12
|
-
autoload :EachOf,
|
13
|
-
autoload :External,
|
14
|
-
autoload :
|
15
|
-
autoload :
|
16
|
-
autoload :
|
17
|
-
autoload :
|
18
|
-
autoload :
|
19
|
-
autoload :
|
20
|
-
autoload :
|
21
|
-
autoload :
|
22
|
-
autoload :
|
23
|
-
autoload :
|
24
|
-
autoload :
|
25
|
-
autoload :
|
10
|
+
autoload :And, 'shex/algebra/and'
|
11
|
+
autoload :Annotation, 'shex/algebra/annotation'
|
12
|
+
autoload :EachOf, 'shex/algebra/each_of'
|
13
|
+
autoload :External, 'shex/algebra/external'
|
14
|
+
autoload :IriStem, 'shex/algebra/stem'
|
15
|
+
autoload :IriStemRange, 'shex/algebra/stem_range'
|
16
|
+
autoload :LanguageStem, 'shex/algebra/stem'
|
17
|
+
autoload :LanguageStemRange,'shex/algebra/stem_range'
|
18
|
+
autoload :LiteralStem, 'shex/algebra/stem'
|
19
|
+
autoload :LiteralStemRange, 'shex/algebra/stem_range'
|
20
|
+
autoload :NodeConstraint, 'shex/algebra/node_constraint'
|
21
|
+
autoload :Not, 'shex/algebra/not'
|
22
|
+
autoload :OneOf, 'shex/algebra/one_of'
|
23
|
+
autoload :Operator, 'shex/algebra/operator'
|
24
|
+
autoload :Or, 'shex/algebra/or'
|
25
|
+
autoload :Schema, 'shex/algebra/schema'
|
26
|
+
autoload :SemAct, 'shex/algebra/semact'
|
27
|
+
autoload :Shape, 'shex/algebra/shape'
|
28
|
+
autoload :ShapeExpression, 'shex/algebra/shape_expression'
|
29
|
+
autoload :Start, 'shex/algebra/start'
|
30
|
+
autoload :Stem, 'shex/algebra/stem'
|
31
|
+
autoload :StemRange, 'shex/algebra/stem_range'
|
26
32
|
autoload :TripleConstraint, 'shex/algebra/triple_constraint'
|
27
33
|
autoload :TripleExpression, 'shex/algebra/triple_expression'
|
28
|
-
autoload :Value,
|
34
|
+
autoload :Value, 'shex/algebra/value'
|
29
35
|
|
30
36
|
|
31
37
|
##
|
@@ -46,22 +52,26 @@ module ShEx
|
|
46
52
|
def self.from_shexj(operator, options = {})
|
47
53
|
raise ArgumentError unless operator.is_a?(Hash)
|
48
54
|
klass = case operator['type']
|
49
|
-
when 'Annotation'
|
50
|
-
when 'EachOf'
|
51
|
-
when '
|
52
|
-
when '
|
53
|
-
when '
|
54
|
-
when '
|
55
|
-
when '
|
56
|
-
when '
|
57
|
-
when '
|
58
|
-
when '
|
59
|
-
when '
|
60
|
-
when '
|
61
|
-
when '
|
62
|
-
when '
|
63
|
-
when '
|
64
|
-
|
55
|
+
when 'Annotation' then Annotation
|
56
|
+
when 'EachOf' then EachOf
|
57
|
+
when 'IriStem' then IriStem
|
58
|
+
when 'IriStemRange' then IriStemRange
|
59
|
+
when 'LanguageStem' then LanguageStem
|
60
|
+
when 'LanguageStemRange' then LanguageStemRange
|
61
|
+
when 'LiteralStem' then LiteralStem
|
62
|
+
when 'LiteralStemRange' then LiteralStemRange
|
63
|
+
when 'NodeConstraint' then NodeConstraint
|
64
|
+
when 'OneOf' then OneOf
|
65
|
+
when 'Schema' then Schema
|
66
|
+
when 'SemAct' then SemAct
|
67
|
+
when 'Shape' then Shape
|
68
|
+
when 'ShapeAnd' then And
|
69
|
+
when 'ShapeExternal' then External
|
70
|
+
when 'ShapeNot' then Not
|
71
|
+
when 'ShapeOr' then Or
|
72
|
+
when 'TripleConstraint' then TripleConstraint
|
73
|
+
when 'Wildcard' then StemRange
|
74
|
+
else raise ArgumentError, "unknown type #{operator['type'].inspect}"
|
65
75
|
end
|
66
76
|
|
67
77
|
klass.from_shexj(operator, options)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
module ShEx::Algebra
|
2
3
|
##
|
3
4
|
class NodeConstraint < Operator
|
@@ -58,7 +59,7 @@ module ShEx::Algebra
|
|
58
59
|
return true unless dt
|
59
60
|
|
60
61
|
not_satisfied "Node was #{value.inspect}, expected datatype #{dt}", depth: depth unless
|
61
|
-
value.is_a?(RDF::Literal) && value.datatype == RDF::URI(dt)
|
62
|
+
value.is_a?(RDF::Literal) && value.datatype == RDF::URI(dt) && value.valid?
|
62
63
|
status "right datatype: #{value}: #{dt}", depth: depth
|
63
64
|
true
|
64
65
|
end
|
@@ -68,11 +69,18 @@ module ShEx::Algebra
|
|
68
69
|
# Checks all length/minlength/maxlength/pattern facets against the string representation of the value.
|
69
70
|
# @return [Boolean] `true` if satisfied, `false` if it does not apply
|
70
71
|
# @raise [ShEx::NotSatisfied] if not satisfied
|
72
|
+
# @todo using the XPath regexp engine supports additional flags "s" and "q"
|
71
73
|
def satisfies_string_facet?(value, depth: 0)
|
72
74
|
length = op_fetch(:length)
|
73
75
|
minlength = op_fetch(:minlength)
|
74
76
|
maxlength = op_fetch(:maxlength)
|
75
|
-
|
77
|
+
pat = (operands.detect {|op| op.is_a?(Array) && op[0] == :pattern} || [])
|
78
|
+
pattern = pat[1]
|
79
|
+
|
80
|
+
flags = 0
|
81
|
+
flags |= Regexp::EXTENDED if pat[2].to_s.include?("x")
|
82
|
+
flags |= Regexp::IGNORECASE if pat[2].to_s.include?("i")
|
83
|
+
flags |= Regexp::MULTILINE if pat[2].to_s.include?("m")
|
76
84
|
|
77
85
|
return true if (length || minlength || maxlength || pattern).nil?
|
78
86
|
|
@@ -80,6 +88,7 @@ module ShEx::Algebra
|
|
80
88
|
when RDF::Node then value.id
|
81
89
|
else value.to_s
|
82
90
|
end
|
91
|
+
|
83
92
|
not_satisfied "Node #{v_s.inspect} length not #{length}", depth: depth if
|
84
93
|
length && v_s.length != length.to_i
|
85
94
|
not_satisfied"Node #{v_s.inspect} length < #{minlength}", depth: depth if
|
@@ -87,7 +96,7 @@ module ShEx::Algebra
|
|
87
96
|
not_satisfied "Node #{v_s.inspect} length > #{maxlength}", depth: depth if
|
88
97
|
maxlength && v_s.length > maxlength.to_i
|
89
98
|
not_satisfied "Node #{v_s.inspect} does not match #{pattern}", depth: depth if
|
90
|
-
pattern && !Regexp.new(pattern).match(v_s)
|
99
|
+
pattern && !Regexp.new(pattern, flags).match(v_s)
|
91
100
|
status "right string facet: #{value}", depth: depth
|
92
101
|
true
|
93
102
|
end
|
data/lib/shex/algebra/not.rb
CHANGED
@@ -19,7 +19,7 @@ module ShEx::Algebra
|
|
19
19
|
# @param (see ShapeExpression#satisfies?)
|
20
20
|
# @return (see ShapeExpression#satisfies?)
|
21
21
|
# @raise (see ShapeExpression#satisfies?)
|
22
|
-
# @see [
|
22
|
+
# @see [http://shex.io/shex-semantics/#shape-expression-semantics]
|
23
23
|
def satisfies?(focus, depth: 0)
|
24
24
|
status ""
|
25
25
|
op = expressions.last
|
@@ -267,12 +267,16 @@ module ShEx::Algebra
|
|
267
267
|
|
268
268
|
operator.each do |k, v|
|
269
269
|
case k
|
270
|
-
when /length|
|
270
|
+
when /length|clusive|digits/ then operands << [k.to_sym, RDF::Literal(v)]
|
271
271
|
when 'id' then id = iri(v, options)
|
272
|
-
when '
|
272
|
+
when 'flags' then ; # consumed in pattern below
|
273
|
+
when 'min', 'max' then operands << [k.to_sym, (v == -1 ? '*' : v)]
|
273
274
|
when 'inverse', 'closed' then operands << k.to_sym
|
274
275
|
when 'nodeKind' then operands << v.to_sym
|
275
276
|
when 'object' then operands << value(v, options)
|
277
|
+
when 'pattern'
|
278
|
+
# Include flags as well
|
279
|
+
operands << [:pattern, RDF::Literal(v), operator['flags']].compact
|
276
280
|
when 'start'
|
277
281
|
if v.is_a?(String)
|
278
282
|
operands << Start.new(iri(v, options))
|
@@ -291,7 +295,7 @@ module ShEx::Algebra
|
|
291
295
|
end
|
292
296
|
when 'stem', 'name'
|
293
297
|
# Value may be :wildcard for stem
|
294
|
-
operands << (v.is_a?(Symbol) ? v :
|
298
|
+
operands << (v.is_a?(Symbol) ? v : value(v, options))
|
295
299
|
when 'predicate' then operands << [:predicate, iri(v, options)]
|
296
300
|
when 'extra', 'datatype'
|
297
301
|
v = [v] unless v.is_a?(Array)
|
@@ -299,7 +303,7 @@ module ShEx::Algebra
|
|
299
303
|
when 'exclusions'
|
300
304
|
v = [v] unless v.is_a?(Array)
|
301
305
|
operands << v.map do |op|
|
302
|
-
op.is_a?(Hash) ?
|
306
|
+
op.is_a?(Hash) && op.has_key?('type') ?
|
303
307
|
ShEx::Algebra.from_shexj(op, options) :
|
304
308
|
value(op, options)
|
305
309
|
end.unshift(:exclusions)
|
@@ -345,11 +349,12 @@ module ShEx::Algebra
|
|
345
349
|
when Array
|
346
350
|
# First element should be a symbol
|
347
351
|
case sym = op.first
|
348
|
-
when :datatype
|
349
|
-
:pattern then obj[op.first.to_s] = op.last.to_s
|
352
|
+
when :datatype then obj['datatype'] = op.last.to_s
|
350
353
|
when :exclusions then obj['exclusions'] = Array(op[1..-1]).map {|v| serialize_value(v)}
|
351
354
|
when :extra then (obj['extra'] ||= []).concat Array(op[1..-1]).map(&:to_s)
|
352
|
-
|
355
|
+
when :pattern
|
356
|
+
obj['pattern'] = op[1]
|
357
|
+
obj['flags'] = op[2] if op[2]
|
353
358
|
when :shapes then obj['shapes'] = Array(op[1..-1]).map {|v| v.to_h}
|
354
359
|
when :minlength,
|
355
360
|
:maxlength,
|
@@ -360,7 +365,7 @@ module ShEx::Algebra
|
|
360
365
|
:maxexclusive,
|
361
366
|
:totaldigits,
|
362
367
|
:fractiondigits then obj[op.first.to_s] = op.last.object
|
363
|
-
when :min, :max then obj[op.first.to_s] = op.last
|
368
|
+
when :min, :max then obj[op.first.to_s] = op.last == '*' ? -1 : op.last
|
364
369
|
when :predicate then obj[op.first.to_s] = op.last.to_s
|
365
370
|
when :base, :prefix
|
366
371
|
# Ignore base and prefix
|
@@ -378,7 +383,7 @@ module ShEx::Algebra
|
|
378
383
|
end
|
379
384
|
when RDF::Value
|
380
385
|
case self
|
381
|
-
when Stem, StemRange then obj['stem'] = op
|
386
|
+
when Stem, StemRange then obj['stem'] = serialize_value(op)
|
382
387
|
when SemAct then obj[op.is_a?(RDF::URI) ? 'name' : 'code'] = op.to_s
|
383
388
|
when TripleConstraint then obj['valueExpr'] = op.to_s
|
384
389
|
when Shape then obj['expression'] = op.to_s
|
@@ -517,10 +522,10 @@ module ShEx::Algebra
|
|
517
522
|
case value
|
518
523
|
when Hash
|
519
524
|
# Either a value object or a node reference
|
520
|
-
if value['uri']
|
521
|
-
iri(value['uri'], options)
|
522
|
-
elsif value['value']
|
523
|
-
RDF::Literal(value['value'], datatype: value['type'], language: value['language'])
|
525
|
+
if value['uri'] || value['@id']
|
526
|
+
iri(value['uri'] || value['@id'], options)
|
527
|
+
elsif value['value'] || value['@value']
|
528
|
+
RDF::Literal(value['value'] || value['@value'], datatype: value['type'] || value['@type'], language: value['language'] || value['@language'])
|
524
529
|
else
|
525
530
|
ShEx::Algebra.from_shexj(value, options)
|
526
531
|
end
|
@@ -541,6 +546,8 @@ module ShEx::Algebra
|
|
541
546
|
merge(value.has_language? ? {'language' => value.language.to_s} : {})
|
542
547
|
when RDF::Resource
|
543
548
|
value.to_s
|
549
|
+
when String
|
550
|
+
{'value' => value}
|
544
551
|
else value.to_h
|
545
552
|
end
|
546
553
|
end
|
data/lib/shex/algebra/schema.rb
CHANGED
@@ -36,19 +36,22 @@ module ShEx::Algebra
|
|
36
36
|
##
|
37
37
|
# Match on schema. Finds appropriate shape for node, and matches that shape.
|
38
38
|
#
|
39
|
-
# @param [RDF::Term] focus
|
40
39
|
# @param [RDF::Queryable] graph
|
41
|
-
# @param [Hash{RDF::
|
40
|
+
# @param [Hash{RDF::Term => <RDF::Resource>}, Array<Array(RDF::Term, RDF::Resource)>] map
|
41
|
+
# A set of (`term`, `resource`) pairs where `term` is a node within `graph`, and `resource` identifies a shape
|
42
|
+
# @param [Array<RDF::Term>] *focus
|
43
|
+
# One or more nodes within `graph` for which to run the start expression.
|
42
44
|
# @param [Array<Schema, String>] shapeExterns ([])
|
43
45
|
# One or more schemas, or paths to ShEx schema resources used for finding external shapes.
|
44
|
-
# @return [
|
46
|
+
# @return [Hash{RDF::Term => Array<ShapeResult>}] Returns _ShapeResults_, a hash of graph nodes to the results of their associated shapes
|
45
47
|
# @param [Hash{Symbol => Object}] options
|
46
48
|
# @option options [String] :base_uri (for resolving focus)
|
47
|
-
# @raise [ShEx::NotSatisfied] along with
|
48
|
-
def execute(
|
49
|
-
@graph, @shapes_entered = graph, {}
|
49
|
+
# @raise [ShEx::NotSatisfied] along with individual shape results
|
50
|
+
def execute(graph, map, focus: [], shapeExterns: [], depth: 0, **options)
|
51
|
+
@graph, @shapes_entered, results = graph, {}, {}
|
50
52
|
@external_schemas = shapeExterns
|
51
|
-
|
53
|
+
@extensions = {}
|
54
|
+
focus = Array(focus).map {|f| value(f, options)}
|
52
55
|
|
53
56
|
logger = options[:logger] || @options[:logger]
|
54
57
|
each_descendant do |op|
|
@@ -57,7 +60,6 @@ module ShEx::Algebra
|
|
57
60
|
end
|
58
61
|
|
59
62
|
# Initialize Extensions
|
60
|
-
@extensions = {}
|
61
63
|
each_descendant do |op|
|
62
64
|
next unless op.is_a?(SemAct)
|
63
65
|
name = op.operands.first.to_s
|
@@ -67,36 +69,74 @@ module ShEx::Algebra
|
|
67
69
|
end
|
68
70
|
|
69
71
|
# If `n` is a Blank Node, we won't find it through normal matching, find an equivalent node in the graph having the same id
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
72
|
+
@map = case map
|
73
|
+
when Hash
|
74
|
+
map.inject({}) do |memo, (node, shapes)|
|
75
|
+
gnode = graph.enum_term.detect {|t| t.node? && t.id == node.id} if node.is_a?(RDF::Node)
|
76
|
+
node = gnode if gnode
|
77
|
+
memo.merge(node => Array(shapes))
|
78
|
+
end
|
79
|
+
when Array
|
80
|
+
map.inject({}) do |memo, (node, shape)|
|
81
|
+
gnode = graph.enum_term.detect {|t| t.node? && t.id == node.id} if node.is_a?(RDF::Node)
|
82
|
+
node = gnode if gnode
|
83
|
+
(memo[node] ||= []).concat(Array(shape))
|
84
|
+
memo
|
85
|
+
end
|
86
|
+
when nil then {}
|
87
|
+
else
|
88
|
+
structure_error "Unrecognized shape map: #{map.inspect}"
|
89
|
+
end
|
75
90
|
|
76
91
|
# First, evaluate semantic acts
|
77
92
|
semantic_actions.all? do |op|
|
78
93
|
op.satisfies?([], depth: depth + 1)
|
79
94
|
end
|
80
95
|
|
81
|
-
# Keep a new Schema, specifically for recording actions
|
82
|
-
satisfied_schema = Schema.new
|
83
96
|
# Next run any start expression
|
84
|
-
if
|
85
|
-
|
97
|
+
if !focus.empty?
|
98
|
+
if start
|
99
|
+
focus.each do |node|
|
100
|
+
node = graph.enum_term.detect {|t| t.node? && t.id == node.id} if node.is_a?(RDF::Node)
|
101
|
+
sr = ShapeResult.new(RDF::URI("http://www.w3.org/ns/shex#Start"))
|
102
|
+
(results[node] ||= []) << sr
|
103
|
+
begin
|
104
|
+
sr.expression = start.satisfies?(node, depth: depth + 1)
|
105
|
+
sr.result = true
|
106
|
+
rescue ShEx::NotSatisfied => e
|
107
|
+
sr.expression = e.expression
|
108
|
+
sr.result = false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
else
|
112
|
+
structure_error "Focus nodes with no start"
|
113
|
+
end
|
86
114
|
end
|
87
115
|
|
88
|
-
# Add shape result(s)
|
89
|
-
satisfied_shapes = {}
|
90
|
-
satisfied_schema.operands << [:shapes, satisfied_shapes] unless shapes.empty?
|
91
|
-
|
92
116
|
# Match against all shapes associated with the ids for focus
|
93
|
-
|
94
|
-
|
95
|
-
|
117
|
+
@map.each do |node, shapes|
|
118
|
+
results[node] ||= []
|
119
|
+
shapes.each do |id|
|
120
|
+
enter_shape(id, node) do |shape|
|
121
|
+
sr = ShapeResult.new(id)
|
122
|
+
results[node] << sr
|
123
|
+
begin
|
124
|
+
sr.expression = shape.satisfies?(node, depth: depth + 1)
|
125
|
+
sr.result = true
|
126
|
+
rescue ShEx::NotSatisfied => e
|
127
|
+
sr.expression = e.expression
|
128
|
+
sr.result = false
|
129
|
+
end
|
130
|
+
end
|
96
131
|
end
|
97
132
|
end
|
98
|
-
|
99
|
-
|
133
|
+
|
134
|
+
if results.values.flatten.all? {|sr| sr.result}
|
135
|
+
status "schema satisfied", depth: depth
|
136
|
+
results
|
137
|
+
else
|
138
|
+
raise ShEx::NotSatisfied.new("Graph does not conform to schema", expression: results)
|
139
|
+
end
|
100
140
|
ensure
|
101
141
|
# Close Semantic Action extensions
|
102
142
|
@extensions.values.each {|ext| ext.close(schema: self, depth: depth, **options)}
|
@@ -105,16 +145,12 @@ module ShEx::Algebra
|
|
105
145
|
##
|
106
146
|
# Match on schema. Finds appropriate shape for node, and matches that shape.
|
107
147
|
#
|
108
|
-
# @param
|
109
|
-
# @param [RDF::Queryable] graph
|
110
|
-
# @param [Hash{RDF::Resource => RDF::Resource}] map
|
111
|
-
# @param [Array<Schema, String>] shapeExterns ([])
|
112
|
-
# One or more schemas, or paths to ShEx schema resources used for finding external shapes.
|
148
|
+
# @param (see ShEx::Algebra::Schema#execute)
|
113
149
|
# @param [Hash{Symbol => Object}] options
|
114
150
|
# @option options [String] :base_uri
|
115
151
|
# @return [Boolean]
|
116
|
-
def satisfies?(
|
117
|
-
execute(
|
152
|
+
def satisfies?(graph, map, **options)
|
153
|
+
execute(graph, map, **options)
|
118
154
|
rescue ShEx::NotSatisfied
|
119
155
|
false
|
120
156
|
end
|
@@ -198,4 +234,41 @@ module ShEx::Algebra
|
|
198
234
|
super
|
199
235
|
end
|
200
236
|
end
|
237
|
+
|
238
|
+
# A shape result
|
239
|
+
class ShapeResult
|
240
|
+
# The label of the shape within the schema, or a URI indicating a start shape
|
241
|
+
# @return [RDF::Resource]
|
242
|
+
attr_reader :shape
|
243
|
+
|
244
|
+
# Does the node conform to the shape
|
245
|
+
# @return [Boolean]
|
246
|
+
attr_accessor :result
|
247
|
+
|
248
|
+
# The annotated {Operator} indicating processing results
|
249
|
+
# @return [ShEx::Algebra::Operator]
|
250
|
+
attr_accessor :expression
|
251
|
+
|
252
|
+
# Holds the result of processing a shape
|
253
|
+
# @param [RDF::Resource] label
|
254
|
+
# @return [ShapeResult]
|
255
|
+
def initialize(shape)
|
256
|
+
@shape = shape
|
257
|
+
end
|
258
|
+
|
259
|
+
# The SXP of {#expression}
|
260
|
+
# @return [String]
|
261
|
+
def reason
|
262
|
+
SXP::Generator.string(expression.to_sxp_bin)
|
263
|
+
end
|
264
|
+
|
265
|
+
##
|
266
|
+
# Returns the binary S-Expression (SXP) representation of this result.
|
267
|
+
#
|
268
|
+
# @return [Array]
|
269
|
+
# @see https://en.wikipedia.org/wiki/S-expression
|
270
|
+
def to_sxp_bin
|
271
|
+
[:ShapeResult, shape, result, expression].map(&:to_sxp_bin)
|
272
|
+
end
|
273
|
+
end
|
201
274
|
end
|