sparql 3.0.1 → 3.1.4

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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +188 -73
  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 +63 -40
  14. data/lib/sparql/algebra/extensions.rb +180 -33
  15. data/lib/sparql/algebra/operator.rb +37 -16
  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 +6 -4
  23. data/lib/sparql/algebra/operator/base.rb +10 -10
  24. data/lib/sparql/algebra/operator/bgp.rb +1 -1
  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 +15 -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 +6 -4
  79. data/lib/sparql/algebra/operator/md5.rb +1 -1
  80. data/lib/sparql/algebra/operator/min.rb +6 -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 +3 -3
  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 +5 -5
  145. data/lib/sparql/grammar.rb +111 -11
  146. data/lib/sparql/grammar/meta.rb +2340 -907
  147. data/lib/sparql/grammar/parser11.rb +69 -69
  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 +38 -65
@@ -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
@@ -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,7 +329,7 @@ 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
@@ -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