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.
- data/AUTHORS +3 -0
- data/CREDITS +0 -0
- data/README.markdown +103 -53
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/bin/sparql +87 -0
- data/lib/sparql.rb +105 -22
- data/lib/sparql/algebra.rb +369 -0
- data/lib/sparql/algebra/evaluatable.rb +37 -0
- data/lib/sparql/algebra/expression.rb +284 -0
- data/lib/sparql/algebra/extensions.rb +159 -0
- data/lib/sparql/algebra/operator.rb +492 -0
- data/lib/sparql/algebra/operator/add.rb +34 -0
- data/lib/sparql/algebra/operator/and.rb +65 -0
- data/lib/sparql/algebra/operator/asc.rb +29 -0
- data/lib/sparql/algebra/operator/ask.rb +46 -0
- data/lib/sparql/algebra/operator/base.rb +46 -0
- data/lib/sparql/algebra/operator/bgp.rb +26 -0
- data/lib/sparql/algebra/operator/bound.rb +48 -0
- data/lib/sparql/algebra/operator/compare.rb +84 -0
- data/lib/sparql/algebra/operator/construct.rb +85 -0
- data/lib/sparql/algebra/operator/dataset.rb +77 -0
- data/lib/sparql/algebra/operator/datatype.rb +42 -0
- data/lib/sparql/algebra/operator/desc.rb +17 -0
- data/lib/sparql/algebra/operator/describe.rb +71 -0
- data/lib/sparql/algebra/operator/distinct.rb +50 -0
- data/lib/sparql/algebra/operator/divide.rb +43 -0
- data/lib/sparql/algebra/operator/equal.rb +32 -0
- data/lib/sparql/algebra/operator/exprlist.rb +52 -0
- data/lib/sparql/algebra/operator/filter.rb +71 -0
- data/lib/sparql/algebra/operator/graph.rb +28 -0
- data/lib/sparql/algebra/operator/greater_than.rb +32 -0
- data/lib/sparql/algebra/operator/greater_than_or_equal.rb +33 -0
- data/lib/sparql/algebra/operator/is_blank.rb +35 -0
- data/lib/sparql/algebra/operator/is_iri.rb +37 -0
- data/lib/sparql/algebra/operator/is_literal.rb +36 -0
- data/lib/sparql/algebra/operator/join.rb +67 -0
- data/lib/sparql/algebra/operator/lang.rb +29 -0
- data/lib/sparql/algebra/operator/lang_matches.rb +53 -0
- data/lib/sparql/algebra/operator/left_join.rb +95 -0
- data/lib/sparql/algebra/operator/less_than.rb +32 -0
- data/lib/sparql/algebra/operator/less_than_or_equal.rb +32 -0
- data/lib/sparql/algebra/operator/minus.rb +31 -0
- data/lib/sparql/algebra/operator/multiply.rb +34 -0
- data/lib/sparql/algebra/operator/not.rb +35 -0
- data/lib/sparql/algebra/operator/not_equal.rb +26 -0
- data/lib/sparql/algebra/operator/or.rb +65 -0
- data/lib/sparql/algebra/operator/order.rb +69 -0
- data/lib/sparql/algebra/operator/plus.rb +31 -0
- data/lib/sparql/algebra/operator/prefix.rb +45 -0
- data/lib/sparql/algebra/operator/project.rb +46 -0
- data/lib/sparql/algebra/operator/reduced.rb +47 -0
- data/lib/sparql/algebra/operator/regex.rb +70 -0
- data/lib/sparql/algebra/operator/same_term.rb +46 -0
- data/lib/sparql/algebra/operator/slice.rb +60 -0
- data/lib/sparql/algebra/operator/str.rb +35 -0
- data/lib/sparql/algebra/operator/subtract.rb +32 -0
- data/lib/sparql/algebra/operator/union.rb +55 -0
- data/lib/sparql/algebra/query.rb +99 -0
- data/lib/sparql/algebra/sxp_extensions.rb +35 -0
- data/lib/sparql/algebra/version.rb +20 -0
- data/lib/sparql/extensions.rb +102 -0
- data/lib/sparql/grammar.rb +298 -0
- data/lib/sparql/grammar/lexer.rb +609 -0
- data/lib/sparql/grammar/parser.rb +1383 -0
- data/lib/sparql/grammar/parser/meta.rb +1801 -0
- data/lib/sparql/results.rb +220 -0
- data/lib/sparql/version.rb +20 -0
- metadata +232 -62
- data/Rakefile +0 -22
- data/coverage/index.html +0 -252
- data/coverage/lib-sparql-execute_sparql_rb.html +0 -621
- data/coverage/lib-sparql_rb.html +0 -622
- data/lib/sparql/execute_sparql.rb +0 -27
- data/lib/sparql/sparql.treetop +0 -159
- data/sparql.gemspec +0 -16
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -24
- data/spec/unit/graph_parsing_spec.rb +0 -76
- data/spec/unit/iri_parsing_spec.rb +0 -46
- data/spec/unit/prefixed_names_parsing_spec.rb +0 -40
- data/spec/unit/primitives_parsing_spec.rb +0 -26
- data/spec/unit/sparql_parsing_spec.rb +0 -72
- 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
|