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,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL GraphPattern `graph` operator.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # This is a wrapper to add a `context` to the query.
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 9 
     | 
    
         
            +
                #   (prefix ((: <http://example/>))
         
     | 
| 
      
 10 
     | 
    
         
            +
                #     (graph ?g
         
     | 
| 
      
 11 
     | 
    
         
            +
                #       (bgp (triple ?s ?p ?o))))
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
         
     | 
| 
      
 14 
     | 
    
         
            +
                class Graph < Operator::Binary
         
     | 
| 
      
 15 
     | 
    
         
            +
                  NAME = [:graph]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # A `graph` is an RDF::Query with a context.
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @param [RDF::URI, RDF::Query::Variable] context
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @param [RDF::Query] bgp
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @return [RDF::Query]
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def self.new(context, bgp)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    bgp.context = context
         
     | 
| 
      
 24 
     | 
    
         
            +
                    bgp
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end # Graph
         
     | 
| 
      
 27 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 28 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL relational `>` (greater than) 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 
     | 
    
         
            +
                # @see http://www.w3.org/TR/xpath-functions/#func-numeric-greater-than
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @see http://www.w3.org/TR/xpath-functions/#func-boolean-greater-than
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see http://www.w3.org/TR/xpath-functions/#func-dateTime-greater-than
         
     | 
| 
      
 14 
     | 
    
         
            +
                class GreaterThan < Compare
         
     | 
| 
      
 15 
     | 
    
         
            +
                  NAME = :>
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # Returns `true` if the first operand is greater than the second
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # operand; returns `false` otherwise.
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @param  [RDF::Literal] left
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #   a literal
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @param  [RDF::Literal] right
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   a literal
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @return [RDF::Literal::Boolean] `true` or `false`
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # @raise  [TypeError] if either operand is not a literal
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def apply(left, right)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    super
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end # GreaterThan
         
     | 
| 
      
 31 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 32 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL relational `>=` (greater than or equal) comparison
         
     | 
| 
      
 5 
     | 
    
         
            +
                # operator.
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 8 
     | 
    
         
            +
                #   (>= ?x ?y)
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#OperatorMapping
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @see http://www.w3.org/TR/xpath-functions/#func-compare
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @see http://www.w3.org/TR/xpath-functions/#func-numeric-greater-than
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see http://www.w3.org/TR/xpath-functions/#func-boolean-greater-than
         
     | 
| 
      
 14 
     | 
    
         
            +
                # @see http://www.w3.org/TR/xpath-functions/#func-dateTime-greater-than
         
     | 
| 
      
 15 
     | 
    
         
            +
                class GreaterThanOrEqual < Compare
         
     | 
| 
      
 16 
     | 
    
         
            +
                  NAME = :>=
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # Returns `true` if the first operand is greater than or equal to the
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # second operand; returns `false` otherwise.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # @param  [RDF::Literal] left
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #   a literal
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @param  [RDF::Literal] right
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   a literal
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # @return [RDF::Literal::Boolean] `true` or `false`
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # @raise  [TypeError] if either operand is not a literal
         
     | 
| 
      
 28 
     | 
    
         
            +
                  def apply(left, right)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    super
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end # GreaterThanOrEqual
         
     | 
| 
      
 32 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 33 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL `isBlank` operator.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
         
     | 
| 
      
 8 
     | 
    
         
            +
                #            (: <http://example.org/things#>))
         
     | 
| 
      
 9 
     | 
    
         
            +
                #     (project (?x ?v)
         
     | 
| 
      
 10 
     | 
    
         
            +
                #       (filter (isBlank ?v)
         
     | 
| 
      
 11 
     | 
    
         
            +
                #         (bgp (triple ?x :p ?v)))))
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#func-isBlank
         
     | 
| 
      
 14 
     | 
    
         
            +
                class IsBlank < Operator::Unary
         
     | 
| 
      
 15 
     | 
    
         
            +
                  include Evaluatable
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  NAME = :isBlank
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Returns `true` if the operand is an `RDF::Node`, `false` otherwise.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # @param  [RDF::Term] term
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #   an RDF term
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [RDF::Literal::Boolean] `true` or `false`
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @raise  [TypeError] if the operand is not an RDF term
         
     | 
| 
      
 26 
     | 
    
         
            +
                  def apply(term)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    case term
         
     | 
| 
      
 28 
     | 
    
         
            +
                      when RDF::Node then RDF::Literal::TRUE
         
     | 
| 
      
 29 
     | 
    
         
            +
                      when RDF::Term then RDF::Literal::FALSE
         
     | 
| 
      
 30 
     | 
    
         
            +
                      else raise TypeError, "expected an RDF::Term, but got #{term.inspect}"
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                end # IsBlank
         
     | 
| 
      
 34 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 35 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL `isIRI`/`isURI` operator.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
         
     | 
| 
      
 8 
     | 
    
         
            +
                #            (: <http://example.org/things#>))
         
     | 
| 
      
 9 
     | 
    
         
            +
                #     (project (?x ?v)
         
     | 
| 
      
 10 
     | 
    
         
            +
                #       (filter (isIRI ?v)
         
     | 
| 
      
 11 
     | 
    
         
            +
                #         (bgp (triple ?x :p ?v)))))
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#func-isIRI
         
     | 
| 
      
 14 
     | 
    
         
            +
                class IsIRI < Operator::Unary
         
     | 
| 
      
 15 
     | 
    
         
            +
                  include Evaluatable
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  NAME = [:isIRI, :isURI]
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Returns `true` if the operand is an `RDF::URI`, `false` otherwise.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # @param  [RDF::Term] term
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #   an RDF term
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [RDF::Literal::Boolean] `true` or `false`
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @raise  [TypeError] if the operand is not an RDF term
         
     | 
| 
      
 26 
     | 
    
         
            +
                  def apply(term)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    case term
         
     | 
| 
      
 28 
     | 
    
         
            +
                      when RDF::URI  then RDF::Literal::TRUE
         
     | 
| 
      
 29 
     | 
    
         
            +
                      when RDF::Term then RDF::Literal::FALSE
         
     | 
| 
      
 30 
     | 
    
         
            +
                      else raise TypeError, "expected an RDF::Term, but got #{term.inspect}"
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  Operator::IsURI = IsIRI
         
     | 
| 
      
 35 
     | 
    
         
            +
                end # IsIRI
         
     | 
| 
      
 36 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 37 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL `isLiteral` operator.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   (prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>)
         
     | 
| 
      
 8 
     | 
    
         
            +
                #            (: <http://example.org/things#>))
         
     | 
| 
      
 9 
     | 
    
         
            +
                #     (project (?x ?v)
         
     | 
| 
      
 10 
     | 
    
         
            +
                #       (filter (isLiteral ?v)
         
     | 
| 
      
 11 
     | 
    
         
            +
                #         (bgp (triple ?x :p ?v)))))
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#func-isLiteral
         
     | 
| 
      
 14 
     | 
    
         
            +
                class IsLiteral < Operator::Unary
         
     | 
| 
      
 15 
     | 
    
         
            +
                  include Evaluatable
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  NAME = :isLiteral
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Returns `true` if the operand is an `RDF::Literal`, `false`
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # otherwise.
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @param  [RDF::Term] term
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   an RDF term
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @return [RDF::Literal::Boolean] `true` or `false`
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # @raise  [TypeError] if the operand is not an RDF term
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def apply(term)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    case term
         
     | 
| 
      
 29 
     | 
    
         
            +
                      when RDF::Literal then RDF::Literal::TRUE
         
     | 
| 
      
 30 
     | 
    
         
            +
                      when RDF::Term    then RDF::Literal::FALSE
         
     | 
| 
      
 31 
     | 
    
         
            +
                      else raise TypeError, "expected an RDF::Term, but got #{term.inspect}"
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end # IsLiteral
         
     | 
| 
      
 35 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 36 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL GraphPattern `join` operator.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   (prefix ((: <http://example/>))
         
     | 
| 
      
 8 
     | 
    
         
            +
                #     (join
         
     | 
| 
      
 9 
     | 
    
         
            +
                #       (bgp (triple ?s ?p ?o))
         
     | 
| 
      
 10 
     | 
    
         
            +
                #       (graph ?g
         
     | 
| 
      
 11 
     | 
    
         
            +
                #         (bgp (triple ?s ?q ?v)))))
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
         
     | 
| 
      
 14 
     | 
    
         
            +
                class Join < Operator::Binary
         
     | 
| 
      
 15 
     | 
    
         
            +
                  include Query
         
     | 
| 
      
 16 
     | 
    
         
            +
                  
         
     | 
| 
      
 17 
     | 
    
         
            +
                  NAME = [:join]
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Executes each operand with `queryable` and performs the `join` operation
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # by creating a new solution set containing the `merge` of all solutions
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # from each set that are `compatible` with each other.
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @param  [RDF::Queryable] queryable
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   the graph or repository to query
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # @param  [Hash{Symbol => Object}] options
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #   any additional keyword options
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @return [RDF::Query::Solutions]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #   the resulting solution sequence
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # @see    http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # @see    http://rdf.rubyforge.org/RDF/Query/Solution.html#merge-instance_method
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # @see    http://rdf.rubyforge.org/RDF/Query/Solution.html#compatible%3F-instance_method
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def execute(queryable, options = {})
         
     | 
| 
      
 34 
     | 
    
         
            +
                    # Join(Ω1, Ω2) = { merge(μ1, μ2) | μ1 in Ω1 and μ2 in Ω2, and μ1 and μ2 are compatible }
         
     | 
| 
      
 35 
     | 
    
         
            +
                    # eval(D(G), Join(P1, P2)) = Join(eval(D(G), P1), eval(D(G), P2))
         
     | 
| 
      
 36 
     | 
    
         
            +
                    #
         
     | 
| 
      
 37 
     | 
    
         
            +
                    # Generate solutions independently, merge based on solution compatibility
         
     | 
| 
      
 38 
     | 
    
         
            +
                    debug(options) {"Join"}
         
     | 
| 
      
 39 
     | 
    
         
            +
                    solutions1 = operand(0).execute(queryable, options.merge(:depth => options[:depth].to_i + 1)) || {}
         
     | 
| 
      
 40 
     | 
    
         
            +
                    debug(options) {"=>(left) #{solutions1.inspect}"}
         
     | 
| 
      
 41 
     | 
    
         
            +
                    solutions2 = operand(1).execute(queryable, options.merge(:depth => options[:depth].to_i + 1)) || {}
         
     | 
| 
      
 42 
     | 
    
         
            +
                    debug(options) {"=>(right) #{solutions2.inspect}"}
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @solutions = solutions1.map do |s1|
         
     | 
| 
      
 44 
     | 
    
         
            +
                      solutions2.map { |s2| s2.merge(s1) if s2.compatible?(s1) }
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end.flatten.compact
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @solutions = RDF::Query::Solutions.new(@solutions)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    debug(options) {"=> #{@solutions.inspect}"}
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @solutions
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  
         
     | 
| 
      
 51 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # Returns an optimized version of this query.
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # Groups of one graph pattern (not a filter) become join(Z, A) and can be replaced by A.
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # The empty graph pattern Z is the identity for join:
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #   Replace join(Z, A) by A
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #   Replace join(A, Z) by A
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # @return [Join, RDF::Query] `self`
         
     | 
| 
      
 60 
     | 
    
         
            +
                  def optimize
         
     | 
| 
      
 61 
     | 
    
         
            +
                    ops = operands.map {|o| o.optimize }.select {|o| o.respond_to?(:empty?) && !o.empty?}
         
     | 
| 
      
 62 
     | 
    
         
            +
                    @operands = ops
         
     | 
| 
      
 63 
     | 
    
         
            +
                    self
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
                end # Join
         
     | 
| 
      
 66 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 67 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL `lang` operator.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#func-lang
         
     | 
| 
      
 7 
     | 
    
         
            +
                class Lang < Operator::Unary
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include Evaluatable
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  NAME = :lang
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Returns the language tag of the operand, if it has one.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # If the operand has no language tag, returns `""`.
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # @param  [RDF::Literal] literal
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #   a literal
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @return [RDF::Literal] a simple literal
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @raise  [TypeError] if the operand is not a literal
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def apply(literal)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    case literal
         
     | 
| 
      
 23 
     | 
    
         
            +
                      when RDF::Literal then RDF::Literal(literal.language.to_s)
         
     | 
| 
      
 24 
     | 
    
         
            +
                      else raise TypeError, "expected an RDF::Literal, but got #{literal.inspect}"
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end # Lang
         
     | 
| 
      
 28 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 29 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL `langMatches` operator.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   (prefix ((: <http://example.org/#>))
         
     | 
| 
      
 8 
     | 
    
         
            +
                #     (filter (langMatches (lang ?v) "en-GB")
         
     | 
| 
      
 9 
     | 
    
         
            +
                #       (bgp (triple :x ?p ?v))))
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#func-langMatches
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @see http://tools.ietf.org/html/rfc4647#section-3.3.1
         
     | 
| 
      
 13 
     | 
    
         
            +
                class LangMatches < Operator::Binary
         
     | 
| 
      
 14 
     | 
    
         
            +
                  include Evaluatable
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  NAME = :langMatches
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # Returns `true` if the language tag (the first operand) matches the
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # language range (the second operand).
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # @param  [RDF::Literal] language_tag
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #   a simple literal containing a language tag
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @param  [RDF::Literal] language_range
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   a simple literal containing a language range, per
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   [RFC 4647 section 2.1](http://tools.ietf.org/html/rfc4647#section-2.1)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # @return [RDF::Literal::Boolean] `true` or `false`
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @raise  [TypeError] if either operand is unbound
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # @raise  [TypeError] if either operand is not a simple literal
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def apply(language_tag, language_range)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    raise TypeError, "expected a plain RDF::Literal for language_tag, but got #{language_tag.inspect}" unless language_tag.is_a?(RDF::Literal) && language_tag.plain?
         
     | 
| 
      
 32 
     | 
    
         
            +
                    language_tag = language_tag.to_s.downcase
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    raise TypeError, "expected a plain RDF::Literal for language_range, but got #{language_range.inspect}" unless language_range.is_a?(RDF::Literal) && language_range.plain?
         
     | 
| 
      
 35 
     | 
    
         
            +
                    language_range = language_range.to_s.downcase
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    case
         
     | 
| 
      
 38 
     | 
    
         
            +
                      # A language range of "*" matches any non-empty language tag.
         
     | 
| 
      
 39 
     | 
    
         
            +
                      when language_range.eql?('*')
         
     | 
| 
      
 40 
     | 
    
         
            +
                        RDF::Literal(!(language_tag.empty?))
         
     | 
| 
      
 41 
     | 
    
         
            +
                      # A language range matches a particular language tag if, in a
         
     | 
| 
      
 42 
     | 
    
         
            +
                      # case-insensitive comparison, it exactly equals the tag, ...
         
     | 
| 
      
 43 
     | 
    
         
            +
                      when language_tag.eql?(language_range)
         
     | 
| 
      
 44 
     | 
    
         
            +
                        RDF::Literal::TRUE
         
     | 
| 
      
 45 
     | 
    
         
            +
                      # ... or if it exactly equals a prefix of the tag such that the
         
     | 
| 
      
 46 
     | 
    
         
            +
                      # first character following the prefix is "-".
         
     | 
| 
      
 47 
     | 
    
         
            +
                      else
         
     | 
| 
      
 48 
     | 
    
         
            +
                        RDF::Literal(language_tag.start_with?(language_range + '-'))
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end # LangMatches
         
     | 
| 
      
 52 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 53 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     | 
| 
         @@ -0,0 +1,95 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SPARQL; module Algebra
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Operator
         
     | 
| 
      
 3 
     | 
    
         
            +
                ##
         
     | 
| 
      
 4 
     | 
    
         
            +
                # The SPARQL GraphPattern `leftjoin` operator.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   (prefix ((: <http://example/>))
         
     | 
| 
      
 8 
     | 
    
         
            +
                #     (leftjoin
         
     | 
| 
      
 9 
     | 
    
         
            +
                #       (bgp (triple ?x :p ?v))
         
     | 
| 
      
 10 
     | 
    
         
            +
                #       (bgp (triple ?y :q ?w))
         
     | 
| 
      
 11 
     | 
    
         
            +
                #       (= ?v 2)))
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
         
     | 
| 
      
 14 
     | 
    
         
            +
                class LeftJoin < Operator
         
     | 
| 
      
 15 
     | 
    
         
            +
                  include Query
         
     | 
| 
      
 16 
     | 
    
         
            +
                  
         
     | 
| 
      
 17 
     | 
    
         
            +
                  NAME = [:leftjoin]
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Executes each operand with `queryable` and performs the `leftjoin` operation
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # by adding every solution from the left, merging compatible solutions from the right
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # that match an optional filter.
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @param  [RDF::Queryable] queryable
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   the graph or repository to query
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # @param  [Hash{Symbol => Object}] options
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #   any additional keyword options
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @return [RDF::Query::Solutions]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #   the resulting solution sequence
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # @see    http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # @see    http://rdf.rubyforge.org/RDF/Query/Solution.html#merge-instance_method
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # @see    http://rdf.rubyforge.org/RDF/Query/Solution.html#compatible%3F-instance_method
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def execute(queryable, options = {})
         
     | 
| 
      
 34 
     | 
    
         
            +
                    filter = operand(2)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    
         
     | 
| 
      
 37 
     | 
    
         
            +
                    debug(options) {"LeftJoin"}
         
     | 
| 
      
 38 
     | 
    
         
            +
                    left = operand(0).execute(queryable, options.merge(:depth => options[:depth].to_i + 1)) || {}
         
     | 
| 
      
 39 
     | 
    
         
            +
                    debug(options) {"=>(left) #{left.inspect}"}
         
     | 
| 
      
 40 
     | 
    
         
            +
                    right = operand(1).execute(queryable, options.merge(:depth => options[:depth].to_i + 1)) || {}
         
     | 
| 
      
 41 
     | 
    
         
            +
                    debug(options) {"=>(right) #{right.inspect}"}
         
     | 
| 
      
 42 
     | 
    
         
            +
                    
         
     | 
| 
      
 43 
     | 
    
         
            +
                    # LeftJoin(Ω1, Ω2, expr) =
         
     | 
| 
      
 44 
     | 
    
         
            +
                    solutions = []
         
     | 
| 
      
 45 
     | 
    
         
            +
                    left.each do |s1|
         
     | 
| 
      
 46 
     | 
    
         
            +
                      load_left = true
         
     | 
| 
      
 47 
     | 
    
         
            +
                      right.each do |s2|
         
     | 
| 
      
 48 
     | 
    
         
            +
                        s = s2.merge(s1)
         
     | 
| 
      
 49 
     | 
    
         
            +
                        expr = filter ? boolean(filter.evaluate(s)).true? : true rescue false
         
     | 
| 
      
 50 
     | 
    
         
            +
                        debug(options) {"===>(evaluate) #{s.inspect}"} if filter
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                        if expr && s1.compatible?(s2)
         
     | 
| 
      
 53 
     | 
    
         
            +
                          # { merge(μ1, μ2) | μ1 in Ω1 and μ2 in Ω2, and μ1 and μ2 are compatible and expr(merge(μ1, μ2)) is true }
         
     | 
| 
      
 54 
     | 
    
         
            +
                          debug(options) {"=>(merge s1 s2) #{s.inspect}"}
         
     | 
| 
      
 55 
     | 
    
         
            +
                          solutions << s
         
     | 
| 
      
 56 
     | 
    
         
            +
                          load_left = false   # Left solution added one or more times due to merge
         
     | 
| 
      
 57 
     | 
    
         
            +
                        end
         
     | 
| 
      
 58 
     | 
    
         
            +
                      end
         
     | 
| 
      
 59 
     | 
    
         
            +
                      if load_left
         
     | 
| 
      
 60 
     | 
    
         
            +
                        debug(options) {"=>(add) #{s1.inspect}"}
         
     | 
| 
      
 61 
     | 
    
         
            +
                        solutions << s1
         
     | 
| 
      
 62 
     | 
    
         
            +
                      end
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
                    
         
     | 
| 
      
 65 
     | 
    
         
            +
                    @solutions = RDF::Query::Solutions.new(solutions)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    debug(options) {"=> #{@solutions.inspect}"}
         
     | 
| 
      
 67 
     | 
    
         
            +
                    @solutions
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                  
         
     | 
| 
      
 70 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # Returns an optimized version of this query.
         
     | 
| 
      
 72 
     | 
    
         
            +
                  #
         
     | 
| 
      
 73 
     | 
    
         
            +
                  # If optimize operands, and if the first two operands are both Queries, replace
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # with the unique sum of the query elements
         
     | 
| 
      
 75 
     | 
    
         
            +
                  #
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # @return [Union, RDF::Query] `self`
         
     | 
| 
      
 77 
     | 
    
         
            +
                  def optimize
         
     | 
| 
      
 78 
     | 
    
         
            +
                    ops = operands.map {|o| o.optimize }.select {|o| o.respond_to?(:empty?) && !o.empty?}
         
     | 
| 
      
 79 
     | 
    
         
            +
                    expr = ops.pop unless ops.last.executable?
         
     | 
| 
      
 80 
     | 
    
         
            +
                    expr = nil if expr.respond_to?(:true?) && expr.true?
         
     | 
| 
      
 81 
     | 
    
         
            +
                    
         
     | 
| 
      
 82 
     | 
    
         
            +
                    # ops now is one or two executable operators
         
     | 
| 
      
 83 
     | 
    
         
            +
                    # expr is a filter expression, which may have been optimized to 'true'
         
     | 
| 
      
 84 
     | 
    
         
            +
                    case ops.length
         
     | 
| 
      
 85 
     | 
    
         
            +
                    when 0
         
     | 
| 
      
 86 
     | 
    
         
            +
                      RDF::Query.new  # Empty query, expr doesn't matter
         
     | 
| 
      
 87 
     | 
    
         
            +
                    when 1
         
     | 
| 
      
 88 
     | 
    
         
            +
                      expr ? Filter.new(expr, ops.first) : ops.first
         
     | 
| 
      
 89 
     | 
    
         
            +
                    else
         
     | 
| 
      
 90 
     | 
    
         
            +
                      expr ? LeftJoin(ops[0], ops[1], expr) : LeftJoin(ops[0], ops[1])
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                end # LeftJoin
         
     | 
| 
      
 94 
     | 
    
         
            +
              end # Operator
         
     | 
| 
      
 95 
     | 
    
         
            +
            end; end # SPARQL::Algebra
         
     |