sparql 3.1.2 → 3.1.7

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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -12
  3. data/VERSION +1 -1
  4. data/bin/sparql +20 -11
  5. data/lib/sinatra/sparql.rb +1 -1
  6. data/lib/sparql.rb +5 -6
  7. data/lib/sparql/algebra.rb +4 -4
  8. data/lib/sparql/algebra/aggregate.rb +1 -1
  9. data/lib/sparql/algebra/evaluatable.rb +4 -4
  10. data/lib/sparql/algebra/expression.rb +46 -31
  11. data/lib/sparql/algebra/extensions.rb +52 -25
  12. data/lib/sparql/algebra/operator.rb +22 -17
  13. data/lib/sparql/algebra/operator/abs.rb +1 -1
  14. data/lib/sparql/algebra/operator/avg.rb +8 -1
  15. data/lib/sparql/algebra/operator/bgp.rb +1 -1
  16. data/lib/sparql/algebra/operator/bnode.rb +1 -1
  17. data/lib/sparql/algebra/operator/ceil.rb +1 -1
  18. data/lib/sparql/algebra/operator/compare.rb +39 -33
  19. data/lib/sparql/algebra/operator/construct.rb +2 -1
  20. data/lib/sparql/algebra/operator/contains.rb +1 -1
  21. data/lib/sparql/algebra/operator/count.rb +1 -1
  22. data/lib/sparql/algebra/operator/dataset.rb +1 -1
  23. data/lib/sparql/algebra/operator/datatype.rb +1 -1
  24. data/lib/sparql/algebra/operator/day.rb +1 -1
  25. data/lib/sparql/algebra/operator/delete.rb +2 -2
  26. data/lib/sparql/algebra/operator/delete_where.rb +1 -1
  27. data/lib/sparql/algebra/operator/divide.rb +1 -1
  28. data/lib/sparql/algebra/operator/encode_for_uri.rb +1 -1
  29. data/lib/sparql/algebra/operator/equal.rb +1 -1
  30. data/lib/sparql/algebra/operator/extend.rb +2 -12
  31. data/lib/sparql/algebra/operator/floor.rb +1 -1
  32. data/lib/sparql/algebra/operator/graph.rb +1 -1
  33. data/lib/sparql/algebra/operator/greater_than.rb +3 -2
  34. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +2 -2
  35. data/lib/sparql/algebra/operator/group_concat.rb +1 -1
  36. data/lib/sparql/algebra/operator/hours.rb +1 -1
  37. data/lib/sparql/algebra/operator/insert.rb +1 -1
  38. data/lib/sparql/algebra/operator/iri.rb +1 -1
  39. data/lib/sparql/algebra/operator/is_blank.rb +1 -1
  40. data/lib/sparql/algebra/operator/is_iri.rb +1 -1
  41. data/lib/sparql/algebra/operator/is_literal.rb +1 -1
  42. data/lib/sparql/algebra/operator/is_numeric.rb +1 -1
  43. data/lib/sparql/algebra/operator/is_triple.rb +30 -0
  44. data/lib/sparql/algebra/operator/lang.rb +1 -1
  45. data/lib/sparql/algebra/operator/lang_matches.rb +1 -1
  46. data/lib/sparql/algebra/operator/lcase.rb +1 -1
  47. data/lib/sparql/algebra/operator/left_join.rb +4 -0
  48. data/lib/sparql/algebra/operator/less_than.rb +3 -2
  49. data/lib/sparql/algebra/operator/less_than_or_equal.rb +2 -2
  50. data/lib/sparql/algebra/operator/max.rb +8 -1
  51. data/lib/sparql/algebra/operator/md5.rb +1 -1
  52. data/lib/sparql/algebra/operator/min.rb +8 -1
  53. data/lib/sparql/algebra/operator/minutes.rb +1 -1
  54. data/lib/sparql/algebra/operator/month.rb +1 -1
  55. data/lib/sparql/algebra/operator/multiply.rb +1 -1
  56. data/lib/sparql/algebra/operator/negate.rb +1 -1
  57. data/lib/sparql/algebra/operator/not.rb +1 -1
  58. data/lib/sparql/algebra/operator/not_equal.rb +3 -1
  59. data/lib/sparql/algebra/operator/now.rb +1 -1
  60. data/lib/sparql/algebra/operator/object.rb +27 -0
  61. data/lib/sparql/algebra/operator/order.rb +7 -1
  62. data/lib/sparql/algebra/operator/plus.rb +1 -1
  63. data/lib/sparql/algebra/operator/predicate.rb +27 -0
  64. data/lib/sparql/algebra/operator/rand.rb +1 -1
  65. data/lib/sparql/algebra/operator/regex.rb +1 -1
  66. data/lib/sparql/algebra/operator/replace.rb +1 -1
  67. data/lib/sparql/algebra/operator/round.rb +1 -1
  68. data/lib/sparql/algebra/operator/same_term.rb +1 -1
  69. data/lib/sparql/algebra/operator/sample.rb +9 -2
  70. data/lib/sparql/algebra/operator/seconds.rb +1 -1
  71. data/lib/sparql/algebra/operator/sha1.rb +1 -1
  72. data/lib/sparql/algebra/operator/sha256.rb +1 -1
  73. data/lib/sparql/algebra/operator/sha384.rb +1 -1
  74. data/lib/sparql/algebra/operator/sha512.rb +1 -1
  75. data/lib/sparql/algebra/operator/str.rb +1 -1
  76. data/lib/sparql/algebra/operator/strafter.rb +1 -1
  77. data/lib/sparql/algebra/operator/strbefore.rb +1 -1
  78. data/lib/sparql/algebra/operator/strdt.rb +1 -1
  79. data/lib/sparql/algebra/operator/strends.rb +1 -1
  80. data/lib/sparql/algebra/operator/strlang.rb +1 -1
  81. data/lib/sparql/algebra/operator/strlen.rb +1 -1
  82. data/lib/sparql/algebra/operator/strstarts.rb +1 -1
  83. data/lib/sparql/algebra/operator/struuid.rb +1 -1
  84. data/lib/sparql/algebra/operator/subject.rb +29 -0
  85. data/lib/sparql/algebra/operator/substr.rb +1 -1
  86. data/lib/sparql/algebra/operator/subtract.rb +1 -1
  87. data/lib/sparql/algebra/operator/sum.rb +1 -1
  88. data/lib/sparql/algebra/operator/timezone.rb +1 -1
  89. data/lib/sparql/algebra/operator/triple.rb +27 -0
  90. data/lib/sparql/algebra/operator/tz.rb +1 -1
  91. data/lib/sparql/algebra/operator/ucase.rb +1 -1
  92. data/lib/sparql/algebra/operator/uuid.rb +1 -1
  93. data/lib/sparql/algebra/operator/with.rb +1 -1
  94. data/lib/sparql/algebra/operator/year.rb +1 -1
  95. data/lib/sparql/extensions.rb +6 -12
  96. data/lib/sparql/grammar.rb +2 -28
  97. data/lib/sparql/grammar/meta.rb +5739 -2365
  98. data/lib/sparql/grammar/parser11.rb +119 -59
  99. data/lib/sparql/grammar/terminals11.rb +3 -0
  100. data/lib/sparql/results.rb +17 -9
  101. metadata +31 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 774ff66fd7f181a0b4aa7eb36c5c3a11c10c7a8c2b02728c358825ab7690e3be
4
- data.tar.gz: 7f71db0b80c137af45f57b54ed335ab8d0d327e9c427d0eadb1fc70ae2c4ba62
3
+ metadata.gz: dcc81f10a741a9060299f9b3f54e1a7a35442fb7b68b7a79b2d1b67e16c0108a
4
+ data.tar.gz: aa6dd52a91d86b9a981edb5f4d56bdd0da288932ece265578ba773bb289a5fe0
5
5
  SHA512:
6
- metadata.gz: fa064eb95aa7106739015840d71ea30bf89c853225517aee1d89fefe0f523fda1d3ed62fbf0c5e1f03d7c8fe10ae41dd14d2585754ea51539c86cc918a468313
7
- data.tar.gz: 67b69a2e66be71bc5a73d74da3243f10950e56165c971f65536bd800edfd8edc25eb9d198cd84c220ae6f0e58a7819adfc9e2dd45439931449224d57227e240b
6
+ metadata.gz: 64669164cff7cdd9b1580bba3576247dafff7feef77ab63ba355afb51bded23a8f996ca952332abea8a4d06f8aca4ae5db25702a69748f8577aeedb720c5f788
7
+ data.tar.gz: d269e6c8edf25e5929876d292290257527f4bfe69def6fbe3e4500283a0ed2418ac4df5420a24fd35f826970ae63c3d679ba998d27184851cc8fe58648521df7
data/README.md CHANGED
@@ -3,10 +3,9 @@
3
3
  This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/sparql.png)](https://badge.fury.io/rb/sparql)
6
-
7
- [![Build Status](https://travis-ci.org/ruby-rdf/sparql.png?branch=master)](https://travis-ci.org/ruby-rdf/sparql)
8
-
9
- [![Coverage Status](https://coveralls.io/repos/ruby-rdf/sparql/badge.svg)](https://coveralls.io/r/ruby-rdf/sparql)
6
+ [![Build Status](https://github.com/ruby-rdf/sparql/workflows/CI/badge.svg?branch=develop)](https://github.com/ruby-rdf/sparql/actions?query=workflow%3ACI)
7
+ [![Coverage Status](https://coveralls.io/repos/ruby-rdf/sparql/badge.svg?branch=develop)](https://coveralls.io/r/ruby-rdf/sparql?branch=develop)
8
+ [![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf)
10
9
 
11
10
  ## Features
12
11
 
@@ -27,7 +26,7 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
27
26
  * Compatible with Ruby >= 2.2.2.
28
27
  * Compatible with older Ruby versions with the help of the [Backports][] gem.
29
28
  * Supports Unicode query strings both on all versions of Ruby.
30
- * Provisional support for [SPARQL*][].
29
+ * Provisional support for [SPARQL-star][].
31
30
 
32
31
  ## Description
33
32
 
@@ -97,9 +96,9 @@ Then, use the function in a query:
97
96
 
98
97
  See {SPARQL::Algebra::Expression.register_extension} for details.
99
98
 
100
- ### SPARQLStar (SPARQL*)
99
+ ### SPARQLStar (SPARQL-star)
101
100
 
102
- The gem supports [SPARQL*][] where patterns may include sub-patterns recursively, for a kind of Reification.
101
+ The gem supports [SPARQL-star][] where patterns may include sub-patterns recursively, for a kind of Reification.
103
102
 
104
103
  For example, the following Turtle* file uses a statement as the subject of another statement:
105
104
 
@@ -160,7 +159,7 @@ As well as a `CONSTRUCT`:
160
159
  <<?bob foaf:age ?age>> ?b ?c .
161
160
  }
162
161
 
163
- Note that results can be serialized only when the format supports [RDF*][].
162
+ Note that results can be serialized only when the format supports [RDF-star][].
164
163
 
165
164
  #### SPARQL results
166
165
 
@@ -419,7 +418,9 @@ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange develo
419
418
  list in the the `README`. Alphabetical order applies.
420
419
  * Do note that in order for us to merge any non-trivial changes (as a rule
421
420
  of thumb, additions larger than about 15 lines of code), we need an
422
- explicit [public domain dedication][PDD] on record from you.
421
+ explicit [public domain dedication][PDD] on record from you,
422
+ which you will be asked to agree to on the first commit to a repo within the organization.
423
+ Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization.
423
424
 
424
425
  ## License
425
426
 
@@ -437,7 +438,7 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are also included in
437
438
  [Rack::LinkedData]: https://rubygems.org/gems/rack-linkeddata
438
439
  [YARD]: https://yardoc.org/
439
440
  [YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
440
- [PDD]: https://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
441
+ [PDD]: https://unlicense.org/#unlicensing-contributions
441
442
  [SPARQL]: https://en.wikipedia.org/wiki/SPARQL
442
443
  [SPARQL 1.0]: https://www.w3.org/TR/sparql11-query/
443
444
  [SPARQL 1.0 tests]:https://www.w3.org/2001/sw/DataAccess/tests/
@@ -447,8 +448,8 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are also included in
447
448
  [grammar]: https://www.w3.org/TR/sparql11-query/#grammar
448
449
  [RDF 1.1]: https://www.w3.org/TR/rdf11-concepts
449
450
  [RDF.rb]: https://rubydoc.info/github/ruby-rdf/rdf
450
- [RDF*]: https://lists.w3.org/Archives/Public/public-rdf-star/
451
- [SPARQL*]: https://arxiv.org/pdf/1406.3399.pdf
451
+ [RDF-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html
452
+ [SPARQL-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#sparql-query-language
452
453
  [Backports]: https://rubygems.org/gems/backports
453
454
  [Linked Data]: https://rubygems.org/gems/linkeddata
454
455
  [SPARQL doc]: https://rubydoc.info/github/ruby-rdf/sparql/frames
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.2
1
+ 3.1.7
data/bin/sparql CHANGED
@@ -38,7 +38,7 @@ def run(input, **options)
38
38
  end
39
39
 
40
40
  query = if options[:sse]
41
- SPARQL::Algebra.parse(input, debug: options[:debug], update: options[:update])
41
+ SPARQL::Algebra.parse(input, logger: options[:logger], update: options[:update])
42
42
  else
43
43
  # Only do grammar debugging if we're generating SSE
44
44
  SPARQL::Grammar.parse(input, **options)
@@ -47,7 +47,7 @@ def run(input, **options)
47
47
  puts ("\nSSE:\n" + query.to_sse) if options[:debug] || options[:to_sse]
48
48
 
49
49
  unless options[:to_sse]
50
- res = query.execute(options[:dataset], debug: options[:debug])
50
+ res = query.execute(options[:dataset], logger: options[:logger])
51
51
  display_results(res, **options)
52
52
  end
53
53
  end
@@ -60,6 +60,11 @@ def server(options)
60
60
  register Sinatra::SPARQL
61
61
  set :repository, repository
62
62
 
63
+ before do
64
+ options[:logger].info "#{request.request_method} [#{request.path_info}], " +
65
+ params.merge(Accept: request.accept.map(&:to_s)).map {|k,v| "#{k}=#{v}" unless k.to_s == "content"}.join(" ")
66
+ end
67
+
63
68
  get '/' do
64
69
  if params["query"]
65
70
  query = params["query"].to_s.match(/^http:/) ? RDF::Util::File.open_file(params["query"]) : ::CGI.unescape(params["query"].to_s)
@@ -78,20 +83,19 @@ def server(options)
78
83
  SPARQL.execute(params['query'], settings.repository)
79
84
  end
80
85
  end
81
- Rack::Server.start(app: app)
86
+ Rack::Server.start(app: app, Port: options.fetch(:port, 9292))
82
87
  rescue LoadError
83
88
  $stderr.puts "Running SPARQL server requires Rack to be in environment: #{$!.message}"
84
89
  end
85
90
 
86
91
  def usage
87
- puts "Usage: #{$0} execute [options] query-file Execute a query against the specified dataset"
88
- puts " #{$0} parse [options] query-file Parse a query into SPARQL S-Expressions (SSE)"
89
- puts " #{$0} query [options] end-point query-file Run the query against a remote end-point"
90
- puts " #{$0} server [options] dataset-file Start a server initialized from the specified dataset"
92
+ puts "Usage: #{File.basename($0)} execute [options] query-file Execute a query against the specified dataset"
93
+ puts " #{File.basename($0)} parse [options] query-file Parse a query into SPARQL S-Expressions (SSE)"
94
+ puts " #{File.basename($0)} query [options] end-point query-file Run the query against a remote end-point"
95
+ puts " #{File.basename($0)} server [options] dataset-file Start a server initialized from the specified dataset"
91
96
  puts "Options:"
92
97
  puts " --dataset: File containing RDF graph or dataset"
93
98
  puts " --debug: Display detailed debug output"
94
- puts " --debug: Display detailed debug output"
95
99
  puts " --execute,-e: Use option argument as the SPARQL input if no query-file given"
96
100
  puts " --format: Output format for results"
97
101
  puts " --port,-p Port on which to run server; defaults to 9292"
@@ -116,20 +120,25 @@ opts = GetoptLong.new(
116
120
  ["--help", "-?", GetoptLong::NO_ARGUMENT]
117
121
  )
118
122
 
123
+ logger = Logger.new(STDERR)
124
+ logger.level = Logger::WARN
125
+ logger.formatter = lambda {|severity, datetime, progname, msg| "%5s %s\n" % [severity, msg]}
126
+
119
127
  options = {
120
128
  dataset: RDF::Repository.new,
129
+ logger: logger,
121
130
  }
122
131
 
123
132
  opts.each do |opt, arg|
124
133
  case opt
125
- when '--dataset' then options[:dataset].load(arg, rdfstar: :PG)
126
- when '--debug' then options[:debug] = true
134
+ when '--dataset' then options[:dataset].load(arg, rdfstar: true)
135
+ when '--debug' then options[:debug] = true ; logger.level = Logger::DEBUG
127
136
  when '--execute' then input = arg
128
137
  when '--format' then options[:format] = arg.to_sym
129
138
  when '--port' then options[:port] = arg.to_i
130
139
  when '--sse' then options[:sse] = true
131
140
  when '--update' then options[:update] = true
132
- when '--verbose' then options[:verbose] = true
141
+ when '--verbose' then options[:verbose] = true ; logger.level = Logger::INFO
133
142
  when "--help" then usage
134
143
  end
135
144
  end
@@ -103,7 +103,7 @@ module Sinatra
103
103
  app.helpers(Sinatra::SPARQL::Helpers)
104
104
  app.send(:include, ::SPARQL)
105
105
  app.send(:include, ::RDF)
106
- app.send(:include, ::LinkedData)
106
+ app.send(:include, ::LinkedData) if defined?(::LinkedData)
107
107
  end
108
108
  end
109
109
  end
data/lib/sparql.rb CHANGED
@@ -1,4 +1,6 @@
1
+ require 'sxp'
1
2
  require 'sparql/extensions'
3
+ require 'sparql/algebra/sxp_extensions'
2
4
 
3
5
  ##
4
6
  # A SPARQL for RDF.rb.
@@ -72,12 +74,9 @@ module SPARQL
72
74
  query = self.parse(query, **options)
73
75
  query = query.optimize(**options) if options[:optimize]
74
76
  queryable = queryable || RDF::Repository.new
75
-
76
- case options.fetch(:debug, nil)
77
- when TrueClass
78
- puts query.to_sxp
79
- when Array
80
- options[:debug] << query.to_sxp
77
+
78
+ if options[:logger]
79
+ options[:logger].debug("SPARQL.execute") {SXP::Generator.string query.to_sxp_bin}
81
80
  end
82
81
 
83
82
  if options.has_key?(:load_datasets)
@@ -142,15 +142,15 @@ module SPARQL
142
142
  #
143
143
  # Operator(:isBlank).new(RDF::Node(:foobar)).to_sxp #=> "(isBlank _:foobar)"
144
144
  # Operator(:isIRI).new(RDF::URI('https://rubygems.org/gems/rdf/')).to_sxp #=> "(isIRI <https://rubygems.org/gems/rdf/>)"
145
- # Operator(:isLiteral).new(RDF::Literal(3.1415)).to_sxp #=> "(isLiteral 3.1415)"
146
- # Operator(:str).new(Operator(:datatype).new(RDF::Literal(3.1415))).to_sxp #=> "(str (datatype 3.1415))"
145
+ # Operator(:isLiteral).new(RDF::Literal(3.1415)).to_sxp #=> "(isLiteral 3.1415e0)"
146
+ # Operator(:str).new(Operator(:datatype).new(RDF::Literal(3.1415))).to_sxp #=> "(str (datatype 3.1415e0))"
147
147
  #
148
148
  # ## Constructing operator expressions using SSE forms
149
149
  #
150
150
  # SPARQL::Algebra::Expression[:isBlank, RDF::Node(:foobar)].to_sxp #=> "(isBlank _:foobar)"
151
151
  # SPARQL::Algebra::Expression[:isIRI, RDF::URI('https://rubygems.org/gems/rdf/')].to_sxp #=> "(isIRI <https://rubygems.org/gems/rdf/>)"
152
- # SPARQL::Algebra::Expression[:isLiteral, RDF::Literal(3.1415)].to_sxp #=> "(isLiteral 3.1415)"
153
- # SPARQL::Algebra::Expression[:str, [:datatype, RDF::Literal(3.1415)]].to_sxp #=> "(str (datatype 3.1415))"
152
+ # SPARQL::Algebra::Expression[:isLiteral, RDF::Literal(3.1415)].to_sxp #=> "(isLiteral 3.1415e0)"
153
+ # SPARQL::Algebra::Expression[:str, [:datatype, RDF::Literal(3.1415)]].to_sxp #=> "(str (datatype 3.1415e0))"
154
154
  #
155
155
  # ## Constructing operator expressions using SSE strings
156
156
  #
@@ -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
 
@@ -4,6 +4,8 @@ module SPARQL; module Algebra
4
4
  #
5
5
  # @abstract
6
6
  module Expression
7
+ include RDF::Util::Logger
8
+
7
9
  ##
8
10
  # @example
9
11
  # Expression.parse('(isLiteral 3.1415)')
@@ -20,13 +22,6 @@ module SPARQL; module Algebra
20
22
  # @yieldreturn [void] ignored
21
23
  # @return [Expression]
22
24
  def self.parse(sse, **options, &block)
23
- begin
24
- require 'sxp' # @see https://rubygems.org/gems/sxp
25
- rescue LoadError
26
- abort "SPARQL::Algebra::Expression.parse requires the SXP gem (hint: `gem install sxp')."
27
- end
28
- require 'sparql/algebra/sxp_extensions'
29
-
30
25
  sse = sse.encode(Encoding::UTF_8)
31
26
  sxp = SXP::Reader::SPARQL.new(sse) do |reader|
32
27
  # Set base_uri if we have one
@@ -60,7 +55,7 @@ module SPARQL; module Algebra
60
55
  def self.open(filename, **options, &block)
61
56
  RDF::Util::File.open_file(filename, **options) do |file|
62
57
  options[:base_uri] ||= filename
63
- Expression.parse(file, options, &block)
58
+ Expression.parse(file, **options, &block)
64
59
  end
65
60
  end
66
61
 
@@ -120,9 +115,12 @@ module SPARQL; module Algebra
120
115
  end
121
116
 
122
117
  debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
123
- options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
124
- operands << options unless options.empty?
125
- operator.new(*operands)
118
+ options.delete_if {|k, v| [:debug, :logger, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
119
+ begin
120
+ operator.new(*operands, **options)
121
+ rescue ArgumentError => e
122
+ error(options) {"Operator=#{operator.inspect}: #{e}"}
123
+ end
126
124
  end
127
125
 
128
126
  ##
@@ -229,21 +227,45 @@ module SPARQL; module Algebra
229
227
  when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
230
228
  raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
231
229
  else
232
- RDF::Literal.new(!value.to_s.empty?, datatype: datatype, validate: true)
230
+ RDF::Literal::Boolean.new(value.value, datatype: datatype, validate: true)
233
231
  end
234
232
  when RDF::XSD.decimal, RDF::XSD.integer
235
233
  case value
236
234
  when RDF::Literal::Boolean
237
235
  RDF::Literal.new(value.object ? 1 : 0, datatype: datatype)
238
- when RDF::Literal::Integer, RDF::Literal::Decimal
239
- RDF::Literal.new(value, datatype: datatype)
236
+ when RDF::Literal::Numeric
237
+ RDF::Literal.new(value.object, datatype: datatype)
240
238
  when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
241
239
  raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
242
240
  else
243
241
  RDF::Literal.new(value.value, datatype: datatype, validate: true)
244
242
  end
245
243
  when RDF::XSD.string
246
- RDF::Literal.new(value, datatype: datatype)
244
+ # Cast to string rules based on https://www.w3.org/TR/xpath-functions/#casting-to-string
245
+ case value
246
+ when RDF::Literal::Integer
247
+ RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
248
+ when RDF::Literal::Decimal
249
+ if value == value.ceil
250
+ RDF::Literal.new(value.ceil, datatype: datatype)
251
+ else
252
+ RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
253
+ end
254
+ when RDF::Literal::Float, RDF::Literal::Double
255
+ if value.abs >= 0.000001 && value.abs < 1000000
256
+ # If SV has an absolute value that is greater than or equal to 0.000001 (one millionth) and less than 1000000 (one million), then the value is converted to an xs:decimal and the resulting xs:decimal is converted to an xs:string according to the rules above, as though using an implementation of xs:decimal that imposes no limits on the totalDigits or fractionDigits facets.
257
+ cast(datatype, RDF::Literal::Decimal.new(value.object))
258
+ elsif value.object.zero?
259
+ # If SV has the value positive or negative zero, TV is "0" or "-0" respectively.
260
+ RDF::Literal.new(value.to_s.start_with?('-') ? '-0' : '0', datatype: datatype)
261
+ else
262
+ # If SV is positive or negative infinity, TV is the string "INF" or "-INF" respectively.
263
+ # In other cases, the result consists of a mantissa, which has the lexical form of an xs:decimal, followed by the letter "E", followed by an exponent which has the lexical form of an xs:integer. Leading zeroes and "+" signs are prohibited in the exponent. For the mantissa, there must be a decimal point, and there must be exactly one digit before the decimal point, which must be non-zero. The "+" sign is prohibited. There must be at least one digit after the decimal point. Apart from this mandatory digit, trailing zero digits are prohibited.
264
+ RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
265
+ end
266
+ else
267
+ RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
268
+ end
247
269
  else
248
270
  raise TypeError, "Expected datatype (#{datatype}) to be a recognized XPath function"
249
271
  end
@@ -315,7 +337,7 @@ module SPARQL; module Algebra
315
337
  # @param [Hash{Symbol => Object}] options ({})
316
338
  # options passed from query
317
339
  # @return [Expression] `self`
318
- def evaluate(bindings, options = {})
340
+ def evaluate(bindings, **options)
319
341
  self
320
342
  end
321
343
 
@@ -363,7 +385,7 @@ module SPARQL; module Algebra
363
385
  # @param [String] node processing node
364
386
  # @param [String] message
365
387
  # @param [Hash{Symbol => Object}] options
366
- # @option options [Boolean] :debug output debug messages to $stderr
388
+ # @option options [Logger] :logger for logging progress
367
389
  # @option options [Integer] :depth (@productions.length)
368
390
  # Processing depth for indenting message output.
369
391
  # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
@@ -371,7 +393,7 @@ module SPARQL; module Algebra
371
393
  # @overload: May be called with node and an option hash
372
394
  # @param [String] node processing node
373
395
  # @param [Hash{Symbol => Object}] options
374
- # @option options [Boolean] :debug output debug messages to $stderr
396
+ # @option options [Logger] :logger for logging progress
375
397
  # @option options [Integer] :depth (@productions.length)
376
398
  # Processing depth for indenting message output.
377
399
  # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
@@ -379,26 +401,19 @@ module SPARQL; module Algebra
379
401
  # @overload: May be called with only options, in which case the block is used to return the output message
380
402
  # @param [String] node processing node
381
403
  # @param [Hash{Symbol => Object}] options
382
- # @option options [Boolean] :debug output debug messages to $stderr
404
+ # @option options [Logger] :logger for logging progress
383
405
  # @option options [Integer] :depth (@productions.length)
384
406
  # Processing depth for indenting message output.
385
407
  # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
386
- def self.debug(*args)
408
+ def self.debug(*args, &block)
387
409
  options = args.last.is_a?(Hash) ? args.pop : {}
388
- return unless options[:debug]
389
- message = args.join(": ")
390
- message = message + yield if block_given?
391
- depth = options[:depth] || 0
392
- case options[:debug]
393
- when Array
394
- options[:debug] << "#{' ' * depth}#{message}"
395
- else
396
- $stderr.puts("#{' ' * depth}#{message}")
397
- end
410
+ return unless options[:logger]
411
+ options[:logger].debug(*args, **options, &block)
398
412
  end
399
413
 
400
414
  def debug(*args, &block)
401
- Expression.debug(*args, &block)
415
+ options = args.last.is_a?(Hash) ? args.pop : {}
416
+ log_debug(*args, **options, &block)
402
417
  end
403
418
  end # Expression
404
419
  end; end # SPARQL::Algebra
@@ -283,6 +283,20 @@ module RDF::Term
283
283
  end
284
284
  end # RDF::Term
285
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
+
286
300
  # Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
287
301
  module RDF::Queryable
288
302
  alias_method :query_without_sparql, :query
@@ -294,7 +308,7 @@ module RDF::Queryable
294
308
  #
295
309
  # @example
296
310
  # queryable.query([nil, RDF::DOAP.developer, nil])
297
- # queryable.query(predicate: RDF::DOAP.developer)
311
+ # queryable.query({predicate: RDF::DOAP.developer})
298
312
  #
299
313
  # op = SPARQL::Algebra::Expression.parse(%q((bgp (triple ?a doap:developer ?b))))
300
314
  # queryable.query(op)
@@ -306,7 +320,7 @@ module RDF::Queryable
306
320
  # @yieldreturn [void] ignored
307
321
  # @return [Enumerator]
308
322
  # @see RDF::Queryable#query_pattern
309
- def query(pattern, options = {}, &block)
323
+ def query(pattern, **options, &block)
310
324
  raise TypeError, "#{self} is not queryable" if respond_to?(:queryable?) && !queryable?
311
325
 
312
326
  if pattern.is_a?(SPARQL::Algebra::Operator) && pattern.respond_to?(:execute)
@@ -335,11 +349,21 @@ class RDF::Statement
335
349
  # Transform Statement Pattern into an SXP
336
350
  # @return [Array]
337
351
  def to_sxp_bin
338
- if has_graph?
339
- [:quad, subject, predicate, object, graph_name]
340
- else
341
- [:triple, subject, predicate, object]
342
- 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)
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
343
367
  end
344
368
 
345
369
  ##
@@ -350,6 +374,8 @@ class RDF::Statement
350
374
  def optimize(**options)
351
375
  self.dup
352
376
  end
377
+
378
+ def executable?; false; end
353
379
  end
354
380
 
355
381
  class RDF::Query
@@ -427,6 +453,7 @@ class RDF::Query
427
453
  variables.values
428
454
  end
429
455
 
456
+ alias_method :optimize_without_expression!, :optimize!
430
457
  ##
431
458
  # Optimize the query, removing lexical shortcuts in URIs
432
459
  #
@@ -434,39 +461,26 @@ class RDF::Query
434
461
  # @see SPARQL::Algebra::Expression#optimize!
435
462
  def optimize!(**options)
436
463
  @patterns = @patterns.map do |pattern|
437
- components = pattern.to_a.map do |term|
464
+ components = pattern.to_quad.map do |term|
438
465
  if term.respond_to?(:lexical=)
439
466
  term.dup.instance_eval {@lexical = nil; self}
440
467
  else
441
468
  term
442
469
  end
443
470
  end
444
- RDF::Query::Pattern.from(components)
445
- end.sort! do |a, b|
446
- (a.cost || 0) <=> (b.cost || 0)
471
+ RDF::Query::Pattern.from(components, **pattern.options)
447
472
  end
448
- self
473
+ self.optimize_without_expression!(**options)
449
474
  end
450
475
 
451
476
  ##
452
- # Returns `true` if this is executable (i.e., contains a graph patterns), `false`
453
- # otherwise.
477
+ # Returns `true` as this is executable.
454
478
  #
455
- # @return [Boolean] `true` or `false`
479
+ # @return [Boolean] `true`
456
480
  def executable?; true; end
457
481
  end
458
482
 
459
483
  class RDF::Query::Pattern
460
- # Transform Query Pattern into an SXP
461
- # @return [Array]
462
- def to_sxp_bin
463
- if has_graph?
464
- [:quad, subject, predicate, object, graph_name]
465
- else
466
- [:triple, subject, predicate, object]
467
- end
468
- end
469
-
470
484
  ##
471
485
  # Return the non-destinguished variables contained within this pattern
472
486
  # @return [Array<RDF::Query::Variable>]
@@ -480,6 +494,12 @@ class RDF::Query::Pattern
480
494
  def vars
481
495
  variables.values
482
496
  end
497
+
498
+ ##
499
+ # Returns `true` as this is executable.
500
+ #
501
+ # @return [Boolean] `true`
502
+ def executable?; true; end
483
503
  end
484
504
 
485
505
  ##
@@ -509,6 +529,13 @@ class RDF::Query::Variable
509
529
  def optimize(**options)
510
530
  self
511
531
  end
532
+
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
538
+ end
512
539
  end # RDF::Query::Variable
513
540
 
514
541
  ##