sparql 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. data/AUTHORS +3 -0
  2. data/CREDITS +0 -0
  3. data/README.markdown +103 -53
  4. data/UNLICENSE +24 -0
  5. data/VERSION +1 -0
  6. data/bin/sparql +87 -0
  7. data/lib/sparql.rb +105 -22
  8. data/lib/sparql/algebra.rb +369 -0
  9. data/lib/sparql/algebra/evaluatable.rb +37 -0
  10. data/lib/sparql/algebra/expression.rb +284 -0
  11. data/lib/sparql/algebra/extensions.rb +159 -0
  12. data/lib/sparql/algebra/operator.rb +492 -0
  13. data/lib/sparql/algebra/operator/add.rb +34 -0
  14. data/lib/sparql/algebra/operator/and.rb +65 -0
  15. data/lib/sparql/algebra/operator/asc.rb +29 -0
  16. data/lib/sparql/algebra/operator/ask.rb +46 -0
  17. data/lib/sparql/algebra/operator/base.rb +46 -0
  18. data/lib/sparql/algebra/operator/bgp.rb +26 -0
  19. data/lib/sparql/algebra/operator/bound.rb +48 -0
  20. data/lib/sparql/algebra/operator/compare.rb +84 -0
  21. data/lib/sparql/algebra/operator/construct.rb +85 -0
  22. data/lib/sparql/algebra/operator/dataset.rb +77 -0
  23. data/lib/sparql/algebra/operator/datatype.rb +42 -0
  24. data/lib/sparql/algebra/operator/desc.rb +17 -0
  25. data/lib/sparql/algebra/operator/describe.rb +71 -0
  26. data/lib/sparql/algebra/operator/distinct.rb +50 -0
  27. data/lib/sparql/algebra/operator/divide.rb +43 -0
  28. data/lib/sparql/algebra/operator/equal.rb +32 -0
  29. data/lib/sparql/algebra/operator/exprlist.rb +52 -0
  30. data/lib/sparql/algebra/operator/filter.rb +71 -0
  31. data/lib/sparql/algebra/operator/graph.rb +28 -0
  32. data/lib/sparql/algebra/operator/greater_than.rb +32 -0
  33. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +33 -0
  34. data/lib/sparql/algebra/operator/is_blank.rb +35 -0
  35. data/lib/sparql/algebra/operator/is_iri.rb +37 -0
  36. data/lib/sparql/algebra/operator/is_literal.rb +36 -0
  37. data/lib/sparql/algebra/operator/join.rb +67 -0
  38. data/lib/sparql/algebra/operator/lang.rb +29 -0
  39. data/lib/sparql/algebra/operator/lang_matches.rb +53 -0
  40. data/lib/sparql/algebra/operator/left_join.rb +95 -0
  41. data/lib/sparql/algebra/operator/less_than.rb +32 -0
  42. data/lib/sparql/algebra/operator/less_than_or_equal.rb +32 -0
  43. data/lib/sparql/algebra/operator/minus.rb +31 -0
  44. data/lib/sparql/algebra/operator/multiply.rb +34 -0
  45. data/lib/sparql/algebra/operator/not.rb +35 -0
  46. data/lib/sparql/algebra/operator/not_equal.rb +26 -0
  47. data/lib/sparql/algebra/operator/or.rb +65 -0
  48. data/lib/sparql/algebra/operator/order.rb +69 -0
  49. data/lib/sparql/algebra/operator/plus.rb +31 -0
  50. data/lib/sparql/algebra/operator/prefix.rb +45 -0
  51. data/lib/sparql/algebra/operator/project.rb +46 -0
  52. data/lib/sparql/algebra/operator/reduced.rb +47 -0
  53. data/lib/sparql/algebra/operator/regex.rb +70 -0
  54. data/lib/sparql/algebra/operator/same_term.rb +46 -0
  55. data/lib/sparql/algebra/operator/slice.rb +60 -0
  56. data/lib/sparql/algebra/operator/str.rb +35 -0
  57. data/lib/sparql/algebra/operator/subtract.rb +32 -0
  58. data/lib/sparql/algebra/operator/union.rb +55 -0
  59. data/lib/sparql/algebra/query.rb +99 -0
  60. data/lib/sparql/algebra/sxp_extensions.rb +35 -0
  61. data/lib/sparql/algebra/version.rb +20 -0
  62. data/lib/sparql/extensions.rb +102 -0
  63. data/lib/sparql/grammar.rb +298 -0
  64. data/lib/sparql/grammar/lexer.rb +609 -0
  65. data/lib/sparql/grammar/parser.rb +1383 -0
  66. data/lib/sparql/grammar/parser/meta.rb +1801 -0
  67. data/lib/sparql/results.rb +220 -0
  68. data/lib/sparql/version.rb +20 -0
  69. metadata +232 -62
  70. data/Rakefile +0 -22
  71. data/coverage/index.html +0 -252
  72. data/coverage/lib-sparql-execute_sparql_rb.html +0 -621
  73. data/coverage/lib-sparql_rb.html +0 -622
  74. data/lib/sparql/execute_sparql.rb +0 -27
  75. data/lib/sparql/sparql.treetop +0 -159
  76. data/sparql.gemspec +0 -16
  77. data/spec/spec.opts +0 -2
  78. data/spec/spec_helper.rb +0 -24
  79. data/spec/unit/graph_parsing_spec.rb +0 -76
  80. data/spec/unit/iri_parsing_spec.rb +0 -46
  81. data/spec/unit/prefixed_names_parsing_spec.rb +0 -40
  82. data/spec/unit/primitives_parsing_spec.rb +0 -26
  83. data/spec/unit/sparql_parsing_spec.rb +0 -72
  84. data/spec/unit/variables_parsing_spec.rb +0 -36
@@ -0,0 +1,34 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ include Evaluatable
4
+
5
+ ##
6
+ # The SPARQL numeric `add` operator.
7
+ #
8
+ # @example
9
+ # (+ 1 ?x)
10
+ # (add 1 ?x)
11
+ #
12
+ # @see http://www.w3.org/TR/xpath-functions/#func-numeric-add
13
+ class Add < Operator::Binary
14
+ NAME = [:+, :add]
15
+
16
+ ##
17
+ # Returns the arithmetic sum of the operands.
18
+ #
19
+ # @param [RDF::Literal::Numeric] left
20
+ # a numeric literal
21
+ # @param [RDF::Literal::Numeric] right
22
+ # a numeric literal
23
+ # @return [RDF::Literal::Numeric]
24
+ # @raise [TypeError] if either operand is not a numeric literal
25
+ def apply(left, right)
26
+ case
27
+ when left.is_a?(RDF::Literal::Numeric) && right.is_a?(RDF::Literal::Numeric)
28
+ left + right
29
+ else raise TypeError, "expected two RDF::Literal::Numeric operands, but got #{left.inspect} and #{right.inspect}"
30
+ end
31
+ end
32
+ end # Add
33
+ end # Operator
34
+ end; end # SPARQL::Algebra
@@ -0,0 +1,65 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL logical `and` operator.
5
+ #
6
+ # @example
7
+ # (&& ?x ?y)
8
+ # (and ?x ?y)
9
+ #
10
+ # @see http://www.w3.org/TR/rdf-sparql-query/#func-logical-and
11
+ # @see http://www.w3.org/TR/rdf-sparql-query/#evaluation
12
+ class And < Operator::Binary
13
+ include Evaluatable
14
+
15
+ NAME = [:and, :'&&']
16
+
17
+ ##
18
+ # Initializes a new operator instance.
19
+ #
20
+ # @param [RDF::Literal::Boolean] left
21
+ # the left operand
22
+ # @param [RDF::Literal::Boolean] right
23
+ # the right operand
24
+ # @param [Hash{Symbol => Object}] options
25
+ # any additional options (see {Operator#initialize})
26
+ # @raise [TypeError] if any operand is invalid
27
+ def initialize(left, right, options = {})
28
+ super
29
+ end
30
+
31
+ ##
32
+ # Returns the logical `AND` of the left operand and the right operand.
33
+ #
34
+ # Note that this operator operates on the effective boolean value
35
+ # (EBV) of its operands.
36
+ #
37
+ # @param [RDF::Query::Solution, #[]] bindings
38
+ # @return [RDF::Literal::Boolean] `true` or `false`
39
+ # @raise [TypeError] if the operands could not be coerced to boolean literals
40
+ def evaluate(bindings = {})
41
+ begin
42
+ left = boolean(operand(0).evaluate(bindings)).true?
43
+ rescue TypeError
44
+ left = nil
45
+ end
46
+
47
+ begin
48
+ right = boolean(operand(1).evaluate(bindings)).true?
49
+ rescue TypeError
50
+ right = nil
51
+ end
52
+
53
+ # From http://www.w3.org/TR/rdf-sparql-query/#evaluation
54
+ # A logical-and that encounters an error on only one branch will return an error if the other branch is
55
+ # TRUE and FALSE if the other branch is FALSE.
56
+ case
57
+ when left.nil? && right.nil? then raise(TypeError)
58
+ when left.nil? then right ? raise(TypeError) : RDF::Literal::FALSE
59
+ when right.nil? then left ? raise(TypeError) : RDF::Literal::FALSE
60
+ else RDF::Literal(left && right)
61
+ end
62
+ end
63
+ end # And
64
+ end # Operator
65
+ end; end # SPARQL::Algebra
@@ -0,0 +1,29 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL ascending sort operator.
5
+ #
6
+ # @example
7
+ # (prefix ((foaf: <http://xmlns.com/foaf/0.1/>))
8
+ # (project (?name)
9
+ # (order ((asc ?name))
10
+ # (bgp (triple ?x foaf:name ?name)))))
11
+ #
12
+ # @see http://www.w3.org/TR/rdf-sparql-query/#func-isLiteral
13
+ class Asc < Operator::Unary
14
+ include Evaluatable
15
+
16
+ NAME = :asc
17
+
18
+ ##
19
+ # Returns the evaluation of it's operand. Default comparison is in
20
+ # ascending order. Ordering is applied in {Order}.
21
+ #
22
+ # @param [RDF::Query::Solution, #[]] bindings
23
+ # @return [RDF::Term]
24
+ def evaluate(bindings = {})
25
+ operand(0).evaluate(bindings)
26
+ end
27
+ end # Asc
28
+ end # Operator
29
+ end; end # SPARQL::Algebra
@@ -0,0 +1,46 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL GraphPattern `ask` operator.
5
+ #
6
+ # @example
7
+ # (prefix ((: <http://example/>))
8
+ # (ask
9
+ # (bgp (triple :x :p ?x))))
10
+ #
11
+ # @see http://www.w3.org/TR/rdf-sparql-query/#ask
12
+ class Ask < Operator::Unary
13
+ include Query
14
+
15
+ NAME = [:ask]
16
+
17
+ ##
18
+ # Executes this query on the given `queryable` graph or repository.
19
+ # Returns true if any solutions are found, false otherwise.
20
+ #
21
+ # @param [RDF::Queryable] queryable
22
+ # the graph or repository to query
23
+ # @param [Hash{Symbol => Object}] options
24
+ # any additional keyword options
25
+ # @return [RDF::Literal::Boolean]
26
+ # the resulting solution sequence
27
+ # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
28
+ def execute(queryable, options = {})
29
+ debug(options) {"Ask #{operands.first}"}
30
+ boolean(!operands.last.
31
+ execute(queryable, options.merge(:depth => options[:depth].to_i + 1)).
32
+ empty?)
33
+ end
34
+
35
+ ##
36
+ # Returns an optimized version of this query.
37
+ #
38
+ # Return optimized query
39
+ #
40
+ # @return [Union, RDF::Query] `self`
41
+ def optimize
42
+ operands = operands.map(&:optimize)
43
+ end
44
+ end # Ask
45
+ end # Operator
46
+ end; end # SPARQL::Algebra
@@ -0,0 +1,46 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL GraphPattern `base` operator.
5
+ #
6
+ # @example
7
+ # (base <http://example.org/>
8
+ # (bgp (triple <a> <b> 123.0)))
9
+ #
10
+ # @see http://www.w3.org/TR/rdf-sparql-query/#QSynIRI
11
+ class Base < Binary
12
+ include Query
13
+
14
+ NAME = [:base]
15
+
16
+ ##
17
+ # Executes this query on the given `queryable` graph or repository.
18
+ # Really a pass-through, as this is a syntactic object used for providing
19
+ # context for relative URIs.
20
+ #
21
+ # @param [RDF::Queryable] queryable
22
+ # the graph or repository to query
23
+ # @param [Hash{Symbol => Object}] options
24
+ # any additional keyword options
25
+ # @return [RDF::Query::Solutions]
26
+ # the resulting solution sequence
27
+ # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
28
+ def execute(queryable, options = {})
29
+ debug(options) {"Base #{operands.first}"}
30
+ @solutions = operands.last.execute(queryable, options.merge(:depth => options[:depth].to_i + 1))
31
+ debug(options) {"=> #{@solutions.inspect}"}
32
+ @solutions
33
+ end
34
+
35
+ ##
36
+ # Returns an optimized version of this query.
37
+ #
38
+ # Return optimized query
39
+ #
40
+ # @return [Union, RDF::Query] `self`
41
+ def optimize
42
+ operands.last.optimize
43
+ end
44
+ end # Base
45
+ end # Operator
46
+ end; end # SPARQL::Algebra
@@ -0,0 +1,26 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL GraphPattern `bgp` operator.
5
+ #
6
+ # Query with `context` set to false.
7
+ #
8
+ # @example
9
+ # (prefix ((: <http://example/>))
10
+ # (bgp (triple ?s ?p ?o)))
11
+ #
12
+ # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
13
+ class BGP < Operator
14
+ NAME = [:bgp]
15
+ ##
16
+ # A `graph` is an RDF::Query with a context.
17
+ #
18
+ # @param [RDF::URI, RDF::Query::Variable] context
19
+ # @param [RDF::Query] bgp
20
+ # @return [RDF::Query]
21
+ def self.new(*patterns)
22
+ RDF::Query.new(*(patterns + [{:context => false}]))
23
+ end
24
+ end # BGP
25
+ end # Operator
26
+ end; end # SPARQL::Algebra
@@ -0,0 +1,48 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL `bound` operator.
5
+ #
6
+ # @example
7
+ # (prefix ((: <http://example.org/ns#>))
8
+ # (project (?a ?c)
9
+ # (filter (! (bound ?e))
10
+ # (leftjoin
11
+ # (bgp (triple ?a :b ?c))
12
+ # (bgp (triple ?c :d ?e))))))
13
+ #
14
+ # @see http://www.w3.org/TR/rdf-sparql-query/#func-bound
15
+ class Bound < Operator::Unary
16
+ include Evaluatable
17
+
18
+ NAME = :bound
19
+
20
+ ##
21
+ # Initializes a new operator instance.
22
+ #
23
+ # @param [RDF::Query::Variable] var
24
+ # a variable
25
+ # @param [Hash{Symbol => Object}] options
26
+ # any additional options (see {Operator#initialize})
27
+ # @raise [TypeError] if any operand is invalid
28
+ def initialize(var, options = {})
29
+ super
30
+ end
31
+
32
+ ##
33
+ # Returns `true` if the operand is a variable that is bound to a
34
+ # value in the given `bindings`, `false` otherwise.
35
+ #
36
+ # @param [RDF::Query::Solution, #[]] bindings
37
+ # @return [RDF::Literal::Boolean] `true` or `false`
38
+ # @raise [TypeError] if the operand is not a variable
39
+ def evaluate(bindings = {})
40
+ case var = operand
41
+ when Variable
42
+ operand.evaluate(bindings) ? RDF::Literal::TRUE : RDF::Literal::FALSE
43
+ else raise TypeError, "expected an RDF::Query::Variable, but got #{var.inspect}"
44
+ end
45
+ end
46
+ end # Bound
47
+ end # Operator
48
+ end; end # SPARQL::Algebra
@@ -0,0 +1,84 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # A SPARQL relational `<=>` comparison operator.
5
+ #
6
+ # @example
7
+ # (<=> ?x ?y)
8
+ #
9
+ # @see http://www.w3.org/TR/rdf-sparql-query/#OperatorMapping
10
+ # @see http://www.w3.org/TR/xpath-functions/#func-compare
11
+ class Compare < Operator::Binary
12
+ include Evaluatable
13
+
14
+ NAME = :<=>
15
+
16
+ ##
17
+ # Returns -1, 0, or 1, depending on whether the first operand is
18
+ # respectively less than, equal to, or greater than the second
19
+ # operand.
20
+ #
21
+ # SPARQL also fixes an order between some kinds of RDF terms that would not otherwise be ordered:
22
+ #
23
+ # (Lowest) no value assigned to the variable or expression in this solution.
24
+ # Blank nodes
25
+ # IRIs
26
+ # RDF literals
27
+ #
28
+ # @param [RDF::Literal] left
29
+ # a literal
30
+ # @param [RDF::Literal] right
31
+ # a literal
32
+ # @return [RDF::Literal::Integer] `-1`, `0`, or `1`
33
+ # @raise [TypeError] if either operand is not a literal
34
+ def apply(left, right)
35
+ case
36
+ # @see http://www.w3.org/TR/rdf-sparql-query/#OperatorMapping
37
+ # @see http://www.w3.org/TR/rdf-sparql-query/#modOrderBy
38
+ when left.is_a?(RDF::Literal) && right.is_a?(RDF::Literal)
39
+ case
40
+ # @see http://www.w3.org/TR/xpath-functions/#string-compare
41
+ # @see http://www.w3.org/TR/xpath-functions/#comp.numeric
42
+ # @see http://www.w3.org/TR/xpath-functions/#op.boolean
43
+ # @see http://www.w3.org/TR/xpath-functions/#comp.duration.datetime
44
+ when (left.simple? && right.simple?) ||
45
+ (left.is_a?(RDF::Literal::Numeric) && right.is_a?(RDF::Literal::Numeric)) ||
46
+ (left.datatype == right.datatype && left.language == right.language)
47
+ RDF::Literal(left.send(self.class.const_get(:NAME), right))
48
+
49
+ # A plain literal is lower than an RDF literal with type xsd:string of the same lexical form.
50
+ when left.simple? && right.datatype == RDF::XSD.string && left.value == right.value
51
+ RDF::Literal(-1)
52
+ when right.simple? && left.datatype == RDF::XSD.string && right.value == left.value
53
+ RDF::Literal(-1)
54
+
55
+ else raise TypeError, "unable to compare #{left.inspect} and #{right.inspect}"
56
+ end
57
+
58
+ when left.is_a?(RDF::URI) && right.is_a?(RDF::URI)
59
+ # Pairs of IRIs are ordered by comparing them as simple literals.
60
+ RDF::Literal(RDF::Literal(left.to_s).send(self.class.const_get(:NAME), RDF::Literal(right.to_s)))
61
+ when left.is_a?(RDF::Node) && right.is_a?(RDF::Node)
62
+ # BNode comparison is undefined.
63
+ RDF::Literal(0)
64
+ when left.nil? && right.nil?
65
+ RDF::Literal(0)
66
+
67
+ # SPARQL also fixes an order between some kinds of RDF terms that would not otherwise be ordered:
68
+ # 2. Blank nodes
69
+ when left.is_a?(RDF::Node) && right.is_a?(RDF::Term)
70
+ RDF::Literal(-1)
71
+ when right.is_a?(RDF::Node) && left.is_a?(RDF::Term)
72
+ RDF::Literal(1)
73
+
74
+ # 3. IRIs
75
+ when left.is_a?(RDF::URI) && right.is_a?(RDF::Term)
76
+ RDF::Literal(-1)
77
+ when right.is_a?(RDF::URI) && left.is_a?(RDF::Term)
78
+ RDF::Literal(1)
79
+ else raise TypeError, "expected two RDF::Term operands, but got #{left.inspect} and #{right.inspect}"
80
+ end
81
+ end
82
+ end # Compare
83
+ end # Operator
84
+ end; end # SPARQL::Algebra
@@ -0,0 +1,85 @@
1
+ module SPARQL; module Algebra
2
+ class Operator
3
+ ##
4
+ # The SPARQL GraphPattern `construct` operator.
5
+ #
6
+ # @example
7
+ # (prefix ((rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>)
8
+ # (foaf: <http://xmlns.com/foaf/0.1/>))
9
+ # (construct ((triple ?s ?p ?o))
10
+ # (project (?s ?p ?o)
11
+ # (bgp (triple ?s ?p ?o)))))
12
+ #
13
+ # @see http://www.w3.org/TR/rdf-sparql-query/#ask
14
+ class Construct < Operator::Binary
15
+ include Query
16
+
17
+ NAME = [:construct]
18
+
19
+ ##
20
+ # Executes this query on the given {RDF::Queryable} object.
21
+ # Binds variables to the array of patterns in the first operand and returns the resulting RDF::Graph object
22
+ #
23
+ # If any such instantiation produces a triple containing an unbound variable or an illegal RDF construct,
24
+ # such as an RDF::Literal in _subject_ or _predicate_ position, then that triple is not included in the output RDF
25
+ # graph. The graph template can contain triples with no variables (known as ground or explicit triples),
26
+ # and these also appear in the output RDF graph returned by the CONSTRUCT query form.
27
+ #
28
+ # @param [RDF::Queryable] queryable
29
+ # the graph or repository to query
30
+ # @param [Hash{Symbol => Object}] options
31
+ # any additional keyword options
32
+ # @return [RDF::Query::Solutions]
33
+ # the resulting solution sequence
34
+ # @see http://www.w3.org/TR/rdf-sparql-query/#construct
35
+ def execute(queryable, options = {})
36
+ debug(options) {"Construct #{operands.first}, #{options.inspect}"}
37
+ graph = RDF::Graph.new
38
+ patterns = operands.first
39
+ query = operands.last
40
+
41
+ query.execute(queryable, options.merge(:depth => options[:depth].to_i + 1)).each do |solution|
42
+ debug(options) {"=> Apply #{solution.inspect} to BGP"}
43
+
44
+ # Create a mapping from BNodes within the pattern list to newly constructed BNodes
45
+ nodes = {}
46
+ patterns.each do |pattern|
47
+ terms = {}
48
+ [:subject, :predicate, :object].each do |r|
49
+ terms[r] = case o = pattern.send(r)
50
+ when RDF::Node then nodes[o] ||= RDF::Node.new
51
+ when RDF::Query::Variable then solution[o]
52
+ else o
53
+ end
54
+ end
55
+
56
+ statement = RDF::Statement.from(terms)
57
+
58
+ # Sanity checking on statement
59
+ if statement.subject.nil? || statement.predicate.nil? || statement.object.nil? ||
60
+ statement.subject.literal? || statement.predicate.literal?
61
+ debug(options) {"==> skip #{statement.inspect}"}
62
+ next
63
+ end
64
+
65
+ debug(options) {"==> add #{statement.inspect}"}
66
+ graph << statement
67
+ end
68
+ end
69
+
70
+ debug(options) {"=>\n#{graph.dump(:ttl, :standard_prefixes => true)}"}
71
+ graph
72
+ end
73
+
74
+ ##
75
+ # Returns an optimized version of this query.
76
+ #
77
+ # Return optimized query
78
+ #
79
+ # @return [Union, RDF::Query] `self`
80
+ def optimize
81
+ operands = operands.map(&:optimize)
82
+ end
83
+ end # Construct
84
+ end # Operator
85
+ end; end # SPARQL::Algebra