rdf-n3 3.0.1 → 3.2.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 +198 -76
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/lib/rdf/n3/algebra/builtin.rb +79 -0
- data/lib/rdf/n3/algebra/formula.rb +446 -0
- data/lib/rdf/n3/algebra/list/append.rb +42 -0
- data/lib/rdf/n3/algebra/list/first.rb +24 -0
- data/lib/rdf/n3/algebra/list/in.rb +48 -0
- data/lib/rdf/n3/algebra/list/iterate.rb +96 -0
- data/lib/rdf/n3/algebra/list/last.rb +24 -0
- data/lib/rdf/n3/algebra/list/length.rb +24 -0
- data/lib/rdf/n3/algebra/list/member.rb +44 -0
- data/lib/rdf/n3/algebra/list_operator.rb +96 -0
- data/lib/rdf/n3/algebra/log/conclusion.rb +65 -0
- data/lib/rdf/n3/algebra/log/conjunction.rb +36 -0
- data/lib/rdf/n3/algebra/log/content.rb +34 -0
- data/lib/rdf/n3/algebra/log/dtlit.rb +41 -0
- data/lib/rdf/n3/algebra/log/equal_to.rb +34 -0
- data/lib/rdf/n3/algebra/log/implies.rb +102 -0
- data/lib/rdf/n3/algebra/log/includes.rb +70 -0
- data/lib/rdf/n3/algebra/log/langlit.rb +41 -0
- data/lib/rdf/n3/algebra/log/n3_string.rb +34 -0
- data/lib/rdf/n3/algebra/log/not_equal_to.rb +23 -0
- data/lib/rdf/n3/algebra/log/not_includes.rb +27 -0
- data/lib/rdf/n3/algebra/log/output_string.rb +40 -0
- data/lib/rdf/n3/algebra/log/parsed_as_n3.rb +36 -0
- data/lib/rdf/n3/algebra/log/semantics.rb +40 -0
- data/lib/rdf/n3/algebra/math/absolute_value.rb +36 -0
- data/lib/rdf/n3/algebra/math/acos.rb +26 -0
- data/lib/rdf/n3/algebra/math/acosh.rb +26 -0
- data/lib/rdf/n3/algebra/math/asin.rb +26 -0
- data/lib/rdf/n3/algebra/math/asinh.rb +26 -0
- data/lib/rdf/n3/algebra/math/atan.rb +26 -0
- data/lib/rdf/n3/algebra/math/atanh.rb +26 -0
- data/lib/rdf/n3/algebra/math/ceiling.rb +28 -0
- data/lib/rdf/n3/algebra/math/cos.rb +40 -0
- data/lib/rdf/n3/algebra/math/cosh.rb +38 -0
- data/lib/rdf/n3/algebra/math/difference.rb +40 -0
- data/lib/rdf/n3/algebra/math/equal_to.rb +54 -0
- data/lib/rdf/n3/algebra/math/exponentiation.rb +35 -0
- data/lib/rdf/n3/algebra/math/floor.rb +28 -0
- data/lib/rdf/n3/algebra/math/greater_than.rb +41 -0
- data/lib/rdf/n3/algebra/math/less_than.rb +41 -0
- data/lib/rdf/n3/algebra/math/negation.rb +38 -0
- data/lib/rdf/n3/algebra/math/not_equal_to.rb +25 -0
- data/lib/rdf/n3/algebra/math/not_greater_than.rb +25 -0
- data/lib/rdf/n3/algebra/math/not_less_than.rb +25 -0
- data/lib/rdf/n3/algebra/math/product.rb +20 -0
- data/lib/rdf/n3/algebra/math/quotient.rb +36 -0
- data/lib/rdf/n3/algebra/math/remainder.rb +35 -0
- data/lib/rdf/n3/algebra/math/rounded.rb +26 -0
- data/lib/rdf/n3/algebra/math/sin.rb +40 -0
- data/lib/rdf/n3/algebra/math/sinh.rb +38 -0
- data/lib/rdf/n3/algebra/math/sum.rb +40 -0
- data/lib/rdf/n3/algebra/math/tan.rb +40 -0
- data/lib/rdf/n3/algebra/math/tanh.rb +38 -0
- data/lib/rdf/n3/algebra/not_implemented.rb +13 -0
- data/lib/rdf/n3/algebra/resource_operator.rb +122 -0
- data/lib/rdf/n3/algebra/str/concatenation.rb +27 -0
- data/lib/rdf/n3/algebra/str/contains.rb +33 -0
- data/lib/rdf/n3/algebra/str/contains_ignoring_case.rb +33 -0
- data/lib/rdf/n3/algebra/str/ends_with.rb +33 -0
- data/lib/rdf/n3/algebra/str/equal_ignoring_case.rb +34 -0
- data/lib/rdf/n3/algebra/str/format.rb +17 -0
- data/lib/rdf/n3/algebra/str/greater_than.rb +38 -0
- data/lib/rdf/n3/algebra/str/less_than.rb +33 -0
- data/lib/rdf/n3/algebra/str/matches.rb +37 -0
- data/lib/rdf/n3/algebra/str/not_equal_ignoring_case.rb +17 -0
- data/lib/rdf/n3/algebra/str/not_greater_than.rb +17 -0
- data/lib/rdf/n3/algebra/str/not_less_than.rb +17 -0
- data/lib/rdf/n3/algebra/str/not_matches.rb +18 -0
- data/lib/rdf/n3/algebra/str/replace.rb +35 -0
- data/lib/rdf/n3/algebra/str/scrape.rb +35 -0
- data/lib/rdf/n3/algebra/str/starts_with.rb +33 -0
- data/lib/rdf/n3/algebra/time/day.rb +35 -0
- data/lib/rdf/n3/algebra/time/day_of_week.rb +27 -0
- data/lib/rdf/n3/algebra/time/gm_time.rb +29 -0
- data/lib/rdf/n3/algebra/time/hour.rb +35 -0
- data/lib/rdf/n3/algebra/time/in_seconds.rb +59 -0
- data/lib/rdf/n3/algebra/time/local_time.rb +29 -0
- data/lib/rdf/n3/algebra/time/minute.rb +35 -0
- data/lib/rdf/n3/algebra/time/month.rb +35 -0
- data/lib/rdf/n3/algebra/time/second.rb +35 -0
- data/lib/rdf/n3/algebra/time/timezone.rb +36 -0
- data/lib/rdf/n3/algebra/time/year.rb +29 -0
- data/lib/rdf/n3/algebra.rb +210 -0
- data/lib/rdf/n3/extensions.rb +221 -0
- data/lib/rdf/n3/format.rb +66 -1
- data/lib/rdf/n3/list.rb +630 -0
- data/lib/rdf/n3/reader.rb +774 -497
- data/lib/rdf/n3/reasoner.rb +282 -0
- data/lib/rdf/n3/refinements.rb +178 -0
- data/lib/rdf/n3/repository.rb +332 -0
- data/lib/rdf/n3/terminals.rb +78 -0
- data/lib/rdf/n3/vocab.rb +36 -3
- data/lib/rdf/n3/writer.rb +461 -250
- data/lib/rdf/n3.rb +11 -8
- metadata +177 -49
- data/AUTHORS +0 -1
- data/History.markdown +0 -99
- data/lib/rdf/n3/patches/array_hacks.rb +0 -53
- data/lib/rdf/n3/reader/meta.rb +0 -641
- data/lib/rdf/n3/reader/parser.rb +0 -237
data/lib/rdf/n3/list.rb
ADDED
@@ -0,0 +1,630 @@
|
|
1
|
+
module RDF::N3
|
2
|
+
##
|
3
|
+
# Sub-class of RDF::List which uses a native representation of values and allows recursive lists.
|
4
|
+
#
|
5
|
+
# Also serves as the vocabulary URI for expanding other methods
|
6
|
+
class List < RDF::List
|
7
|
+
# Allow a list to be treated as a term in a statement.
|
8
|
+
include ::RDF::Term
|
9
|
+
|
10
|
+
URI = RDF::URI("http://www.w3.org/2000/10/swap/list#")
|
11
|
+
|
12
|
+
# Returns a vocubulary term
|
13
|
+
def self.method_missing(property, *args, &block)
|
14
|
+
property = RDF::Vocabulary.camelize(property.to_s)
|
15
|
+
if args.empty? && !to_s.empty?
|
16
|
+
RDF::Vocabulary::Term.intern("#{URI}#{property}", attributes: {})
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Returns the base URI for this vocabulary.
|
24
|
+
#
|
25
|
+
# @return [URI]
|
26
|
+
def self.to_uri
|
27
|
+
URI
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Attempts to create an RDF::N3::List from subject, or returns the node as is, if unable.
|
32
|
+
#
|
33
|
+
# @param [RDF::Resource] subject
|
34
|
+
# @return [RDF::List, RDF::Resource] returns either the original resource, or a list based on that resource
|
35
|
+
def self.try_list(subject, graph)
|
36
|
+
return subject unless subject && (subject.node? || subject.uri? && subject == RDF.nil)
|
37
|
+
ln = RDF::List.new(subject: subject, graph: graph)
|
38
|
+
return subject unless ln.valid?
|
39
|
+
|
40
|
+
# Return a new list, outside of this queryable, with any embedded lists also expanded
|
41
|
+
values = ln.to_a.map {|li| try_list(li, graph)}
|
42
|
+
RDF::N3::List.new(subject: subject, graph: graph, values: values)
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Initializes a newly-constructed list.
|
47
|
+
#
|
48
|
+
# Instantiates a new list based at `subject`, which **must** be an RDF::Node. List may be initialized using passed `values`.
|
49
|
+
#
|
50
|
+
# @example add constructed list to existing graph
|
51
|
+
# l = RDF::N3::List(values: (1, 2, 3))
|
52
|
+
# g = RDF::Graph.new << l
|
53
|
+
# g.count # => l.count
|
54
|
+
#
|
55
|
+
# If values is not provided, but subject and graph are, then will attempt to recursively represent lists.
|
56
|
+
#
|
57
|
+
# @param [RDF::Resource] subject (RDF.nil)
|
58
|
+
# Subject should be an {RDF::Node}, not a {RDF::URI}. A list with an IRI head will not validate, but is commonly used to detect if a list is valid.
|
59
|
+
# @param [RDF::Graph] graph (RDF::Graph.new)
|
60
|
+
# @param [Array<RDF::Term>] values
|
61
|
+
# Any values which are not terms are coerced to `RDF::Literal`.
|
62
|
+
# @yield [list]
|
63
|
+
# @yieldparam [RDF::List] list
|
64
|
+
def initialize(subject: nil, graph: nil, values: nil, &block)
|
65
|
+
@subject = subject || (Array(values).empty? ? RDF.nil : RDF::Node.new)
|
66
|
+
@graph = graph
|
67
|
+
@valid = true
|
68
|
+
|
69
|
+
@values = case
|
70
|
+
when values
|
71
|
+
values.map do |v|
|
72
|
+
# Convert values, as necessary.
|
73
|
+
case v
|
74
|
+
when RDF::Value then v.to_term
|
75
|
+
when Symbol then RDF::Node.intern(v)
|
76
|
+
when Array then RDF::N3::List.new(values: v)
|
77
|
+
when nil then RDF.nil
|
78
|
+
else RDF::Literal.new(v)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
when subject && graph
|
82
|
+
ln = RDF::List.new(subject: subject, graph: graph)
|
83
|
+
@valid = ln.valid?
|
84
|
+
ln.to_a.map {|li| self.class.try_list(li, graph)}
|
85
|
+
else
|
86
|
+
[]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Lists are valid, unless established via RDF::List, in which case they are only valid if the RDF::List is valid.
|
92
|
+
#
|
93
|
+
# @return [Boolean]
|
94
|
+
def valid?; @valid; end
|
95
|
+
|
96
|
+
##
|
97
|
+
# @see RDF::Value#==
|
98
|
+
def ==(other)
|
99
|
+
case other
|
100
|
+
when Array, RDF::List then to_a == other.to_a
|
101
|
+
else
|
102
|
+
false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# The list hash is the hash of it's members.
|
108
|
+
#
|
109
|
+
# @see RDF::Value#hash
|
110
|
+
def hash
|
111
|
+
to_a.hash
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Element Assignment — Sets the element at `index`, or replaces a subarray from the `start` index for `length` elements, or replaces a subarray specified by the `range` of indices.
|
116
|
+
#
|
117
|
+
# @overload []=(index, term)
|
118
|
+
# Replaces the element at `index` with `term`.
|
119
|
+
# @param [Integer] index
|
120
|
+
# @param [RDF::Term] term
|
121
|
+
# A non-RDF::Term is coerced to a Literal.
|
122
|
+
# @return [RDF::Term]
|
123
|
+
# @raise [IndexError]
|
124
|
+
#
|
125
|
+
# @overload []=(start, length, value)
|
126
|
+
# Replaces a subarray from the `start` index for `length` elements with `value`. Value is a {RDF::Term}, Array of {RDF::Term}, or {RDF::List}.
|
127
|
+
# @param [Integer] start
|
128
|
+
# @param [Integer] length
|
129
|
+
# @param [RDF::Term, Array<RDF::Term>, RDF::List] value
|
130
|
+
# A non-RDF::Term is coerced to a Literal.
|
131
|
+
# @return [RDF::Term, RDF::List]
|
132
|
+
# @raise [IndexError]
|
133
|
+
#
|
134
|
+
# @overload []=(range, value)
|
135
|
+
# Replaces a subarray from the `start` index for `length` elements with `value`. Value is a {RDF::Term}, Array of {RDF::Term}, or {RDF::List}.
|
136
|
+
# @param [Range] range
|
137
|
+
# @param [RDF::Term, Array<RDF::Term>, RDF::List] value
|
138
|
+
# A non-RDF::Term is coerced to a Literal.
|
139
|
+
# @return [RDF::Term, RDF::List]
|
140
|
+
# @raise [IndexError]
|
141
|
+
def []=(*args)
|
142
|
+
value = case args.last
|
143
|
+
when Array then args.last
|
144
|
+
when RDF::List then args.last.to_a
|
145
|
+
else [args.last]
|
146
|
+
end.map do |v|
|
147
|
+
# Convert values, as necessary.
|
148
|
+
case v
|
149
|
+
when RDF::Value then v.to_term
|
150
|
+
when Symbol then RDF::Node.intern(v)
|
151
|
+
when Array then RDF::N3::List.new(values: v)
|
152
|
+
when nil then RDF.nil
|
153
|
+
else RDF::Literal.new(v)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
ret = case args.length
|
158
|
+
when 3
|
159
|
+
start, length = args[0], args[1]
|
160
|
+
@subject = nil if start == 0
|
161
|
+
@values[start, length] = value
|
162
|
+
when 2
|
163
|
+
case args.first
|
164
|
+
when Integer
|
165
|
+
raise ArgumentError, "Index form of []= takes a single term" if args.last.is_a?(Array)
|
166
|
+
@values[args.first] = value.first
|
167
|
+
when Range
|
168
|
+
@values[args.first] = value
|
169
|
+
else
|
170
|
+
raise ArgumentError, "Index form of must use an integer or range"
|
171
|
+
end
|
172
|
+
else
|
173
|
+
raise ArgumentError, "List []= takes one or two index values"
|
174
|
+
end
|
175
|
+
|
176
|
+
# Fill any nil entries in @values with rdf:nil
|
177
|
+
@values.map! {|v| v || RDF.nil}
|
178
|
+
|
179
|
+
@subject = RDF.nil if @values.empty?
|
180
|
+
@subject ||= RDF::Node.new
|
181
|
+
ret # Returns inserted values
|
182
|
+
end
|
183
|
+
|
184
|
+
##
|
185
|
+
# Appends an element to the head of this list. Existing references are not updated, as the list subject changes as a side-effect.
|
186
|
+
#
|
187
|
+
# @example
|
188
|
+
# RDF::List[].unshift(1).unshift(2).unshift(3) #=> RDF::List[3, 2, 1]
|
189
|
+
#
|
190
|
+
# @param [RDF::Term, Array<RDF::Term>, RDF::List] value
|
191
|
+
# A non-RDF::Term is coerced to a Literal
|
192
|
+
# @return [RDF::List]
|
193
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-unshift
|
194
|
+
#
|
195
|
+
def unshift(value)
|
196
|
+
value = normalize_value(value)
|
197
|
+
@values.unshift(value)
|
198
|
+
@subject = nil
|
199
|
+
|
200
|
+
return self
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# Removes and returns the element at the head of this list.
|
205
|
+
#
|
206
|
+
# @example
|
207
|
+
# RDF::List[1,2,3].shift #=> 1
|
208
|
+
#
|
209
|
+
# @return [RDF::Term]
|
210
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-shift
|
211
|
+
def shift
|
212
|
+
return nil if empty?
|
213
|
+
@subject = nil
|
214
|
+
@values.shift
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Empties this list
|
219
|
+
#
|
220
|
+
# @example
|
221
|
+
# RDF::List[1, 2, 2, 3].clear #=> RDF::List[]
|
222
|
+
#
|
223
|
+
# @return [RDF::List]
|
224
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-clear
|
225
|
+
def clear
|
226
|
+
@values.clear
|
227
|
+
@subject = nil
|
228
|
+
self
|
229
|
+
end
|
230
|
+
|
231
|
+
##
|
232
|
+
# Appends an element to the tail of this list.
|
233
|
+
#
|
234
|
+
# @example
|
235
|
+
# RDF::List[] << 1 << 2 << 3 #=> RDF::List[1, 2, 3]
|
236
|
+
#
|
237
|
+
# @param [RDF::Term] value
|
238
|
+
# @return [RDF::List]
|
239
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-3C-3C
|
240
|
+
def <<(value)
|
241
|
+
value = normalize_value(value)
|
242
|
+
@subject = nil
|
243
|
+
@values << value
|
244
|
+
self
|
245
|
+
end
|
246
|
+
|
247
|
+
##
|
248
|
+
# Returns `true` if this list is empty.
|
249
|
+
#
|
250
|
+
# @example
|
251
|
+
# RDF::List[].empty? #=> true
|
252
|
+
# RDF::List[1, 2, 3].empty? #=> false
|
253
|
+
#
|
254
|
+
# @return [Boolean]
|
255
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-empty-3F
|
256
|
+
def empty?
|
257
|
+
@values.empty?
|
258
|
+
end
|
259
|
+
|
260
|
+
##
|
261
|
+
# Returns the length of this list.
|
262
|
+
#
|
263
|
+
# @example
|
264
|
+
# RDF::List[].length #=> 0
|
265
|
+
# RDF::List[1, 2, 3].length #=> 3
|
266
|
+
#
|
267
|
+
# @return [Integer]
|
268
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-length
|
269
|
+
def length
|
270
|
+
@values.length
|
271
|
+
end
|
272
|
+
|
273
|
+
##
|
274
|
+
# Returns the index of the first element equal to `value`, or `nil` if
|
275
|
+
# no match was found.
|
276
|
+
#
|
277
|
+
# @example
|
278
|
+
# RDF::List['a', 'b', 'c'].index('a') #=> 0
|
279
|
+
# RDF::List['a', 'b', 'c'].index('d') #=> nil
|
280
|
+
#
|
281
|
+
# @param [RDF::Term] value
|
282
|
+
# @return [Integer]
|
283
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-index
|
284
|
+
def index(value)
|
285
|
+
@values.index(value)
|
286
|
+
end
|
287
|
+
|
288
|
+
##
|
289
|
+
# Returns element at `index` with default.
|
290
|
+
#
|
291
|
+
# @example
|
292
|
+
# RDF::List[1, 2, 3].fetch(0) #=> RDF::Literal(1)
|
293
|
+
# RDF::List[1, 2, 3].fetch(4) #=> IndexError
|
294
|
+
# RDF::List[1, 2, 3].fetch(4, nil) #=> nil
|
295
|
+
# RDF::List[1, 2, 3].fetch(4) { |n| n*n } #=> 16
|
296
|
+
#
|
297
|
+
# @return [RDF::Term, nil]
|
298
|
+
# @see http://ruby-doc.org/core-1.9/classes/Array.html#M000420
|
299
|
+
def fetch(*args, &block)
|
300
|
+
@values.fetch(*args, &block)
|
301
|
+
end
|
302
|
+
|
303
|
+
##
|
304
|
+
# Returns the element at `index`.
|
305
|
+
#
|
306
|
+
# @example
|
307
|
+
# RDF::List[1, 2, 3].at(0) #=> 1
|
308
|
+
# RDF::List[1, 2, 3].at(4) #=> nil
|
309
|
+
#
|
310
|
+
# @return [RDF::Term, nil]
|
311
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-at
|
312
|
+
def at(index)
|
313
|
+
@values.at(index)
|
314
|
+
end
|
315
|
+
|
316
|
+
##
|
317
|
+
# Returns the first element in this list.
|
318
|
+
#
|
319
|
+
# @example
|
320
|
+
# RDF::List[*(1..10)].first #=> RDF::Literal(1)
|
321
|
+
#
|
322
|
+
# @return [RDF::Term]
|
323
|
+
def first
|
324
|
+
@values.first
|
325
|
+
end
|
326
|
+
|
327
|
+
##
|
328
|
+
# Returns the last element in this list.
|
329
|
+
#
|
330
|
+
# @example
|
331
|
+
# RDF::List[*(1..10)].last #=> RDF::Literal(10)
|
332
|
+
#
|
333
|
+
# @return [RDF::Term]
|
334
|
+
# @see http://ruby-doc.org/core-2.2.2/Array.html#method-i-last
|
335
|
+
def last
|
336
|
+
@values.last
|
337
|
+
end
|
338
|
+
|
339
|
+
##
|
340
|
+
# Returns a list containing all but the first element of this list.
|
341
|
+
#
|
342
|
+
# @example
|
343
|
+
# RDF::List[1, 2, 3].rest #=> RDF::List[2, 3]
|
344
|
+
#
|
345
|
+
# @return [RDF::List]
|
346
|
+
def rest
|
347
|
+
self.class.new(values: @values[1..-1])
|
348
|
+
end
|
349
|
+
|
350
|
+
##
|
351
|
+
# Returns a list containing the last element of this list.
|
352
|
+
#
|
353
|
+
# @example
|
354
|
+
# RDF::List[1, 2, 3].tail #=> RDF::List[3]
|
355
|
+
#
|
356
|
+
# @return [RDF::List]
|
357
|
+
def tail
|
358
|
+
self.class.new(values: @values[-1..-1])
|
359
|
+
end
|
360
|
+
|
361
|
+
##
|
362
|
+
# Yields each element in this list.
|
363
|
+
#
|
364
|
+
# @example
|
365
|
+
# RDF::List[1, 2, 3].each do |value|
|
366
|
+
# puts value.inspect
|
367
|
+
# end
|
368
|
+
#
|
369
|
+
# @return [Enumerator]
|
370
|
+
# @see http://ruby-doc.org/core-1.9/classes/Enumerable.html
|
371
|
+
def each(&block)
|
372
|
+
return to_enum unless block_given?
|
373
|
+
|
374
|
+
@values.each(&block)
|
375
|
+
end
|
376
|
+
|
377
|
+
##
|
378
|
+
# Yields each statement constituting this list. Uses actual statements if a graph was set, otherwise, the saved values.
|
379
|
+
#
|
380
|
+
# This will recursively get statements for sub-lists as well.
|
381
|
+
#
|
382
|
+
# @example
|
383
|
+
# RDF::List[1, 2, 3].each_statement do |statement|
|
384
|
+
# puts statement.inspect
|
385
|
+
# end
|
386
|
+
#
|
387
|
+
# @return [Enumerator]
|
388
|
+
# @see RDF::Enumerable#each_statement
|
389
|
+
def each_statement(&block)
|
390
|
+
return enum_statement unless block_given?
|
391
|
+
|
392
|
+
if graph
|
393
|
+
RDF::List.new(subject: subject, graph: graph).each_statement(&block)
|
394
|
+
elsif @values.length > 0
|
395
|
+
# Create a subject for each entry based on the subject bnode
|
396
|
+
subjects = (0..(@values.count-1)).map {|ndx| ndx > 0 ? RDF::Node.intern("#{subject.id}_#{ndx}") : subject}
|
397
|
+
*values, last = @values
|
398
|
+
while !values.empty?
|
399
|
+
subj = subjects.shift
|
400
|
+
value = values.shift
|
401
|
+
block.call(RDF::Statement(subj, RDF.first, value.list? ? value.subject : value))
|
402
|
+
block.call(RDF::Statement(subj, RDF.rest, subjects.first))
|
403
|
+
end
|
404
|
+
subj = subjects.shift
|
405
|
+
block.call(RDF::Statement(subj, RDF.first, last.list? ? last.subject : last))
|
406
|
+
block.call(RDF::Statement(subj, RDF.rest, RDF.nil))
|
407
|
+
end
|
408
|
+
|
409
|
+
# If a graph was used, also get statements from sub-lists
|
410
|
+
@values.select(&:list?).each {|li| li.each_statement(&block)}
|
411
|
+
end
|
412
|
+
|
413
|
+
##
|
414
|
+
# Yields each subject term constituting this list along with sub-lists.
|
415
|
+
#
|
416
|
+
# @example
|
417
|
+
# RDF::List[1, 2, 3].each_subject do |subject|
|
418
|
+
# puts subject.inspect
|
419
|
+
# end
|
420
|
+
#
|
421
|
+
# @return [Enumerator]
|
422
|
+
# @see RDF::Enumerable#each
|
423
|
+
def each_subject(&block)
|
424
|
+
return enum_subject unless block_given?
|
425
|
+
|
426
|
+
each_statement {|st| block.call(st.subject) if st.predicate == RDF.rest}
|
427
|
+
end
|
428
|
+
|
429
|
+
##
|
430
|
+
# Enumerate via depth-first recursive descent over list members, yielding each member
|
431
|
+
# @yield term
|
432
|
+
# @yieldparam [RDF::Term] term
|
433
|
+
# @return [Enumerator]
|
434
|
+
def each_descendant(&block)
|
435
|
+
if block_given?
|
436
|
+
each do |term|
|
437
|
+
term.each_descendant(&block) if term.list?
|
438
|
+
block.call(term)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
enum_for(:each_descendant)
|
442
|
+
end
|
443
|
+
|
444
|
+
##
|
445
|
+
# Does this list, or any recusive list have any blank node members?
|
446
|
+
#
|
447
|
+
# @return [Boolean]
|
448
|
+
def has_nodes?
|
449
|
+
@values.any? {|e| e.node? || e.list? && e.has_nodes?}
|
450
|
+
end
|
451
|
+
|
452
|
+
##
|
453
|
+
# Substitutes blank node members with existential variables, recusively.
|
454
|
+
#
|
455
|
+
# @param [RDF::Node] scope
|
456
|
+
# @return [RDF::N3::List]
|
457
|
+
def to_ndvar(scope)
|
458
|
+
values = @values.map do |e|
|
459
|
+
case e
|
460
|
+
when RDF::Node then e.to_ndvar(scope)
|
461
|
+
when RDF::N3::List then e.to_ndvar(scope)
|
462
|
+
else e
|
463
|
+
end
|
464
|
+
end
|
465
|
+
RDF::N3::List.new(values: values)
|
466
|
+
end
|
467
|
+
|
468
|
+
##
|
469
|
+
# Returns the elements in this list as an array.
|
470
|
+
#
|
471
|
+
# @example
|
472
|
+
# RDF::List[].to_a #=> []
|
473
|
+
# RDF::List[1, 2, 3].to_a #=> [RDF::Literal(1), RDF::Literal(2), RDF::Literal(3)]
|
474
|
+
#
|
475
|
+
# @return [Array]
|
476
|
+
def to_a
|
477
|
+
@values
|
478
|
+
end
|
479
|
+
|
480
|
+
##
|
481
|
+
# Checks pattern equality against another list, considering nesting.
|
482
|
+
#
|
483
|
+
# @param [List, Array] other
|
484
|
+
# @return [Boolean]
|
485
|
+
def eql?(other)
|
486
|
+
other = RDF::N3::List[*other] if other.is_a?(Array)
|
487
|
+
return false if !other.is_a?(RDF::List) || count != other.count
|
488
|
+
@values.each_with_index do |li, ndx|
|
489
|
+
case li
|
490
|
+
when RDF::Query::Pattern, RDF::N3::List
|
491
|
+
return false unless li.eql?(other.at(ndx))
|
492
|
+
else
|
493
|
+
return false unless li == other.at(ndx)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
true
|
497
|
+
end
|
498
|
+
|
499
|
+
##
|
500
|
+
# A list is variable if any of its members are variable?
|
501
|
+
#
|
502
|
+
# @return [Boolean]
|
503
|
+
def variable?
|
504
|
+
@values.any?(&:variable?)
|
505
|
+
end
|
506
|
+
|
507
|
+
##
|
508
|
+
# Return the variables contained this list
|
509
|
+
# @return [Array<RDF::Query::Variable>]
|
510
|
+
def vars
|
511
|
+
@values.vars
|
512
|
+
end
|
513
|
+
|
514
|
+
##
|
515
|
+
# Returns all variables in this list.
|
516
|
+
#
|
517
|
+
# Note: this returns a hash containing distinct variables only.
|
518
|
+
#
|
519
|
+
# @return [Hash{Symbol => Variable}]
|
520
|
+
def variables
|
521
|
+
@values.inject({}) do |hash, li|
|
522
|
+
li.respond_to?(:variables) ? hash.merge(li.variables) : hash
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
##
|
527
|
+
# Returns the number of variables in this list, recursively.
|
528
|
+
#
|
529
|
+
# @return [Integer]
|
530
|
+
def variable_count
|
531
|
+
variables.length
|
532
|
+
end
|
533
|
+
|
534
|
+
##
|
535
|
+
# Returns all values the list in the same pattern position
|
536
|
+
#
|
537
|
+
# @param [Symbol] var
|
538
|
+
# @param [RDF::N3::List] list
|
539
|
+
# @return [Array<RDF::Term>]
|
540
|
+
def var_values(var, list)
|
541
|
+
results = []
|
542
|
+
@values.each_index do |ndx|
|
543
|
+
maybe_var = @values[ndx]
|
544
|
+
next unless maybe_var.respond_to?(:var_values)
|
545
|
+
results.push(*Array(maybe_var.var_values(var, list.at(ndx))))
|
546
|
+
end
|
547
|
+
results.flatten.compact
|
548
|
+
end
|
549
|
+
|
550
|
+
##
|
551
|
+
# Evaluates the list using the given variable `bindings`.
|
552
|
+
#
|
553
|
+
# @param [Hash{Symbol => RDF::Term}] bindings
|
554
|
+
# a query solution containing zero or more variable bindings
|
555
|
+
# @param [Hash{Symbol => Object}] options ({})
|
556
|
+
# options passed from query
|
557
|
+
# @return [RDF::N3::List]
|
558
|
+
# @see SPARQL::Algebra::Expression.evaluate
|
559
|
+
def evaluate(bindings, formulae: {}, **options)
|
560
|
+
# if values are constant, simply return ourselves
|
561
|
+
return self if to_a.none? {|li| li.node? || li.variable?}
|
562
|
+
bindings = bindings.to_h unless bindings.is_a?(Hash)
|
563
|
+
# Create a new list subject using a combination of the current subject and a hash of the binding values
|
564
|
+
subj = "#{subject.id}_#{bindings.values.sort.hash}"
|
565
|
+
values = to_a.map do |o|
|
566
|
+
o = o.evaluate(bindings, formulae: formulae, **options) || o
|
567
|
+
end
|
568
|
+
RDF::N3::List.new(subject: RDF::Node.intern(subj), values: values)
|
569
|
+
end
|
570
|
+
|
571
|
+
##
|
572
|
+
# Returns a query solution constructed by binding any variables in this list with the corresponding terms in the given `list`.
|
573
|
+
#
|
574
|
+
# @param [RDF::N3::List] list
|
575
|
+
# a native list with patterns to bind.
|
576
|
+
# @return [RDF::Query::Solution]
|
577
|
+
# @see RDF::Query::Pattern#solution
|
578
|
+
def solution(list)
|
579
|
+
RDF::Query::Solution.new do |solution|
|
580
|
+
@values.each_with_index do |li, ndx|
|
581
|
+
if li.respond_to?(:solution)
|
582
|
+
solution.merge!(li.solution(list[ndx]))
|
583
|
+
elsif li.is_a?(RDF::Query::Variable)
|
584
|
+
solution[li.to_sym] = list[ndx]
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
##
|
591
|
+
# Returns the base representation of this term.
|
592
|
+
#
|
593
|
+
# @return [Sring]
|
594
|
+
def to_base
|
595
|
+
"(#{@values.map(&:to_base).join(' ')})"
|
596
|
+
end
|
597
|
+
|
598
|
+
# Transform Statement into an SXP
|
599
|
+
# @return [Array]
|
600
|
+
def to_sxp_bin
|
601
|
+
to_a.to_sxp_bin
|
602
|
+
end
|
603
|
+
|
604
|
+
##
|
605
|
+
# Creates a new list by recusively mapping the values of the list
|
606
|
+
#
|
607
|
+
# @return [RDF::N3::list]
|
608
|
+
def transform(&block)
|
609
|
+
values = self.to_a.map {|v| v.list? ? v.map(&block) : block.call(v)}
|
610
|
+
RDF::N3::List.new(values: values)
|
611
|
+
end
|
612
|
+
|
613
|
+
private
|
614
|
+
|
615
|
+
##
|
616
|
+
# Normalizes `Array` to `RDF::List` and `nil` to `RDF.nil`.
|
617
|
+
#
|
618
|
+
# @param value [Object]
|
619
|
+
# @return [RDF::Value, Object] normalized value
|
620
|
+
def normalize_value(value)
|
621
|
+
case value
|
622
|
+
when RDF::Value then value.to_term
|
623
|
+
when Array then RDF::N3::List.new(values: value)
|
624
|
+
when Symbol then RDF::Node.intern(value)
|
625
|
+
when nil then RDF.nil
|
626
|
+
else RDF::Literal.new(value)
|
627
|
+
end
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|