sparql 0.0.1 → 0.0.2

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