require 'rdf' # @see https://rubygems.org/gems/rdf require 'sparql/algebra' require 'json' module SPARQL ## # A SPARQL grammar for RDF.rb. # # ## Representation # The parser natively generates native SPARQL S-Expressions (SSE), # a hierarch of `SPARQL::Algebra::Operator` instances # which can be executed against a queryable object, such as a Repository identically # to `RDF::Query`. # # Other elements within the hierarchy # are generated using RDF objects, such as `RDF::URI`, `RDF::Node`, `RDF::Literal`, and `RDF::Query`. # # See {SPARQL::Grammar::Parser} for a full listing # of algebra operations and RDF objects generated by the parser. # # The native SSE representation may be serialized to a textual representation of SSE as # serialized general S-Expressions (SXP). # The SXP generated closely follows that of [OpenJena ARQ](https://openjena.org/wiki/SSE), which is intended principally for # running the SPARQL rules. Additionally, SSE is generated for CONSTRUCT, ASK, DESCRIBE and FROM operators. # # SXP is generated by serializing the parser result as follows: # # sse = SPARQL::Grammar.parse("SELECT * WHERE { ?s ?p ?o }") # sxp = sse.to_sxp # # The following examples illustrate SPARQL transformations: # # SPARQL: # # SELECT * WHERE { ?a ?b ?c } # # SXP: # # (bgp (triple ?a ?b ?c)) # # SPARQL: # # SELECT * FROM <a> WHERE { ?a ?b ?c } # # SXP: # # (dataset (<a>) (bgp (triple ?a ?b ?c))) # # SPARQL: # # SELECT * FROM NAMED <a> WHERE { ?a ?b ?c } # # SXP: # # (dataset ((named <a>)) (bgp (triple ?a ?b ?c))) # # SPARQL: # # SELECT DISTINCT * WHERE {?a ?b ?c} # # SXP: # # (distinct (bgp (triple ?a ?b ?c))) # # SPARQL: # # SELECT ?a ?b WHERE {?a ?b ?c} # # SXP: # # (project (?a ?b) (bgp (triple ?a ?b ?c))) # # SPARQL: # # CONSTRUCT {?a ?b ?c} WHERE {?a ?b ?c FILTER (?a)} # # SXP: # # (construct ((triple ?a ?b ?c)) (filter ?a (bgp (triple ?a ?b ?c)))) # # SPARQL: # # SELECT * WHERE {<a> <b> <c> OPTIONAL {<d> <e> <f>}} # # SXP: # # (leftjoin (bgp (triple <a> <b> <c>)) (bgp (triple <d> <e> <f>))) # # SPARQL: # # SELECT * WHERE {<a> <b> <c> {<d> <e> <f>}} # # SXP: # # (join (bgp (triple <a> <b> <c>)) (bgp (triple <d> <e> <f>))) # # SPARQL: # # PREFIX : <http://example/> # # SELECT * # { # { ?s ?p ?o } # UNION # { GRAPH ?g { ?s ?p ?o } } # } # # SXP: # # (prefix ((: <http://example/>)) # (union # (bgp (triple ?s ?p ?o)) # (graph ?g # (bgp (triple ?s ?p ?o))))) # # SPARQL: # # LOAD <etc/doap.ttl> # # SXP: # # (update (load <etc/doap.ttl>)) # # SPARQL: # # PREFIX : <http://example.org/> # # INSERT { # ?s ?p "q" # } # USING :g1 # USING :g2 # WHERE { # ?s ?p ?o # } # # SXP: # # (prefix ((: <http://example.org/>)) # (update # (modify # (using (:g1 :g2) # (bgp (triple ?s ?p ?o))) # (insert ((triple ?s ?p "q")))))) # # SPARQL: # # PREFIX : <http://example.org/> # # SELECT * WHERE # { # ?s :p ?v . # BIND (2*?v AS ?v2) . # ?s :p1 ?v2 . # } # # SXP: # # (prefix ((: <http://example.org/>)) # (join # (extend ((?v2 (* 2 ?v))) # (bgp (triple ?s :p ?v))) # (bgp (triple ?s :p1 ?v2)))) # # SPARQL: # # PREFIX : <http://bigdata.com> # PREFIX foaf: <http://xmlns.com/foaf/0.1/> # PREFIX dct: <http://purl.org/dc/elements/1.1/> # # SELECT ?age ?src WHERE { # ?bob foaf:name "Bob" . # <<?bob foaf:age ?age>> dct:source ?src . # } # # SXP: # # (prefix # ( # (: <http://bigdata.com>) # (foaf: <http://xmlns.com/foaf/0.1/>) # (dct: <http://purl.org/dc/elements/1.1/>)) # (project # (?age ?src) # (bgp # (triple ?bob foaf:name "Bob") # (triple (qtriple ?bob foaf:age ?age) dct:source ?src)) )) # # SPARQL: # # PREFIX : <http://bigdata.com> # PREFIX foaf: <http://xmlns.com/foaf/0.1/> # PREFIX dct: <http://purl.org/dc/elements/1.1/> # # CONSTRUCT { # ?bob foaf:name "Bob" . # <<?bob foaf:age ?age>> dct:creator <http://example.com/crawlers#c1>; # dct:source ?src . # } # WHERE { # ?bob foaf:name "Bob" . # <<?bob foaf:age ?age>> dct:source ?src . # } # # SXP: # # (prefix # ( # (: <http://bigdata.com>) # (foaf: <http://xmlns.com/foaf/0.1/>) # (dct: <http://purl.org/dc/elements/1.1/>)) # (construct # ( # (triple ?bob foaf:name "Bob") # (triple (qtriple ?bob foaf:age ?age) dct:creator <http://example.com/crawlers#c1>) # (triple (qtriple ?bob foaf:age ?age) dct:source ?src)) # (bgp # (triple ?bob foaf:name "Bob") # (triple (qtriple ?bob foaf:age ?age) dct:source ?src)) )) # ## Implementation Notes # The parser is driven through a rules table contained in lib/sparql/grammar/meta.rb. This includes branch rules to indicate productions to be taken based on a current production. # # The meta.rb file is generated from etc/sparql11.bnf using the `ebnf` gem. # # ebnf --ll1 Query --format rb \ # --mod-name SPARQL::Grammar::Meta \ # --output lib/sparql/grammar/meta.rb \ # etc/sparql11.bnf # # @see http://www.w3.org/TR/sparql11-query/#grammar # @see https://rubygems.org/gems/ebnf module Grammar autoload :Parser, 'sparql/grammar/parser11' autoload :Terminals, 'sparql/grammar/terminals11' # Make all defined non-autoloaded constants immutable: constants.each { |name| const_get(name).freeze unless autoload?(name) } ## # Parse the given SPARQL `query` string. # # @example # result = SPARQL::Grammar.parse("SELECT * WHERE { ?s ?p ?o }") # # @param [IO, StringIO, Lexer, Array, String, #to_s] query # Query may be an array of lexed tokens, a lexer, or a # string or open file. # @param [Hash{Symbol => Object}] options # @option options (see SPARQL::Grammar::Parser#initialize) # @return [Parser] # @raise [Parser::Error] on invalid input def self.parse(query, **options, &block) Parser.new(query, **options).parse(options[:update] ? :UpdateUnit : :QueryUnit) end ## # Parses input from the given file name or URL. # # @param [String, #to_s] filename # @param [Hash{Symbol => Object}] options # any additional options (see `RDF::Reader#initialize` and `RDF::Format.for`) # @option options [Symbol] :format (:ntriples) # @option options (see parse) # @yield [reader] # @yieldparam [RDF::Reader] reader # @yieldreturn [void] ignored # @raise [RDF::FormatError] if no reader found for the specified format def self.open(filename, **options, &block) RDF::Util::File.open_file(filename, **options) do |file| self.parse(file, **options, &block) end end ## # Returns `true` if the given SPARQL `query` string is valid. # # @example # SPARQL::Grammar.valid?("SELECT ?s WHERE { ?s ?p ?o }") #=> true # SPARQL::Grammar.valid?("SELECT s WHERE { ?s ?p ?o }") #=> false # # @param [String, #to_s] query # @param [Hash{Symbol => Object}] options # @return [Boolean] def self.valid?(query, **options) Parser.new(query, **options).valid? end ## # Tokenizes the given SPARQL `query` string. # # @example # lexer = SPARQL::Grammar.tokenize("SELECT * WHERE { ?s ?p ?o }") # lexer.each_token do |token| # puts token.inspect # end # # @param [String, #to_s] query # @param [Hash{Symbol => Object}] options # @yield [lexer] # @yieldparam [Lexer] lexer # @return [Lexer] # @raise [Lexer::Error] on invalid input def self.tokenize(query, **options, &block) Lexer.tokenize(query, **options, &block) end end # Grammar end # SPARQL