sparql 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1304 @@
1
+ require 'ebnf'
2
+ require 'ebnf/ll1/parser'
3
+ require 'sparql/grammar/meta'
4
+
5
+ module SPARQL::Grammar
6
+ ##
7
+ # A parser for the SPARQL 1.1 grammar.
8
+ #
9
+ # @see http://www.w3.org/TR/rdf-sparql-query/#grammar
10
+ # @see http://en.wikipedia.org/wiki/LR_parser
11
+ class Parser
12
+ include SPARQL::Grammar::Meta
13
+ include SPARQL::Grammar::Terminals
14
+ include EBNF::LL1::Parser
15
+
16
+ # Builtin functions
17
+ BUILTINS = %w{
18
+ ABS BNODE CEIL COALESCE CONCAT
19
+ CONTAINS DATATYPE DAY ENCODE_FOR_URI EXISTS
20
+ FLOOR HOURS IF IRI LANGMATCHES LANG LCASE
21
+ MD5 MINUTES MONTH NOW RAND ROUND SECONDS
22
+ SHA1 SHA224 SHA256 SHA384 SHA512
23
+ STRDT STRENDS STRLANG STRLEN STRSTARTS SUBSTR STR
24
+ TIMEZONE TZ UCASE URI YEAR
25
+ isBLANK isIRI isURI isLITERAL isNUMERIC sameTerm
26
+ }.map {|s| s.downcase.to_sym}.freeze
27
+
28
+ BUILTIN_RULES = [:regex, :substr, :exists, :not_exists].freeze
29
+
30
+ ##
31
+ # Any additional options for the parser.
32
+ #
33
+ # @return [Hash]
34
+ attr_reader :options
35
+
36
+ ##
37
+ # The current input string being processed.
38
+ #
39
+ # @return [String]
40
+ attr_accessor :input
41
+
42
+ ##
43
+ # The current input tokens being processed.
44
+ #
45
+ # @return [Array<Token>]
46
+ attr_reader :tokens
47
+
48
+ ##
49
+ # The internal representation of the result using hierarch of RDF objects and SPARQL::Algebra::Operator
50
+ # objects.
51
+ # @return [Array]
52
+ # @see http://sparql.rubyforge.org/algebra
53
+ attr_accessor :result
54
+
55
+ # Terminals passed to lexer. Order matters!
56
+ terminal(:ANON, ANON) do |prod, token, input|
57
+ add_prod_datum(:BlankNode, bnode)
58
+ end
59
+ terminal(:NIL, NIL) do |prod, token, input|
60
+ add_prod_datum(:NIL, RDF['nil'])
61
+ end
62
+ terminal(:BLANK_NODE_LABEL, BLANK_NODE_LABEL) do |prod, token, input|
63
+ add_prod_datum(:BlankNode, bnode(token.value[2..-1]))
64
+ end
65
+ terminal(:IRIREF, IRIREF, :unescape => true) do |prod, token, input|
66
+ begin
67
+ add_prod_datum(:iri, iri(token.value[1..-2]))
68
+ rescue ArgumentError => e
69
+ raise Error, e.message
70
+ end
71
+ end
72
+ terminal(:DOUBLE_POSITIVE, DOUBLE_POSITIVE) do |prod, token, input|
73
+ # Note that a Turtle Double may begin with a '.[eE]', so tack on a leading
74
+ # zero if necessary
75
+ value = token.value.sub(/\.([eE])/, '.0\1')
76
+ add_prod_datum(:literal, literal(value, :datatype => RDF::XSD.double))
77
+ end
78
+ terminal(:DECIMAL_POSITIVE, DECIMAL_POSITIVE) do |prod, token, input|
79
+ # Note that a Turtle Decimal may begin with a '.', so tack on a leading
80
+ # zero if necessary
81
+ value = token.value
82
+ value = "0#{token.value}" if token.value[0,1] == "."
83
+ add_prod_datum(:literal, literal(value, :datatype => RDF::XSD.decimal))
84
+ end
85
+ terminal(:INTEGER_POSITIVE, INTEGER_POSITIVE) do |prod, token, input|
86
+ add_prod_datum(:literal, literal(token.value, :datatype => RDF::XSD.integer))
87
+ end
88
+ terminal(:DOUBLE_NEGATIVE, DOUBLE_NEGATIVE) do |prod, token, input|
89
+ # Note that a Turtle Double may begin with a '.[eE]', so tack on a leading
90
+ # zero if necessary
91
+ value = token.value.sub(/\.([eE])/, '.0\1')
92
+ add_prod_datum(:literal, literal(value, :datatype => RDF::XSD.double))
93
+ end
94
+ terminal(:DECIMAL_NEGATIVE, DECIMAL_NEGATIVE) do |prod, token, input|
95
+ # Note that a Turtle Decimal may begin with a '.', so tack on a leading
96
+ # zero if necessary
97
+ value = token.value
98
+ value = "0#{token.value}" if token.value[0,1] == "."
99
+ add_prod_datum(:literal, literal(value, :datatype => RDF::XSD.decimal))
100
+ end
101
+ terminal(:INTEGER_NEGATIVE, INTEGER_NEGATIVE) do |prod, token, input|
102
+ add_prod_datum(:resource, literal(token.value, :datatype => RDF::XSD.integer))
103
+ end
104
+ terminal(:DOUBLE, DOUBLE) do |prod, token, input|
105
+ # Note that a Turtle Double may begin with a '.[eE]', so tack on a leading
106
+ # zero if necessary
107
+ value = token.value.sub(/\.([eE])/, '.0\1')
108
+ add_prod_datum(:literal, literal(value, :datatype => RDF::XSD.double))
109
+ end
110
+ terminal(:DECIMAL, DECIMAL) do |prod, token, input|
111
+ # Note that a Turtle Decimal may begin with a '.', so tack on a leading
112
+ # zero if necessary
113
+ value = token.value
114
+ #value = "0#{token.value}" if token.value[0,1] == "."
115
+ add_prod_datum(:literal, literal(value, :datatype => RDF::XSD.decimal))
116
+ end
117
+ terminal(:INTEGER, INTEGER) do |prod, token, input|
118
+ add_prod_datum(:literal, literal(token.value, :datatype => RDF::XSD.integer))
119
+ end
120
+ terminal(:LANGTAG, LANGTAG) do |prod, token, input|
121
+ add_prod_datum(:language, token.value[1..-1])
122
+ end
123
+ terminal(:PNAME_LN, PNAME_LN, :unescape => true) do |prod, token, input|
124
+ prefix, suffix = token.value.split(":", 2)
125
+ add_prod_datum(:PrefixedName, ns(prefix, suffix))
126
+ end
127
+ terminal(:PNAME_NS, PNAME_NS) do |prod, token, input|
128
+ prefix = token.value[0..-2]
129
+ # [68] PrefixedName ::= PNAME_LN | PNAME_NS
130
+ add_prod_datum(:PrefixedName, ns(prefix, nil))
131
+ # [4] PrefixDecl := 'PREFIX' PNAME_NS IRI_REF";
132
+ add_prod_datum(:prefix, prefix && prefix.to_sym)
133
+ end
134
+ terminal(:STRING_LITERAL_LONG1, STRING_LITERAL_LONG1, :unescape => true) do |prod, token, input|
135
+ add_prod_datum(:string, token.value[3..-4])
136
+ end
137
+ terminal(:STRING_LITERAL_LONG2, STRING_LITERAL_LONG2, :unescape => true) do |prod, token, input|
138
+ add_prod_datum(:string, token.value[3..-4])
139
+ end
140
+ terminal(:STRING_LITERAL1, STRING_LITERAL1, :unescape => true) do |prod, token, input|
141
+ add_prod_datum(:string, token.value[1..-2])
142
+ end
143
+ terminal(:STRING_LITERAL2, STRING_LITERAL2, :unescape => true) do |prod, token, input|
144
+ add_prod_datum(:string, token.value[1..-2])
145
+ end
146
+ terminal(:VAR1, VAR1) do |prod, token, input|
147
+ add_prod_datum(:Var, variable(token.value[1..-1]))
148
+ end
149
+ terminal(:VAR2, VAR2) do |prod, token, input|
150
+ add_prod_datum(:Var, variable(token.value[1..-1]))
151
+ end
152
+
153
+ # Keyword terminals
154
+ terminal(nil, STR_EXPR, :map => STR_MAP) do |prod, token, input|
155
+ case token.value
156
+ when '+', '-'
157
+ case prod
158
+ when :_AdditiveExpression_1, :_AdditiveExpression_4, :_AdditiveExpression_5
159
+ add_prod_datum(:AdditiveExpression, token.value)
160
+ when :_UnaryExpression_2, :_UnaryExpression_3
161
+ add_prod_datum(:UnaryExpression, token.value)
162
+ else
163
+ raise "Unexpected production #{prod} for #{token}"
164
+ end
165
+ when '*', '/' then add_prod_datum(:MultiplicativeExpression, token.value)
166
+ when '=', '!=', '<',
167
+ '>', '<=', '>=' then add_prod_datum(:RelationalExpression, token.value)
168
+ when '&&' then add_prod_datum(:ConditionalAndExpression, token.value)
169
+ when '||' then add_prod_datum(:ConditionalOrExpression, token.value)
170
+ when '!' then add_prod_datum(:UnaryExpression, token.value)
171
+ when 'a' then add_prod_datum(:Verb, RDF.type)
172
+ when /true|false/ then add_prod_datum(:literal, RDF::Literal::Boolean.new(token.value.downcase))
173
+ when /ASC|DESC/ then add_prod_datum(:OrderDirection, token.value.downcase.to_sym)
174
+ when /DISTINCT|REDUCED/ then add_prod_datum(:DISTINCT_REDUCED, token.value.downcase.to_sym)
175
+ when %r{
176
+ ABS|BNODE|BOUND|CEIL|COALESCE|CONCAT
177
+ |CONTAINS|DATATYPE|DAY|ENCODE_FOR_URI|EXISTS
178
+ |FLOOR|HOURS|IF|IRI|LANGMATCHES|LANG|LCASE
179
+ |MD5|MINUTES|MONTH|NOW|RAND|ROUND|SECONDS
180
+ |SHA1|SHA224|SHA256|SHA384|SHA512
181
+ |STRDT|STRENDS|STRLANG|STRLEN|STRSTARTS|SUBSTR|STR
182
+ |TIMEZONE|TZ|UCASE|URI|YEAR
183
+ |isBLANK|isIRI|isURI|isLITERAL|isNUMERIC|sameTerm
184
+ }x
185
+ add_prod_datum(token.value.downcase.to_sym, token.value.downcase.to_sym)
186
+ else
187
+ #add_prod_datum(:string, token.value)
188
+ end
189
+ end
190
+
191
+ # Productions
192
+ # [2] Query ::= Prologue
193
+ # ( SelectQuery | ConstructQuery | DescribeQuery | AskQuery ) BindingsClause
194
+ production(:Query) do |input, data, callback|
195
+ if data[:query]
196
+ query = data[:query].first
197
+ if data[:PrefixDecl]
198
+ pfx = data[:PrefixDecl].shift
199
+ data[:PrefixDecl].each {|p| pfx.merge!(p)}
200
+ pfx.operands[1] = query
201
+ query = pfx
202
+ end
203
+ query = SPARQL::Algebra::Expression[:base, data[:BaseDecl].first, query] if data[:BaseDecl]
204
+ add_prod_datum(:query, query)
205
+ end
206
+ end
207
+
208
+ # [4] Prologue ::= ( BaseDecl | PrefixDecl )*
209
+ production(:Prologue) do |input, data, callback|
210
+ unless resolve_iris?
211
+ # Only output if we're not resolving URIs internally
212
+ add_prod_datum(:BaseDecl, data[:BaseDecl])
213
+ add_prod_datum(:PrefixDecl, data[:PrefixDecl]) if data[:PrefixDecl]
214
+ end
215
+ end
216
+
217
+ # [5] BaseDecl ::= 'BASE' IRI_REF
218
+ production(:BaseDecl) do |input, data, callback|
219
+ iri = data[:iri].last
220
+ debug("BaseDecl") {"Defined base as #{iri}"}
221
+ self.base_uri = iri(iri)
222
+ add_prod_datum(:BaseDecl, iri) unless resolve_iris?
223
+ end
224
+
225
+ # [6] PrefixDecl ::= 'PREFIX' PNAME_NS IRI_REF
226
+ production(:PrefixDecl) do |input, data, callback|
227
+ if data[:iri]
228
+ pfx = data[:prefix].last
229
+ self.prefix(pfx, data[:iri].last)
230
+ prefix_op = SPARQL::Algebra::Operator::Prefix.new([["#{pfx}:".to_sym, data[:iri].last]], [])
231
+ add_prod_datum(:PrefixDecl, prefix_op)
232
+ end
233
+ end
234
+
235
+ # [7] SelectQuery ::= SelectClause DatasetClause* WhereClause SolutionModifier
236
+ production(:SelectQuery) do |input, data, callback|
237
+ query = merge_modifiers(data)
238
+ add_prod_datum :query, query
239
+ end
240
+
241
+ # [10] ConstructQuery ::= 'CONSTRUCT'
242
+ # ( ConstructTemplate DatasetClause* WhereClause SolutionModifier | DatasetClause* 'WHERE' '{' TriplesTemplate? '}' SolutionModifier )
243
+ production(:ConstructQuery) do |input, data, callback|
244
+ query = merge_modifiers(data)
245
+ template = data[:ConstructTemplate] || []
246
+ add_prod_datum :query, SPARQL::Algebra::Expression[:construct, template, query]
247
+ end
248
+
249
+ # [11] DescribeQuery ::= 'DESCRIBE' ( VarOrIRIref+ | '*' )
250
+ # DatasetClause* WhereClause? SolutionModifier
251
+ production(:DescribeQuery) do |input, data, callback|
252
+ query = merge_modifiers(data)
253
+ to_describe = data[:VarOrIRIref] || []
254
+ add_prod_datum :query, SPARQL::Algebra::Expression[:describe, to_describe, query]
255
+ end
256
+
257
+ # [12] AskQuery ::= 'ASK' DatasetClause* WhereClause
258
+ production(:AskQuery) do |input, data, callback|
259
+ query = merge_modifiers(data)
260
+ add_prod_datum :query, SPARQL::Algebra::Expression[:ask, query]
261
+ end
262
+
263
+ # [14] DefaultGraphClause ::= SourceSelector
264
+ production(:DefaultGraphClause) do |input, data, callback|
265
+ add_prod_datum :dataset, data[:IRIref]
266
+ end
267
+
268
+ # [15] NamedGraphClause ::= 'NAMED' SourceSelector
269
+ production(:NamedGraphClause) do |input, data, callback|
270
+ add_prod_data :dataset, data[:IRIref].unshift(:named)
271
+ end
272
+
273
+ # [18] SolutionModifier ::= GroupClause? HavingClause? OrderClause? LimitOffsetClauses?
274
+
275
+ # [19] GroupClause ::= 'GROUP' 'BY' GroupCondition+
276
+ #production(:GroupClause) do |input, data, callback|
277
+ #end
278
+
279
+ # [20] GroupCondition ::= BuiltInCall | FunctionCall
280
+ # | '(' Expression ( 'AS' Var )? ')' | Var
281
+ #production(:GroupClause) do |input, data, callback|
282
+ #end
283
+
284
+ # [21] HavingClause ::= 'HAVING' HavingCondition+
285
+ #production(:GroupClause) do |input, data, callback|
286
+ #end
287
+
288
+ # [23] OrderClause ::= 'ORDER' 'BY' OrderCondition+
289
+ production(:OrderClause) do |input, data, callback|
290
+ if res = data[:OrderCondition]
291
+ res = [res] if [:asc, :desc].include?(res[0]) # Special case when there's only one condition and it's ASC (x) or DESC (x)
292
+ add_prod_data :order, res
293
+ end
294
+ end
295
+
296
+ # [24] OrderCondition ::= ( ( 'ASC' | 'DESC' )
297
+ # BrackettedExpression )
298
+ # | ( Constraint | Var )
299
+ production(:OrderCondition) do |input, data, callback|
300
+ if data[:OrderDirection]
301
+ add_prod_datum(:OrderCondition, SPARQL::Algebra::Expression.for(data[:OrderDirection] + data[:Expression]))
302
+ else
303
+ add_prod_datum(:OrderCondition, data[:Constraint] || data[:Var])
304
+ end
305
+ end
306
+
307
+ # [25] LimitOffsetClauses ::= LimitClause OffsetClause?
308
+ # | OffsetClause LimitClause?
309
+ production(:LimitOffsetClauses) do |input, data, callback|
310
+ if data[:limit] || data[:offset]
311
+ limit = data[:limit] ? data[:limit].last : :_
312
+ offset = data[:offset] ? data[:offset].last : :_
313
+ add_prod_data :slice, offset, limit
314
+ end
315
+ end
316
+
317
+ # [26] LimitClause ::= 'LIMIT' INTEGER
318
+ production(:LimitClause) do |input, data, callback|
319
+ add_prod_datum(:limit, data[:literal])
320
+ end
321
+
322
+ # [27] OffsetClause ::= 'OFFSET' INTEGER
323
+ production(:OffsetClause) do |input, data, callback|
324
+ add_prod_datum(:offset, data[:literal])
325
+ end
326
+
327
+ # [54] [55] GroupGraphPatternSub ::= TriplesBlock?
328
+ # ( GraphPatternNotTriples '.'? TriplesBlock? )*
329
+ production(:GroupGraphPatternSub) do |input, data, callback|
330
+ query_list = data[:query_list]
331
+ debug("GroupGraphPatternSub") {"ql #{query_list.to_a.inspect}"}
332
+ debug("GroupGraphPatternSub") {"q #{data[:query] ? data[:query].first.inspect : 'nil'}"}
333
+
334
+ if query_list
335
+ lhs = data[:query].to_a.first
336
+ while !query_list.empty?
337
+ rhs = query_list.shift
338
+ # Make the right-hand-side a Join with only a single operand, if it's not already and Operator
339
+ rhs = SPARQL::Algebra::Expression.for(:join, :placeholder, rhs) unless rhs.is_a?(SPARQL::Algebra::Operator)
340
+ debug("GroupGraphPatternSub") {"<= q: #{rhs.inspect}"}
341
+ debug("GroupGraphPatternSub") {"<= lhs: #{lhs ? lhs.inspect : 'nil'}"}
342
+ lhs ||= SPARQL::Algebra::Operator::BGP.new if rhs.is_a?(SPARQL::Algebra::Operator::LeftJoin)
343
+ if lhs
344
+ if rhs.operand(0) == :placeholder
345
+ rhs.operands[0] = lhs
346
+ else
347
+ rhs = SPARQL::Algebra::Operator::Join.new(lhs, rhs)
348
+ end
349
+ end
350
+ lhs = rhs
351
+ lhs = lhs.operand(1) if lhs.operand(0) == :placeholder
352
+ debug("GroupGraphPatternSub(itr)") {"=> lhs: #{lhs.inspect}"}
353
+ end
354
+ # Trivial simplification for :join or :union of one query
355
+ case lhs
356
+ when SPARQL::Algebra::Operator::Join, SPARQL::Algebra::Operator::Union
357
+ if lhs.operand(0) == :placeholder
358
+ lhs = lhs.operand(1)
359
+ debug("GroupGraphPatternSub(simplify)") {"=> lhs: #{lhs.inspect}"}
360
+ end
361
+ end
362
+ res = lhs
363
+ elsif data[:query]
364
+ res = data[:query].first
365
+ end
366
+
367
+ debug("GroupGraphPatternSub(pre-filter)") {"res: #{res.inspect}"}
368
+
369
+ if data[:filter]
370
+ expr, query = flatten_filter(data[:filter])
371
+ query = res || SPARQL::Algebra::Operator::BGP.new
372
+ # query should be nil
373
+ res = SPARQL::Algebra::Operator::Filter.new(expr, query)
374
+ end
375
+ add_prod_datum(:query, res)
376
+ end
377
+
378
+ # _GroupGraphPatternSub_2 ::= ( GraphPatternNotTriples '.'? TriplesBlock? )
379
+ # Create a stack of GroupQuerys having a single graph element and resolve in GroupGraphPattern
380
+ production(:_GroupGraphPatternSub_2) do |input, data, callback|
381
+ lhs = data[:query_list]
382
+ [data[:query]].flatten.compact.each do |rhs|
383
+ rhs = SPARQL::Algebra::Expression.for(:join, :placeholder, rhs) if rhs.is_a?(RDF::Query)
384
+ add_prod_data(:query_list, rhs)
385
+ end
386
+ add_prod_datum(:query_list, lhs) if lhs
387
+ add_prod_datum(:filter, data[:filter])
388
+ end
389
+
390
+ # _GroupGraphPatternSub_3
391
+
392
+ # [56] TriplesBlock ::= TriplesSameSubjectPath
393
+ # ( '.' TriplesBlock? )?
394
+ production(:TriplesBlock) do |input, data, callback|
395
+ query = SPARQL::Algebra::Operator::BGP.new
396
+ data[:pattern].each {|p| query << p}
397
+
398
+ # Append triples from ('.' TriplesBlock? )?
399
+ data[:query].to_a.each {|q| query += q}
400
+ add_prod_datum(:query, query)
401
+ end
402
+
403
+ # [57] GraphPatternNotTriples ::= GroupOrUnionGraphPattern
404
+ # | OptionalGraphPattern
405
+ # | MinusGraphPattern
406
+ # | GraphGraphPattern
407
+ # | ServiceGraphPattern
408
+ # | Filter | Bind
409
+ production(:GraphPatternNotTriples) do |input, data, callback|
410
+ add_prod_datum(:filter, data[:filter])
411
+
412
+ if data[:query]
413
+ res = data[:query].to_a.first
414
+ # FIXME?
415
+ #res = SPARQL::Algebra::Expression.for(:join, :placeholder, res) unless res.is_a?(SPARQL::Algebra::Operator)
416
+ add_prod_data(:query, res)
417
+ end
418
+ end
419
+
420
+ # [58] OptionalGraphPattern ::= 'OPTIONAL' GroupGraphPattern
421
+ production(:OptionalGraphPattern) do |input, data, callback|
422
+ if data[:query]
423
+ expr = nil
424
+ query = data[:query].first
425
+ if query.is_a?(SPARQL::Algebra::Operator::Filter)
426
+ # Change to expression on left-join with query element
427
+ expr, query = query.operands
428
+ add_prod_data(:query, SPARQL::Algebra::Expression.for(:leftjoin, :placeholder, query, expr))
429
+ else
430
+ add_prod_data(:query, SPARQL::Algebra::Expression.for(:leftjoin, :placeholder, query))
431
+ end
432
+ end
433
+ end
434
+
435
+ # [59] GraphGraphPattern ::= 'GRAPH' VarOrIRIref GroupGraphPattern
436
+ production(:GraphGraphPattern) do |input, data, callback|
437
+ if data[:query]
438
+ name = (data[:VarOrIRIref]).last
439
+ bgp = data[:query].first
440
+ if name
441
+ add_prod_data(:query, SPARQL::Algebra::Expression.for(:graph, name, bgp))
442
+ else
443
+ add_prod_data(:query, bgp)
444
+ end
445
+ end
446
+ end
447
+
448
+ # [63] GroupOrUnionGraphPattern ::= GroupGraphPattern
449
+ # ( 'UNION' GroupGraphPattern )*
450
+ production(:GroupOrUnionGraphPattern) do |input, data, callback|
451
+ res = data[:query].to_a.first
452
+ if data[:union]
453
+ while !data[:union].empty?
454
+ # Join union patterns together as Union operators
455
+ #puts "res: res: #{res}, input_prod: #{input_prod}, data[:union]: #{data[:union].first}"
456
+ lhs = res
457
+ rhs = data[:union].shift
458
+ res = SPARQL::Algebra::Expression.for(:union, lhs, rhs)
459
+ end
460
+ end
461
+ add_prod_datum(:query, res)
462
+ end
463
+
464
+ # ( 'UNION' GroupGraphPattern )*
465
+ production(:_GroupOrUnionGraphPattern_1) do |input, data, callback|
466
+ # Add [:union rhs] to stack based on ":union"
467
+ add_prod_data(:union, data[:query].to_a.first)
468
+ add_prod_data(:union, data[:union].first) if data[:union]
469
+ end
470
+
471
+ # [64] Filter ::= 'FILTER' Constraint
472
+ production(:Filter) do |input, data, callback|
473
+ add_prod_datum(:filter, data[:Constraint])
474
+ end
475
+
476
+ # [65] Constraint ::= BrackettedExpression | BuiltInCall
477
+ # | FunctionCall
478
+ production(:Constraint) do |input, data, callback|
479
+ if data[:Expression]
480
+ # Resolve expression to the point it is either an atom or an s-exp
481
+ add_prod_data(:Constraint, data[:Expression].to_a.first)
482
+ elsif data[:BuiltInCall]
483
+ add_prod_datum(:Constraint, data[:BuiltInCall])
484
+ elsif data[:Function]
485
+ add_prod_datum(:Constraint, data[:Function])
486
+ end
487
+ end
488
+
489
+ # [66] FunctionCall ::= IRIref ArgList
490
+ production(:FunctionCall) do |input, data, callback|
491
+ add_prod_data(:Function, data[:IRIref] + data[:ArgList])
492
+ end
493
+
494
+ # [67] ArgList ::= NIL
495
+ # | '(' 'DISTINCT'? Expression ( ',' Expression )* ')'
496
+ production(:ArgList) do |input, data, callback|
497
+ data.values.each {|v| add_prod_datum(:ArgList, v)}
498
+ end
499
+
500
+ # [68] ExpressionList ::= NIL
501
+ # | '(' Expression ( ',' Expression )* ')'
502
+ production(:ExpressionList) do |input, data, callback|
503
+ data.values.each {|v| add_prod_datum(:ExpressionList, v)}
504
+ end
505
+
506
+ # [69] ConstructTemplate ::= '{' ConstructTriples? '}'
507
+ start_production(:ConstructTemplate) do |input, data, callback|
508
+ # Generate BNodes instead of non-distinguished variables
509
+ self.nd_var_gen = false
510
+ end
511
+ production(:ConstructTemplate) do |input, data, callback|
512
+ # Generate BNodes instead of non-distinguished variables
513
+ self.nd_var_gen = "0"
514
+ add_prod_datum(:ConstructTemplate, data[:pattern])
515
+ add_prod_datum(:ConstructTemplate, data[:ConstructTemplate])
516
+ end
517
+
518
+ # [71] TriplesSameSubject ::= VarOrTerm PropertyListNotEmpty
519
+ # | TriplesNode PropertyList
520
+ production(:TriplesSameSubject) do |input, data, callback|
521
+ add_prod_datum(:pattern, data[:pattern])
522
+ end
523
+
524
+ # [72] PropertyListNotEmpty ::= Verb ObjectList
525
+ # ( ';' ( Verb ObjectList )? )*
526
+ start_production(:PropertyListNotEmpty) do |input, data, callback|
527
+ subject = input[:VarOrTerm] || input[:TriplesNode] || input[:GraphNode]
528
+ error(nil, "Expected VarOrTerm or TriplesNode or GraphNode", :production => :PropertyListNotEmpty) if validate? && !subject
529
+ data[:Subject] = subject
530
+ end
531
+ production(:PropertyListNotEmpty) do |input, data, callback|
532
+ add_prod_datum(:pattern, data[:pattern])
533
+ end
534
+
535
+ # [74] ObjectList ::= Object ( ',' Object )*
536
+ start_production(:ObjectList) do |input, data, callback|
537
+ # Called after Verb. The prod_data stack should have Subject and Verb elements
538
+ data[:Subject] = prod_data[:Subject]
539
+ error(nil, "Expected Subject", :production => :ObjectList) if !prod_data[:Subject] && validate?
540
+ error(nil, "Expected Verb", :production => :ObjectList) if !prod_data[:Verb] && validate?
541
+ data[:Subject] = prod_data[:Subject]
542
+ data[:Verb] = prod_data[:Verb].to_a.last
543
+ end
544
+ production(:ObjectList) do |input, data, callback|
545
+ add_prod_datum(:pattern, data[:pattern])
546
+ end
547
+
548
+ # [75] Object ::= GraphNode
549
+ production(:Object) do |input, data, callback|
550
+ object = data[:VarOrTerm] || data[:TriplesNode] || data[:GraphNode]
551
+ if object
552
+ add_pattern(:Object, :subject => prod_data[:Subject], :predicate => prod_data[:Verb], :object => object)
553
+ add_prod_datum(:pattern, data[:pattern])
554
+ end
555
+ end
556
+
557
+ # [76] Verb ::= VarOrIRIref | 'a'
558
+ production(:Verb) do |input, data, callback|
559
+ data.values.each {|v| add_prod_datum(:Verb, v)}
560
+ end
561
+
562
+ # [78] PropertyListNotEmptyPath ::= ( VerbPath | VerbSimple ) ObjectList ( ';' ( ( VerbPath | VerbSimple ) ObjectList )? )*
563
+ start_production(:PropertyListNotEmptyPath) do |input, data, callback|
564
+ subject = input[:VarOrTerm]
565
+ error(nil, "Expected VarOrTerm", :production => ::PropertyListNotEmptyPath) if validate? && !subject
566
+ data[:Subject] = subject
567
+ end
568
+ production(:PropertyListNotEmptyPath) do |input, data, callback|
569
+ add_prod_datum(:pattern, data[:pattern])
570
+ end
571
+
572
+ # [80] VerbPath ::= Path
573
+ production(:VerbPath) do |input, data, callback|
574
+ data.values.each {|v| add_prod_datum(:Verb, v)}
575
+ end
576
+
577
+ # [81] VerbSimple ::= Var
578
+ production(:VerbSimple) do |input, data, callback|
579
+ data.values.each {|v| add_prod_datum(:Verb, v)}
580
+ end
581
+
582
+ # [92] TriplesNode ::= Collection | BlankNodePropertyList
583
+ start_production(:TriplesNode) do |input, data, callback|
584
+ # Called after Verb. The prod_data stack should have Subject and Verb elements
585
+ data[:TriplesNode] = bnode
586
+ end
587
+ production(:TriplesNode) do |input, data, callback|
588
+ add_prod_datum(:pattern, data[:pattern])
589
+ add_prod_datum(:TriplesNode, data[:TriplesNode])
590
+ end
591
+
592
+ # [94] Collection ::= '(' GraphNode+ ')'
593
+ start_production(:Collection) do |input, data, callback|
594
+ # Tells the TriplesNode production to collect and not generate statements
595
+ data[:Collection] = prod_data[:TriplesNode]
596
+ end
597
+ production(:Collection) do |input, data, callback|
598
+ expand_collection(data)
599
+ end
600
+
601
+ # [95] GraphNode ::= VarOrTerm | TriplesNode
602
+ production(:GraphNode) do |input, data, callback|
603
+ term = data[:VarOrTerm] || data[:TriplesNode]
604
+ add_prod_datum(:pattern, data[:pattern])
605
+ add_prod_datum(:GraphNode, term)
606
+ end
607
+
608
+ # [96] VarOrTerm ::= Var | GraphTerm
609
+ production(:VarOrTerm) do |input, data, callback|
610
+ data.values.each {|v| add_prod_datum(:VarOrTerm, v)}
611
+ end
612
+
613
+ # [97] VarOrIRIref ::= Var | IRIref
614
+ production(:VarOrIRIref) do |input, data, callback|
615
+ data.values.each {|v| add_prod_datum(:VarOrIRIref, v)}
616
+ end
617
+
618
+ # [99] GraphTerm ::= IRIref | RDFLiteral | NumericLiteral
619
+ # | BooleanLiteral | BlankNode | NIL
620
+ production(:GraphTerm) do |input, data, callback|
621
+ add_prod_datum(:GraphTerm,
622
+ data[:IRIref] ||
623
+ data[:literal] ||
624
+ data[:BlankNode] ||
625
+ data[:NIL])
626
+ end
627
+
628
+ # [100] Expression ::= ConditionalOrExpression
629
+ production(:Expression) do |input, data, callback|
630
+ add_prod_datum(:Expression, data[:Expression])
631
+ end
632
+
633
+ # [101] ConditionalOrExpression ::= ConditionalAndExpression
634
+ # ( '||' ConditionalAndExpression )*
635
+ production(:ConditionalOrExpression) do |input, data, callback|
636
+ add_operator_expressions(:_OR, data)
637
+ end
638
+
639
+ # ( '||' ConditionalAndExpression )*
640
+ production(:_ConditionalOrExpression_1) do |input, data, callback|
641
+ accumulate_operator_expressions(:ConditionalOrExpression, :_OR, data)
642
+ end
643
+
644
+ # [102] ConditionalAndExpression ::= ValueLogical ( '&&' ValueLogical )*
645
+ production(:ConditionalAndExpression) do |input, data, callback|
646
+ add_operator_expressions(:_AND, data)
647
+ end
648
+
649
+ # ( '||' ConditionalAndExpression )*
650
+ production(:_ConditionalAndExpression_1) do |input, data, callback|
651
+ accumulate_operator_expressions(:ConditionalAndExpression, :_AND, data)
652
+ end
653
+
654
+ # [104] RelationalExpression ::= NumericExpression
655
+ # ( '=' NumericExpression
656
+ # | '!=' NumericExpression
657
+ # | '<' NumericExpression
658
+ # | '>' NumericExpression
659
+ # | '<=' NumericExpression
660
+ # | '>=' NumericExpression
661
+ # | 'IN' ExpressionList
662
+ # | 'NOT' 'IN' ExpressionList
663
+ # )?
664
+ production(:RelationalExpression) do |input, data, callback|
665
+ if data[:_Compare_Numeric]
666
+ add_prod_datum(:Expression, SPARQL::Algebra::Expression.for(data[:_Compare_Numeric].insert(1, *data[:Expression])))
667
+ else
668
+ # NumericExpression with no comparitor
669
+ add_prod_datum(:Expression, data[:Expression])
670
+ end
671
+ end
672
+
673
+ # ( '=' NumericExpression | '!=' NumericExpression | ... )?
674
+ production(:_RelationalExpression_1) do |input, data, callback|
675
+ if data[:RelationalExpression]
676
+ add_prod_datum(:_Compare_Numeric, data[:RelationalExpression] + data[:Expression])
677
+ end
678
+ end
679
+
680
+ # [106] AdditiveExpression ::= MultiplicativeExpression
681
+ # ( '+' MultiplicativeExpression
682
+ # | '-' MultiplicativeExpression
683
+ # | ( NumericLiteralPositive | NumericLiteralNegative )
684
+ # ( ( '*' UnaryExpression )
685
+ # | ( '/' UnaryExpression ) )?
686
+ # )*
687
+ production(:AdditiveExpression) do |input, data, callback|
688
+ add_operator_expressions(:_Add_Sub, data)
689
+ end
690
+
691
+ # ( '+' MultiplicativeExpression
692
+ # | '-' MultiplicativeExpression
693
+ # | ( NumericLiteralPositive | NumericLiteralNegative )
694
+ # ( ( '*' UnaryExpression )
695
+ # | ( '/' UnaryExpression ) )?
696
+ # )*
697
+ production(:_AdditiveExpression_1) do |input, data, callback|
698
+ accumulate_operator_expressions(:AdditiveExpression, :_Add_Sub, data)
699
+ end
700
+
701
+ # | ( NumericLiteralPositive | NumericLiteralNegative )
702
+ production(:_AdditiveExpression_7) do |input, data, callback|
703
+ val = data[:literal].first.to_s
704
+ op, val = val[0,1], val[1..-1]
705
+ add_prod_datum(:AdditiveExpression, op)
706
+ add_prod_datum(:Expression, data[:literal])
707
+ end
708
+
709
+ # [107] MultiplicativeExpression ::= UnaryExpression
710
+ # ( '*' UnaryExpression
711
+ # | '/' UnaryExpression )*
712
+ production(:MultiplicativeExpression) do |input, data, callback|
713
+ add_operator_expressions(:_Mul_Div, data)
714
+ end
715
+
716
+ # ( '*' UnaryExpression
717
+ # | '/' UnaryExpression )*
718
+ production(:_MultiplicativeExpression_1) do |input, data, callback|
719
+ accumulate_operator_expressions(:MultiplicativeExpression, :_Mul_Div, data)
720
+ end
721
+
722
+ # [108] UnaryExpression ::= '!' PrimaryExpression
723
+ # | '+' PrimaryExpression
724
+ # | '-' PrimaryExpression
725
+ # | PrimaryExpression
726
+ production(:UnaryExpression) do |input, data, callback|
727
+ case data[:UnaryExpression]
728
+ when ["!"]
729
+ add_prod_datum(:Expression, SPARQL::Algebra::Expression[:not, data[:Expression].first])
730
+ when ["-"]
731
+ e = data[:Expression].first
732
+ if e.is_a?(RDF::Literal::Numeric)
733
+ add_prod_datum(:Expression, -e) # Simple optimization to match ARQ generation
734
+ else
735
+ add_prod_datum(:Expression, SPARQL::Algebra::Expression[:minus, e])
736
+ end
737
+ else
738
+ add_prod_datum(:Expression, data[:Expression])
739
+ end
740
+ end
741
+
742
+ # [109] PrimaryExpression ::= BrackettedExpression | BuiltInCall
743
+ # | IRIrefOrFunction | RDFLiteral
744
+ # | NumericLiteral | BooleanLiteral
745
+ # | Var | Aggregate
746
+ production(:PrimaryExpression) do |input, data, callback|
747
+ if data[:Expression]
748
+ add_prod_datum(:Expression, data[:Expression])
749
+ elsif data[:BuiltInCall]
750
+ add_prod_datum(:Expression, data[:BuiltInCall])
751
+ elsif data[:IRIref]
752
+ add_prod_datum(:Expression, data[:IRIref])
753
+ elsif data[:Function]
754
+ add_prod_datum(:Expression, data[:Function]) # Maintain array representation
755
+ elsif data[:literal]
756
+ add_prod_datum(:Expression, data[:literal])
757
+ elsif data[:Var]
758
+ add_prod_datum(:Expression, data[:Var])
759
+ end
760
+
761
+ # Keep track of this for parent UnaryExpression production
762
+ add_prod_datum(:UnaryExpression, data[:UnaryExpression])
763
+ end
764
+
765
+ # [111] BuiltInCall ::= 'STR' '(' Expression ')'
766
+ # | 'LANG' '(' Expression ')'
767
+ # | 'LANGMATCHES' '(' Expression ',' Expression ')'
768
+ # | 'DATATYPE' '(' Expression ')'
769
+ # | 'BOUND' '(' Var ')'
770
+ # | 'IRI' '(' Expression ')'
771
+ # | 'URI' '(' Expression ')'
772
+ # | 'BNODE' ( '(' Expression ')' | NIL )
773
+ # | 'RAND' NIL
774
+ # | 'ABS' '(' Expression ')'
775
+ # | 'CEIL' '(' Expression ')'
776
+ # | 'FLOOR' '(' Expression ')'
777
+ # | 'ROUND' '(' Expression ')'
778
+ # | 'CONCAT' ExpressionList
779
+ # | SubstringExpression
780
+ # | 'STRLEN' '(' Expression ')'
781
+ # | 'UCASE' '(' Expression ')'
782
+ # | 'LCASE' '(' Expression ')'
783
+ # | 'ENCODE_FOR_URI' '(' Expression ')'
784
+ # | 'CONTAINS' '(' Expression ',' Expression ')'
785
+ # | 'STRSTARTS' '(' Expression ',' Expression ')'
786
+ # | 'STRENDS' '(' Expression ',' Expression ')'
787
+ # | 'YEAR' '(' Expression ')'
788
+ # | 'MONTH' '(' Expression ')'
789
+ # | 'DAY' '(' Expression ')'
790
+ # | 'HOURS' '(' Expression ')'
791
+ # | 'MINUTES' '(' Expression ')'
792
+ # | 'SECONDS' '(' Expression ')'
793
+ # | 'TIMEZONE' '(' Expression ')'
794
+ # | 'TZ' '(' Expression ')'
795
+ # | 'NOW' NIL
796
+ # | 'MD5' '(' Expression ')'
797
+ # | 'SHA1' '(' Expression ')'
798
+ # | 'SHA224' '(' Expression ')'
799
+ # | 'SHA256' '(' Expression ')'
800
+ # | 'SHA384' '(' Expression ')'
801
+ # | 'SHA512' '(' Expression ')'
802
+ # | 'COALESCE' ExpressionList
803
+ # | 'IF' '(' Expression ',' Expression ',' Expression ')'
804
+ # | 'STRLANG' '(' Expression ',' Expression ')'
805
+ # | 'STRDT' '(' Expression ',' Expression ')'
806
+ # | 'sameTerm' '(' Expression ',' Expression ')'
807
+ # | 'isIRI' '(' Expression ')'
808
+ # | 'isURI' '(' Expression ')'
809
+ # | 'isBLANK' '(' Expression ')'
810
+ # | 'isLITERAL' '(' Expression ')'
811
+ # | 'isNUMERIC' '(' Expression ')'
812
+ # | RegexExpression
813
+ # | ExistsFunc
814
+ # | NotExistsFunc
815
+ production(:BuiltInCall) do |input, data, callback|
816
+ if builtin = data.keys.detect {|k| BUILTINS.include?(k)}
817
+ add_prod_datum(:BuiltInCall, SPARQL::Algebra::Expression.for(data[:Expression].unshift(builtin)))
818
+ elsif builtin_rule = data.keys.detect {|k| BUILTIN_RULES.include?(k)}
819
+ add_prod_datum(:BuiltInCall, SPARQL::Algebra::Expression.for(data[builtin_rule].unshift(builtin_rule)))
820
+ elsif data[:bound]
821
+ add_prod_datum(:BuiltInCall, SPARQL::Algebra::Expression.for(data[:Var].unshift(:bound)))
822
+ elsif data[:BuiltInCall]
823
+ add_prod_datum(:BuiltInCall, SPARQL::Algebra::Expression.for(data[:BuiltInCall] + data[:Expression]))
824
+ end
825
+ end
826
+
827
+ # [112] RegexExpression ::= 'REGEX' '(' Expression ',' Expression
828
+ # ( ',' Expression )? ')'
829
+ production(:RegexExpression) do |input, data, callback|
830
+ add_prod_datum(:regex, data[:Expression])
831
+ end
832
+
833
+ # [113] SubstringExpression ::= 'SUBSTR'
834
+ # '(' Expression ',' Expression
835
+ # ( ',' Expression )? ')'
836
+ production(:SubstringExpression) do |input, data, callback|
837
+ add_prod_datum(:substr, data[:Expression])
838
+ end
839
+
840
+ # [114] ExistsFunc ::= 'EXISTS' GroupGraphPattern
841
+ production(:ExistsFunc) do |input, data, callback|
842
+ add_prod_datum(:exists, data[:query])
843
+ end
844
+
845
+ # [115] NotExistsFunc ::= 'NOT' 'EXISTS' GroupGraphPattern
846
+ production(:NotExistsFunc) do |input, data, callback|
847
+ add_prod_datum(:not_exists, data[:query])
848
+ end
849
+
850
+ # [117] IRIrefOrFunction ::= IRIref ArgList?
851
+ production(:IRIrefOrFunction) do |input, data, callback|
852
+ if data.has_key?(:ArgList)
853
+ # Function is (func arg1 arg2 ...)
854
+ add_prod_data(:Function, data[:IRIref] + data[:ArgList])
855
+ else
856
+ add_prod_datum(:IRIref, data[:IRIref])
857
+ end
858
+ end
859
+
860
+ # [118] RDFLiteral ::= String ( LANGTAG | ( '^^' IRIref ) )?
861
+ production(:RDFLiteral) do |input, data, callback|
862
+ if data[:string]
863
+ lit = data.dup
864
+ str = lit.delete(:string).last
865
+ lit[:datatype] = lit.delete(:IRIref).last if lit[:IRIref]
866
+ lit[:language] = lit.delete(:language).last.downcase if lit[:language]
867
+ add_prod_datum(:literal, RDF::Literal.new(str, lit)) if str
868
+ end
869
+ end
870
+
871
+ # [121] NumericLiteralPositive ::= INTEGER_POSITIVE
872
+ # | DECIMAL_POSITIVE
873
+ # | DOUBLE_POSITIVE
874
+ production(:NumericLiteralPositive) do |input, data, callback|
875
+ num = data.values.flatten.last
876
+ add_prod_datum(:literal, num)
877
+
878
+ # Keep track of this for parent UnaryExpression production
879
+ add_prod_datum(:UnaryExpression, data[:UnaryExpression])
880
+ end
881
+
882
+ # [122] NumericLiteralNegative ::= INTEGER_NEGATIVE
883
+ # | DECIMAL_NEGATIVE
884
+ # | DOUBLE_NEGATIVE
885
+ production(:NumericLiteralNegative) do |input, data, callback|
886
+ num = data.values.flatten.last
887
+ add_prod_datum(:literal, num)
888
+
889
+ # Keep track of this for parent UnaryExpression production
890
+ add_prod_datum(:UnaryExpression, data[:UnaryExpression])
891
+ end
892
+
893
+ # [125] IRIref ::= IRI_REF | PrefixedName
894
+ production(:IRIref) do |input, data, callback|
895
+ add_prod_datum(:IRIref, data[:iri])
896
+ end
897
+
898
+ # [126] PrefixedName ::= PNAME_LN | PNAME_NS
899
+ production(:PrefixedName) do |input, data, callback|
900
+ add_prod_datum(:iri, data[:PrefixedName])
901
+ end
902
+
903
+ ##
904
+ # Initializes a new parser instance.
905
+ #
906
+ # @param [String, #to_s] input
907
+ # @param [Hash{Symbol => Object}] options
908
+ # @option options [Hash] :prefixes (Hash.new)
909
+ # the prefix mappings to use (for acessing intermediate parser productions)
910
+ # @option options [#to_s] :base_uri (nil)
911
+ # the base URI to use when resolving relative URIs (for acessing intermediate parser productions)
912
+ # @option options [#to_s] :anon_base ("b0")
913
+ # Basis for generating anonymous Nodes
914
+ # @option options [Boolean] :resolve_iris (false)
915
+ # Resolve prefix and relative IRIs, otherwise, when serializing the parsed SSE
916
+ # as S-Expressions, use the original prefixed and relative URIs along with `base` and `prefix`
917
+ # definitions.
918
+ # @option options [Boolean] :validate (false)
919
+ # whether to validate the parsed statements and values
920
+ # @option options [Boolean] :progress
921
+ # Show progress of parser productions
922
+ # @option options [Boolean] :debug
923
+ # Detailed debug output
924
+ # @return [SPARQL::Grammar::Parser]
925
+ def initialize(input = nil, options = {})
926
+ @input = input.to_s.dup
927
+ @input.force_encoding(Encoding::UTF_8) if @input.respond_to?(:force_encoding)
928
+ @options = {:anon_base => "b0", :validate => false}.merge(options)
929
+
930
+ debug("base IRI") {base_uri.inspect}
931
+ debug("validate") {validate?.inspect}
932
+
933
+ @vars = {}
934
+ @nd_var_gen = "0"
935
+
936
+ if block_given?
937
+ case block.arity
938
+ when 0 then instance_eval(&block)
939
+ else block.call(self)
940
+ end
941
+ end
942
+ end
943
+
944
+ ##
945
+ # Returns `true` if the input string is syntactically valid.
946
+ #
947
+ # @return [Boolean]
948
+ def valid?
949
+ parse
950
+ rescue Error
951
+ false
952
+ end
953
+
954
+ # @return [String]
955
+ def to_sxp_bin
956
+ @result
957
+ end
958
+
959
+ def to_s
960
+ @result.to_sxp
961
+ end
962
+
963
+ alias_method :ll1_parse, :parse
964
+
965
+ # Parse query
966
+ #
967
+ # The result is a SPARQL Algebra S-List. Productions return an array such as the following:
968
+ #
969
+ # (prefix ((: <http://example/>))
970
+ # (union
971
+ # (bgp (triple ?s ?p ?o))
972
+ # (graph ?g
973
+ # (bgp (triple ?s ?p ?o)))))
974
+ #
975
+ # @param [Symbol, #to_s] prod The starting production for the parser.
976
+ # It may be a URI from the grammar, or a symbol representing the local_name portion of the grammar URI.
977
+ # @return [Array]
978
+ # @see http://www.w3.org/2001/sw/DataAccess/rq23/rq24-algebra.html
979
+ # @see http://axel.deri.ie/sparqltutorial/ESWC2007_SPARQL_Tutorial_unit2b.pdf
980
+ def parse(prod = START)
981
+ ll1_parse(@input, prod.to_sym, @options.merge(:branch => BRANCH,
982
+ :first => FIRST,
983
+ :follow => FOLLOW)
984
+ ) {}
985
+
986
+ # The last thing on the @prod_data stack is the result
987
+ @result = case
988
+ when !prod_data.is_a?(Hash)
989
+ prod_data
990
+ when prod_data.empty?
991
+ nil
992
+ when prod_data[:query]
993
+ prod_data[:query].to_a.length == 1 ? prod_data[:query].first : prod_data[:query]
994
+ else
995
+ key = prod_data.keys.first
996
+ [key] + prod_data[key] # Creates [:key, [:triple], ...]
997
+ end
998
+ end
999
+
1000
+ private
1001
+ ##
1002
+ # Returns the URI prefixes currently defined for this parser.
1003
+ #
1004
+ # @example
1005
+ # prefixes[:dc] #=> RDF::URI('http://purl.org/dc/terms/')
1006
+ #
1007
+ # @return [Hash{Symbol => RDF::URI}]
1008
+ # @since 0.3.0
1009
+ def prefixes
1010
+ @options[:prefixes] ||= {}
1011
+ end
1012
+
1013
+ ##
1014
+ # Defines the given URI prefixes for this parser.
1015
+ #
1016
+ # @example
1017
+ # prefixes = {
1018
+ # :dc => RDF::URI('http://purl.org/dc/terms/'),
1019
+ # }
1020
+ #
1021
+ # @param [Hash{Symbol => RDF::URI}] prefixes
1022
+ # @return [Hash{Symbol => RDF::URI}]
1023
+ # @since 0.3.0
1024
+ def prefixes=(prefixes)
1025
+ @options[:prefixes] = prefixes
1026
+ end
1027
+
1028
+ ##
1029
+ # Defines the given named URI prefix for this parser.
1030
+ #
1031
+ # @example Defining a URI prefix
1032
+ # prefix :dc, RDF::URI('http://purl.org/dc/terms/')
1033
+ #
1034
+ # @example Returning a URI prefix
1035
+ # prefix(:dc) #=> RDF::URI('http://purl.org/dc/terms/')
1036
+ #
1037
+ # @overload prefix(name, uri)
1038
+ # @param [Symbol, #to_s] name
1039
+ # @param [RDF::URI, #to_s] uri
1040
+ #
1041
+ # @overload prefix(name)
1042
+ # @param [Symbol, #to_s] name
1043
+ #
1044
+ # @return [RDF::URI]
1045
+ def prefix(name, iri = nil)
1046
+ name = name.to_s.empty? ? nil : (name.respond_to?(:to_sym) ? name.to_sym : name.to_s.to_sym)
1047
+ iri.nil? ? prefixes[name] : prefixes[name] = iri
1048
+ end
1049
+
1050
+ ##
1051
+ # Returns the Base URI defined for the parser,
1052
+ # as specified or when parsing a BASE prologue element.
1053
+ #
1054
+ # @example
1055
+ # base #=> RDF::URI('http://example.com/')
1056
+ #
1057
+ # @return [HRDF::URI]
1058
+ def base_uri
1059
+ RDF::URI(@options[:base_uri])
1060
+ end
1061
+
1062
+ ##
1063
+ # Set the Base URI to use for this parser.
1064
+ #
1065
+ # @param [RDF::URI, #to_s] iri
1066
+ #
1067
+ # @example
1068
+ # base_uri = RDF::URI('http://purl.org/dc/terms/')
1069
+ #
1070
+ # @return [RDF::URI]
1071
+ def base_uri=(iri)
1072
+ @options[:base_uri] = RDF::URI(iri)
1073
+ end
1074
+
1075
+ ##
1076
+ # Returns `true` if parsed statements and values should be validated.
1077
+ #
1078
+ # @return [Boolean] `true` or `false`
1079
+ # @since 0.3.0
1080
+ def resolve_iris?
1081
+ @options[:resolve_iris]
1082
+ end
1083
+
1084
+ ##
1085
+ # Returns `true` when resolving IRIs, otherwise BASE and PREFIX are retained in the output algebra.
1086
+ #
1087
+ # @return [Boolean] `true` or `false`
1088
+ # @since 1.0.3
1089
+ def validate?
1090
+ @options[:validate]
1091
+ end
1092
+
1093
+ # Used for generating BNode labels
1094
+ attr_accessor :nd_var_gen
1095
+
1096
+ # Generate a BNode identifier
1097
+ def bnode(id = nil)
1098
+ if @nd_var_gen
1099
+ # Use non-distinguished variables within patterns
1100
+ variable(id, false)
1101
+ else
1102
+ unless id
1103
+ id = @options[:anon_base]
1104
+ @options[:anon_base] = @options[:anon_base].succ
1105
+ end
1106
+ RDF::Node.new(id)
1107
+ end
1108
+ end
1109
+
1110
+ ##
1111
+ # Return variable allocated to an ID.
1112
+ # If no ID is provided, a new variable
1113
+ # is allocated. Otherwise, any previous assignment will be used.
1114
+ #
1115
+ # The variable has a #distinguished? method applied depending on if this
1116
+ # is a disinguished or non-distinguished variable. Non-distinguished
1117
+ # variables are effectively the same as BNodes.
1118
+ # @return [RDF::Query::Variable]
1119
+ def variable(id, distinguished = true)
1120
+ id = nil if id.to_s.empty?
1121
+
1122
+ if id
1123
+ @vars[id] ||= begin
1124
+ v = RDF::Query::Variable.new(id)
1125
+ v.distinguished = distinguished
1126
+ v
1127
+ end
1128
+ else
1129
+ unless distinguished
1130
+ # Allocate a non-distinguished variable identifier
1131
+ id = @nd_var_gen
1132
+ @nd_var_gen = id.succ
1133
+ end
1134
+ v = RDF::Query::Variable.new(id)
1135
+ v.distinguished = distinguished
1136
+ v
1137
+ end
1138
+ end
1139
+
1140
+ # Create URIs
1141
+ def iri(value)
1142
+ # If we have a base URI, use that when constructing a new URI
1143
+ iri = if base_uri
1144
+ u = base_uri.join(value.to_s)
1145
+ u.lexical = "<#{value}>" unless u.to_s == value.to_s || resolve_iris?
1146
+ u
1147
+ else
1148
+ RDF::URI(value)
1149
+ end
1150
+
1151
+ #iri.validate! if validate? && iri.respond_to?(:validate)
1152
+ #iri = RDF::URI.intern(iri) if intern?
1153
+ iri
1154
+ end
1155
+
1156
+ def ns(prefix, suffix)
1157
+ base = prefix(prefix).to_s
1158
+ suffix = suffix.to_s.sub(/^\#/, "") if base.index("#")
1159
+ debug {"ns(#{prefix.inspect}): base: '#{base}', suffix: '#{suffix}'"}
1160
+ iri = iri(base + suffix.to_s)
1161
+ # Cause URI to be serialized as a lexical
1162
+ iri.lexical = "#{prefix}:#{suffix}" unless resolve_iris?
1163
+ iri
1164
+ end
1165
+
1166
+ # Create a literal
1167
+ def literal(value, options = {})
1168
+ options = options.dup
1169
+ # Internal representation is to not use xsd:string, although it could arguably go the other way.
1170
+ options.delete(:datatype) if options[:datatype] == RDF::XSD.string
1171
+ debug("literal") do
1172
+ "value: #{value.inspect}, " +
1173
+ "options: #{options.inspect}, " +
1174
+ "validate: #{validate?.inspect}, "
1175
+ end
1176
+ RDF::Literal.new(value, options.merge(:validate => validate?))
1177
+ end
1178
+
1179
+ # Take collection of objects and create RDF Collection using rdf:first, rdf:rest and rdf:nil
1180
+ # @param [Hash] data Production Data
1181
+ def expand_collection(data)
1182
+ # Add any triples generated from deeper productions
1183
+ add_prod_datum(:pattern, data[:pattern])
1184
+
1185
+ # Create list items for each element in data[:GraphNode]
1186
+ first = col = data[:Collection]
1187
+ list = data[:GraphNode].to_a.flatten.compact
1188
+ last = list.pop
1189
+
1190
+ list.each do |r|
1191
+ add_pattern(:Collection, :subject => first, :predicate => RDF["first"], :object => r)
1192
+ rest = bnode()
1193
+ add_pattern(:Collection, :subject => first, :predicate => RDF["rest"], :object => rest)
1194
+ first = rest
1195
+ end
1196
+
1197
+ if last
1198
+ add_pattern(:Collection, :subject => first, :predicate => RDF["first"], :object => last)
1199
+ end
1200
+ add_pattern(:Collection, :subject => first, :predicate => RDF["rest"], :object => RDF["nil"])
1201
+ end
1202
+
1203
+ # add a pattern
1204
+ #
1205
+ # @param [String] production Production generating pattern
1206
+ # @param [Hash{Symbol => Object}] options
1207
+ def add_pattern(production, options)
1208
+ progress(production, "add_pattern: #{options.inspect}")
1209
+ progress(production, "[:pattern, #{options[:subject]}, #{options[:predicate]}, #{options[:object]}]")
1210
+ triple = {}
1211
+ options.each_pair do |r, v|
1212
+ if v.is_a?(Array) && v.flatten.length == 1
1213
+ v = v.flatten.first
1214
+ end
1215
+ if validate? && !v.is_a?(RDF::Term)
1216
+ error("add_pattern", "Expected #{r} to be a resource, but it was #{v.inspect}",
1217
+ :production => production)
1218
+ end
1219
+ triple[r] = v
1220
+ end
1221
+ add_prod_datum(:pattern, RDF::Query::Pattern.new(triple))
1222
+ end
1223
+
1224
+ # Flatten a Data in form of :filter => [op+ bgp?], without a query into filter and query creating exprlist, if necessary
1225
+ # @return [Array[:expr, query]]
1226
+ def flatten_filter(data)
1227
+ query = data.pop if data.last.respond_to?(:execute)
1228
+ expr = data.length > 1 ? SPARQL::Algebra::Operator::Exprlist.new(*data) : data.first
1229
+ [expr, query]
1230
+ end
1231
+
1232
+ # Merge query modifiers, datasets, and projections
1233
+ def merge_modifiers(data)
1234
+ query = data[:query] ? data[:query].first : SPARQL::Algebra::Operator::BGP.new
1235
+
1236
+ # Add datasets and modifiers in order
1237
+ query = SPARQL::Algebra::Expression[:order, data[:order].first, query] if data[:order]
1238
+
1239
+ query = SPARQL::Algebra::Expression[:project, data[:Var], query] if data[:Var]
1240
+
1241
+ query = SPARQL::Algebra::Expression[data[:DISTINCT_REDUCED].first, query] if data[:DISTINCT_REDUCED]
1242
+
1243
+ query = SPARQL::Algebra::Expression[:slice, data[:slice][0], data[:slice][1], query] if data[:slice]
1244
+
1245
+ query = SPARQL::Algebra::Expression[:dataset, data[:dataset], query] if data[:dataset]
1246
+
1247
+ query
1248
+ end
1249
+
1250
+ # Add joined expressions in for prod1 (op prod2)* to form (op (op 1 2) 3)
1251
+ def add_operator_expressions(production, data)
1252
+ # Iterate through expression to create binary operations
1253
+ res = data[:Expression]
1254
+ while data[production] && !data[production].empty?
1255
+ res = SPARQL::Algebra::Expression[data[production].shift + res + data[production].shift]
1256
+ end
1257
+ add_prod_datum(:Expression, res)
1258
+ end
1259
+
1260
+ # Accumulate joined expressions in for prod1 (op prod2)* to form (op (op 1 2) 3)
1261
+ def accumulate_operator_expressions(operator, production, data)
1262
+ if data[operator]
1263
+ # Add [op data] to stack based on "production"
1264
+ add_prod_datum(production, [data[operator], data[:Expression]])
1265
+ # Add previous [op data] information
1266
+ add_prod_datum(production, data[production])
1267
+ else
1268
+ # No operator, forward :Expression
1269
+ add_prod_datum(:Expression, data[:Expression])
1270
+ end
1271
+ end
1272
+
1273
+ ##
1274
+ # Progress output when debugging
1275
+ # @overload debug(node, message)
1276
+ # @param [String] node relative location in input
1277
+ # @param [String] message ("")
1278
+ #
1279
+ # @overload debug(message)
1280
+ # @param [String] message ("")
1281
+ #
1282
+ # @yieldreturn [String] added to message
1283
+ def debug(*args)
1284
+ return unless @options[:debug] || RDF::Turtle.debug?
1285
+ options = args.last.is_a?(Hash) ? args.pop : {}
1286
+ debug_level = options.fetch(:level, 1)
1287
+ return unless debug_level <= DEBUG_LEVEL
1288
+ depth = options[:depth] || self.depth
1289
+ message = args.pop
1290
+ message = message.call if message.is_a?(Proc)
1291
+ args << message if message
1292
+ args << yield if block_given?
1293
+ message = "#{args.join(': ')}"
1294
+ str = "[#{@lineno}]#{' ' * depth}#{message}"
1295
+ case @options[:debug]
1296
+ when Array
1297
+ options[:debug] << str
1298
+ else
1299
+ $stderr.puts str
1300
+ end
1301
+ end
1302
+
1303
+ end # class Parser
1304
+ end # module SPARQL::Grammar