rdf-n3 2.2.0 → 3.1.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 (101) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +192 -69
  3. data/UNLICENSE +1 -1
  4. data/VERSION +1 -1
  5. data/lib/rdf/n3.rb +11 -8
  6. data/lib/rdf/n3/algebra.rb +204 -0
  7. data/lib/rdf/n3/algebra/builtin.rb +79 -0
  8. data/lib/rdf/n3/algebra/formula.rb +446 -0
  9. data/lib/rdf/n3/algebra/list/append.rb +42 -0
  10. data/lib/rdf/n3/algebra/list/first.rb +24 -0
  11. data/lib/rdf/n3/algebra/list/in.rb +48 -0
  12. data/lib/rdf/n3/algebra/list/last.rb +24 -0
  13. data/lib/rdf/n3/algebra/list/length.rb +24 -0
  14. data/lib/rdf/n3/algebra/list/member.rb +44 -0
  15. data/lib/rdf/n3/algebra/list_operator.rb +83 -0
  16. data/lib/rdf/n3/algebra/log/conclusion.rb +65 -0
  17. data/lib/rdf/n3/algebra/log/conjunction.rb +36 -0
  18. data/lib/rdf/n3/algebra/log/content.rb +34 -0
  19. data/lib/rdf/n3/algebra/log/equal_to.rb +34 -0
  20. data/lib/rdf/n3/algebra/log/implies.rb +102 -0
  21. data/lib/rdf/n3/algebra/log/includes.rb +70 -0
  22. data/lib/rdf/n3/algebra/log/n3_string.rb +34 -0
  23. data/lib/rdf/n3/algebra/log/not_equal_to.rb +23 -0
  24. data/lib/rdf/n3/algebra/log/not_includes.rb +27 -0
  25. data/lib/rdf/n3/algebra/log/output_string.rb +40 -0
  26. data/lib/rdf/n3/algebra/log/parsed_as_n3.rb +36 -0
  27. data/lib/rdf/n3/algebra/log/semantics.rb +40 -0
  28. data/lib/rdf/n3/algebra/math/absolute_value.rb +36 -0
  29. data/lib/rdf/n3/algebra/math/acos.rb +26 -0
  30. data/lib/rdf/n3/algebra/math/acosh.rb +26 -0
  31. data/lib/rdf/n3/algebra/math/asin.rb +26 -0
  32. data/lib/rdf/n3/algebra/math/asinh.rb +26 -0
  33. data/lib/rdf/n3/algebra/math/atan.rb +26 -0
  34. data/lib/rdf/n3/algebra/math/atanh.rb +26 -0
  35. data/lib/rdf/n3/algebra/math/ceiling.rb +28 -0
  36. data/lib/rdf/n3/algebra/math/cos.rb +40 -0
  37. data/lib/rdf/n3/algebra/math/cosh.rb +38 -0
  38. data/lib/rdf/n3/algebra/math/difference.rb +40 -0
  39. data/lib/rdf/n3/algebra/math/equal_to.rb +54 -0
  40. data/lib/rdf/n3/algebra/math/exponentiation.rb +35 -0
  41. data/lib/rdf/n3/algebra/math/floor.rb +28 -0
  42. data/lib/rdf/n3/algebra/math/greater_than.rb +41 -0
  43. data/lib/rdf/n3/algebra/math/less_than.rb +41 -0
  44. data/lib/rdf/n3/algebra/math/negation.rb +38 -0
  45. data/lib/rdf/n3/algebra/math/not_equal_to.rb +25 -0
  46. data/lib/rdf/n3/algebra/math/not_greater_than.rb +25 -0
  47. data/lib/rdf/n3/algebra/math/not_less_than.rb +25 -0
  48. data/lib/rdf/n3/algebra/math/product.rb +20 -0
  49. data/lib/rdf/n3/algebra/math/quotient.rb +36 -0
  50. data/lib/rdf/n3/algebra/math/remainder.rb +35 -0
  51. data/lib/rdf/n3/algebra/math/rounded.rb +26 -0
  52. data/lib/rdf/n3/algebra/math/sin.rb +40 -0
  53. data/lib/rdf/n3/algebra/math/sinh.rb +38 -0
  54. data/lib/rdf/n3/algebra/math/sum.rb +40 -0
  55. data/lib/rdf/n3/algebra/math/tan.rb +40 -0
  56. data/lib/rdf/n3/algebra/math/tanh.rb +38 -0
  57. data/lib/rdf/n3/algebra/not_implemented.rb +13 -0
  58. data/lib/rdf/n3/algebra/resource_operator.rb +123 -0
  59. data/lib/rdf/n3/algebra/str/concatenation.rb +27 -0
  60. data/lib/rdf/n3/algebra/str/contains.rb +33 -0
  61. data/lib/rdf/n3/algebra/str/contains_ignoring_case.rb +33 -0
  62. data/lib/rdf/n3/algebra/str/ends_with.rb +33 -0
  63. data/lib/rdf/n3/algebra/str/equal_ignoring_case.rb +34 -0
  64. data/lib/rdf/n3/algebra/str/format.rb +17 -0
  65. data/lib/rdf/n3/algebra/str/greater_than.rb +38 -0
  66. data/lib/rdf/n3/algebra/str/less_than.rb +33 -0
  67. data/lib/rdf/n3/algebra/str/matches.rb +37 -0
  68. data/lib/rdf/n3/algebra/str/not_equal_ignoring_case.rb +17 -0
  69. data/lib/rdf/n3/algebra/str/not_greater_than.rb +17 -0
  70. data/lib/rdf/n3/algebra/str/not_less_than.rb +17 -0
  71. data/lib/rdf/n3/algebra/str/not_matches.rb +18 -0
  72. data/lib/rdf/n3/algebra/str/replace.rb +35 -0
  73. data/lib/rdf/n3/algebra/str/scrape.rb +35 -0
  74. data/lib/rdf/n3/algebra/str/starts_with.rb +33 -0
  75. data/lib/rdf/n3/algebra/time/day.rb +35 -0
  76. data/lib/rdf/n3/algebra/time/day_of_week.rb +27 -0
  77. data/lib/rdf/n3/algebra/time/gm_time.rb +29 -0
  78. data/lib/rdf/n3/algebra/time/hour.rb +35 -0
  79. data/lib/rdf/n3/algebra/time/in_seconds.rb +59 -0
  80. data/lib/rdf/n3/algebra/time/local_time.rb +29 -0
  81. data/lib/rdf/n3/algebra/time/minute.rb +35 -0
  82. data/lib/rdf/n3/algebra/time/month.rb +35 -0
  83. data/lib/rdf/n3/algebra/time/second.rb +35 -0
  84. data/lib/rdf/n3/algebra/time/timezone.rb +36 -0
  85. data/lib/rdf/n3/algebra/time/year.rb +29 -0
  86. data/lib/rdf/n3/extensions.rb +221 -0
  87. data/lib/rdf/n3/format.rb +66 -1
  88. data/lib/rdf/n3/list.rb +630 -0
  89. data/lib/rdf/n3/reader.rb +834 -492
  90. data/lib/rdf/n3/reasoner.rb +282 -0
  91. data/lib/rdf/n3/refinements.rb +178 -0
  92. data/lib/rdf/n3/repository.rb +332 -0
  93. data/lib/rdf/n3/terminals.rb +80 -0
  94. data/lib/rdf/n3/vocab.rb +36 -3
  95. data/lib/rdf/n3/writer.rb +476 -239
  96. metadata +187 -68
  97. data/AUTHORS +0 -1
  98. data/History.markdown +0 -99
  99. data/lib/rdf/n3/patches/array_hacks.rb +0 -53
  100. data/lib/rdf/n3/reader/meta.rb +0 -641
  101. data/lib/rdf/n3/reader/parser.rb +0 -237
@@ -0,0 +1,282 @@
1
+ # coding: utf-8
2
+ module RDF::N3
3
+ ##
4
+ # A Notation-3/Turtle reasoner in Ruby
5
+ #
6
+ # Takes either a parsed formula or an `RDF::Queryable` and updates it by reasoning over formula defined within the queryable.
7
+ #
8
+ # @author [Gregg Kellogg](http://greggkellogg.net/)
9
+ class Reasoner
10
+ include RDF::Enumerable
11
+ include RDF::Mutable
12
+ include RDF::Util::Logger
13
+
14
+ # The top-level parsed formula, including builtins and variables.
15
+ # @return [RDF::N3::Algebra::Formula]
16
+ attr_reader :formula
17
+
18
+ # Opens a Notation-3 file, and parses it to initialize the reasoner
19
+ #
20
+ # @param [String, #to_s] file
21
+ # @yield [reasoner] `self`
22
+ # @yieldparam [RDF::N3::Reasoner] reasoner
23
+ # @yieldreturn [void] ignored
24
+ # @return [RDF::N3::Reasoner]
25
+ def self.open(file)
26
+ RDF::N3::Reader.open(file, **options) do |reader|
27
+ RDF::N3::Reasoner.new(reader, **options, &block)
28
+ end
29
+ end
30
+
31
+ ##
32
+ # Initializes a new reasoner. If input is an IO or string, it is taken as n3 source and parsed first. Otherwise, it is a parsed formula.
33
+ #
34
+ # It returns the evaluated formula, or yields triples.
35
+ #
36
+ # @example Initializing from a reader
37
+ # reader = RDF::N3::Reader.new(":a :b :c .")
38
+ # reasoner = RDF::N3::Reasoner.new(reader)
39
+ # reasoner.each_triple {}
40
+ #
41
+ # @example Initializing as a mutable
42
+ # reasoner = RDF::N3::Reasoner.new do |r|
43
+ # r << RDF::N3::Reader.new(":a :b :c .")
44
+ # end
45
+ # reasoner.each_triple {}
46
+ #
47
+ # @example Initializing with multiple inputs
48
+ # reasoner = RDF::N3::Reasoner.new
49
+ # RDF::NTriples::Reader.open("example.nt") {|r| reasoner << r}
50
+ # RDF::N3::Reader.open("rules.n3") {|r| reasoner << r}
51
+ # reasoner.each_triple {}
52
+ #
53
+ # @param [RDF::Mutable] input (nil)
54
+ # Input should be parsed N3 using native lists (see `:list_terms` option to {RDF::N3::Reader#initialize})
55
+ # @param [Hash{Symbol => Object}] options
56
+ # @option options [#to_s] :base_uri (nil)
57
+ # the base URI to use when resolving relative URIs (for acessing intermediate parser productions)
58
+ # @yield [reasoner] `self`
59
+ # @yieldparam [RDF::N3::Reasoner] reasoner
60
+ # @yieldreturn [void] ignored
61
+ # @return [RDF::N3::Reasoner]
62
+ def initialize(input, **options, &block)
63
+ @options = options.merge(strings: {}) # for --strings and log:outputString
64
+ @mutable = case input
65
+ when RDF::Mutable then input
66
+ when RDF::Enumerable then RDF::N3::Repository.new {|r| r << input}
67
+ else RDF::N3::Repository.new
68
+ end
69
+
70
+ @formula = input if input.is_a?(RDF::N3::Algebra::Formula)
71
+
72
+ log_debug("reasoner: expression") {SXP::Generator.string(formula.to_sxp_bin)}
73
+
74
+ if block_given?
75
+ case block.arity
76
+ when 0 then instance_eval(&block)
77
+ else block.call(self)
78
+ end
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Returns a copy of this reasoner
84
+ def dup
85
+ repo = RDF::N3::Repository.new {|r| r << @mutable}
86
+ self.class.new(repo) do |reasoner|
87
+ reasoner.instance_variable_set(:@options, @options.dup)
88
+ reasoner.instance_variable_set(:@formula, @formula.dup) if @formula
89
+ end
90
+ end
91
+
92
+ ##
93
+ # Inserts an RDF statement the datastore, resets `formula`.
94
+ #
95
+ # @param [RDF::Statement] statement
96
+ # @return [void]
97
+ def insert_statement(statement)
98
+ @formula = nil
99
+ @mutable.insert_statement(statement)
100
+ end
101
+
102
+ ##
103
+ # Updates the datastore by reasoning over the formula, optionally yielding each conclusion; uses triples from the graph associated with this formula as the dataset over which to reason.
104
+ #
105
+ # @param [Hash{Symbol => Object}] options
106
+ # @option options [Boolean] :apply
107
+ # @option options [Boolean] :rules
108
+ # @option options [Boolean] :think
109
+ # @yield [statement]
110
+ # @yieldparam [RDF::Statement] statement
111
+ # @return [RDF::N3::Reasoner] `self`
112
+ def execute(**options, &block)
113
+ @options[:logger] = options[:logger] if options.has_key?(:logger)
114
+
115
+ # The knowledge base is the non-variable portions of formula
116
+ knowledge_base = RDF::N3::Repository.new {|r| r << formula}
117
+ log_debug("reasoner: knowledge_base") {SXP::Generator.string(knowledge_base.statements.to_sxp_bin)}
118
+
119
+ # If thinking, continuously execute until results stop growing
120
+ count = -1
121
+ log_info("reasoner: start") { "count: #{count}"}
122
+ solutions = RDF::Query::Solutions(RDF::Query::Solution.new)
123
+ while knowledge_base.count > count
124
+ log_info("reasoner: do") { "count: #{count}"}
125
+ count = knowledge_base.count
126
+ log_depth {formula.execute(knowledge_base, solutions: solutions, **options)}
127
+ knowledge_base << formula
128
+ solutions = RDF::Query::Solutions(RDF::Query::Solution.new) if solutions.empty?
129
+ log_debug("reasoner: solutions") {SXP::Generator.string solutions.to_sxp_bin}
130
+ log_debug("reasoner: datastore") {SXP::Generator.string knowledge_base.statements.to_sxp_bin}
131
+ log_info("reasoner: inferred") {SXP::Generator.string knowledge_base.statements.select(&:inferred?).to_sxp_bin}
132
+ log_info("reasoner: formula") do
133
+ SXP::Generator.string RDF::N3::Algebra::Formula.from_enumerable(knowledge_base).to_sxp_bin
134
+ end
135
+ @formula = nil # cause formula to be re-calculated from knowledge-base
136
+ unless options[:think]
137
+ count = knowledge_base.count
138
+ break
139
+ end
140
+ end
141
+ log_info("reasoner: end") { "count: #{count}"}
142
+
143
+ # Add updates back to mutable, containg builtins and variables.
144
+ @mutable << knowledge_base
145
+
146
+ each(&block) if block_given?
147
+ self
148
+ end
149
+ alias_method :reason!, :execute
150
+
151
+ ##
152
+ # Reason with results in a duplicate datastore
153
+ #
154
+ # @see execute
155
+ def reason(**options, &block)
156
+ self.dup.reason!(**options, &block)
157
+ end
158
+
159
+ ##
160
+ # Yields each statement in the datastore
161
+ #
162
+ # @yieldparam [RDF::Statement] statement
163
+ # @yieldreturn [void] ignored
164
+ # @return [void]
165
+ def each(&block)
166
+ @mutable.each(&block)
167
+ end
168
+
169
+ ##
170
+ # Yields data, excluding formulae or variables and statements referencing formulae or variables
171
+ #
172
+ # @overload data
173
+ # @yield [statement]
174
+ # each statement
175
+ # @yieldparam [RDF::Statement] statement
176
+ # @yieldreturn [void] ignored
177
+ # @return [void]
178
+ #
179
+ # @overload data
180
+ # @return [Enumerator<RDF::Statement>]
181
+ # @return [RDF::Enumerator]
182
+ # @yield [statement]
183
+ # @yieldparam [RDF::Statement] statement
184
+ def data(&block)
185
+ if block_given?
186
+ project_graph(nil) do |statement|
187
+ block.call(statement) unless statement.variable? ||
188
+ has_graph?(statement.subject) ||
189
+ has_graph?(statement.object)
190
+ end
191
+ end
192
+ enum_data
193
+ end
194
+ alias_method :each_datum, :data
195
+
196
+ ##
197
+ # Returns an enumerator for {#data}.
198
+ # FIXME: enum_for doesn't seem to be working properly
199
+ # in JRuby 1.7, so specs are marked pending
200
+ #
201
+ # @return [Enumerator<RDF::Statement>]
202
+ # @see #each_statement
203
+ def enum_data
204
+ # Ensure that statements are queryable, countable and enumerable
205
+ this = self
206
+ RDF::Queryable::Enumerator.new do |yielder|
207
+ this.send(:each_datum) {|y| yielder << y}
208
+ end
209
+ end
210
+
211
+ ##
212
+ # Yields conclusions, excluding formulae and those statements in the original dataset, or returns an enumerator over the conclusions
213
+ #
214
+ # @overload conclusions
215
+ # @yield [statement]
216
+ # each statement
217
+ # @yieldparam [RDF::Statement] statement
218
+ # @yieldreturn [void] ignored
219
+ # @return [void]
220
+ #
221
+ # @overload conclusions
222
+ # @return [Enumerator<RDF::Statement>]
223
+ # @return [RDF::Enumerator]
224
+ # @yield [statement]
225
+ # @yieldparam [RDF::Statement] statement
226
+ def conclusions(&block)
227
+ if block_given?
228
+ # Invoke {#each} in the containing class:
229
+ each_statement {|s| block.call(s) if s.inferred?}
230
+ end
231
+ enum_conclusions
232
+ end
233
+ alias_method :each_conclusion, :conclusions
234
+
235
+ ##
236
+ # Returns an enumerator for {#conclusions}.
237
+ # FIXME: enum_for doesn't seem to be working properly
238
+ # in JRuby 1.7, so specs are marked pending
239
+ #
240
+ # @return [Enumerator<RDF::Statement>]
241
+ # @see #each_statement
242
+ def enum_conclusions
243
+ # Ensure that statements are queryable, countable and enumerable
244
+ this = self
245
+ RDF::Queryable::Enumerator.new do |yielder|
246
+ this.send(:each_conclusion) {|y| yielder << y}
247
+ end
248
+ end
249
+
250
+ ##
251
+ # Returns the concatenated strings from log:outputString
252
+ #
253
+ # @return [String]
254
+ def strings
255
+ @options[:strings].
256
+ sort_by {|k, v| k}.
257
+ map {|(k,v)| v.join("")}.
258
+ join("")
259
+ end
260
+
261
+ ##
262
+ # Returns the top-level formula for this file.
263
+ #
264
+ # Transforms an RDF dataset into a recursive formula structure.
265
+ #
266
+ # @return [RDF::N3::Algebra::Formula]
267
+ def formula
268
+ @formula ||= RDF::N3::Algebra::Formula.from_enumerable(@mutable, **@options)
269
+ end
270
+
271
+ ##
272
+ # Returns the SPARQL S-Expression (SSE) representation of the parsed formula.
273
+ # Formulae are represented as subjects and objects in the containing graph, along with their universals and existentials
274
+ #
275
+ # @return [Array] `self`
276
+ # @see http://openjena.org/wiki/SSE
277
+ def to_sxp_bin
278
+ formula.to_sxp_bin
279
+ end
280
+ end
281
+ end
282
+
@@ -0,0 +1,178 @@
1
+ # Refinements on core RDF class behavior for RDF::N3.
2
+ module RDF::N3::Refinements
3
+ # @!parse
4
+ # # Refinements on RDF::Term
5
+ # module RDF::Term
6
+ # ##
7
+ # # As a term is constant, this returns itself.
8
+ # #
9
+ # # @param [Hash{Symbol => RDF::Term}] bindings
10
+ # # a query solution containing zero or more variable bindings
11
+ # # @param [Hash{Symbol => Object}] options ({})
12
+ # # options passed from query
13
+ # # @return [RDF::Term]
14
+ # # @see SPARQL::Algebra::Expression.evaluate
15
+ # def evaluate(bindings, formulae: nil, **options); end
16
+ # end
17
+ refine ::RDF::Term do
18
+ def evaluate(bindings, formulae:, **options)
19
+ self
20
+ end
21
+ end
22
+
23
+ # @!parse
24
+ # # Refinements on RDF::Node
25
+ # module RDF::Term
26
+ # ##
27
+ # # Blank node may refer to a formula.
28
+ # #
29
+ # # @param [Hash{Symbol => RDF::Term}] bindings
30
+ # # a query solution containing zero or more variable bindings
31
+ # # @param [Hash{Symbol => Object}] options ({})
32
+ # # options passed from query
33
+ # # @return [RDF::Node, RDF::N3::Algebra::Formula]
34
+ # # @see SPARQL::Algebra::Expression.evaluate
35
+ # def evaluate(bindings, formulae:, **options); end
36
+ # end
37
+ refine ::RDF::Node do
38
+ ##
39
+ # @return [RDF::Node, RDF::N3::Algebra::Formula]
40
+ def evaluate(bindings, formulae:, **options)
41
+ node? ? formulae.fetch(self, self) : self
42
+ end
43
+ end
44
+
45
+ # @!parse
46
+ # # Refinements on RDF::Statement
47
+ # class ::RDF::Statement
48
+ # # Refines `valid?` to allow literal subjects and BNode predicates.
49
+ # # @return [Boolean]
50
+ # def valid?; end
51
+ #
52
+ # # Refines `invalid?` to allow literal subjects and BNode predicates.
53
+ # # @return [Boolean]
54
+ # def invalid?; end
55
+ #
56
+ # # Refines `validate!` to allow literal subjects and BNode predicates.
57
+ # # @return [RDF::Value] `self`
58
+ # # @raise [ArgumentError] if the value is invalid
59
+ # def validate!; end
60
+ #
61
+ # ##
62
+ # # As a statement is constant, this returns itself.
63
+ # #
64
+ # # @param [Hash{Symbol => RDF::Term}] bindings
65
+ # # a query solution containing zero or more variable bindings
66
+ # # @param [Hash{Symbol => Object}] options ({})
67
+ # # options passed from query
68
+ # # @return [RDF::Statement]
69
+ # # @see SPARQL::Algebra::Expression.evaluate
70
+ # def evaluate(bindings, formulae:, **options); end
71
+ # end
72
+ refine ::RDF::Statement do
73
+ ##
74
+ # Override `valid?` terms as subjects and resources as predicates.
75
+ #
76
+ # @return [Boolean]
77
+ def valid?
78
+ has_subject? && subject.term? && subject.valid? &&
79
+ has_predicate? && predicate.term? && predicate.valid? &&
80
+ has_object? && object.term? && object.valid? &&
81
+ (has_graph? ? (graph_name.resource? && graph_name.valid?) : true)
82
+ end
83
+
84
+ ##
85
+ # @return [Boolean]
86
+ def invalid?
87
+ !valid?
88
+ end
89
+
90
+ ##
91
+ # Default validate! implementation, overridden in concrete classes
92
+ # @return [RDF::Value] `self`
93
+ # @raise [ArgumentError] if the value is invalid
94
+ def validate!
95
+ raise ArgumentError, "#{self.inspect} is not valid" if invalid?
96
+ self
97
+ end
98
+ alias_method :validate, :validate!
99
+
100
+ ##
101
+ # @return [RDF::Statement]
102
+ def evaluate(bindings, formulae:, **options)
103
+ self
104
+ end
105
+ end
106
+
107
+ # @!parse
108
+ # # Refinements on RDF::Query::Pattern
109
+ # class ::RDF::Query::Pattern
110
+ # # Refines `#valid?` to allow literal subjects and BNode predicates.
111
+ # # @return [Boolean]
112
+ # def valid?; end
113
+ #
114
+ # ##
115
+ # # Evaluates the pattern using the given variable `bindings` by cloning the pattern replacing variables with their bindings recursively. If the resulting pattern is constant, it is cast as a statement.
116
+ # #
117
+ # # @param [Hash{Symbol => RDF::Term}] bindings
118
+ # # a query solution containing zero or more variable bindings
119
+ # # @param [Hash{Symbol => Object}] options ({})
120
+ # # options passed from query
121
+ # # @return [RDF::Statement, RDF::N3::Algebra::Formula]
122
+ # # @see SPARQL::Algebra::Expression.evaluate
123
+ # def evaluate(bindings, formulae:, **options); end
124
+ # end
125
+ refine ::RDF::Query::Pattern do
126
+ ##
127
+ # Is this pattern composed only of valid components?
128
+ #
129
+ # @return [Boolean] `true` or `false`
130
+ def valid?
131
+ (has_subject? ? (subject.term? || subject.variable?) && subject.valid? : true) &&
132
+ (has_predicate? ? (predicate.term? || predicate.variable?) && predicate.valid? : true) &&
133
+ (has_object? ? (object.term? || object.variable?) && object.valid? : true) &&
134
+ (has_graph? ? (graph_name.resource? || graph_name.variable?) && graph_name.valid? : true)
135
+ rescue NoMethodError
136
+ false
137
+ end
138
+
139
+ # @return [RDF::Statement, RDF::N3::Algebra::Formula]
140
+ def evaluate(bindings, formulae:, **options)
141
+ elements = self.to_quad.map do |term|
142
+ term.evaluate(bindings, formulae: formulae, **options)
143
+ end.compact.map do |term|
144
+ term.node? ? formulae.fetch(term, term) : term
145
+ end
146
+
147
+ self.class.from(elements)
148
+ end
149
+ end
150
+
151
+ # @!parse
152
+ # # Refinements on RDF::Query::Variable
153
+ # class RDF::Query::Variable
154
+ # ##
155
+ # # If variable is bound, replace with the bound value, otherwise, returns itself
156
+ # #
157
+ # # @param [Hash{Symbol => RDF::Term}] bindings
158
+ # # a query solution containing zero or more variable bindings
159
+ # # @param [Hash{Symbol => Object}] options ({})
160
+ # # options passed from query
161
+ # # @return [RDF::Term]
162
+ # # @see SPARQL::Algebra::Expression.evaluate
163
+ # def evaluate(bindings, formulae:, **options); end
164
+ # end
165
+ refine ::RDF::Query::Variable do
166
+ ##
167
+ # @return [RDF::Term]
168
+ def evaluate(bindings, formulae:, **options)
169
+ value = bindings.has_key?(name) ? bindings[name] : self
170
+ value.node? ? formulae.fetch(value, value) : value
171
+ end
172
+ end
173
+
174
+ refine ::RDF::Graph do
175
+ # Allow a graph to be treated as a term in a statement.
176
+ include ::RDF::Term
177
+ end
178
+ end