rdf-n3 3.1.1 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +148 -69
  3. data/UNLICENSE +1 -1
  4. data/VERSION +1 -1
  5. data/lib/rdf/n3.rb +8 -8
  6. data/lib/rdf/n3/algebra.rb +147 -68
  7. data/lib/rdf/n3/algebra/builtin.rb +79 -0
  8. data/lib/rdf/n3/algebra/formula.rb +355 -94
  9. data/lib/rdf/n3/algebra/list/append.rb +33 -4
  10. data/lib/rdf/n3/algebra/list/first.rb +24 -0
  11. data/lib/rdf/n3/algebra/list/in.rb +42 -3
  12. data/lib/rdf/n3/algebra/list/last.rb +17 -4
  13. data/lib/rdf/n3/algebra/list/length.rb +24 -0
  14. data/lib/rdf/n3/algebra/list/member.rb +39 -2
  15. data/lib/rdf/n3/algebra/list_operator.rb +83 -0
  16. data/lib/rdf/n3/algebra/log/conclusion.rb +57 -1
  17. data/lib/rdf/n3/algebra/log/conjunction.rb +28 -1
  18. data/lib/rdf/n3/algebra/log/content.rb +34 -0
  19. data/lib/rdf/n3/algebra/log/equal_to.rb +34 -0
  20. data/lib/rdf/n3/algebra/log/implies.rb +55 -30
  21. data/lib/rdf/n3/algebra/log/includes.rb +58 -1
  22. data/lib/rdf/n3/algebra/log/n3_string.rb +34 -0
  23. data/lib/rdf/n3/algebra/log/not_equal_to.rb +23 -0
  24. data/lib/rdf/n3/algebra/log/not_includes.rb +27 -0
  25. data/lib/rdf/n3/algebra/log/output_string.rb +40 -0
  26. data/lib/rdf/n3/algebra/log/parsed_as_n3.rb +36 -0
  27. data/lib/rdf/n3/algebra/log/semantics.rb +40 -0
  28. data/lib/rdf/n3/algebra/math/absolute_value.rb +36 -0
  29. data/lib/rdf/n3/algebra/math/acos.rb +26 -0
  30. data/lib/rdf/n3/algebra/math/acosh.rb +26 -0
  31. data/lib/rdf/n3/algebra/math/asin.rb +26 -0
  32. data/lib/rdf/n3/algebra/math/asinh.rb +26 -0
  33. data/lib/rdf/n3/algebra/math/atan.rb +26 -0
  34. data/lib/rdf/n3/algebra/math/atanh.rb +26 -0
  35. data/lib/rdf/n3/algebra/math/ceiling.rb +28 -0
  36. data/lib/rdf/n3/algebra/math/cos.rb +40 -0
  37. data/lib/rdf/n3/algebra/math/cosh.rb +38 -0
  38. data/lib/rdf/n3/algebra/math/difference.rb +34 -3
  39. data/lib/rdf/n3/algebra/math/equal_to.rb +54 -0
  40. data/lib/rdf/n3/algebra/math/exponentiation.rb +29 -3
  41. data/lib/rdf/n3/algebra/math/floor.rb +28 -0
  42. data/lib/rdf/n3/algebra/math/greater_than.rb +41 -0
  43. data/lib/rdf/n3/algebra/math/less_than.rb +41 -0
  44. data/lib/rdf/n3/algebra/math/negation.rb +31 -2
  45. data/lib/rdf/n3/algebra/math/not_equal_to.rb +25 -0
  46. data/lib/rdf/n3/algebra/math/not_greater_than.rb +25 -0
  47. data/lib/rdf/n3/algebra/math/not_less_than.rb +25 -0
  48. data/lib/rdf/n3/algebra/math/product.rb +14 -3
  49. data/lib/rdf/n3/algebra/math/quotient.rb +30 -3
  50. data/lib/rdf/n3/algebra/math/remainder.rb +29 -3
  51. data/lib/rdf/n3/algebra/math/rounded.rb +20 -3
  52. data/lib/rdf/n3/algebra/math/sin.rb +40 -0
  53. data/lib/rdf/n3/algebra/math/sinh.rb +38 -0
  54. data/lib/rdf/n3/algebra/math/sum.rb +35 -4
  55. data/lib/rdf/n3/algebra/math/tan.rb +40 -0
  56. data/lib/rdf/n3/algebra/math/tanh.rb +38 -0
  57. data/lib/rdf/n3/algebra/not_implemented.rb +13 -0
  58. data/lib/rdf/n3/algebra/resource_operator.rb +123 -0
  59. data/lib/rdf/n3/algebra/str/concatenation.rb +21 -3
  60. data/lib/rdf/n3/algebra/str/contains.rb +28 -4
  61. data/lib/rdf/n3/algebra/str/contains_ignoring_case.rb +33 -0
  62. data/lib/rdf/n3/algebra/str/ends_with.rb +33 -0
  63. data/lib/rdf/n3/algebra/str/equal_ignoring_case.rb +34 -0
  64. data/lib/rdf/n3/algebra/str/format.rb +12 -4
  65. data/lib/rdf/n3/algebra/str/greater_than.rb +38 -0
  66. data/lib/rdf/n3/algebra/str/less_than.rb +33 -0
  67. data/lib/rdf/n3/algebra/str/matches.rb +33 -5
  68. data/lib/rdf/n3/algebra/str/not_equal_ignoring_case.rb +17 -0
  69. data/lib/rdf/n3/algebra/str/not_greater_than.rb +17 -0
  70. data/lib/rdf/n3/algebra/str/not_less_than.rb +17 -0
  71. data/lib/rdf/n3/algebra/str/not_matches.rb +18 -0
  72. data/lib/rdf/n3/algebra/str/replace.rb +28 -5
  73. data/lib/rdf/n3/algebra/str/scrape.rb +31 -5
  74. data/lib/rdf/n3/algebra/str/starts_with.rb +33 -0
  75. data/lib/rdf/n3/algebra/time/day.rb +35 -0
  76. data/lib/rdf/n3/algebra/time/day_of_week.rb +27 -0
  77. data/lib/rdf/n3/algebra/time/gm_time.rb +29 -0
  78. data/lib/rdf/n3/algebra/time/hour.rb +35 -0
  79. data/lib/rdf/n3/algebra/time/in_seconds.rb +59 -0
  80. data/lib/rdf/n3/algebra/time/local_time.rb +29 -0
  81. data/lib/rdf/n3/algebra/time/minute.rb +35 -0
  82. data/lib/rdf/n3/algebra/time/month.rb +35 -0
  83. data/lib/rdf/n3/algebra/time/second.rb +35 -0
  84. data/lib/rdf/n3/algebra/time/timezone.rb +36 -0
  85. data/lib/rdf/n3/algebra/time/year.rb +29 -0
  86. data/lib/rdf/n3/extensions.rb +180 -21
  87. data/lib/rdf/n3/format.rb +65 -0
  88. data/lib/rdf/n3/list.rb +630 -0
  89. data/lib/rdf/n3/reader.rb +762 -485
  90. data/lib/rdf/n3/reasoner.rb +57 -68
  91. data/lib/rdf/n3/refinements.rb +178 -0
  92. data/lib/rdf/n3/repository.rb +332 -0
  93. data/lib/rdf/n3/terminals.rb +80 -0
  94. data/lib/rdf/n3/vocab.rb +35 -7
  95. data/lib/rdf/n3/writer.rb +208 -148
  96. metadata +110 -52
  97. data/AUTHORS +0 -1
  98. data/History.markdown +0 -99
  99. data/lib/rdf/n3/algebra/log/equalTo.rb +0 -7
  100. data/lib/rdf/n3/algebra/log/notEqualTo.rb +0 -7
  101. data/lib/rdf/n3/algebra/log/notIncludes.rb +0 -12
  102. data/lib/rdf/n3/algebra/log/outputString.rb +0 -7
  103. data/lib/rdf/n3/algebra/math/absoluteValue.rb +0 -9
  104. data/lib/rdf/n3/algebra/math/equalTo.rb +0 -9
  105. data/lib/rdf/n3/algebra/math/greaterThan.rb +0 -9
  106. data/lib/rdf/n3/algebra/math/integerQuotient.rb +0 -9
  107. data/lib/rdf/n3/algebra/math/lessThan.rb +0 -9
  108. data/lib/rdf/n3/algebra/math/memberCount.rb +0 -9
  109. data/lib/rdf/n3/algebra/math/notEqualTo.rb +0 -9
  110. data/lib/rdf/n3/algebra/math/notGreaterThan.rb +0 -9
  111. data/lib/rdf/n3/algebra/math/notLessThan.rb +0 -9
  112. data/lib/rdf/n3/algebra/str/containsIgnoringCase.rb +0 -9
  113. data/lib/rdf/n3/algebra/str/endsWith.rb +0 -9
  114. data/lib/rdf/n3/algebra/str/equalIgnoringCase.rb +0 -9
  115. data/lib/rdf/n3/algebra/str/greaterThan.rb +0 -9
  116. data/lib/rdf/n3/algebra/str/lessThan.rb +0 -9
  117. data/lib/rdf/n3/algebra/str/notEqualIgnoringCase.rb +0 -9
  118. data/lib/rdf/n3/algebra/str/notGreaterThan.rb +0 -9
  119. data/lib/rdf/n3/algebra/str/notLessThan.rb +0 -9
  120. data/lib/rdf/n3/algebra/str/notMatches.rb +0 -9
  121. data/lib/rdf/n3/algebra/str/startsWith.rb +0 -56
  122. data/lib/rdf/n3/patches/array_hacks.rb +0 -53
  123. data/lib/rdf/n3/reader/meta.rb +0 -641
  124. data/lib/rdf/n3/reader/parser.rb +0 -239
@@ -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