sparql 3.1.2 → 3.1.7

Sign up to get free protection for your applications and to get access to all the features.
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
  ##