sparql 3.0.2 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +189 -74
  3. data/UNLICENSE +1 -1
  4. data/VERSION +1 -1
  5. data/bin/sparql +44 -24
  6. data/lib/rack/sparql.rb +1 -1
  7. data/lib/rack/sparql/conneg.rb +3 -3
  8. data/lib/sinatra/sparql.rb +5 -5
  9. data/lib/sparql.rb +17 -16
  10. data/lib/sparql/algebra.rb +15 -23
  11. data/lib/sparql/algebra/aggregate.rb +4 -4
  12. data/lib/sparql/algebra/evaluatable.rb +2 -2
  13. data/lib/sparql/algebra/expression.rb +70 -44
  14. data/lib/sparql/algebra/extensions.rb +181 -34
  15. data/lib/sparql/algebra/operator.rb +44 -23
  16. data/lib/sparql/algebra/operator/abs.rb +2 -2
  17. data/lib/sparql/algebra/operator/add.rb +3 -3
  18. data/lib/sparql/algebra/operator/alt.rb +4 -4
  19. data/lib/sparql/algebra/operator/and.rb +7 -7
  20. data/lib/sparql/algebra/operator/asc.rb +3 -3
  21. data/lib/sparql/algebra/operator/ask.rb +4 -14
  22. data/lib/sparql/algebra/operator/avg.rb +13 -4
  23. data/lib/sparql/algebra/operator/base.rb +10 -10
  24. data/lib/sparql/algebra/operator/bgp.rb +2 -2
  25. data/lib/sparql/algebra/operator/bnode.rb +5 -5
  26. data/lib/sparql/algebra/operator/bound.rb +3 -3
  27. data/lib/sparql/algebra/operator/ceil.rb +2 -2
  28. data/lib/sparql/algebra/operator/clear.rb +3 -3
  29. data/lib/sparql/algebra/operator/coalesce.rb +3 -13
  30. data/lib/sparql/algebra/operator/compare.rb +8 -8
  31. data/lib/sparql/algebra/operator/concat.rb +4 -4
  32. data/lib/sparql/algebra/operator/construct.rb +4 -14
  33. data/lib/sparql/algebra/operator/contains.rb +2 -2
  34. data/lib/sparql/algebra/operator/copy.rb +3 -3
  35. data/lib/sparql/algebra/operator/count.rb +3 -3
  36. data/lib/sparql/algebra/operator/create.rb +3 -3
  37. data/lib/sparql/algebra/operator/dataset.rb +6 -17
  38. data/lib/sparql/algebra/operator/datatype.rb +1 -1
  39. data/lib/sparql/algebra/operator/day.rb +1 -1
  40. data/lib/sparql/algebra/operator/delete.rb +9 -7
  41. data/lib/sparql/algebra/operator/delete_data.rb +3 -3
  42. data/lib/sparql/algebra/operator/delete_where.rb +6 -6
  43. data/lib/sparql/algebra/operator/desc.rb +1 -1
  44. data/lib/sparql/algebra/operator/describe.rb +3 -13
  45. data/lib/sparql/algebra/operator/distinct.rb +4 -14
  46. data/lib/sparql/algebra/operator/divide.rb +1 -1
  47. data/lib/sparql/algebra/operator/drop.rb +3 -3
  48. data/lib/sparql/algebra/operator/encode_for_uri.rb +3 -3
  49. data/lib/sparql/algebra/operator/equal.rb +2 -2
  50. data/lib/sparql/algebra/operator/exists.rb +5 -5
  51. data/lib/sparql/algebra/operator/exprlist.rb +3 -13
  52. data/lib/sparql/algebra/operator/extend.rb +19 -18
  53. data/lib/sparql/algebra/operator/filter.rb +6 -16
  54. data/lib/sparql/algebra/operator/floor.rb +2 -2
  55. data/lib/sparql/algebra/operator/graph.rb +6 -17
  56. data/lib/sparql/algebra/operator/greater_than.rb +5 -5
  57. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +5 -5
  58. data/lib/sparql/algebra/operator/group.rb +25 -25
  59. data/lib/sparql/algebra/operator/group_concat.rb +6 -6
  60. data/lib/sparql/algebra/operator/hours.rb +1 -1
  61. data/lib/sparql/algebra/operator/if.rb +5 -15
  62. data/lib/sparql/algebra/operator/in.rb +4 -14
  63. data/lib/sparql/algebra/operator/insert.rb +7 -5
  64. data/lib/sparql/algebra/operator/insert_data.rb +3 -3
  65. data/lib/sparql/algebra/operator/iri.rb +1 -1
  66. data/lib/sparql/algebra/operator/is_blank.rb +1 -1
  67. data/lib/sparql/algebra/operator/is_iri.rb +1 -1
  68. data/lib/sparql/algebra/operator/is_literal.rb +1 -1
  69. data/lib/sparql/algebra/operator/is_numeric.rb +1 -1
  70. data/lib/sparql/algebra/operator/join.rb +12 -10
  71. data/lib/sparql/algebra/operator/lang.rb +1 -1
  72. data/lib/sparql/algebra/operator/lang_matches.rb +3 -3
  73. data/lib/sparql/algebra/operator/lcase.rb +2 -2
  74. data/lib/sparql/algebra/operator/left_join.rb +19 -12
  75. data/lib/sparql/algebra/operator/less_than.rb +5 -5
  76. data/lib/sparql/algebra/operator/less_than_or_equal.rb +5 -5
  77. data/lib/sparql/algebra/operator/load.rb +3 -3
  78. data/lib/sparql/algebra/operator/max.rb +13 -4
  79. data/lib/sparql/algebra/operator/md5.rb +1 -1
  80. data/lib/sparql/algebra/operator/min.rb +13 -4
  81. data/lib/sparql/algebra/operator/minus.rb +12 -11
  82. data/lib/sparql/algebra/operator/minutes.rb +1 -1
  83. data/lib/sparql/algebra/operator/modify.rb +4 -4
  84. data/lib/sparql/algebra/operator/month.rb +1 -1
  85. data/lib/sparql/algebra/operator/move.rb +3 -3
  86. data/lib/sparql/algebra/operator/multiply.rb +1 -1
  87. data/lib/sparql/algebra/operator/negate.rb +1 -1
  88. data/lib/sparql/algebra/operator/not.rb +1 -1
  89. data/lib/sparql/algebra/operator/not_equal.rb +2 -2
  90. data/lib/sparql/algebra/operator/notexists.rb +4 -4
  91. data/lib/sparql/algebra/operator/notin.rb +4 -14
  92. data/lib/sparql/algebra/operator/notoneof.rb +5 -6
  93. data/lib/sparql/algebra/operator/now.rb +1 -1
  94. data/lib/sparql/algebra/operator/or.rb +7 -7
  95. data/lib/sparql/algebra/operator/order.rb +6 -16
  96. data/lib/sparql/algebra/operator/path.rb +6 -5
  97. data/lib/sparql/algebra/operator/path_opt.rb +16 -16
  98. data/lib/sparql/algebra/operator/path_plus.rb +8 -8
  99. data/lib/sparql/algebra/operator/path_star.rb +4 -4
  100. data/lib/sparql/algebra/operator/plus.rb +3 -3
  101. data/lib/sparql/algebra/operator/prefix.rb +10 -10
  102. data/lib/sparql/algebra/operator/project.rb +4 -14
  103. data/lib/sparql/algebra/operator/rand.rb +1 -1
  104. data/lib/sparql/algebra/operator/reduced.rb +4 -14
  105. data/lib/sparql/algebra/operator/regex.rb +6 -6
  106. data/lib/sparql/algebra/operator/replace.rb +4 -4
  107. data/lib/sparql/algebra/operator/reverse.rb +4 -4
  108. data/lib/sparql/algebra/operator/round.rb +2 -2
  109. data/lib/sparql/algebra/operator/same_term.rb +8 -5
  110. data/lib/sparql/algebra/operator/sample.rb +11 -4
  111. data/lib/sparql/algebra/operator/seconds.rb +1 -1
  112. data/lib/sparql/algebra/operator/seq.rb +5 -6
  113. data/lib/sparql/algebra/operator/sequence.rb +4 -4
  114. data/lib/sparql/algebra/operator/sha1.rb +1 -1
  115. data/lib/sparql/algebra/operator/sha256.rb +1 -1
  116. data/lib/sparql/algebra/operator/sha384.rb +1 -1
  117. data/lib/sparql/algebra/operator/sha512.rb +1 -1
  118. data/lib/sparql/algebra/operator/slice.rb +4 -14
  119. data/lib/sparql/algebra/operator/str.rb +1 -1
  120. data/lib/sparql/algebra/operator/strafter.rb +2 -2
  121. data/lib/sparql/algebra/operator/strbefore.rb +2 -2
  122. data/lib/sparql/algebra/operator/strdt.rb +2 -2
  123. data/lib/sparql/algebra/operator/strends.rb +2 -2
  124. data/lib/sparql/algebra/operator/strlang.rb +2 -2
  125. data/lib/sparql/algebra/operator/strlen.rb +2 -2
  126. data/lib/sparql/algebra/operator/strstarts.rb +2 -2
  127. data/lib/sparql/algebra/operator/struuid.rb +1 -1
  128. data/lib/sparql/algebra/operator/substr.rb +4 -4
  129. data/lib/sparql/algebra/operator/subtract.rb +1 -1
  130. data/lib/sparql/algebra/operator/sum.rb +6 -4
  131. data/lib/sparql/algebra/operator/table.rb +3 -3
  132. data/lib/sparql/algebra/operator/timezone.rb +1 -1
  133. data/lib/sparql/algebra/operator/tz.rb +1 -1
  134. data/lib/sparql/algebra/operator/ucase.rb +2 -2
  135. data/lib/sparql/algebra/operator/union.rb +9 -8
  136. data/lib/sparql/algebra/operator/update.rb +4 -4
  137. data/lib/sparql/algebra/operator/using.rb +4 -4
  138. data/lib/sparql/algebra/operator/uuid.rb +1 -1
  139. data/lib/sparql/algebra/operator/with.rb +7 -7
  140. data/lib/sparql/algebra/operator/year.rb +1 -1
  141. data/lib/sparql/algebra/query.rb +2 -2
  142. data/lib/sparql/algebra/update.rb +2 -2
  143. data/lib/sparql/algebra/version.rb +1 -1
  144. data/lib/sparql/extensions.rb +11 -17
  145. data/lib/sparql/grammar.rb +113 -13
  146. data/lib/sparql/grammar/meta.rb +2340 -907
  147. data/lib/sparql/grammar/parser11.rb +57 -52
  148. data/lib/sparql/grammar/terminals11.rb +2 -0
  149. data/lib/sparql/results.rb +73 -46
  150. data/lib/sparql/version.rb +1 -1
  151. metadata +46 -63
@@ -4,7 +4,7 @@ require 'json'
4
4
  # Extensions for Ruby's `NilClass` class.
5
5
  class NilClass
6
6
 
7
- def evaluate(bindings, options = {})
7
+ def evaluate(bindings, **options)
8
8
  self
9
9
  end
10
10
 
@@ -27,6 +27,22 @@ class Object
27
27
  def to_sse
28
28
  SXP::Generator.string(self.to_sxp_bin)
29
29
  end
30
+
31
+ ##
32
+ # A duplicate of this object.
33
+ #
34
+ # @return [Object] a copy of `self`
35
+ # @see SPARQL::Algebra::Expression#optimize
36
+ def optimize(**options)
37
+ self.deep_dup
38
+ end
39
+
40
+ ##
41
+ # Default for deep_dup is shallow dup
42
+ # @return [Object]
43
+ def deep_dup
44
+ dup
45
+ end
30
46
  end
31
47
 
32
48
  ##
@@ -53,8 +69,8 @@ class Array
53
69
  # options passed from query
54
70
  # @return [RDF::Term]
55
71
  # @see SPARQL::Algebra::Expression.evaluate
56
- def evaluate(bindings, options = {})
57
- SPARQL::Algebra::Expression.extension(*self.map {|o| o.evaluate(bindings, options)})
72
+ def evaluate(bindings, **options)
73
+ SPARQL::Algebra::Expression.extension(*self.map {|o| o.evaluate(bindings, **options)})
58
74
  end
59
75
 
60
76
  ##
@@ -66,11 +82,34 @@ class Array
66
82
  # @param [Hash{Symbol => Object}] options
67
83
  # @raise [NotImplementedError]
68
84
  # If an attempt is made to perform an unsupported operation
69
- # @see http://www.w3.org/TR/sparql11-query/#sparqlAlgebra
70
- def execute(queryable, options = {})
85
+ # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra
86
+ def execute(queryable, **options)
71
87
  raise NotImplementedError, "SPARQL::Algebra '#{first}' operator not implemented"
72
88
  end
73
89
 
90
+ ##
91
+ # Return an optimized version of this array.
92
+ #
93
+ # @return [Array] a copy of `self`
94
+ # @see SPARQL::Algebra::Expression#optimize
95
+ def optimize(**options)
96
+ self.map do |op|
97
+ op.optimize(**options) if op.respond_to?(:optimize)
98
+ end
99
+ end
100
+
101
+ ##
102
+ # Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
103
+ #
104
+ # @param [RDF::Query::Solution] solution
105
+ # @return [self]
106
+ def bind(solution)
107
+ map! do |op|
108
+ op.respond_to?(:bind) ? op.bind(solution) : op
109
+ end
110
+ self
111
+ end
112
+
74
113
  ##
75
114
  # Returns `true` if any of the operands are variables, `false`
76
115
  # otherwise.
@@ -78,7 +117,7 @@ class Array
78
117
  # @return [Boolean] `true` or `false`
79
118
  # @see #constant?
80
119
  def variable?
81
- any?(&:variable?)
120
+ any? {|op| op.respond_to?(:variable?) && op.variable?}
82
121
  end
83
122
  def constant?; !(variable?); end
84
123
 
@@ -166,6 +205,12 @@ class Array
166
205
  each {|e| e.validate! if e.respond_to?(:validate!)}
167
206
  self
168
207
  end
208
+
209
+ ##
210
+ # Deep duplicate
211
+ def deep_dup
212
+ map(&:deep_dup)
213
+ end
169
214
  end
170
215
 
171
216
  ##
@@ -179,6 +224,21 @@ class Hash
179
224
  to_a.to_sxp_bin
180
225
  end
181
226
  def to_sxp; to_sxp_bin; end
227
+
228
+ ##
229
+ # A duplicate of this hash.
230
+ #
231
+ # @return [Hash] a copy of `self`
232
+ # @see SPARQL::Algebra::Expression#optimize
233
+ def optimize(**options)
234
+ self.deep_dup
235
+ end
236
+
237
+ ##
238
+ # Deep duplicate
239
+ def deep_dup
240
+ inject({}) {|memo, (k, v)| memo.merge(k => v.deep_dup)}
241
+ end
182
242
  end
183
243
 
184
244
  ##
@@ -191,7 +251,7 @@ module RDF::Term
191
251
  # @param [Hash{Symbol => Object}] options ({})
192
252
  # options passed from query
193
253
  # @return [RDF::Term]
194
- def evaluate(bindings, options = {})
254
+ def evaluate(bindings, **options)
195
255
  self
196
256
  end
197
257
 
@@ -210,8 +270,33 @@ module RDF::Term
210
270
  def vars
211
271
  variable? ? [self] : []
212
272
  end
273
+
274
+ ##
275
+ # A duplicate of this term.
276
+ #
277
+ # @return [RDF::Term] a copy of `self`
278
+ # @see SPARQL::Algebra::Expression#optimize
279
+ def optimize(**options)
280
+ optimized = self.deep_dup
281
+ optimized.lexical = nil if optimized.respond_to?(:lexical=)
282
+ optimized
283
+ end
213
284
  end # RDF::Term
214
285
 
286
+ class RDF::Literal::Double
287
+ ##
288
+ # Returns the SXP representation of this object.
289
+ #
290
+ # @return [String]
291
+ def to_sxp
292
+ case
293
+ when nan? then 'nan.0'
294
+ when infinite? then (infinite? > 0 ? '+inf.0' : '-inf.0')
295
+ else canonicalize.to_s.downcase
296
+ end
297
+ end
298
+ end
299
+
215
300
  # Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
216
301
  module RDF::Queryable
217
302
  alias_method :query_without_sparql, :query
@@ -235,7 +320,7 @@ module RDF::Queryable
235
320
  # @yieldreturn [void] ignored
236
321
  # @return [Enumerator]
237
322
  # @see RDF::Queryable#query_pattern
238
- def query(pattern, options = {}, &block)
323
+ def query(pattern, **options, &block)
239
324
  raise TypeError, "#{self} is not queryable" if respond_to?(:queryable?) && !queryable?
240
325
 
241
326
  if pattern.is_a?(SPARQL::Algebra::Operator) && pattern.respond_to?(:execute)
@@ -243,7 +328,7 @@ module RDF::Queryable
243
328
  solutions = if method(:query_execute).arity == 1
244
329
  query_execute(pattern, &block)
245
330
  else
246
- query_execute(pattern, options, &block)
331
+ query_execute(pattern, **options, &block)
247
332
  end
248
333
  after_query(pattern) if respond_to?(:after_query)
249
334
 
@@ -252,25 +337,45 @@ module RDF::Queryable
252
337
  solutions
253
338
  else
254
339
  # Return an enumerator
255
- enum_for(:query, pattern, options)
340
+ enum_for(:query, pattern, **options)
256
341
  end
257
342
  else
258
- query_without_sparql(pattern, options, &block)
343
+ query_without_sparql(pattern, **options, &block)
259
344
  end
260
345
  end
261
-
262
346
  end
263
347
 
264
348
  class RDF::Statement
265
349
  # Transform Statement Pattern into an SXP
266
350
  # @return [Array]
267
351
  def to_sxp_bin
268
- if has_graph?
269
- [:quad, subject, predicate, object, graph_name]
270
- else
271
- [:triple, subject, predicate, object]
272
- end
352
+ [ (has_graph? ? :quad : :triple),
353
+ (:inferred if inferred?),
354
+ subject,
355
+ predicate,
356
+ object,
357
+ graph_name
358
+ ].compact.map(&:to_sxp_bin)
273
359
  end
360
+
361
+ ##
362
+ # Returns an S-Expression (SXP) representation
363
+ #
364
+ # @return [String]
365
+ def to_sxp
366
+ to_sxp_bin.to_sxp
367
+ end
368
+
369
+ ##
370
+ # A duplicate of this Statement.
371
+ #
372
+ # @return [RDF::Statement] a copy of `self`
373
+ # @see SPARQL::Algebra::Expression#optimize
374
+ def optimize(**options)
375
+ self.dup
376
+ end
377
+
378
+ def executable?; false; end
274
379
  end
275
380
 
276
381
  class RDF::Query
@@ -306,6 +411,16 @@ class RDF::Query
306
411
  end
307
412
  end
308
413
 
414
+ ##
415
+ # Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
416
+ #
417
+ # @param [RDF::Query::Solution] solution
418
+ # @return [self]
419
+ def bind(solution)
420
+ patterns.each {|p| p.bind(solution)}
421
+ self
422
+ end
423
+
309
424
  # Query results in a boolean result (e.g., ASK)
310
425
  # @return [Boolean]
311
426
  def query_yields_boolean?
@@ -338,25 +453,34 @@ class RDF::Query
338
453
  variables.values
339
454
  end
340
455
 
456
+ alias_method :optimize_without_expression!, :optimize!
341
457
  ##
342
- # Returns `true` if this is executable (i.e., contains a graph patterns), `false`
343
- # otherwise.
458
+ # Optimize the query, removing lexical shortcuts in URIs
344
459
  #
345
- # @return [Boolean] `true` or `false`
460
+ # @return [self]
461
+ # @see SPARQL::Algebra::Expression#optimize!
462
+ def optimize!(**options)
463
+ @patterns = @patterns.map do |pattern|
464
+ components = pattern.to_quad.map do |term|
465
+ if term.respond_to?(:lexical=)
466
+ term.dup.instance_eval {@lexical = nil; self}
467
+ else
468
+ term
469
+ end
470
+ end
471
+ RDF::Query::Pattern.from(components, **pattern.options)
472
+ end
473
+ self.optimize_without_expression!(**options)
474
+ end
475
+
476
+ ##
477
+ # Returns `true` as this is executable.
478
+ #
479
+ # @return [Boolean] `true`
346
480
  def executable?; true; end
347
481
  end
348
482
 
349
483
  class RDF::Query::Pattern
350
- # Transform Query Pattern into an SXP
351
- # @return [Array]
352
- def to_sxp_bin
353
- if has_graph?
354
- [:quad, subject, predicate, object, graph_name]
355
- else
356
- [:triple, subject, predicate, object]
357
- end
358
- end
359
-
360
484
  ##
361
485
  # Return the non-destinguished variables contained within this pattern
362
486
  # @return [Array<RDF::Query::Variable>]
@@ -370,6 +494,12 @@ class RDF::Query::Pattern
370
494
  def vars
371
495
  variables.values
372
496
  end
497
+
498
+ ##
499
+ # Returns `true` as this is executable.
500
+ #
501
+ # @return [Boolean] `true`
502
+ def executable?; true; end
373
503
  end
374
504
 
375
505
  ##
@@ -386,14 +516,18 @@ class RDF::Query::Variable
386
516
  # options passed from query
387
517
  # @return [RDF::Term] the value of this variable
388
518
  # @raise [TypeError] if the variable is not bound
389
- def evaluate(bindings, options = {})
519
+ def evaluate(bindings, **options)
390
520
  raise TypeError if bindings.respond_to?(:bound?) && !bindings.bound?(self)
391
521
  bindings[name.to_sym]
392
522
  end
393
523
 
394
- def to_s
395
- prefix = distinguished? || name.to_s[0,1] == '.' ? '?' : "??"
396
- unbound? ? "#{prefix}#{name}" : "#{prefix}#{name}=#{value}"
524
+ ##
525
+ # Return self
526
+ #
527
+ # @return [RDF::Query::Variable] a copy of `self`
528
+ # @see SPARQL::Algebra::Expression#optimize
529
+ def optimize(**options)
530
+ self
397
531
  end
398
532
  end # RDF::Query::Variable
399
533
 
@@ -424,3 +558,16 @@ class RDF::Query::Solutions
424
558
  end
425
559
  alias_method :filter!, :filter
426
560
  end # RDF::Query::Solutions
561
+
562
+ ##
563
+ # Extensions for `RDF::Query::Solution`.
564
+ class RDF::Query::Solution
565
+ ##
566
+ # Returns the SXP representation of this object, defaults to `self`.
567
+ #
568
+ # @return [String]
569
+ def to_sxp_bin
570
+ to_a.to_sxp_bin
571
+ end
572
+ def to_sxp; to_sxp_bin; end
573
+ end # RDF::Query::Solution
@@ -146,8 +146,6 @@ module SPARQL; module Algebra
146
146
  autoload :Using, 'sparql/algebra/operator/using'
147
147
  autoload :With, 'sparql/algebra/operator/with'
148
148
 
149
-
150
-
151
149
  ##
152
150
  # Returns an operator class for the given operator `name`.
153
151
  #
@@ -331,15 +329,15 @@ module SPARQL; module Algebra
331
329
  # @overload initialize(*operands)
332
330
  # @param [Array<RDF::Term>] operands
333
331
  #
334
- # @overload initialize(*operands, options)
332
+ # @overload initialize(*operands, **options)
335
333
  # @param [Array<RDF::Term>] operands
336
334
  # @param [Hash{Symbol => Object}] options
337
335
  # any additional options
338
336
  # @option options [Boolean] :memoize (false)
339
337
  # whether to memoize results for particular operands
340
338
  # @raise [TypeError] if any operand is invalid
341
- def initialize(*operands)
342
- @options = operands.last.is_a?(Hash) ? operands.pop.dup : {}
339
+ def initialize(*operands, **options)
340
+ @options = options.dup
343
341
  @operands = operands.map! do |operand|
344
342
  case operand
345
343
  when Array
@@ -357,6 +355,22 @@ module SPARQL; module Algebra
357
355
  end
358
356
  end
359
357
 
358
+ ##
359
+ # Deep duplicate operands
360
+ def deep_dup
361
+ self.class.new(*operands.map(&:deep_dup), **@options)
362
+ end
363
+
364
+ ##
365
+ # Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
366
+ #
367
+ # @param [RDF::Query::Solution] solution
368
+ # @return [self]
369
+ def bind(solution)
370
+ @operands.each {|op| op.bind(solution)}
371
+ self
372
+ end
373
+
360
374
  ##
361
375
  # Base URI used for reading data sources with relative URIs
362
376
  #
@@ -434,7 +448,7 @@ module SPARQL; module Algebra
434
448
  # @return [Boolean] `true` or `false`
435
449
  # @see #constant?
436
450
  def variable?
437
- operands.any?(&:variable?)
451
+ operands.any? {|op| op.respond_to?(:variable?) && op.variable?}
438
452
  end
439
453
 
440
454
  ##
@@ -491,23 +505,37 @@ module SPARQL; module Algebra
491
505
  #
492
506
  # For constant expressions containing no variables, returns the result
493
507
  # of evaluating the expression with empty bindings; otherwise returns
494
- # `self`.
508
+ # a copy of `self`.
495
509
  #
496
510
  # Optimization is not possible if the expression raises an exception,
497
511
  # such as a `TypeError` or `ZeroDivisionError`, which must be conserved
498
512
  # at runtime.
499
513
  #
500
514
  # @return [SPARQL::Algebra::Expression]
501
- def optimize
515
+ # @see RDF::Query#optimize
516
+ def optimize(**options)
502
517
  if constant?
503
518
  # Note that if evaluation results in a `TypeError` or other error,
504
519
  # we must return `self` so that the error is conserved at runtime:
505
520
  evaluate(RDF::Query::Solution.new) rescue self
506
521
  else
507
- super # returns `self`
522
+ super # returns a copy of `self`
508
523
  end
509
524
  end
510
525
 
526
+ ##
527
+ # Optimizes this query by optimizing its constituent operands
528
+ # according to their cost estimates.
529
+ #
530
+ # @return [self]
531
+ # @see RDF::Query#optimize!
532
+ def optimize!(**options)
533
+ @operands.map! do |op|
534
+ op.optimize(**options) if op.respond_to?(:optimize)
535
+ end
536
+ self
537
+ end
538
+
511
539
  ##
512
540
  # Rewrite operands by yielding each operand. Recursively descends
513
541
  # through operands implementing this method.
@@ -532,7 +560,7 @@ module SPARQL; module Algebra
532
560
  # Returns the SPARQL S-Expression (SSE) representation of this operator.
533
561
  #
534
562
  # @return [Array]
535
- # @see http://openjena.org/wiki/SSE
563
+ # @see https://openjena.org/wiki/SSE
536
564
  def to_sxp_bin
537
565
  operator = [self.class.const_get(:NAME)].flatten.first
538
566
  [operator, *(operands || []).map(&:to_sxp_bin)]
@@ -543,13 +571,6 @@ module SPARQL; module Algebra
543
571
  #
544
572
  # @return [String]
545
573
  def to_sxp
546
- begin
547
- require 'sxp' # @see http://rubygems.org/gems/sxp
548
- rescue LoadError
549
- abort "SPARQL::Algebra::Operator#to_sxp requires the SXP gem (hint: `gem install sxp')."
550
- end
551
- require 'sparql/algebra/sxp_extensions'
552
-
553
574
  to_sxp_bin.to_sxp
554
575
  end
555
576
 
@@ -647,7 +668,7 @@ module SPARQL; module Algebra
647
668
  # @param [RDF::Literal] literal
648
669
  # @return [RDF::Literal::Boolean] `true` or `false`
649
670
  # @raise [TypeError] if the literal could not be coerced to an `RDF::Literal::Boolean`
650
- # @see http://www.w3.org/TR/sparql11-query/#ebv
671
+ # @see https://www.w3.org/TR/sparql11-query/#ebv
651
672
  def boolean(literal)
652
673
  case literal
653
674
  when FalseClass then RDF::Literal::FALSE
@@ -724,7 +745,7 @@ module SPARQL; module Algebra
724
745
  ##
725
746
  # @param [Hash{Symbol => Object}] options
726
747
  # any additional options (see {Operator#initialize})
727
- def initialize(options = {})
748
+ def initialize(**options)
728
749
  super
729
750
  end
730
751
  end # Nullary
@@ -743,7 +764,7 @@ module SPARQL; module Algebra
743
764
  # the operand
744
765
  # @param [Hash{Symbol => Object}] options
745
766
  # any additional options (see {Operator#initialize})
746
- def initialize(arg, options = {})
767
+ def initialize(arg, **options)
747
768
  super
748
769
  end
749
770
  end # Unary
@@ -764,7 +785,7 @@ module SPARQL; module Algebra
764
785
  # the second operand
765
786
  # @param [Hash{Symbol => Object}] options
766
787
  # any additional options (see {Operator#initialize})
767
- def initialize(arg1, arg2, options = {})
788
+ def initialize(arg1, arg2, **options)
768
789
  super
769
790
  end
770
791
  end # Binary
@@ -787,7 +808,7 @@ module SPARQL; module Algebra
787
808
  # the third operand
788
809
  # @param [Hash{Symbol => Object}] options
789
810
  # any additional options (see {Operator#initialize})
790
- def initialize(arg1, arg2, arg3, options = {})
811
+ def initialize(arg1, arg2, arg3, **options)
791
812
  super
792
813
  end
793
814
  end # Ternary
@@ -812,7 +833,7 @@ module SPARQL; module Algebra
812
833
  # the forth operand
813
834
  # @param [Hash{Symbol => Object}] options
814
835
  # any additional options (see {Operator#initialize})
815
- def initialize(arg1, arg2, arg3, arg4, options = {})
836
+ def initialize(arg1, arg2, arg3, arg4, **options)
816
837
  super
817
838
  end
818
839
  end # Ternary