sparql 0.0.1 → 0.0.2

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 (84) hide show
  1. data/AUTHORS +3 -0
  2. data/CREDITS +0 -0
  3. data/README.markdown +103 -53
  4. data/UNLICENSE +24 -0
  5. data/VERSION +1 -0
  6. data/bin/sparql +87 -0
  7. data/lib/sparql.rb +105 -22
  8. data/lib/sparql/algebra.rb +369 -0
  9. data/lib/sparql/algebra/evaluatable.rb +37 -0
  10. data/lib/sparql/algebra/expression.rb +284 -0
  11. data/lib/sparql/algebra/extensions.rb +159 -0
  12. data/lib/sparql/algebra/operator.rb +492 -0
  13. data/lib/sparql/algebra/operator/add.rb +34 -0
  14. data/lib/sparql/algebra/operator/and.rb +65 -0
  15. data/lib/sparql/algebra/operator/asc.rb +29 -0
  16. data/lib/sparql/algebra/operator/ask.rb +46 -0
  17. data/lib/sparql/algebra/operator/base.rb +46 -0
  18. data/lib/sparql/algebra/operator/bgp.rb +26 -0
  19. data/lib/sparql/algebra/operator/bound.rb +48 -0
  20. data/lib/sparql/algebra/operator/compare.rb +84 -0
  21. data/lib/sparql/algebra/operator/construct.rb +85 -0
  22. data/lib/sparql/algebra/operator/dataset.rb +77 -0
  23. data/lib/sparql/algebra/operator/datatype.rb +42 -0
  24. data/lib/sparql/algebra/operator/desc.rb +17 -0
  25. data/lib/sparql/algebra/operator/describe.rb +71 -0
  26. data/lib/sparql/algebra/operator/distinct.rb +50 -0
  27. data/lib/sparql/algebra/operator/divide.rb +43 -0
  28. data/lib/sparql/algebra/operator/equal.rb +32 -0
  29. data/lib/sparql/algebra/operator/exprlist.rb +52 -0
  30. data/lib/sparql/algebra/operator/filter.rb +71 -0
  31. data/lib/sparql/algebra/operator/graph.rb +28 -0
  32. data/lib/sparql/algebra/operator/greater_than.rb +32 -0
  33. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +33 -0
  34. data/lib/sparql/algebra/operator/is_blank.rb +35 -0
  35. data/lib/sparql/algebra/operator/is_iri.rb +37 -0
  36. data/lib/sparql/algebra/operator/is_literal.rb +36 -0
  37. data/lib/sparql/algebra/operator/join.rb +67 -0
  38. data/lib/sparql/algebra/operator/lang.rb +29 -0
  39. data/lib/sparql/algebra/operator/lang_matches.rb +53 -0
  40. data/lib/sparql/algebra/operator/left_join.rb +95 -0
  41. data/lib/sparql/algebra/operator/less_than.rb +32 -0
  42. data/lib/sparql/algebra/operator/less_than_or_equal.rb +32 -0
  43. data/lib/sparql/algebra/operator/minus.rb +31 -0
  44. data/lib/sparql/algebra/operator/multiply.rb +34 -0
  45. data/lib/sparql/algebra/operator/not.rb +35 -0
  46. data/lib/sparql/algebra/operator/not_equal.rb +26 -0
  47. data/lib/sparql/algebra/operator/or.rb +65 -0
  48. data/lib/sparql/algebra/operator/order.rb +69 -0
  49. data/lib/sparql/algebra/operator/plus.rb +31 -0
  50. data/lib/sparql/algebra/operator/prefix.rb +45 -0
  51. data/lib/sparql/algebra/operator/project.rb +46 -0
  52. data/lib/sparql/algebra/operator/reduced.rb +47 -0
  53. data/lib/sparql/algebra/operator/regex.rb +70 -0
  54. data/lib/sparql/algebra/operator/same_term.rb +46 -0
  55. data/lib/sparql/algebra/operator/slice.rb +60 -0
  56. data/lib/sparql/algebra/operator/str.rb +35 -0
  57. data/lib/sparql/algebra/operator/subtract.rb +32 -0
  58. data/lib/sparql/algebra/operator/union.rb +55 -0
  59. data/lib/sparql/algebra/query.rb +99 -0
  60. data/lib/sparql/algebra/sxp_extensions.rb +35 -0
  61. data/lib/sparql/algebra/version.rb +20 -0
  62. data/lib/sparql/extensions.rb +102 -0
  63. data/lib/sparql/grammar.rb +298 -0
  64. data/lib/sparql/grammar/lexer.rb +609 -0
  65. data/lib/sparql/grammar/parser.rb +1383 -0
  66. data/lib/sparql/grammar/parser/meta.rb +1801 -0
  67. data/lib/sparql/results.rb +220 -0
  68. data/lib/sparql/version.rb +20 -0
  69. metadata +232 -62
  70. data/Rakefile +0 -22
  71. data/coverage/index.html +0 -252
  72. data/coverage/lib-sparql-execute_sparql_rb.html +0 -621
  73. data/coverage/lib-sparql_rb.html +0 -622
  74. data/lib/sparql/execute_sparql.rb +0 -27
  75. data/lib/sparql/sparql.treetop +0 -159
  76. data/sparql.gemspec +0 -16
  77. data/spec/spec.opts +0 -2
  78. data/spec/spec_helper.rb +0 -24
  79. data/spec/unit/graph_parsing_spec.rb +0 -76
  80. data/spec/unit/iri_parsing_spec.rb +0 -46
  81. data/spec/unit/prefixed_names_parsing_spec.rb +0 -40
  82. data/spec/unit/primitives_parsing_spec.rb +0 -26
  83. data/spec/unit/sparql_parsing_spec.rb +0 -72
  84. data/spec/unit/variables_parsing_spec.rb +0 -36
@@ -0,0 +1,369 @@
1
+ require 'rdf' # @see http://rubygems.org/gems/rdf
2
+ require 'rdf/xsd'
3
+
4
+ module SPARQL
5
+ ##
6
+ # A SPARQL algebra for RDF.rb.
7
+ #
8
+ # Parses Sparql S-Expressions (SSE) into SPARQL Algebra operators.
9
+ #
10
+ # Operators implementing {SPARQL::Algebra::Query#execute} may directly
11
+ # execute an object implementing {RDF::Queryable}, and so may be treated
12
+ # equivalently to {RDF::Query}.
13
+ #
14
+ # Operators implementing {SPARQL::Algebra::Expression#evaluate} may be
15
+ # evaluated with RDF::Query::Solution bindings to yield an appropriate result.
16
+ #
17
+ # An entire SSE expression is parsed into a recursive set of {SPARQL::Algebra::Operator}
18
+ # instances, with each operand representing an additional operator.
19
+ #
20
+ # {RDF::Query} and {RDF::Query::Pattern} are used as primitives for `bgp` and `triple` expressions.
21
+ #
22
+ # # Queries
23
+ #
24
+ # require 'sparql/algebra'
25
+ #
26
+ # include SPARQL::Algebra
27
+ #
28
+ # ## Basic Query
29
+ # BASE <http://example.org/x/>
30
+ # PREFIX : <>
31
+ #
32
+ # SELECT * WHERE { :x ?p ?v }
33
+ #
34
+ # is equivalent to
35
+ #
36
+ # (base <http://example.org/x/>
37
+ # (prefix ((: <>))
38
+ # (bgp (triple :x ?p ?v))))
39
+ #
40
+ # ## Prefixes
41
+ #
42
+ # PREFIX ns: <http://example.org/ns#>
43
+ # PREFIX x: <http://example.org/x/>
44
+ #
45
+ # SELECT * WHERE { x:x ns:p ?v }
46
+ #
47
+ # is equivalent to
48
+ #
49
+ # (prefix ((ns: <http://example.org/ns#>)
50
+ # (x: <http://example.org/x/>))
51
+ # (bgp (triple x:x ns:p ?v)))
52
+ #
53
+ # ## Ask
54
+ #
55
+ # PREFIX : <http://example/>
56
+ #
57
+ # ASK WHERE { :x :p ?x }
58
+ #
59
+ # is equivalent to
60
+ #
61
+ # (prefix ((: <http://example/>))
62
+ # (ask
63
+ # (bgp (triple :x :p ?x))))
64
+ #
65
+ # ## Datasets
66
+ #
67
+ # PREFIX : <http://example/>
68
+ #
69
+ # SELECT *
70
+ # FROM <data-g1.ttl>
71
+ # FROM NAMED <data-g2.ttl>
72
+ # { ?s ?p ?o }
73
+ #
74
+ # is equivalent to
75
+ #
76
+ # (prefix ((: <http://example/>))
77
+ # (dataset (<data-g1.ttl> (named <data-g2.ttl>))
78
+ # (bgp (triple ?s ?p ?o))))
79
+ #
80
+ # ## Join
81
+ #
82
+ # PREFIX : <http://example/>
83
+ #
84
+ # SELECT *
85
+ # {
86
+ # ?s ?p ?o
87
+ # GRAPH ?g { ?s ?q ?v }
88
+ # }
89
+ #
90
+ # is equivalent to
91
+ #
92
+ # (prefix ((: <http://example/>))
93
+ # (join
94
+ # (bgp (triple ?s ?p ?o))
95
+ # (graph ?g
96
+ # (bgp (triple ?s ?q ?v)))))
97
+ #
98
+ # ## Union
99
+ #
100
+ # PREFIX : <http://example/>
101
+ #
102
+ # SELECT *
103
+ # {
104
+ # { ?s ?p ?o }
105
+ # UNION
106
+ # { GRAPH ?g { ?s ?p ?o } }
107
+ # }
108
+ #
109
+ # is equivalent to
110
+ #
111
+ # (prefix ((: <http://example/>))
112
+ # (union
113
+ # (bgp (triple ?s ?p ?o))
114
+ # (graph ?g
115
+ # (bgp (triple ?s ?p ?o)))))
116
+ #
117
+ # ## LeftJoin
118
+ #
119
+ # PREFIX : <http://example/>
120
+ #
121
+ # SELECT *
122
+ # {
123
+ # ?x :p ?v .
124
+ # OPTIONAL
125
+ # {
126
+ # ?y :q ?w .
127
+ # FILTER(?v=2)
128
+ # }
129
+ # }
130
+ #
131
+ # is equivalent to
132
+ #
133
+ # (prefix ((: <http://example/>))
134
+ # (leftjoin
135
+ # (bgp (triple ?x :p ?v))
136
+ # (bgp (triple ?y :q ?w))
137
+ # (= ?v 2)))
138
+ #
139
+ # # Expressions
140
+ #
141
+ # ## Constructing operator expressions manually
142
+ #
143
+ # Operator(:isBlank).new(RDF::Node(:foobar)).to_sxp #=> "(isBlank _:foobar)"
144
+ # Operator(:isIRI).new(RDF::URI('http://rdf.rubyforge.org/')).to_sxp #=> "(isIRI <http://rdf.rubyforge.org/>)"
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))"
147
+ #
148
+ # ## Constructing operator expressions using SSE forms
149
+ #
150
+ # Expression[:isBlank, RDF::Node(:foobar)].to_sxp #=> "(isBlank _:foobar)"
151
+ # Expression[:isIRI, RDF::URI('http://rdf.rubyforge.org/')].to_sxp #=> "(isIRI <http://rdf.rubyforge.org/>)"
152
+ # Expression[:isLiteral, RDF::Literal(3.1415)].to_sxp #=> "(isLiteral 3.1415)"
153
+ # Expression[:str, [:datatype, RDF::Literal(3.1415)]].to_sxp #=> "(str (datatype 3.1415))"
154
+ #
155
+ # ## Constructing operator expressions using SSE strings
156
+ #
157
+ # Expression.parse('(isBlank _:foobar)')
158
+ # Expression.parse('(isIRI <http://rdf.rubyforge.org/>)')
159
+ # Expression.parse('(isLiteral 3.1415)')
160
+ # Expression.parse('(str (datatype 3.1415))')
161
+ #
162
+ # ## Evaluating operators standalone
163
+ #
164
+ # Operator(:isBlank).evaluate(RDF::Node(:foobar)) #=> RDF::Literal::TRUE
165
+ # Operator(:isIRI).evaluate(RDF::DC.title) #=> RDF::Literal::TRUE
166
+ # Operator(:isLiteral).evaluate(RDF::Literal(3.1415)) #=> RDF::Literal::TRUE
167
+ #
168
+ # ## Optimizing expressions containing constant subexpressions
169
+ #
170
+ # Expression.parse('(sameTerm ?var ?var)').optimize #=> RDF::Literal::TRUE
171
+ # Expression.parse('(* -2 (- (* (+ 1 2) (+ 3 4))))').optimize #=> RDF::Literal(42)
172
+ #
173
+ # ## Evaluating expressions on a solution sequence
174
+ #
175
+ # # Find all people and their names & e-mail addresses:
176
+ # solutions = RDF::Query.execute(RDF::Graph.load('etc/doap.ttl')) do |query|
177
+ # query.pattern [:person, RDF.type, RDF::FOAF.Person]
178
+ # query.pattern [:person, RDF::FOAF.name, :name]
179
+ # query.pattern [:person, RDF::FOAF.mbox, :email], :optional => true
180
+ # end
181
+ #
182
+ # # Find people who have a name but don't have a known e-mail address:
183
+ # expression = Expression[:not, [:bound, Variable(:email)]] # ...or just...
184
+ # expression = Expression.parse('(not (bound ?email))')
185
+ # solutions.filter!(expression)
186
+ #
187
+ # @example Optimizations
188
+ #
189
+ # Some very simple optimizations are currently implemented for `FILTER`
190
+ # expressions. Use the following to obtain optimized SSE forms:
191
+ #
192
+ # Expression.parse(sse).optimize.to_sse
193
+ #
194
+ # ## Constant comparison folding
195
+ #
196
+ # (sameTerm ?x ?x) #=> true
197
+ #
198
+ # ## Constant arithmetic folding
199
+ #
200
+ # (!= ?x (+ 123)) #=> (!= ?x 123)
201
+ # (!= ?x (- -1.0)) #=> (!= ?x 1.0)
202
+ # (!= ?x (+ 1 2)) #=> (!= ?x 3)
203
+ # (!= ?x (- 4 5)) #=> (!= ?x -1)
204
+ # (!= ?x (* 6 7)) #=> (!= ?x 42)
205
+ # (!= ?x (/ 0 0.0)) #=> (!= ?x NaN)
206
+ #
207
+ # ## Memoization
208
+ #
209
+ # Expressions can optionally be [memoized][memoization], which can speed up
210
+ # repeatedly executing the expression on a solution sequence:
211
+ #
212
+ # Expression.parse(sse, :memoize => true)
213
+ # Operator.new(*operands, :memoize => true)
214
+ #
215
+ # Memoization is implemented using RDF.rb's [RDF::Util::Cache][] utility
216
+ # library, a weak-reference cache that allows values contained in the cache to
217
+ # be garbage collected. This allows the cache to dynamically adjust to
218
+ # changing memory conditions, caching more objects when memory is plentiful,
219
+ # but evicting most objects if memory pressure increases to the point of
220
+ # scarcity.
221
+ #
222
+ # [memoization]: http://en.wikipedia.org/wiki/Memoization
223
+ # [RDF::Util::Cache]: http://rdf.rubyforge.org/RDF/Util/Cache.html
224
+ #
225
+ # ## Documentation
226
+ #
227
+ # * {SPARQL::Algebra}
228
+ # * {SPARQL::Algebra::Expression}
229
+ # * {SPARQL::Algebra::Query}
230
+ # * {SPARQL::Algebra::Operator}
231
+ # * {SPARQL::Algebra::Operator::Add}
232
+ # * {SPARQL::Algebra::Operator::And}
233
+ # * {SPARQL::Algebra::Operator::Asc}
234
+ # * {SPARQL::Algebra::Operator::Ask}
235
+ # * {SPARQL::Algebra::Operator::Base}
236
+ # * {SPARQL::Algebra::Operator::Bound}
237
+ # * {SPARQL::Algebra::Operator::Compare}
238
+ # * {SPARQL::Algebra::Operator::Construct}
239
+ # * {SPARQL::Algebra::Operator::Dataset}
240
+ # * {SPARQL::Algebra::Operator::Datatype}
241
+ # * {SPARQL::Algebra::Operator::Desc}
242
+ # * {SPARQL::Algebra::Operator::Describe}
243
+ # * {SPARQL::Algebra::Operator::Distinct}
244
+ # * {SPARQL::Algebra::Operator::Divide}
245
+ # * {SPARQL::Algebra::Operator::Equal}
246
+ # * {SPARQL::Algebra::Operator::Exprlist}
247
+ # * {SPARQL::Algebra::Operator::Filter}
248
+ # * {SPARQL::Algebra::Operator::Graph}
249
+ # * {SPARQL::Algebra::Operator::GreaterThan}
250
+ # * {SPARQL::Algebra::Operator::GreaterThanOrEqual}
251
+ # * {SPARQL::Algebra::Operator::IsBlank}
252
+ # * {SPARQL::Algebra::Operator::IsIRI}
253
+ # * {SPARQL::Algebra::Operator::IsLiteral}
254
+ # * {SPARQL::Algebra::Operator::Join}
255
+ # * {SPARQL::Algebra::Operator::Lang}
256
+ # * {SPARQL::Algebra::Operator::LangMatches}
257
+ # * {SPARQL::Algebra::Operator::LeftJoin}
258
+ # * {SPARQL::Algebra::Operator::LessThan}
259
+ # * {SPARQL::Algebra::Operator::LessThanOrEqual}
260
+ # * {SPARQL::Algebra::Operator::Minus}
261
+ # * {SPARQL::Algebra::Operator::Multiply}
262
+ # * {SPARQL::Algebra::Operator::Not}
263
+ # * {SPARQL::Algebra::Operator::NotEqual}
264
+ # * {SPARQL::Algebra::Operator::Or}
265
+ # * {SPARQL::Algebra::Operator::Order}
266
+ # * {SPARQL::Algebra::Operator::Plus}
267
+ # * {SPARQL::Algebra::Operator::Prefix}
268
+ # * {SPARQL::Algebra::Operator::Project}
269
+ # * {SPARQL::Algebra::Operator::Reduced}
270
+ # * {SPARQL::Algebra::Operator::Regex}
271
+ # * {SPARQL::Algebra::Operator::SameTerm}
272
+ # * {SPARQL::Algebra::Operator::Slice}
273
+ # * {SPARQL::Algebra::Operator::Str}
274
+ # * {SPARQL::Algebra::Operator::Subtract}
275
+ # * {SPARQL::Algebra::Operator::Union}
276
+ #
277
+ # TODO
278
+ # ====
279
+ # * Need to come up with appropriate SXP for SPARQL 1.1
280
+ # * Operator#optimize needs to be completed and tested.
281
+ #
282
+ # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
283
+ module Algebra
284
+ include RDF
285
+
286
+ autoload :Evaluatable, 'sparql/algebra/evaluatable'
287
+ autoload :Expression, 'sparql/algebra/expression'
288
+ autoload :Operator, 'sparql/algebra/operator'
289
+ autoload :Query, 'sparql/algebra/query'
290
+
291
+ ##
292
+ # @example
293
+ # sse = (prefix ((foaf: <http://xmlns.com/foaf/0.1/>))
294
+ # (project (?name ?mbox)
295
+ # (join
296
+ # (bgp (triple ?x foaf:name ?name))
297
+ # (bgp (triple ?x foaf:mbox ?mbox)))))
298
+ # }
299
+ # @param [String] sse
300
+ # a SPARQL S-Expression (SSE) string
301
+ # @param [Hash{Symbol => Object}] options
302
+ # any additional options (see {Operator#initialize})
303
+ # @return [SPARQL::Algebra::Operator]
304
+ def parse(sse, options = {})
305
+ Expression.parse(sse, options)
306
+ end
307
+ module_function :parse
308
+
309
+ ##
310
+ # Parses input from the given file name or URL.
311
+ #
312
+ # @param [String, #to_s] filename
313
+ # @param [Hash{Symbol => Object}] options
314
+ # any additional options (see {Operator#initialize})
315
+ # @option options [RDF::URI, #to_s] :base_uri
316
+ # Base URI used for loading relative URIs.
317
+ #
318
+ # @yield [expression]
319
+ # @yieldparam [SPARQL::Algebra::Expression] expression
320
+ # @yieldreturn [void] ignored
321
+ # @return [Expression]
322
+ def open(sse, options = {})
323
+ Expression.open(sse, options)
324
+ end
325
+ module_function :open
326
+
327
+ ##
328
+ # @example
329
+ # Expression(:isLiteral, RDF::Literal(3.1415))
330
+ #
331
+ # @param [Array] sse
332
+ # a SPARQL S-Expression (SSE) form
333
+ # @return [SPARQL::Algebra::Expression]
334
+ def Expression(*sse)
335
+ Expression.for(*sse)
336
+ end
337
+ alias_method :Expr, :Expression
338
+ module_function :Expr, :Expression
339
+
340
+ ##
341
+ # @example
342
+ # Operator(:isLiteral)
343
+ #
344
+ # @param [Symbol, #to_sym] name
345
+ # @return [Class]
346
+ def Operator(name, arity = nil)
347
+ Operator.for(name, arity)
348
+ end
349
+ alias_method :Op, :Operator
350
+ module_function :Op, :Operator
351
+
352
+ ##
353
+ # @example
354
+ # Variable(:foobar)
355
+ #
356
+ # @param [Symbol, #to_sym] name
357
+ # @return [Variable]
358
+ # @see http://rdf.rubyforge.org/RDF/Query/Variable.html
359
+ def Variable(name)
360
+ Variable.new(name)
361
+ end
362
+ alias_method :Var, :Variable
363
+ module_function :Var, :Variable
364
+
365
+ Variable = RDF::Query::Variable
366
+ end # Algebra
367
+ end # SPARQL
368
+
369
+ require 'sparql/algebra/extensions'
@@ -0,0 +1,37 @@
1
+ module SPARQL; module Algebra
2
+ ##
3
+ # Mixin for Algebra::Operator sub-classes that evaluate bindings to return a result
4
+ #
5
+ # @abstract
6
+ module Evaluatable
7
+ ##
8
+ # Evaluates this operator using the given variable `bindings`.
9
+ #
10
+ # @param [RDF::Query::Solution, #[]] bindings
11
+ # a query solution containing zero or more variable bindings
12
+ # @return [RDF::Term]
13
+ # @abstract
14
+ def evaluate(bindings = {})
15
+ args = operands.map { |operand| operand.evaluate(bindings) }
16
+ options[:memoize] ? memoize(*args) : apply(*args)
17
+ end
18
+
19
+ ##
20
+ # @param [Array<RDF::Term>] operands
21
+ # evaluated operands
22
+ # @return [RDF::Term] the memoized result
23
+ def memoize(*operands)
24
+ @cache ||= RDF::Util::Cache.new(options[:memoize].is_a?(Integer) ? options[:memoize] : -1)
25
+ @cache[operands] ||= apply(*operands)
26
+ end
27
+
28
+ ##
29
+ # @param [Array<RDF::Term>] operands
30
+ # evaluated operands
31
+ # @return [RDF::Term]
32
+ # @abstract
33
+ def apply(*operands)
34
+ raise NotImplementedError, "#{self.class}#apply(#{operands.map(&:class).join(', ')})"
35
+ end
36
+ end # Query
37
+ end; end # SPARQL::Algebra
@@ -0,0 +1,284 @@
1
+ module SPARQL; module Algebra
2
+ ##
3
+ # A SPARQL algebra expression.
4
+ #
5
+ # @abstract
6
+ module Expression
7
+ ##
8
+ # @example
9
+ # Expression.parse('(isLiteral 3.1415)')
10
+ #
11
+ # @param [IO, String, #read, #to_s] sse
12
+ # a SPARQL S-Expression (SSE) string or IO object responding to #read
13
+ # @param [Hash{Symbol => Object}] options
14
+ # any additional options (see {Operator#initialize})
15
+ # @option options [RDF::URI, #to_s] :base_uri
16
+ # Base URI used for loading relative URIs.
17
+ #
18
+ # @yield [expression]
19
+ # @yieldparam [SPARQL::Algebra::Expression] expression
20
+ # @yieldreturn [void] ignored
21
+ # @return [Expression]
22
+ def self.parse(sse, options = {}, &block)
23
+ begin
24
+ require 'sxp' # @see http://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
+ if sse.respond_to?(:force_encoding) # Ruby 1.9+
31
+ sse = sse.dup.force_encoding(Encoding::UTF_8)
32
+ end
33
+ sxp = SXP::Reader::SPARQL.new(sse) do |reader|
34
+ # Set base_uri if we have one
35
+ reader.base_uri = options[:base_uri] if options[:base_uri]
36
+ end
37
+ sxp_result = sxp.read
38
+
39
+ debug(options) {"base_uri: #{options[:base_uri]}"}
40
+ Operator.base_uri = options.delete(:base_uri) if options.has_key?(:base_uri)
41
+ Operator.prefixes = sxp.prefixes || {}
42
+
43
+ expression = self.new(sxp_result, options)
44
+
45
+ yield(expression) if block_given?
46
+ expression
47
+ end
48
+
49
+ ##
50
+ # Parses input from the given file name or URL.
51
+ #
52
+ # @param [String, #to_s] filename
53
+ # @param [Hash{Symbol => Object}] options
54
+ # any additional options (see {Operator#initialize})
55
+ # @option options [RDF::URI, #to_s] :base_uri
56
+ # Base URI used for loading relative URIs.
57
+ #
58
+ # @yield [expression]
59
+ # @yieldparam [SPARQL::Algebra::Expression] expression
60
+ # @yieldreturn [void] ignored
61
+ # @return [Expression]
62
+ def self.open(filename, options = {}, &block)
63
+ RDF::Util::File.open_file(filename, options) do |file|
64
+ options[:base_uri] ||= filename
65
+ Expression.parse(file, options, &block)
66
+ end
67
+ end
68
+
69
+ ##
70
+ # @example
71
+ # Expression.for(:isLiteral, RDF::Literal(3.1415))
72
+ # Expression[:isLiteral, RDF::Literal(3.1415)]
73
+ #
74
+ # @param [Array] sse
75
+ # a SPARQL S-Expression (SSE) form
76
+ # @return [Expression]
77
+ def self.for(*sse)
78
+ self.new(sse)
79
+ end
80
+ class << self; alias_method :[], :for; end
81
+
82
+ ##
83
+ # @example
84
+ # Expression.new([:isLiteral, RDF::Literal(3.1415)], :version => 1.0)
85
+ #
86
+ # @param [Array] sse
87
+ # a SPARQL S-Expression (SSE) form
88
+ # @param [Hash{Symbol => Object}] options
89
+ # any additional options (see {Operator#initialize})
90
+ # @return [Expression]
91
+ # @raise [TypeError] if any of the operands is invalid
92
+ def self.new(sse, options = {})
93
+ raise ArgumentError, "invalid SPARQL::Algebra::Expression form: #{sse.inspect}" unless sse.is_a?(Array)
94
+
95
+ operator = Operator.for(sse.first, sse.length - 1)
96
+ unless operator
97
+ return case sse.first
98
+ when Array
99
+ debug(options) {"Map array elements #{sse}"}
100
+ sse.map {|s| self.new(s, options.merge(:depth => options[:depth].to_i + 1))}
101
+ else
102
+ debug(options) {"No operator found for #{sse.first}"}
103
+ sse.map do |s|
104
+ s.is_a?(Array) ?
105
+ self.new(s, options.merge(:depth => options[:depth].to_i + 1)) :
106
+ s
107
+ end
108
+ end
109
+ end
110
+
111
+ operands = sse[1..-1].map do |operand|
112
+ debug(options) {"Operator=#{operator.inspect}, Operand=#{operand.inspect}"}
113
+ case operand
114
+ when Array
115
+ self.new(operand, options.merge(:depth => options[:depth].to_i + 1))
116
+ when Operator, Variable, RDF::Term, RDF::Query, Symbol
117
+ operand
118
+ when TrueClass, FalseClass, Numeric, String, DateTime, Date, Time
119
+ RDF::Literal(operand)
120
+ else raise TypeError, "invalid SPARQL::Algebra::Expression operand: #{operand.inspect}"
121
+ end
122
+ end
123
+
124
+ debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
125
+ options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri].include?(k) }
126
+ operands << options unless options.empty?
127
+ operator.new(*operands)
128
+ end
129
+
130
+ ##
131
+ # Casts operand as the specified datatype
132
+ #
133
+ # @param [RDF::URI] datatype
134
+ # Datatype to evaluate, one of:
135
+ # xsd:integer, xsd:decimal xsd:float, xsd:double, xsd:string, xsd:boolean, or xsd:dateTime
136
+ # @param [RDF::Term] value
137
+ # Value, which should be a typed literal, where the type must be that specified
138
+ # @raise [TypeError] if datatype is not a URI or value cannot be cast to datatype
139
+ # @return [Boolean]
140
+ # @see http://www.w3.org/TR/rdf-sparql-query/#FunctionMapping
141
+ def self.cast(datatype, value)
142
+ case datatype
143
+ when RDF::XSD.dateTime
144
+ case value
145
+ when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time
146
+ RDF::Literal.new(value, :datatype => datatype)
147
+ when RDF::Literal::Numeric, RDF::Literal::Boolean, RDF::URI, RDF::Node
148
+ raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
149
+ else
150
+ RDF::Literal.new(value.value, :datatype => datatype, :validate => true)
151
+ end
152
+ when RDF::XSD.float, RDF::XSD.double
153
+ case value
154
+ when RDF::Literal::Numeric, RDF::Literal::Boolean
155
+ RDF::Literal.new(value, :datatype => datatype)
156
+ when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
157
+ raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
158
+ else
159
+ RDF::Literal.new(value.value, :datatype => datatype, :validate => true)
160
+ end
161
+ when RDF::XSD.boolean
162
+ case value
163
+ when RDF::Literal::Boolean
164
+ value
165
+ when RDF::Literal::Numeric
166
+ RDF::Literal::Boolean.new(value.value != 0)
167
+ when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
168
+ raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
169
+ else
170
+ RDF::Literal.new(!value.to_s.empty?, :datatype => datatype, :validate => true)
171
+ end
172
+ when RDF::XSD.decimal, RDF::XSD.integer
173
+ case value
174
+ when RDF::Literal::Integer, RDF::Literal::Decimal, RDF::Literal::Boolean
175
+ RDF::Literal.new(value, :datatype => datatype)
176
+ when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
177
+ raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
178
+ else
179
+ RDF::Literal.new(value.value, :datatype => datatype, :validate => true)
180
+ end
181
+ when RDF::XSD.string
182
+ RDF::Literal.new(value, :datatype => datatype)
183
+ else
184
+ raise TypeError, "Expected datatype (#{datatype}) to be an XSD type"
185
+ end
186
+ rescue
187
+ raise TypeError, $!.message
188
+ end
189
+
190
+ ##
191
+ # Returns `false`.
192
+ #
193
+ # @return [Boolean] `true` or `false`
194
+ # @see #variable?
195
+ def variable?
196
+ false
197
+ end
198
+
199
+ ##
200
+ # Returns `true`.
201
+ #
202
+ # @return [Boolean] `true` or `false`
203
+ # @see #variable?
204
+ def constant?
205
+ !(variable?)
206
+ end
207
+
208
+ ##
209
+ # Returns an optimized version of this expression.
210
+ #
211
+ # This is the default implementation, which simply returns `self`.
212
+ # Subclasses can override this method in order to implement something
213
+ # more useful.
214
+ #
215
+ # @return [Expression] `self`
216
+ def optimize
217
+ self
218
+ end
219
+
220
+ ##
221
+ # Evaluates this expression using the given variable `bindings`.
222
+ #
223
+ # This is the default implementation, which simply returns `self`.
224
+ # Subclasses can override this method in order to implement something
225
+ # more useful.
226
+ #
227
+ # @param [RDF::Query::Solution, #[]] bindings
228
+ # @return [Expression] `self`
229
+ def evaluate(bindings = {})
230
+ self
231
+ end
232
+
233
+ ##
234
+ # Returns the SPARQL S-Expression (SSE) representation of this expression.
235
+ #
236
+ # This is the default implementation, which simply returns `self`.
237
+ # Subclasses can override this method in order to implement something
238
+ # more useful.
239
+ #
240
+ # @return [Array] `self`
241
+ # @see http://openjena.org/wiki/SSE
242
+ def to_sse
243
+ self
244
+ end
245
+
246
+ private
247
+ # @overload: May be called with node, message and an option hash
248
+ # @param [String] node processing node
249
+ # @param [String] message
250
+ # @param [Hash{Symbol => Object}] options
251
+ # @option options [Boolean] :debug output debug messages to $stderr
252
+ # @option options [Integer] :depth (@productions.length)
253
+ # Processing depth for indenting message output.
254
+ # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
255
+ #
256
+ # @overload: May be called with node and an option hash
257
+ # @param [String] node processing node
258
+ # @param [Hash{Symbol => Object}] options
259
+ # @option options [Boolean] :debug output debug messages to $stderr
260
+ # @option options [Integer] :depth (@productions.length)
261
+ # Processing depth for indenting message output.
262
+ # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
263
+ #
264
+ # @overload: May be called with only options, in which case the block is used to return the output message
265
+ # @param [String] node processing node
266
+ # @param [Hash{Symbol => Object}] options
267
+ # @option options [Boolean] :debug output debug messages to $stderr
268
+ # @option options [Integer] :depth (@productions.length)
269
+ # Processing depth for indenting message output.
270
+ # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
271
+ def self.debug(*args)
272
+ options = args.last.is_a?(Hash) ? args.pop : {}
273
+ return unless options[:debug]
274
+ message = args.join(": ")
275
+ message = message + yield if block_given?
276
+ depth = options[:depth] || 0
277
+ $stderr.puts("#{' ' * depth}#{message}") if options[:debug]
278
+ end
279
+
280
+ def debug(*args, &block)
281
+ Expression.debug(*args, &block)
282
+ end
283
+ end # Expression
284
+ end; end # SPARQL::Algebra