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,159 @@
1
+ require 'json'
2
+
3
+ ##
4
+ # Extensions for Ruby's `Object` class.
5
+ class Object
6
+ ##
7
+ # Returns the SXP representation of this object, defaults to `self'.
8
+ #
9
+ # @return [String]
10
+ def to_sse
11
+ self
12
+ end
13
+ end
14
+
15
+ ##
16
+ # Extensions for Ruby's `Object` class.
17
+ class Array
18
+ ##
19
+ # Returns the SXP representation of this object, defaults to `self'.
20
+ #
21
+ # @return [String]
22
+ def to_sse
23
+ map {|x| x.to_sse}
24
+ end
25
+
26
+ ##
27
+ # Evaluates the array using the given variable `bindings`.
28
+ #
29
+ # In this case, the Array has two elements, the first of which is
30
+ # an XSD datatype, and the second is the expression to be evaluated.
31
+ # The result is cast as a literal of the appropriate type
32
+ #
33
+ # @param [RDF::Query::Solution, #[]] bindings
34
+ # a query solution containing zero or more variable bindings
35
+ # @return [RDF::Term]
36
+ def evaluate(bindings)
37
+ dt, val = self.map {|o| o.evaluate(bindings)}
38
+ SPARQL::Algebra::Expression.cast(*self.map {|o| o.evaluate(bindings)})
39
+ end
40
+ end
41
+
42
+ ##
43
+ # Extensions for `RDF::Term`.
44
+ module RDF::Term
45
+ include SPARQL::Algebra::Expression
46
+
47
+ def evaluate(bindings)
48
+ self
49
+ end
50
+ end # RDF::Term
51
+
52
+ # Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
53
+ module RDF::Queryable
54
+ alias_method :query_without_sparql, :query
55
+ ##
56
+ # Queries `self` for RDF statements matching the given `pattern`.
57
+ #
58
+ # Monkey patch to RDF::Queryable#query to execute a {SPARQL::Algebra::Operator}
59
+ # in addition to an {RDF::Query} object.
60
+ #
61
+ # @example
62
+ # queryable.query([nil, RDF::DOAP.developer, nil])
63
+ # queryable.query(:predicate => RDF::DOAP.developer)
64
+ #
65
+ # op = SPARQL::Algebra::Expression.parse(%q((bgp (triple ?a doap:developer ?b))))
66
+ # queryable.query(op)
67
+ #
68
+ # @param [RDF::Query, RDF::Statement, Array(RDF::Term), Hash, SPARQL::Operator] pattern
69
+ # @yield [statement]
70
+ # each matching statement
71
+ # @yieldparam [RDF::Statement] statement
72
+ # @yieldreturn [void] ignored
73
+ # @return [Enumerator]
74
+ # @see RDF::Queryable#query_pattern
75
+ def query(pattern, &block)
76
+ raise TypeError, "#{self} is not readable" if respond_to?(:readable?) && !readable?
77
+
78
+ if pattern.is_a?(SPARQL::Algebra::Operator) && pattern.respond_to?(:execute)
79
+ before_query(pattern) if respond_to?(:before_query)
80
+ query_execute(pattern, &block)
81
+ after_query(pattern) if respond_to?(:after_query)
82
+ enum_for(:query_execute, pattern)
83
+ else
84
+ query_without_sparql(pattern, &block)
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+ class RDF::Query
91
+ # Equivalence for Queries:
92
+ # Same Patterns
93
+ # Same Context
94
+ # @return [Boolean]
95
+ def ==(other)
96
+ other.is_a?(RDF::Query) && patterns == other.patterns && context == context
97
+ end
98
+
99
+ # Transform Query into an Array form of an SSE
100
+ #
101
+ # If Query is named, it's treated as a GroupGraphPattern, otherwise, a BGP
102
+ #
103
+ # @return [Array]
104
+ def to_sse
105
+ res = [:bgp] + patterns.map(&:to_sse)
106
+ (context ? [:graph, context, res] : res)
107
+ end
108
+ end
109
+
110
+ class RDF::Query::Pattern
111
+ # Transform Query Pattern into an SXP
112
+ # @return [Array]
113
+ def to_sse
114
+ [:triple, subject, predicate, object]
115
+ end
116
+ end
117
+
118
+ ##
119
+ # Extensions for `RDF::Query::Variable`.
120
+ class RDF::Query::Variable
121
+ include SPARQL::Algebra::Expression
122
+
123
+ ##
124
+ # Returns the value of this variable in the given `bindings`.
125
+ #
126
+ # @param [RDF::Query::Solution, #[]] bindings
127
+ # @return [RDF::Term] the value of this variable
128
+ def evaluate(bindings = {})
129
+ bindings[name.to_sym]
130
+ end
131
+ end # RDF::Query::Variable
132
+
133
+ ##
134
+ # Extensions for `RDF::Query::Solutions`.
135
+ class RDF::Query::Solutions
136
+ alias_method :filter_without_expression, :filter
137
+
138
+ ##
139
+ # Filters this solution sequence by the given `criteria`.
140
+ #
141
+ # @param [SPARQL::Algebra::Expression] expression
142
+ # @yield [solution]
143
+ # each solution
144
+ # @yieldparam [RDF::Query::Solution] solution
145
+ # @yieldreturn [Boolean]
146
+ # @return [void] `self`
147
+ def filter(expression = {}, &block)
148
+ case expression
149
+ when SPARQL::Algebra::Expression
150
+ filter_without_expression do |solution|
151
+ expression.evaluate(solution).true?
152
+ end
153
+ filter_without_expression(&block) if block_given?
154
+ self
155
+ else filter_without_expression(expression, &block)
156
+ end
157
+ end
158
+ alias_method :filter!, :filter
159
+ end # RDF::Query::Solutions
@@ -0,0 +1,492 @@
1
+ module SPARQL; module Algebra
2
+ ##
3
+ # A SPARQL operator.
4
+ #
5
+ # @abstract
6
+ class Operator
7
+ include Expression
8
+
9
+ # Unary operators
10
+ autoload :Not, 'sparql/algebra/operator/not'
11
+ autoload :Plus, 'sparql/algebra/operator/plus'
12
+ autoload :Minus, 'sparql/algebra/operator/minus'
13
+ autoload :Bound, 'sparql/algebra/operator/bound'
14
+ autoload :IsBlank, 'sparql/algebra/operator/is_blank'
15
+ autoload :IsIRI, 'sparql/algebra/operator/is_iri'
16
+ autoload :IsURI, 'sparql/algebra/operator/is_iri'
17
+ autoload :IsLiteral, 'sparql/algebra/operator/is_literal'
18
+ autoload :Str, 'sparql/algebra/operator/str'
19
+ autoload :Lang, 'sparql/algebra/operator/lang'
20
+ autoload :Datatype, 'sparql/algebra/operator/datatype'
21
+
22
+ # Binary operators
23
+ autoload :Or, 'sparql/algebra/operator/or'
24
+ autoload :And, 'sparql/algebra/operator/and'
25
+ autoload :Compare, 'sparql/algebra/operator/compare'
26
+ autoload :Equal, 'sparql/algebra/operator/equal'
27
+ autoload :NotEqual, 'sparql/algebra/operator/not_equal'
28
+ autoload :LessThan, 'sparql/algebra/operator/less_than'
29
+ autoload :GreaterThan, 'sparql/algebra/operator/greater_than'
30
+ autoload :LessThanOrEqual, 'sparql/algebra/operator/less_than_or_equal'
31
+ autoload :GreaterThanOrEqual, 'sparql/algebra/operator/greater_than_or_equal'
32
+ autoload :Multiply, 'sparql/algebra/operator/multiply'
33
+ autoload :Divide, 'sparql/algebra/operator/divide'
34
+ autoload :Add, 'sparql/algebra/operator/add'
35
+ autoload :Subtract, 'sparql/algebra/operator/subtract'
36
+ autoload :SameTerm, 'sparql/algebra/operator/same_term'
37
+ autoload :LangMatches, 'sparql/algebra/operator/lang_matches'
38
+ autoload :Regex, 'sparql/algebra/operator/regex'
39
+
40
+ # Miscellaneous
41
+ autoload :Asc, 'sparql/algebra/operator/asc'
42
+ autoload :Desc, 'sparql/algebra/operator/desc'
43
+ autoload :Exprlist, 'sparql/algebra/operator/exprlist'
44
+
45
+ # Query operators
46
+ autoload :Ask, 'sparql/algebra/operator/ask'
47
+ autoload :Base, 'sparql/algebra/operator/base'
48
+ autoload :BGP, 'sparql/algebra/operator/bgp'
49
+ autoload :Construct, 'sparql/algebra/operator/construct'
50
+ autoload :Dataset, 'sparql/algebra/operator/dataset'
51
+ autoload :Describe, 'sparql/algebra/operator/describe'
52
+ autoload :Distinct, 'sparql/algebra/operator/distinct'
53
+ autoload :Filter, 'sparql/algebra/operator/filter'
54
+ autoload :Graph, 'sparql/algebra/operator/graph'
55
+ autoload :Join, 'sparql/algebra/operator/join'
56
+ autoload :LeftJoin, 'sparql/algebra/operator/left_join'
57
+ autoload :Order, 'sparql/algebra/operator/order'
58
+ autoload :Prefix, 'sparql/algebra/operator/prefix'
59
+ autoload :Project, 'sparql/algebra/operator/project'
60
+ autoload :Reduced, 'sparql/algebra/operator/reduced'
61
+ autoload :Slice, 'sparql/algebra/operator/slice'
62
+ autoload :Union, 'sparql/algebra/operator/union'
63
+
64
+ ##
65
+ # Returns an operator class for the given operator `name`.
66
+ #
67
+ # @param [Symbol, #to_s] name
68
+ # @param [Integer] arity
69
+ # @return [Class] an operator class, or `nil` if an operator was not found
70
+ def self.for(name, arity = nil)
71
+ # TODO: refactor this to dynamically introspect loaded operator classes.
72
+ case (name.to_s.downcase.to_sym rescue nil)
73
+ when :<=> then Compare # non-standard
74
+ when :'=' then Equal
75
+ when :'!=' then NotEqual
76
+ when :< then LessThan
77
+ when :> then GreaterThan
78
+ when :<= then LessThanOrEqual
79
+ when :>= then GreaterThanOrEqual
80
+ when :* then Multiply
81
+ when :'/' then Divide
82
+ when :+ then arity.eql?(1) ? Plus : Add
83
+ when :- then arity.eql?(1) ? Minus : Subtract
84
+ when :not, :'!' then Not
85
+ when :plus then Plus
86
+ when :minus then Minus
87
+ when :bound then Bound
88
+ when :isblank then IsBlank
89
+ when :isiri then IsIRI
90
+ when :isuri then IsIRI # alias
91
+ when :isliteral then IsLiteral
92
+ when :str then Str
93
+ when :lang then Lang
94
+ when :datatype then Datatype
95
+ when :or, :'||' then Or
96
+ when :and, :'&&' then And
97
+ when :multiply then Multiply
98
+ when :divide then Divide
99
+ when :add then Add
100
+ when :subtract then Subtract
101
+ when :sameterm then SameTerm
102
+ when :langmatches then LangMatches
103
+ when :regex then Regex
104
+
105
+ # Miscellaneous
106
+ when :asc then Asc
107
+ when :desc then Desc
108
+ when :exprlist then Exprlist
109
+
110
+ # Datasets
111
+ when :dataset then Dataset
112
+
113
+ # Query forms
114
+ when :ask then Ask
115
+ when :base then Base
116
+ when :bgp then BGP
117
+ when :construct then Construct
118
+ when :describe then Describe
119
+ when :distinct then Distinct
120
+ when :filter then Filter
121
+ when :graph then Graph
122
+ when :join then Join
123
+ when :leftjoin then LeftJoin
124
+ when :order then Order
125
+ when :prefix then Prefix
126
+ when :project then Project
127
+ when :reduced then Reduced
128
+ when :slice then Slice
129
+ when :triple then RDF::Query::Pattern
130
+ when :union then Union
131
+ else nil # not found
132
+ end
133
+ end
134
+
135
+ ##
136
+ # @param [Array<RDF::Term>] operands
137
+ # @return [RDF::Term]
138
+ # @see Operator#evaluate
139
+ def self.evaluate(*operands)
140
+ self.new(*operands).evaluate(RDF::Query::Solution.new)
141
+ end
142
+
143
+ ##
144
+ # Returns the arity of this operator class.
145
+ #
146
+ # @example
147
+ # Operator.arity #=> -1
148
+ # Operator::Nullary.arity #=> 0
149
+ # Operator::Unary.arity #=> 1
150
+ # Operator::Binary.arity #=> 2
151
+ # Operator::Ternary.arity #=> 3
152
+ #
153
+ # @return [Integer] an integer in the range `(-1..3)`
154
+ def self.arity
155
+ self.const_get(:ARITY)
156
+ end
157
+
158
+ ARITY = -1 # variable arity
159
+
160
+ ##
161
+ # Initializes a new operator instance.
162
+ #
163
+ # @param [Array<RDF::Term>] operands
164
+ # @param [Hash{Symbol => Object}] options
165
+ # any additional options
166
+ # @option options [Boolean] :memoize (false)
167
+ # whether to memoize results for particular operands
168
+ # @raise [TypeError] if any operand is invalid
169
+ def initialize(*operands)
170
+ @options = operands.last.is_a?(Hash) ? operands.pop.dup : {}
171
+ @operands = operands.map! do |operand|
172
+ case operand
173
+ when Operator, Variable, RDF::Term, RDF::Query, RDF::Query::Pattern, Array, Symbol
174
+ operand
175
+ when TrueClass, FalseClass, Numeric, String, DateTime, Date, Time
176
+ RDF::Literal(operand)
177
+ else raise TypeError, "invalid SPARQL::Algebra::Operator operand: #{operand.inspect}"
178
+ end
179
+ end
180
+ end
181
+
182
+ ##
183
+ # Base URI used for reading data sources with relative URIs
184
+ #
185
+ # @return [RDF::URI]
186
+ def base_uri
187
+ Operator.base_uri
188
+ end
189
+
190
+ ##
191
+ # Base URI used for reading data sources with relative URIs
192
+ #
193
+ # @return [RDF::URI]
194
+ def self.base_uri
195
+ @base_uri
196
+ end
197
+
198
+ ##
199
+ # Set Base URI associated with SPARQL document, typically done
200
+ # when reading SPARQL from a URI
201
+ #
202
+ # @param [RDF::URI] base
203
+ # @return [RDF::URI]
204
+ def self.base_uri=(uri)
205
+ @base_uri = RDF::URI(uri)
206
+ end
207
+
208
+ ##
209
+ # Prefixes useful for future serialization
210
+ #
211
+ # @return [Hash{Symbol => RDF::URI}]
212
+ # Prefix definitions
213
+ def prefixes
214
+ Operator.prefixes
215
+ end
216
+
217
+ ##
218
+ # Prefixes useful for future serialization
219
+ #
220
+ # @return [Hash{Symbol => RDF::URI}]
221
+ # Prefix definitions
222
+ def self.prefixes
223
+ @prefixes
224
+ end
225
+
226
+ ##
227
+ # Prefixes useful for future serialization
228
+ #
229
+ # @param [Hash{Symbol => RDF::URI}] hash
230
+ # Prefix definitions
231
+ # @return [Hash{Symbol => RDF::URI}]
232
+ def self.prefixes=(hash)
233
+ @prefixes = hash
234
+ end
235
+
236
+ ##
237
+ # Any additional options for this operator.
238
+ #
239
+ # @return [Hash]
240
+ attr_reader :options
241
+
242
+ ##
243
+ # The operands to this operator.
244
+ #
245
+ # @return [Array]
246
+ attr_reader :operands
247
+
248
+ ##
249
+ # Returns the operand at the given `index`.
250
+ #
251
+ # @param [Integer] index
252
+ # an operand index in the range `(0...(operands.count))`
253
+ # @return [RDF::Term]
254
+ def operand(index = 0)
255
+ operands[index]
256
+ end
257
+
258
+ ##
259
+ # Returns `true` if any of the operands are variables, `false`
260
+ # otherwise.
261
+ #
262
+ # @return [Boolean] `true` or `false`
263
+ # @see #constant?
264
+ def variable?
265
+ operands.any? do |operand|
266
+ operand.is_a?(Variable) ||
267
+ (operand.respond_to?(:variable?) && operand.variable?)
268
+ end
269
+ end
270
+
271
+ ##
272
+ # Returns `true` if this is evaluatable (i.e., returns values for a binding), `false`
273
+ # otherwise.
274
+ #
275
+ # @return [Boolean] `true` or `false`
276
+ def evaluatable?
277
+ respond_to?(:evaluate)
278
+ end
279
+
280
+ ##
281
+ # Returns `true` if this is executable (i.e., contains a graph patterns), `false`
282
+ # otherwise.
283
+ #
284
+ # @return [Boolean] `true` or `false`
285
+ def executable?
286
+ respond_to?(:execute)
287
+ end
288
+
289
+ ##
290
+ # Returns `true` if none of the operands are variables, `false`
291
+ # otherwise.
292
+ #
293
+ # @return [Boolean] `true` or `false`
294
+ # @see #variable?
295
+ def constant?
296
+ !(variable?)
297
+ end
298
+
299
+ ##
300
+ # Returns an optimized version of this expression.
301
+ #
302
+ # For constant expressions containing no variables, returns the result
303
+ # of evaluating the expression with empty bindings; otherwise returns
304
+ # `self`.
305
+ #
306
+ # Optimization is not possible if the expression raises an exception,
307
+ # such as a `TypeError` or `ZeroDivisionError`, which must be conserved
308
+ # at runtime.
309
+ #
310
+ # @return [SPARQL::Algebra::Expression]
311
+ def optimize
312
+ if constant?
313
+ # Note that if evaluation results in a `TypeError` or other error,
314
+ # we must return `self` so that the error is conserved at runtime:
315
+ evaluate rescue self
316
+ else
317
+ super # returns `self`
318
+ end
319
+ end
320
+
321
+ ##
322
+ # Returns the SPARQL S-Expression (SSE) representation of this operator.
323
+ #
324
+ # @return [Array]
325
+ # @see http://openjena.org/wiki/SSE
326
+ def to_sse
327
+ operator = [self.class.const_get(:NAME)].flatten.first
328
+ [operator, *(operands || []).map(&:to_sse)]
329
+ end
330
+
331
+ ##
332
+ # Returns an S-Expression (SXP) representation of this operator
333
+ #
334
+ # @return [String]
335
+ def to_sxp
336
+ begin
337
+ require 'sxp' # @see http://rubygems.org/gems/sxp
338
+ rescue LoadError
339
+ abort "SPARQL::Algebra::Operator#to_sxp requires the SXP gem (hint: `gem install sxp')."
340
+ end
341
+ require 'sparql/algebra/sxp_extensions'
342
+
343
+ to_sse.to_sxp
344
+ end
345
+
346
+ ##
347
+ # Returns a developer-friendly representation of this operator.
348
+ #
349
+ # @return [String]
350
+ def inspect
351
+ sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, operands.map(&:inspect).join(', '))
352
+ end
353
+
354
+ ##
355
+ # @param [Statement] other
356
+ # @return [Boolean]
357
+ def eql?(other)
358
+ other.class == self.class && other.operands == self.operands
359
+ end
360
+ alias_method :==, :eql?
361
+ protected
362
+
363
+ ##
364
+ # Returns the effective boolean value (EBV) of the given `literal`.
365
+ #
366
+ # @param [RDF::Literal] literal
367
+ # @return [RDF::Literal::Boolean] `true` or `false`
368
+ # @raise [TypeError] if the literal could not be coerced to an `RDF::Literal::Boolean`
369
+ # @see http://www.w3.org/TR/rdf-sparql-query/#ebv
370
+ def boolean(literal)
371
+ case literal
372
+ when FalseClass then RDF::Literal::FALSE
373
+ when TrueClass then RDF::Literal::TRUE
374
+ # If the argument is a typed literal with a datatype of
375
+ # `xsd:boolean`, the EBV is the value of that argument.
376
+ # However, the EBV of any literal whose type is `xsd:boolean` is
377
+ # false if the lexical form is not valid for that datatype.
378
+ when RDF::Literal::Boolean
379
+ RDF::Literal(literal.valid? && literal.true?)
380
+ # If the argument is a numeric type or a typed literal with a
381
+ # datatype derived from a numeric type, the EBV is false if the
382
+ # operand value is NaN or is numerically equal to zero; otherwise
383
+ # the EBV is true.
384
+ # However, the EBV of any literal whose type is numeric is
385
+ # false if the lexical form is not valid for that datatype.
386
+ when RDF::Literal::Numeric
387
+ RDF::Literal(literal.valid? && !(literal.zero?) && !(literal.respond_to?(:nan?) && literal.nan?))
388
+ # If the argument is a plain literal or a typed literal with a
389
+ # datatype of `xsd:string`, the EBV is false if the operand value
390
+ # has zero length; otherwise the EBV is true.
391
+ else case
392
+ when literal.is_a?(RDF::Literal) && (literal.plain? || literal.datatype.eql?(RDF::XSD.string))
393
+ RDF::Literal(!(literal.value.empty?))
394
+ # All other arguments, including unbound arguments, produce a type error.
395
+ else raise TypeError, "could not coerce #{literal.inspect} to an RDF::Literal::Boolean"
396
+ end
397
+ end
398
+ end
399
+
400
+ private
401
+
402
+ @@subclasses = [] # @private
403
+
404
+ ##
405
+ # @private
406
+ # @return [void]
407
+ def self.inherited(child)
408
+ @@subclasses << child unless child.superclass.equal?(Operator) # grandchildren only
409
+ super
410
+ end
411
+
412
+ ##
413
+ # A SPARQL nullary operator.
414
+ #
415
+ # Operators of this kind take no operands.
416
+ #
417
+ # @abstract
418
+ class Nullary < Operator
419
+ ARITY = 0
420
+
421
+ ##
422
+ # @param [Hash{Symbol => Object}] options
423
+ # any additional options (see {Operator#initialize})
424
+ def initialize(options = {})
425
+ super
426
+ end
427
+ end # Nullary
428
+
429
+ ##
430
+ # A SPARQL unary operator.
431
+ #
432
+ # Operators of this kind take one operand.
433
+ #
434
+ # @abstract
435
+ class Unary < Operator
436
+ ARITY = 1
437
+
438
+ ##
439
+ # @param [RDF::Term] arg
440
+ # the operand
441
+ # @param [Hash{Symbol => Object}] options
442
+ # any additional options (see {Operator#initialize})
443
+ def initialize(arg, options = {})
444
+ super
445
+ end
446
+ end # Unary
447
+
448
+ ##
449
+ # A SPARQL binary operator.
450
+ #
451
+ # Operators of this kind take two operands.
452
+ #
453
+ # @abstract
454
+ class Binary < Operator
455
+ ARITY = 2
456
+
457
+ ##
458
+ # @param [RDF::Term] arg1
459
+ # the first operand
460
+ # @param [RDF::Term] arg2
461
+ # the second operand
462
+ # @param [Hash{Symbol => Object}] options
463
+ # any additional options (see {Operator#initialize})
464
+ def initialize(arg1, arg2, options = {})
465
+ super
466
+ end
467
+ end # Binary
468
+
469
+ ##
470
+ # A SPARQL ternary operator.
471
+ #
472
+ # Operators of this kind take three operands.
473
+ #
474
+ # @abstract
475
+ class Ternary < Operator
476
+ ARITY = 3
477
+
478
+ ##
479
+ # @param [RDF::Term] arg1
480
+ # the first operand
481
+ # @param [RDF::Term] arg2
482
+ # the second operand
483
+ # @param [RDF::Term] arg3
484
+ # the third operand
485
+ # @param [Hash{Symbol => Object}] options
486
+ # any additional options (see {Operator#initialize})
487
+ def initialize(arg1, arg2, arg3, options = {})
488
+ super
489
+ end
490
+ end # Ternary
491
+ end # Operator
492
+ end; end # SPARQL::Algebra