sparql 3.1.6 → 3.2.1

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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -22
  3. data/VERSION +1 -1
  4. data/bin/sparql +14 -4
  5. data/lib/sparql/algebra/aggregate.rb +1 -1
  6. data/lib/sparql/algebra/evaluatable.rb +4 -4
  7. data/lib/sparql/algebra/expression.rb +28 -3
  8. data/lib/sparql/algebra/extensions.rb +109 -45
  9. data/lib/sparql/algebra/operator/abs.rb +23 -3
  10. data/lib/sparql/algebra/operator/add.rb +21 -2
  11. data/lib/sparql/algebra/operator/alt.rb +26 -2
  12. data/lib/sparql/algebra/operator/and.rb +25 -3
  13. data/lib/sparql/algebra/operator/asc.rb +20 -1
  14. data/lib/sparql/algebra/operator/ask.rb +17 -1
  15. data/lib/sparql/algebra/operator/avg.rb +20 -2
  16. data/lib/sparql/algebra/operator/base.rb +18 -1
  17. data/lib/sparql/algebra/operator/bgp.rb +13 -1
  18. data/lib/sparql/algebra/operator/bnode.rb +34 -11
  19. data/lib/sparql/algebra/operator/bound.rb +22 -1
  20. data/lib/sparql/algebra/operator/ceil.rb +26 -3
  21. data/lib/sparql/algebra/operator/clear.rb +26 -2
  22. data/lib/sparql/algebra/operator/coalesce.rb +33 -11
  23. data/lib/sparql/algebra/operator/compare.rb +48 -40
  24. data/lib/sparql/algebra/operator/concat.rb +26 -2
  25. data/lib/sparql/algebra/operator/construct.rb +29 -6
  26. data/lib/sparql/algebra/operator/contains.rb +25 -3
  27. data/lib/sparql/algebra/operator/copy.rb +19 -2
  28. data/lib/sparql/algebra/operator/count.rb +53 -7
  29. data/lib/sparql/algebra/operator/create.rb +20 -2
  30. data/lib/sparql/algebra/operator/dataset.rb +27 -2
  31. data/lib/sparql/algebra/operator/datatype.rb +26 -7
  32. data/lib/sparql/algebra/operator/day.rb +24 -6
  33. data/lib/sparql/algebra/operator/delete.rb +29 -2
  34. data/lib/sparql/algebra/operator/delete_data.rb +23 -2
  35. data/lib/sparql/algebra/operator/delete_where.rb +24 -2
  36. data/lib/sparql/algebra/operator/desc.rb +20 -1
  37. data/lib/sparql/algebra/operator/describe.rb +27 -4
  38. data/lib/sparql/algebra/operator/distinct.rb +20 -3
  39. data/lib/sparql/algebra/operator/divide.rb +27 -3
  40. data/lib/sparql/algebra/operator/drop.rb +27 -3
  41. data/lib/sparql/algebra/operator/encode_for_uri.rb +23 -3
  42. data/lib/sparql/algebra/operator/equal.rb +13 -3
  43. data/lib/sparql/algebra/operator/exists.rb +28 -4
  44. data/lib/sparql/algebra/operator/exprlist.rb +15 -2
  45. data/lib/sparql/algebra/operator/extend.rb +64 -6
  46. data/lib/sparql/algebra/operator/filter.rb +27 -5
  47. data/lib/sparql/algebra/operator/floor.rb +26 -3
  48. data/lib/sparql/algebra/operator/function_call.rb +64 -0
  49. data/lib/sparql/algebra/operator/graph.rb +69 -6
  50. data/lib/sparql/algebra/operator/greater_than.rb +14 -4
  51. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +14 -4
  52. data/lib/sparql/algebra/operator/group.rb +105 -8
  53. data/lib/sparql/algebra/operator/group_concat.rb +44 -8
  54. data/lib/sparql/algebra/operator/hours.rb +24 -6
  55. data/lib/sparql/algebra/operator/if.rb +20 -3
  56. data/lib/sparql/algebra/operator/in.rb +18 -1
  57. data/lib/sparql/algebra/operator/insert.rb +24 -2
  58. data/lib/sparql/algebra/operator/insert_data.rb +23 -2
  59. data/lib/sparql/algebra/operator/iri.rb +22 -5
  60. data/lib/sparql/algebra/operator/is_blank.rb +21 -4
  61. data/lib/sparql/algebra/operator/is_iri.rb +21 -4
  62. data/lib/sparql/algebra/operator/is_literal.rb +21 -4
  63. data/lib/sparql/algebra/operator/is_numeric.rb +23 -6
  64. data/lib/sparql/algebra/operator/is_triple.rb +33 -1
  65. data/lib/sparql/algebra/operator/join.rb +56 -1
  66. data/lib/sparql/algebra/operator/lang.rb +26 -1
  67. data/lib/sparql/algebra/operator/lang_matches.rb +23 -2
  68. data/lib/sparql/algebra/operator/lcase.rb +23 -3
  69. data/lib/sparql/algebra/operator/left_join.rb +42 -1
  70. data/lib/sparql/algebra/operator/less_than.rb +14 -4
  71. data/lib/sparql/algebra/operator/less_than_or_equal.rb +14 -4
  72. data/lib/sparql/algebra/operator/load.rb +25 -2
  73. data/lib/sparql/algebra/operator/max.rb +20 -2
  74. data/lib/sparql/algebra/operator/md5.rb +23 -6
  75. data/lib/sparql/algebra/operator/min.rb +22 -4
  76. data/lib/sparql/algebra/operator/minus.rb +65 -7
  77. data/lib/sparql/algebra/operator/minutes.rb +24 -6
  78. data/lib/sparql/algebra/operator/modify.rb +41 -5
  79. data/lib/sparql/algebra/operator/month.rb +24 -6
  80. data/lib/sparql/algebra/operator/move.rb +20 -2
  81. data/lib/sparql/algebra/operator/multiply.rb +27 -4
  82. data/lib/sparql/algebra/operator/negate.rb +24 -4
  83. data/lib/sparql/algebra/operator/not.rb +25 -4
  84. data/lib/sparql/algebra/operator/not_equal.rb +16 -1
  85. data/lib/sparql/algebra/operator/notexists.rb +30 -6
  86. data/lib/sparql/algebra/operator/notin.rb +20 -3
  87. data/lib/sparql/algebra/operator/notoneof.rb +21 -2
  88. data/lib/sparql/algebra/operator/now.rb +25 -6
  89. data/lib/sparql/algebra/operator/object.rb +33 -1
  90. data/lib/sparql/algebra/operator/or.rb +26 -3
  91. data/lib/sparql/algebra/operator/order.rb +71 -2
  92. data/lib/sparql/algebra/operator/path.rb +29 -2
  93. data/lib/sparql/algebra/operator/path_opt.rb +21 -2
  94. data/lib/sparql/algebra/operator/path_plus.rb +21 -2
  95. data/lib/sparql/algebra/operator/path_star.rb +20 -2
  96. data/lib/sparql/algebra/operator/plus.rb +43 -4
  97. data/lib/sparql/algebra/operator/predicate.rb +33 -1
  98. data/lib/sparql/algebra/operator/prefix.rb +24 -3
  99. data/lib/sparql/algebra/operator/project.rb +69 -5
  100. data/lib/sparql/algebra/operator/rand.rb +31 -3
  101. data/lib/sparql/algebra/operator/reduced.rb +20 -3
  102. data/lib/sparql/algebra/operator/regex.rb +27 -19
  103. data/lib/sparql/algebra/operator/replace.rb +27 -7
  104. data/lib/sparql/algebra/operator/reverse.rb +31 -2
  105. data/lib/sparql/algebra/operator/round.rb +26 -3
  106. data/lib/sparql/algebra/operator/same_term.rb +25 -7
  107. data/lib/sparql/algebra/operator/sample.rb +33 -9
  108. data/lib/sparql/algebra/operator/seconds.rb +24 -6
  109. data/lib/sparql/algebra/operator/seq.rb +20 -2
  110. data/lib/sparql/algebra/operator/sequence.rb +4 -11
  111. data/lib/sparql/algebra/operator/sha1.rb +19 -2
  112. data/lib/sparql/algebra/operator/sha256.rb +19 -2
  113. data/lib/sparql/algebra/operator/sha384.rb +19 -2
  114. data/lib/sparql/algebra/operator/sha512.rb +19 -2
  115. data/lib/sparql/algebra/operator/slice.rb +27 -5
  116. data/lib/sparql/algebra/operator/str.rb +22 -2
  117. data/lib/sparql/algebra/operator/strafter.rb +26 -3
  118. data/lib/sparql/algebra/operator/strbefore.rb +26 -3
  119. data/lib/sparql/algebra/operator/strdt.rb +23 -2
  120. data/lib/sparql/algebra/operator/strends.rb +26 -4
  121. data/lib/sparql/algebra/operator/strlang.rb +25 -7
  122. data/lib/sparql/algebra/operator/strlen.rb +24 -3
  123. data/lib/sparql/algebra/operator/strstarts.rb +26 -3
  124. data/lib/sparql/algebra/operator/struuid.rb +30 -10
  125. data/lib/sparql/algebra/operator/subject.rb +33 -1
  126. data/lib/sparql/algebra/operator/substr.rb +24 -3
  127. data/lib/sparql/algebra/operator/subtract.rb +29 -3
  128. data/lib/sparql/algebra/operator/sum.rb +25 -7
  129. data/lib/sparql/algebra/operator/table.rb +76 -4
  130. data/lib/sparql/algebra/operator/timezone.rb +24 -6
  131. data/lib/sparql/algebra/operator/tz.rb +23 -6
  132. data/lib/sparql/algebra/operator/ucase.rb +24 -3
  133. data/lib/sparql/algebra/operator/union.rb +29 -6
  134. data/lib/sparql/algebra/operator/update.rb +46 -4
  135. data/lib/sparql/algebra/operator/using.rb +49 -2
  136. data/lib/sparql/algebra/operator/uuid.rb +28 -9
  137. data/lib/sparql/algebra/operator/with.rb +38 -4
  138. data/lib/sparql/algebra/operator/year.rb +24 -6
  139. data/lib/sparql/algebra/operator.rb +135 -14
  140. data/lib/sparql/algebra/sxp_extensions.rb +3 -3
  141. data/lib/sparql/algebra.rb +20 -3
  142. data/lib/sparql/grammar/meta.rb +1103 -907
  143. data/lib/sparql/grammar/parser11.rb +63 -56
  144. metadata +43 -29
  145. data/lib/sparql/algebra/operator/triple.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 383f162fe715a1ff7eebb78e095a77f1e1823d29045c673b57c5bf68acbcb70f
4
- data.tar.gz: fc7f7ea801e556ee25a77dd340ea7268a7a5d0c39ee9bf2686cb71e887a58446
3
+ metadata.gz: e9bcab10eb6706316b68b44a0fd917d7b735b18a8353b51e49ef124fc966176d
4
+ data.tar.gz: 6da59b4e90fd1b7a7d80c42d941f7c27f6bf95892d17302caf16a13809b03afa
5
5
  SHA512:
6
- metadata.gz: 662583eb75d9cca1a34d8d8299cee4eb7e97dc6d3251982300a077c888523bd63f8350be3ce1fed753692e2f26d056afd7381f26ac8115f6b72a7e8f2e89296e
7
- data.tar.gz: 8cb4ee544f52b9599fb1d5dc6ac3b8a68a7755caded5b3baef706222565c9b59511e69f0921c2b00ece9f3fd22aebff543caa37c93a6d45b02a9e8211660931d
6
+ metadata.gz: d2f99aee7d5b9482bbd653333eeefd215da872291993a2ba138ed9bcfc3f92812672cd1508dfbd11a729d527c9724f0a80ec816c5c84fd2df9933b4b91b5a296
7
+ data.tar.gz: 41a11caf8e85ca4bd01213bb0c0c236314560338dbd2825bd2437afbf4fd9855b994150737dbcad00838dc5ac50e368d72e5bfe2e516566a6319f824bdcacc56
data/README.md CHANGED
@@ -23,8 +23,7 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
23
23
  * Compatible with any [Rack][] or [Sinatra][] application and any Rack-based framework.
24
24
  * Helper method for describing [SPARQL Service Description][SSD]
25
25
  * Implementation Report: {file:etc/earl.html EARL}
26
- * Compatible with Ruby >= 2.2.2.
27
- * Compatible with older Ruby versions with the help of the [Backports][] gem.
26
+ * Compatible with Ruby >= 2.6.
28
27
  * Supports Unicode query strings both on all versions of Ruby.
29
28
  * Provisional support for [SPARQL-star][].
30
29
 
@@ -248,27 +247,27 @@ a full set of RDF formats.
248
247
  ### Querying a repository with a SPARQL query
249
248
 
250
249
  queryable = RDF::Repository.load("etc/doap.ttl")
251
- sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
252
- queryable.query(sse) do |result|
250
+ query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
251
+ queryable.query(query) do |result|
253
252
  result.inspect
254
253
  end
255
254
 
256
255
  ### Executing a SPARQL query against a repository
257
256
 
258
257
  queryable = RDF::Repository.load("etc/doap.ttl")
259
- sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
260
- sse.execute(queryable) do |result|
258
+ query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
259
+ query.execute(queryable) do |result|
261
260
  result.inspect
262
261
  end
263
262
 
264
263
  ### Updating a repository
265
264
 
266
265
  queryable = RDF::Repository.load("etc/doap.ttl")
267
- sse = SPARQL.parse(%(
266
+ update = SPARQL.parse(%(
268
267
  PREFIX doap: <http://usefulinc.com/ns/doap#>
269
268
  INSERT DATA { <https://rubygems> doap:implements <http://www.w3.org/TR/sparql11-update/>}
270
269
  ), update: true)
271
- sse.execute(queryable)
270
+ update.execute(queryable)
272
271
 
273
272
  ### Rendering solutions as JSON, XML, CSV, TSV or HTML
274
273
  queryable = RDF::Repository.load("etc/doap.ttl")
@@ -277,8 +276,15 @@ a full set of RDF formats.
277
276
 
278
277
  ### Parsing a SPARQL query string to SSE
279
278
 
280
- sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
281
- sse.to_sxp #=> (bgp (triple ?s ?p ?o))
279
+ query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
280
+ query.to_sxp #=> (bgp (triple ?s ?p ?o))
281
+
282
+ ### Parsing a SSE to SPARQL query or update string to SPARQL
283
+
284
+ # Note: if the SSE uses extension functions, they either must be XSD casting functions, or custom functions which are registered extensions. (See [SPARQL Extension Functions](#sparql-extension-functions))
285
+
286
+ query = SPARQL::Algebra.parse(%{(bgp (triple ?s ?p ?o))})
287
+ sparql = query.to_sparql #=> "SELECT * WHERE { ?s ?p ?o }"
282
288
 
283
289
  ### Command line processing
284
290
 
@@ -289,6 +295,10 @@ a full set of RDF formats.
289
295
  sparql parse etc/input.rq
290
296
  sparql parse -e "SELECT * WHERE { ?s ?p ?o }"
291
297
 
298
+ # Generate SPARQL Query from SSE
299
+ sparql parse --sse etc/input.sse --format sparql
300
+ sparql parse --sse --format sparql -e "(dataset (<http://usefulinc.com/ns/doap>) (bgp (triple ?s ?p ?o))))"
301
+
292
302
  # Run query using SSE input
293
303
  sparql execute --dataset etc/doap.ttl --sse etc/input.sse
294
304
  sparql execute --sse -e "(dataset (<etc/doap.ttl>) (bgp (triple ?s ?p ?o))))"
@@ -368,19 +378,19 @@ Full documentation available on [Rubydoc.info][SPARQL doc]
368
378
 
369
379
  ## Dependencies
370
380
 
371
- * [Ruby](https://ruby-lang.org/) (>= 2.2.2)
372
- * [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.0)
373
- * [SPARQL::Client](https://rubygems.org/gems/sparql-client) (~> 3.0)
374
- * [SXP](https://rubygems.org/gems/sxp) (~> 1.0)
375
- * [Builder](https://rubygems.org/gems/builder) (>= 3.0.0)
376
- * [JSON](https://rubygems.org/gems/json) (>= 1.8.2)
377
- * Soft dependency on [Linked Data][] (>= 3.0)
378
- * Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (>= 1.7)
381
+ * [Ruby](https://ruby-lang.org/) (>= 2.6)
382
+ * [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2)
383
+ * [SPARQL::Client](https://rubygems.org/gems/sparql-client) (~> 3.1)
384
+ * [SXP](https://rubygems.org/gems/sxp) (~> 1.2)
385
+ * [Builder](https://rubygems.org/gems/builder) (~> 3.2)
386
+ * [JSON](https://rubygems.org/gems/json) (~> 2.6)
387
+ * Soft dependency on [Linked Data][] (>= 3.1)
388
+ * Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (~> 1.12)
379
389
  Falls back to REXML for XML parsing Builder for XML serializing. Nokogiri is much more efficient
380
- * Soft dependency on [Equivalent XML](https://rubygems.org/gems/equivalent-xml) (>= 0.3.0)
390
+ * Soft dependency on [Equivalent XML](https://rubygems.org/gems/equivalent-xml) (>= 0.6)
381
391
  Equivalent XML performs more efficient comparisons of XML Literals when Nokogiri is included
382
- * Soft dependency on [Rack][] (>= 2.0)
383
- * Soft dependency on [Sinatra][] (>= 2.0)
392
+ * Soft dependency on [Rack][] (~> 2.2)
393
+ * Soft dependency on [Sinatra][] (~> 2.1)
384
394
 
385
395
  ## Installation
386
396
 
@@ -450,7 +460,6 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are also included in
450
460
  [RDF.rb]: https://rubydoc.info/github/ruby-rdf/rdf
451
461
  [RDF-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html
452
462
  [SPARQL-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#sparql-query-language
453
- [Backports]: https://rubygems.org/gems/backports
454
463
  [Linked Data]: https://rubygems.org/gems/linkeddata
455
464
  [SPARQL doc]: https://rubydoc.info/github/ruby-rdf/sparql/frames
456
465
  [SPARQL XML]: https://www.w3.org/TR/rdf-sparql-XMLres/
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.6
1
+ 3.2.1
data/bin/sparql CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'rubygems'
3
3
  $:.unshift("../../lib", __FILE__)
4
+ require 'logger'
4
5
  require 'sparql'
5
6
  begin
6
7
  require 'linkeddata'
@@ -44,9 +45,18 @@ def run(input, **options)
44
45
  SPARQL::Grammar.parse(input, **options)
45
46
  end
46
47
 
47
- puts ("\nSSE:\n" + query.to_sse) if options[:debug] || options[:to_sse]
48
+ puts ("\nSSE:\n" + query.to_sse) if options[:debug]
48
49
 
49
- unless options[:to_sse]
50
+ if options[:parse_only]
51
+ case options[:format]
52
+ when :sparql
53
+ puts ("\nSPARQL:\n" + query.to_sparql)
54
+ when nil, :sse
55
+ puts ("\nSSE:\n" + query.to_sse)
56
+ else
57
+ $stderr.puts "Unknown output format for parsing: #{options[:format]}. Use 'sse' or 'sparql'"
58
+ end
59
+ else
50
60
  res = query.execute(options[:dataset], logger: options[:logger])
51
61
  display_results(res, **options)
52
62
  end
@@ -97,7 +107,7 @@ def usage
97
107
  puts " --dataset: File containing RDF graph or dataset"
98
108
  puts " --debug: Display detailed debug output"
99
109
  puts " --execute,-e: Use option argument as the SPARQL input if no query-file given"
100
- puts " --format: Output format for results"
110
+ puts " --format: Output format for results (json, xml, csv, tsv, html, sparql, sse, or another RDF format)"
101
111
  puts " --port,-p Port on which to run server; defaults to 9292"
102
112
  puts " --sse: Query input is in SSE format"
103
113
  puts " --update: Process query as a SPARQL Update"
@@ -150,7 +160,7 @@ end
150
160
 
151
161
  case cmd
152
162
  when 'execute', 'parse'
153
- options[:to_sse] = true if cmd == 'parse'
163
+ options[:parse_only] = true if cmd == 'parse'
154
164
  input ||= ARGV.empty? ? $stdin.read : RDF::Util::File.open_file(ARGV.first).read
155
165
  run(input, **options)
156
166
  when 'query'
@@ -44,7 +44,7 @@ module SPARQL; module Algebra
44
44
  # Enumerable yielding evaluated operands
45
45
  # @return [RDF::Term]
46
46
  # @abstract
47
- def apply(enum)
47
+ def apply(enum, **options)
48
48
  raise NotImplementedError, "#{self.class}#apply(#{operands.map(&:class).join(', ')})"
49
49
  end
50
50
 
@@ -15,16 +15,16 @@ module SPARQL; module Algebra
15
15
  # @abstract
16
16
  def evaluate(bindings, **options)
17
17
  args = operands.map { |operand| operand.evaluate(bindings, depth: options[:depth].to_i + 1, **options) }
18
- options[:memoize] ? memoize(*args) : apply(*args)
18
+ options[:memoize] ? memoize(*args, **options) : apply(*args, **options)
19
19
  end
20
20
 
21
21
  ##
22
22
  # @param [Array<RDF::Term>] operands
23
23
  # evaluated operands
24
24
  # @return [RDF::Term] the memoized result
25
- def memoize(*operands)
25
+ def memoize(*operands, **options)
26
26
  @cache ||= RDF::Util::Cache.new(options[:memoize].is_a?(Integer) ? options[:memoize] : -1)
27
- @cache[operands] ||= apply(*operands)
27
+ @cache[operands] ||= apply(*operands, **options)
28
28
  end
29
29
 
30
30
  ##
@@ -32,7 +32,7 @@ module SPARQL; module Algebra
32
32
  # evaluated operands
33
33
  # @return [RDF::Term]
34
34
  # @abstract
35
- def apply(*operands)
35
+ def apply(*operands, **options)
36
36
  raise NotImplementedError, "#{self.class}#apply(#{operands.map(&:class).join(', ')})"
37
37
  end
38
38
 
@@ -66,9 +66,11 @@ module SPARQL; module Algebra
66
66
  #
67
67
  # @param [Array] sse
68
68
  # a SPARQL S-Expression (SSE) form
69
+ # @param [Hash{Symbol => Object}] options
70
+ # any additional options (see {Operator#initialize})
69
71
  # @return [Expression]
70
- def self.for(*sse)
71
- self.new(sse)
72
+ def self.for(*sse, **options)
73
+ self.new(sse, **options)
72
74
  end
73
75
  class << self; alias_method :[], :for; end
74
76
 
@@ -86,6 +88,13 @@ module SPARQL; module Algebra
86
88
  raise ArgumentError, "invalid SPARQL::Algebra::Expression form: #{sse.inspect}" unless sse.is_a?(Array)
87
89
 
88
90
  operator = Operator.for(sse.first, sse.length - 1)
91
+
92
+ # If we don't find an operator, and sse.first is an extension IRI, use a function call
93
+ if !operator && sse.first.is_a?(RDF::URI) && self.extension?(sse.first)
94
+ operator = Operator.for(:function_call, sse.length)
95
+ sse.unshift(:function_call)
96
+ end
97
+
89
98
  unless operator
90
99
  return case sse.first
91
100
  when Array
@@ -115,11 +124,16 @@ module SPARQL; module Algebra
115
124
  end
116
125
 
117
126
  debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
127
+ logger = options[:logger]
118
128
  options.delete_if {|k, v| [:debug, :logger, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
119
129
  begin
120
130
  operator.new(*operands, **options)
121
131
  rescue ArgumentError => e
122
- error(options) {"Operator=#{operator.inspect}: #{e}"}
132
+ if logger
133
+ logger.error("Operator=#{operator.inspect}: #{e}")
134
+ else
135
+ raise "Operator=#{operator.inspect}: #{e}"
136
+ end
123
137
  end
124
138
  end
125
139
 
@@ -163,6 +177,17 @@ module SPARQL; module Algebra
163
177
  @extensions ||= {}
164
178
  end
165
179
 
180
+ ##
181
+ # Is an extension function available?
182
+ #
183
+ # It's either a registered extension, or an XSD casting function
184
+ #
185
+ # @param [RDF::URI] function
186
+ # @return [Boolean]
187
+ def self.extension?(function)
188
+ function.to_s.start_with?(RDF::XSD.to_s) || self.extensions[function]
189
+ end
190
+
166
191
  ##
167
192
  # Invoke an extension function.
168
193
  #
@@ -43,6 +43,15 @@ class Object
43
43
  def deep_dup
44
44
  dup
45
45
  end
46
+
47
+ ##
48
+ #
49
+ # Returns a partial SPARQL grammar for this term.
50
+ #
51
+ # @return [String]
52
+ def to_sparql(**options)
53
+ to_sxp(**options)
54
+ end
46
55
  end
47
56
 
48
57
  ##
@@ -57,20 +66,14 @@ class Array
57
66
  end
58
67
 
59
68
  ##
60
- # Evaluates the array using the given variable `bindings`.
61
69
  #
62
- # In this case, the Array has two elements, the first of which is
63
- # an XSD datatype, and the second is the expression to be evaluated.
64
- # The result is cast as a literal of the appropriate type
70
+ # Returns a partial SPARQL grammar for this array.
65
71
  #
66
- # @param [RDF::Query::Solution] bindings
67
- # a query solution containing zero or more variable bindings
68
- # @param [Hash{Symbol => Object}] options ({})
69
- # options passed from query
70
- # @return [RDF::Term]
71
- # @see SPARQL::Algebra::Expression.evaluate
72
- def evaluate(bindings, **options)
73
- SPARQL::Algebra::Expression.extension(*self.map {|o| o.evaluate(bindings, **options)})
72
+ # @param [String] delimiter (" ")
73
+ # If the first element is an IRI, treat it as an extension function
74
+ # @return [String]
75
+ def to_sparql(delimiter: " ", **options)
76
+ map {|e| e.to_sparql(**options)}.join(delimiter)
74
77
  end
75
78
 
76
79
  ##
@@ -216,15 +219,6 @@ end
216
219
  ##
217
220
  # Extensions for Ruby's `Hash` class.
218
221
  class Hash
219
- ##
220
- # Returns the SXP representation of this object, defaults to `self`.
221
- #
222
- # @return [String]
223
- def to_sxp_bin
224
- to_a.to_sxp_bin
225
- end
226
- def to_sxp; to_sxp_bin; end
227
-
228
222
  ##
229
223
  # A duplicate of this hash.
230
224
  #
@@ -278,24 +272,19 @@ module RDF::Term
278
272
  # @see SPARQL::Algebra::Expression#optimize
279
273
  def optimize(**options)
280
274
  optimized = self.deep_dup
281
- optimized.lexical = nil if optimized.respond_to?(:lexical=)
282
- optimized
275
+ #optimized.lexical = nil if optimized.respond_to?(:lexical=)
276
+ #optimized
283
277
  end
284
- end # RDF::Term
285
278
 
286
- class RDF::Literal::Double
287
279
  ##
288
- # Returns the SXP representation of this object.
280
+ #
281
+ # Returns a partial SPARQL grammar for this term.
289
282
  #
290
283
  # @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
284
+ def to_sparql(**options)
285
+ to_sxp(**options)
297
286
  end
298
- end
287
+ end # RDF::Term
299
288
 
300
289
  # Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
301
290
  module RDF::Queryable
@@ -343,6 +332,15 @@ module RDF::Queryable
343
332
  query_without_sparql(pattern, **options, &block)
344
333
  end
345
334
  end
335
+
336
+ ##
337
+ #
338
+ # Returns a partial SPARQL grammar for this term.
339
+ #
340
+ # @return [String]
341
+ def to_sparql(**options)
342
+ raise NotImplementedError, "SPARQL::Algebra '#{first}' operator not implemented"
343
+ end
346
344
  end
347
345
 
348
346
  class RDF::Statement
@@ -361,9 +359,31 @@ class RDF::Statement
361
359
  ##
362
360
  # Returns an S-Expression (SXP) representation
363
361
  #
362
+ # @param [Hash{Symbol => RDF::URI}] prefixes (nil)
363
+ # @param [RDF::URI] base_uri (nil)
364
364
  # @return [String]
365
- def to_sxp
366
- to_sxp_bin.to_sxp
365
+ def to_sxp(prefixes: nil, base_uri: nil)
366
+ to_sxp_bin.to_sxp(prefixes: prefixes, base_uri: base_uri)
367
+ end
368
+
369
+ ##
370
+ #
371
+ # Returns a partial SPARQL grammar for this term.
372
+ #
373
+ # @param [Boolean] as_statement (false) serialize as < ... >, otherwise TRIPLE(...)
374
+ # @return [String]
375
+ def to_sparql(as_statement: false, **options)
376
+ if as_statement
377
+ to_triple.map do |term|
378
+ if term.is_a?(::RDF::Statement)
379
+ "<<" + term.to_sparql(as_statement: true, **options) + ">>"
380
+ else
381
+ term.to_sparql(**options)
382
+ end
383
+ end.join(" ")
384
+ else
385
+ "TRIPLE(#{to_triple.to_sparql(as_statement: true, **options)})"
386
+ end
367
387
  end
368
388
 
369
389
  ##
@@ -411,6 +431,38 @@ class RDF::Query
411
431
  end
412
432
  end
413
433
 
434
+ ##
435
+ #
436
+ # Returns a partial SPARQL grammar for this query.
437
+ #
438
+ # @param [Boolean] top_level (true)
439
+ # Treat this as a top-level, generating SELECT ... WHERE {}
440
+ # @param [Array<Operator>] filter_ops ([])
441
+ # Filter Operations
442
+ # @return [String]
443
+ def to_sparql(top_level: true, filter_ops: [], **options)
444
+ str = @patterns.map { |e| e.to_sparql(as_statement: true, top_level: false, **options) }.join(". \n")
445
+ str = "GRAPH #{graph_name.to_sparql(**options)} {\n#{str}\n}\n" if graph_name
446
+ if top_level
447
+ SPARQL::Algebra::Operator.to_sparql(str, filter_ops: filter_ops, **options)
448
+ else
449
+ # Filters
450
+ filter_ops.each do |op|
451
+ str << "\nFILTER (#{op.to_sparql(**options)}) ."
452
+ end
453
+
454
+ # Extensons
455
+ extensions = options.fetch(:extensions, [])
456
+ extensions.each do |as, expression|
457
+ v = expression.to_sparql(as_statement: true, **options)
458
+ v = "<< #{v} >>" if expression.is_a?(RDF::Statement)
459
+ str << "\nBIND (" << v << " AS " << as.to_sparql(**options) << ") ."
460
+ end
461
+ str = "{#{str}}" unless filter_ops.empty? && extensions.empty?
462
+ str
463
+ end
464
+ end
465
+
414
466
  ##
415
467
  # Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
416
468
  #
@@ -462,11 +514,11 @@ class RDF::Query
462
514
  def optimize!(**options)
463
515
  @patterns = @patterns.map do |pattern|
464
516
  components = pattern.to_quad.map do |term|
465
- if term.respond_to?(:lexical=)
466
- term.dup.instance_eval {@lexical = nil; self}
467
- else
517
+ #if term.respond_to?(:lexical=)
518
+ # term.dup.instance_eval {@lexical = nil; self}
519
+ #else
468
520
  term
469
- end
521
+ #end
470
522
  end
471
523
  RDF::Query::Pattern.from(components, **pattern.options)
472
524
  end
@@ -530,11 +582,15 @@ class RDF::Query::Variable
530
582
  self
531
583
  end
532
584
 
533
- # Display variable as SXP
534
- # @return [Array]
535
- def to_sxp
536
- prefix = distinguished? ? (existential? ? '$' : '?') : (existential? ? '$$' : '??')
537
- unbound? ? "#{prefix}#{name}".to_sym.to_sxp : ["#{prefix}#{name}".to_sym, value].to_sxp
585
+ ##
586
+ #
587
+ # Returns a partial SPARQL grammar for this term.
588
+ #
589
+ # The Non-distinguished form (`??xxx`) is not part of the grammar, so replace with a blank-node
590
+ #
591
+ # @return [String]
592
+ def to_sparql(**options)
593
+ self.distinguished? ? super : "_:_nd#{self.name}"
538
594
  end
539
595
  end # RDF::Query::Variable
540
596
 
@@ -576,5 +632,13 @@ class RDF::Query::Solution
576
632
  def to_sxp_bin
577
633
  to_a.to_sxp_bin
578
634
  end
579
- def to_sxp; to_sxp_bin; end
635
+
636
+ # Transform Solution into an SXP
637
+ #
638
+ # @param [Hash{Symbol => RDF::URI}] prefixes (nil)
639
+ # @param [RDF::URI] base_uri (nil)
640
+ # @return [String]
641
+ def to_sxp(prefixes: nil, base_uri: nil)
642
+ to_sxp_bin.to_sxp(prefixes: prefixes, base_uri: base_uri)
643
+ end
580
644
  end # RDF::Query::Solution
@@ -3,8 +3,19 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL logical `abs` operator.
5
5
  #
6
- # @example
7
- # (abs ?x)
6
+ # [121] BuiltInCall ::= ... | 'ABS' '(' Expression ')'
7
+ #
8
+ # @example SPARQL Grammar
9
+ # PREFIX : <http://example.org/>
10
+ # SELECT * WHERE {
11
+ # ?s :num ?num
12
+ # FILTER(ABS(?num) >= 2)
13
+ # }
14
+ #
15
+ # @example SSE
16
+ # (prefix ((: <http://example.org/>))
17
+ # (filter (>= (abs ?num) 2)
18
+ # (bgp (triple ?s :num ?num))))
8
19
  #
9
20
  # @see https://www.w3.org/TR/sparql11-query/#func-abs
10
21
  # @see https://www.w3.org/TR/xpath-functions/#func-abs
@@ -20,12 +31,21 @@ module SPARQL; module Algebra
20
31
  # the operand
21
32
  # @return [RDF::Literal] literal of same type
22
33
  # @raise [TypeError] if the operand is not a numeric value
23
- def apply(operand)
34
+ def apply(operand, **options)
24
35
  case operand
25
36
  when RDF::Literal::Numeric then operand.abs
26
37
  else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}"
27
38
  end
28
39
  end
40
+
41
+ ##
42
+ #
43
+ # Returns a partial SPARQL grammar for this operator.
44
+ #
45
+ # @return [String]
46
+ def to_sparql(**options)
47
+ "ABS(#{operands.first.to_sparql(**options)})"
48
+ end
29
49
  end # Abs
30
50
  end # Operator
31
51
  end; end # SPARQL::Algebra
@@ -6,8 +6,15 @@ module SPARQL; module Algebra
6
6
  #
7
7
  # The ADD operation is a shortcut for inserting all data from an input graph into a destination graph. Data from the input graph is not affected, and initial data from the destination graph, if any, is kept intact.
8
8
  #
9
- # @example
10
- # (add default <a>)
9
+ # [35] Add ::= "ADD" "SILENT"? GraphOrDefault "TO" GraphOrDefault
10
+ #
11
+ # @example SPARQL Update
12
+ # PREFIX : <http://example.org/>
13
+ # ADD DEFAULT TO :g1
14
+ #
15
+ # @example SSE
16
+ # (prefix ((: <http://example.org/>))
17
+ # (update (add default :g1)))
11
18
  #
12
19
  # @see https://www.w3.org/TR/sparql11-update/#add
13
20
  class Add < Operator
@@ -51,6 +58,18 @@ module SPARQL; module Algebra
51
58
  end
52
59
  queryable
53
60
  end
61
+
62
+ ##
63
+ #
64
+ # Returns a partial SPARQL grammar for this operator.
65
+ #
66
+ # @return [String]
67
+ def to_sparql(**options)
68
+ *args, last = operands.dup
69
+ args += [:TO, last]
70
+
71
+ "ADD " + args.to_sparql(**options)
72
+ end
54
73
  end # Add
55
74
  end # Operator
56
75
  end; end # SPARQL::Algebra
@@ -7,8 +7,23 @@ module SPARQL; module Algebra
7
7
  #
8
8
  # eval(Path(X, alt(P,Q), Y)) = Union(eval(Path(X, P, Y)), eval(Path(X, Q, Y)))
9
9
  #
10
- # @example
11
- # (alt a b)
10
+ # [89] PathAlternative ::= PathSequence ( '|' PathSequence )*
11
+ #
12
+ # @example SPARQL Query
13
+ # PREFIX : <http://www.example.org/>
14
+ # SELECT ?t
15
+ # WHERE {
16
+ # :a :p1|:p2/:p3|:p4 ?t
17
+ # }
18
+ #
19
+ # @example SSE
20
+ # (prefix ((: <http://www.example.org/>))
21
+ # (project (?t)
22
+ # (path :a
23
+ # (alt
24
+ # (alt :p1 (seq :p2 :p3))
25
+ # :p4)
26
+ # ?t)))
12
27
  #
13
28
  # @see https://www.w3.org/TR/sparql11-query/#defn_evalPP_alternative
14
29
  class Alt < Operator::Binary
@@ -57,6 +72,15 @@ module SPARQL; module Algebra
57
72
  query = Union.new(qa, qb)
58
73
  queryable.query(query, depth: options[:depth].to_i + 1, **options, &block)
59
74
  end
75
+
76
+ ##
77
+ #
78
+ # Returns a partial SPARQL grammar for this operator.
79
+ #
80
+ # @return [String]
81
+ def to_sparql(**options)
82
+ "(#{operands.first.to_sparql(**options)}|#{operands.last.to_sparql(**options)})"
83
+ end
60
84
  end # Alt
61
85
  end # Operator
62
86
  end; end # SPARQL::Algebra
@@ -3,9 +3,22 @@ module SPARQL; module Algebra
3
3
  ##
4
4
  # The SPARQL logical `and` operator.
5
5
  #
6
- # @example
7
- # (&& ?x ?y)
8
- # (and ?x ?y)
6
+ # [112] ConditionalAndExpression::= ValueLogical ( '&&' ValueLogical )*
7
+ #
8
+ # @example SPARQL Grammar
9
+ # PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
10
+ # PREFIX : <http://example.org/ns#>
11
+ # SELECT ?a
12
+ # WHERE { ?a :p ?v .
13
+ # FILTER ("true"^^xsd:boolean && ?v) .
14
+ # }
15
+ #
16
+ # @example SSE
17
+ # (prefix
18
+ # ((xsd: <http://www.w3.org/2001/XMLSchema#>) (: <http://example.org/ns#>))
19
+ # (project (?a)
20
+ # (filter (&& true ?v)
21
+ # (bgp (triple ?a :p ?v)))))
9
22
  #
10
23
  # @see https://www.w3.org/TR/sparql11-query/#func-logical-and
11
24
  # @see https://www.w3.org/TR/sparql11-query/#evaluation
@@ -60,6 +73,15 @@ module SPARQL; module Algebra
60
73
  else RDF::Literal(left && right)
61
74
  end
62
75
  end
76
+
77
+ ##
78
+ #
79
+ # Returns a partial SPARQL grammar for this operator.
80
+ #
81
+ # @return [String]
82
+ def to_sparql(**options)
83
+ "(#{operands.first.to_sparql(**options)} && #{operands.last.to_sparql(**options)})"
84
+ end
63
85
  end # And
64
86
  end # Operator
65
87
  end; end # SPARQL::Algebra