├── .coveralls.yml ├── .github └── workflows │ ├── ci.yml │ ├── generate-docs.yml │ └── gitstamp.yaml ├── .gitignore ├── .yardopts ├── AUTHORS ├── CONTRIBUTING.md ├── CREDITS ├── Gemfile ├── README.md ├── Rakefile ├── UNLICENSE ├── VERSION ├── bin ├── console └── sparql ├── dependencyci.yml ├── etc ├── .earl ├── .gitignore ├── README ├── doap.ttl ├── earl-rdf-star.ttl ├── earl.html ├── earl.jsonld ├── earl.ttl ├── from_default.rq ├── input.rq ├── input.sse ├── manifest-cache.nt ├── sparql11.bnf ├── sparql11.html ├── sparql11.sxp ├── sparql12.bnf ├── sparql12.peg.sxp ├── sparql12.sxp └── template.haml ├── examples ├── annotation-example.rq ├── annotation-path.rq ├── annotation-path2.rq ├── earl-data │ ├── manifest.ttl │ └── rdf.rb-earl.ttl ├── earl_prof.rb ├── function_call.sse ├── group_by.rq ├── group_by.sse ├── in_with_uris.rb ├── issue-21.sparql ├── issue-25.rq ├── issue11.rb ├── issue27.rq ├── issue3.rb ├── issue33.rq ├── issue42-simple.rq ├── issue42.rq ├── issue7.rb ├── issue7.rq ├── issue9.rb ├── issue9.rq ├── issue9.sse ├── issue9.sse2 ├── issue9_filter.rq ├── issue9_results-arq.ttl ├── issue9_results-rb.ttl ├── issue9_short.rq ├── issue9_short2.rq ├── jcarrol-exists.sparql ├── json-ld-example.sparql ├── laksa-path-expression.sparql ├── multiple_joins.rq ├── multiple_options.rq ├── path2.rq ├── patteren-involving-bind.rq ├── pp12.rq ├── pp2.rq ├── pp2_.rq ├── pp_2.rq ├── query_bench.rb ├── query_prof.rb ├── rdfa-test-0272.rb ├── rdfstar-bob-bind.rq ├── rdfstar-bob-construct.rq ├── rdfstar-bob-data.nt ├── rdfstar-bob-data.ttl ├── rdfstar-bob.rq ├── rdfstar-issue76.rq ├── reto-shared-variables.sparql └── star-example.rq ├── lib ├── rack │ ├── sparql.rb │ └── sparql │ │ └── conneg.rb ├── sinatra │ ├── sparql.rb │ └── sparql │ │ └── extensions.rb ├── sparql.rb └── sparql │ ├── algebra.rb │ ├── algebra │ ├── aggregate.rb │ ├── evaluatable.rb │ ├── expression.rb │ ├── extensions.rb │ ├── operator.rb │ ├── operator │ │ ├── abs.rb │ │ ├── add.rb │ │ ├── adjust.rb │ │ ├── alt.rb │ │ ├── and.rb │ │ ├── asc.rb │ │ ├── ask.rb │ │ ├── avg.rb │ │ ├── base.rb │ │ ├── bgp.rb │ │ ├── bnode.rb │ │ ├── bound.rb │ │ ├── ceil.rb │ │ ├── clear.rb │ │ ├── coalesce.rb │ │ ├── compare.rb │ │ ├── concat.rb │ │ ├── construct.rb │ │ ├── contains.rb │ │ ├── copy.rb │ │ ├── count.rb │ │ ├── create.rb │ │ ├── dataset.rb │ │ ├── datatype.rb │ │ ├── day.rb │ │ ├── delete.rb │ │ ├── delete_data.rb │ │ ├── delete_where.rb │ │ ├── desc.rb │ │ ├── describe.rb │ │ ├── distinct.rb │ │ ├── divide.rb │ │ ├── drop.rb │ │ ├── encode_for_uri.rb │ │ ├── equal.rb │ │ ├── exists.rb │ │ ├── exprlist.rb │ │ ├── extend.rb │ │ ├── filter.rb │ │ ├── floor.rb │ │ ├── function_call.rb │ │ ├── graph.rb │ │ ├── greater_than.rb │ │ ├── greater_than_or_equal.rb │ │ ├── group.rb │ │ ├── group_concat.rb │ │ ├── hours.rb │ │ ├── if.rb │ │ ├── in.rb │ │ ├── insert.rb │ │ ├── insert_data.rb │ │ ├── iri.rb │ │ ├── is_blank.rb │ │ ├── is_iri.rb │ │ ├── is_literal.rb │ │ ├── is_numeric.rb │ │ ├── is_triple.rb │ │ ├── join.rb │ │ ├── lang.rb │ │ ├── lang_matches.rb │ │ ├── lcase.rb │ │ ├── left_join.rb │ │ ├── less_than.rb │ │ ├── less_than_or_equal.rb │ │ ├── load.rb │ │ ├── max.rb │ │ ├── md5.rb │ │ ├── min.rb │ │ ├── minus.rb │ │ ├── minutes.rb │ │ ├── modify.rb │ │ ├── month.rb │ │ ├── move.rb │ │ ├── multiply.rb │ │ ├── negate.rb │ │ ├── not.rb │ │ ├── not_equal.rb │ │ ├── notexists.rb │ │ ├── notin.rb │ │ ├── notoneof.rb │ │ ├── now.rb │ │ ├── object.rb │ │ ├── or.rb │ │ ├── order.rb │ │ ├── path.rb │ │ ├── path_opt.rb │ │ ├── path_plus.rb │ │ ├── path_range.rb │ │ ├── path_star.rb │ │ ├── path_zero.rb │ │ ├── plus.rb │ │ ├── predicate.rb │ │ ├── prefix.rb │ │ ├── project.rb │ │ ├── rand.rb │ │ ├── reduced.rb │ │ ├── regex.rb │ │ ├── replace.rb │ │ ├── reverse.rb │ │ ├── round.rb │ │ ├── same_term.rb │ │ ├── sample.rb │ │ ├── seconds.rb │ │ ├── seq.rb │ │ ├── sequence.rb │ │ ├── service.rb │ │ ├── sha1.rb │ │ ├── sha256.rb │ │ ├── sha384.rb │ │ ├── sha512.rb │ │ ├── slice.rb │ │ ├── str.rb │ │ ├── strafter.rb │ │ ├── strbefore.rb │ │ ├── strdt.rb │ │ ├── strends.rb │ │ ├── strlang.rb │ │ ├── strlen.rb │ │ ├── strstarts.rb │ │ ├── struuid.rb │ │ ├── subject.rb │ │ ├── substr.rb │ │ ├── subtract.rb │ │ ├── sum.rb │ │ ├── table.rb │ │ ├── timezone.rb │ │ ├── triple.rb │ │ ├── tz.rb │ │ ├── ucase.rb │ │ ├── union.rb │ │ ├── update.rb │ │ ├── using.rb │ │ ├── uuid.rb │ │ ├── with.rb │ │ └── year.rb │ ├── query.rb │ ├── sxp_extensions.rb │ ├── update.rb │ └── version.rb │ ├── extensions.rb │ ├── grammar.rb │ ├── grammar │ ├── meta.rb │ ├── meta11.rb │ ├── parser.rb │ ├── parser11.rb │ └── terminals.rb │ ├── results.rb │ ├── server.rb │ └── version.rb ├── script ├── sparql2sse └── tc ├── sinatra_rackup.rb ├── sparql.gemspec └── spec ├── .gitignore ├── algebra ├── algebra_helper.rb ├── ask_spec.rb ├── construct_spec.rb ├── describe_spec.rb ├── evaluatable_spec.rb ├── examples_spec.rb ├── expression_spec.rb ├── extensions_spec.rb ├── operator │ ├── and │ │ └── boolean.sse │ ├── bound │ │ └── variable.sse │ ├── datatype │ │ └── literal.sse │ ├── divide │ │ └── numeric.sse │ ├── equal │ │ ├── boolean.sse │ │ ├── datetime.sse │ │ ├── numeric.sse │ │ ├── string.sse │ │ └── term.sse │ ├── greater_than │ │ ├── boolean.sse │ │ ├── datetime.sse │ │ ├── numeric.sse │ │ └── string.sse │ ├── greater_than_or_equal │ │ ├── boolean.sse │ │ ├── datetime.sse │ │ ├── numeric.sse │ │ └── string.sse │ ├── is_blank │ │ └── term.sse │ ├── is_iri │ │ └── term.sse │ ├── is_literal │ │ └── term.sse │ ├── is_uri │ │ └── term.sse │ ├── lang │ │ └── literal.sse │ ├── lang_matches │ │ └── literal.sse │ ├── less_than │ │ ├── boolean.sse │ │ ├── datetime.sse │ │ ├── numeric.sse │ │ └── string.sse │ ├── less_than_or_equal │ │ ├── boolean.sse │ │ ├── datetime.sse │ │ ├── numeric.sse │ │ └── string.sse │ ├── multiply │ │ └── numeric.sse │ ├── negate │ │ └── numeric.sse │ ├── not │ │ └── boolean.sse │ ├── not_equal │ │ ├── boolean.sse │ │ ├── datetime.sse │ │ ├── numeric.sse │ │ ├── string.sse │ │ └── term.sse │ ├── or │ │ └── boolean.sse │ ├── plus │ │ └── numeric.sse │ ├── regex │ │ └── literal.sse │ ├── same_term │ │ └── term.sse │ ├── str │ │ ├── iri.sse │ │ └── literal.sse │ └── subtract │ │ └── numeric.sse ├── operator_spec.rb ├── optimize_spec.rb ├── query_spec.rb ├── rewrite_spec.rb ├── to_sparql_spec.rb └── update_spec.rb ├── cbd_spec.rb ├── fixtures ├── data0.rdf ├── data1.rdf ├── data2.rdf └── data3.rdf ├── grammar ├── examples_spec.rb ├── lexer_spec.rb ├── misc_spec.rb ├── parser_shared_examples.rb └── parser_spec.rb ├── module_spec.rb ├── rack_spec.rb ├── rdfstar_spec.rb ├── readme_spec.rb ├── results_spec.rb ├── sep002_spec.rb ├── sep003_spec.rb ├── server_spec.rb ├── sinatra_spec.rb ├── spec.opts ├── spec_helper.rb ├── suite_helper.rb ├── suite_spec.rb ├── support ├── extensions │ ├── comparitors.rb │ └── isomorphic.rb ├── matchers │ ├── be_one_of.rb │ ├── generate.rb │ ├── have_result_set.rb │ ├── produces.rb │ ├── solutions.rb │ └── xpath_matcher.rb └── models.rb ├── version_spec.rb └── w3c-sparql-dev /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: Jrom81OgSyBklzReIFjUpSMdZbsoPwaFU 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs continuous CI across different versions of ruby on all branches and pull requests to develop. 2 | 3 | name: CI 4 | on: 5 | push: 6 | branches: [ '**' ] 7 | pull_request: 8 | branches: [ develop ] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | tests: 13 | name: Ruby ${{ matrix.ruby }} 14 | if: "contains(github.event.commits[0].message, '[ci skip]') == false" 15 | runs-on: ubuntu-latest 16 | env: 17 | CI: true 18 | ALLOW_FAILURES: ${{ endsWith(matrix.ruby, 'head') }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | ruby: ['3.0', 3.1, 3.2, 3.3, 3.4, ruby-head, jruby] 23 | steps: 24 | - name: Clone repository 25 | uses: actions/checkout@v3 26 | - name: Set up Ruby 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: ${{ matrix.ruby }} 30 | - name: Install dependencies 31 | run: bundle install --jobs 4 --retry 3 32 | - name: Run tests 33 | run: ruby --version; bundle exec rspec spec || $ALLOW_FAILURES 34 | - name: Coveralls GitHub Action 35 | uses: coverallsapp/github-action@v2 36 | if: "matrix.ruby == '3.3'" 37 | with: 38 | github-token: ${{ secrets.GITHUB_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/generate-docs.yml: -------------------------------------------------------------------------------- 1 | name: Build & deploy documentation 2 | on: 3 | push: 4 | branches: 5 | - master 6 | workflow_dispatch: 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | name: Update gh-pages with docs 11 | steps: 12 | - name: Clone repository 13 | uses: actions/checkout@v3 14 | - name: Set up Ruby 15 | uses: ruby/setup-ruby@v1 16 | with: 17 | ruby-version: "3.4" 18 | - name: Install required gem dependencies 19 | run: gem install yard --no-document 20 | - name: Build YARD Ruby Documentation 21 | run: yardoc 22 | - name: Deploy 23 | uses: peaceiris/actions-gh-pages@v3 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ./doc/yard 27 | publish_branch: gh-pages 28 | -------------------------------------------------------------------------------- /.github/workflows/gitstamp.yaml: -------------------------------------------------------------------------------- 1 | # See: https://github.com/artob/gitstamp-action 2 | --- 3 | name: Gitstamp 4 | on: 5 | push: 6 | branches: 7 | - develop 8 | jobs: 9 | gitstamp: 10 | runs-on: ubuntu-latest 11 | name: Timestamp commit with Gitstamp 12 | steps: 13 | - name: Clone repository 14 | uses: actions/checkout@v2 15 | - name: Submit Gitstamp transaction 16 | uses: artob/gitstamp-action@v1 17 | with: 18 | wallet-key: ${{ secrets.GITSTAMP_KEYFILE }} 19 | commit-link: true 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/ 2 | /pkg/ 3 | /.yardoc/ 4 | /.bundle/ 5 | /vendor/ 6 | Gemfile.lock 7 | /.rbx/ 8 | /.pryrc 9 | /coverage/ 10 | *.html 11 | *.haml 12 | *.byebug_history 13 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --title "SPARQL - SPARQL for RDF.rb" 2 | --output-dir doc/yard 3 | --protected 4 | --no-private 5 | --hide-void-return 6 | --markup markdown 7 | --readme README.md 8 | - 9 | AUTHORS 10 | CREDITS 11 | UNLICENSE 12 | VERSION 13 | etc/sparql11.bnf 14 | etc/sparql11.html 15 | etc/earl.html 16 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | * Gregg Kellogg 2 | * Arto Bendiken 3 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | * Pius Uzamere 2 | * Tuan-Nhi Le 3 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 3.3.2 2 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "sparql" 6 | 7 | require "irb" 8 | require 'amazing_print' 9 | IRB.start(__FILE__) 10 | -------------------------------------------------------------------------------- /dependencyci.yml: -------------------------------------------------------------------------------- 1 | platform: 2 | Rubygems: 3 | rdf-isomorphic: 4 | tests: 5 | unmaintained: skip -------------------------------------------------------------------------------- /etc/.gitignore: -------------------------------------------------------------------------------- 1 | /J80-1001.pdf 2 | /SSE.bnf 3 | /ssf-sparql.lisp 4 | /woods-1969-atn-for-nla.pdf 5 | /dydra-update-sse.txt 6 | -------------------------------------------------------------------------------- /etc/README: -------------------------------------------------------------------------------- 1 | This is a collection of individual EARL reports for 2 | test subjects claiming Turtle processor conformance. 3 | 4 | The consolodated report is saved to index.html generated 5 | using the earl-report Ruby gem. Run it as follows: 6 | 7 | gem install earl-report 8 | 9 | earl-report --format json --manifest manifest-cache.nt -o earl.jsonld earl.ttl 10 | earl-report --json --format html --template template.haml -o earl.html earl.jsonld 11 | 12 | To generate the manifest-cache.nt: 13 | 14 | earl-report --format ntriples -o manifest-cache.nt doap.ttl 15 | -------------------------------------------------------------------------------- /etc/from_default.rq: -------------------------------------------------------------------------------- 1 | SELECT * FROM WHERE { ?s ?p ?o } 2 | -------------------------------------------------------------------------------- /etc/input.rq: -------------------------------------------------------------------------------- 1 | SELECT * WHERE { ?s ?p ?o } 2 | -------------------------------------------------------------------------------- /etc/input.sse: -------------------------------------------------------------------------------- 1 | (dataset () (bgp (triple ?s ?p ?o)))) 2 | -------------------------------------------------------------------------------- /examples/annotation-example.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | PREFIX foaf: 3 | PREFIX ex: 4 | 5 | SELECT ?age ?c WHERE { 6 | ?bob foaf:name "Bob" {| ex:certainty ?c |}. 7 | } 8 | -------------------------------------------------------------------------------- /examples/annotation-path.rq: -------------------------------------------------------------------------------- 1 | SELECT * { 2 | ?x :p ?o {| :q/:r ?z |} . 3 | } 4 | -------------------------------------------------------------------------------- /examples/annotation-path2.rq: -------------------------------------------------------------------------------- 1 | SELECT * { 2 | ?x :p ?o {| :q [ rdfs:subClassOf*/rdf:type ?T ] |} . 3 | } 4 | -------------------------------------------------------------------------------- /examples/earl_prof.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path("../../lib", __FILE__)) 3 | 4 | require "bundler/setup" 5 | require 'ruby-prof' 6 | require 'sparql' 7 | require 'rdf/turtle' 8 | require 'fileutils' 9 | graph = RDF::Graph.new 10 | %w(manifest rdf.rb-earl).each {|f| graph.load("./earl-data/#{f}.ttl")} 11 | 12 | query = SPARQL.parse %( 13 | PREFIX dc: 14 | PREFIX mf: 15 | PREFIX rdf: 16 | PREFIX rdfs: 17 | 18 | SELECT ?lh ?uri ?type ?title ?description ?testAction ?testResult ?manUri ?manTitle ?manDescription 19 | WHERE { 20 | ?uri a ?type; 21 | mf:name ?title; 22 | mf:action ?testAction . 23 | OPTIONAL { ?uri rdfs:comment ?description . } 24 | OPTIONAL { ?uri mf:result ?testResult . } 25 | OPTIONAL { 26 | ?manUri a mf:Manifest; mf:entries ?lh . 27 | ?lh rdf:first ?uri . 28 | OPTIONAL { ?manUri mf:name ?manTitle . } 29 | OPTIONAL { ?manUri rdfs:comment ?manDescription . } 30 | } 31 | } 32 | ).freeze 33 | 34 | #class MF < RDF::Vocabulary("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#") 35 | #end 36 | # 37 | #query = RDF::Query.new do 38 | # pattern [:uri, RDF.type, :type] 39 | # pattern [:uri, MF.name, :title] 40 | # pattern [:uri, MF.action, :testAction] 41 | # pattern [:uri, RDF::RDFS.comment, :description], optional: true 42 | # pattern [:uri, MF.result, :testResult], optional: true 43 | # pattern [:manUri, RDF.type, MF.Manifest], optional: true 44 | # pattern [:manUri, MF.entries, :lh], optional: true 45 | # pattern [:lh, RDF.first, :uri], optional: true 46 | # pattern [:manUri, MF.name, :manTitle], optional: true 47 | # pattern [:manUri, RDF::RDFS.comment, :manDescription], optional: true 48 | #end 49 | 50 | output_dir = File.expand_path("../../doc/profiles/#{File.basename __FILE__, ".rb"}", __FILE__) 51 | FileUtils.mkdir_p(output_dir) 52 | 53 | result = RubyProf.profile do 54 | query.execute(graph) do |solution| 55 | solution[:uri] 56 | end 57 | end 58 | 59 | # Print a graph profile to text 60 | printer = RubyProf::MultiPrinter.new(result) 61 | printer.print(path: output_dir, profile: "profile") 62 | puts "output saved in #{output_dir}" 63 | -------------------------------------------------------------------------------- /examples/function_call.sse: -------------------------------------------------------------------------------- 1 | (prefix ((: ) 2 | (rdf: ) 3 | (xsd: )) 4 | (project (?a ?v ?boolean) 5 | (extend ((?boolean (xsd:boolean ?v))) 6 | (bgp (triple ?a :p ?v))))) -------------------------------------------------------------------------------- /examples/group_by.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdf: 2 | SELECT (SUM(?val) AS ?sum) (COUNT(?a) AS ?count) 3 | WHERE { 4 | ?a rdf:value ?val . 5 | } GROUP BY ?a 6 | -------------------------------------------------------------------------------- /examples/group_by.sse: -------------------------------------------------------------------------------- 1 | (prefix ((rdf: )) 2 | (project (?sum ?count) 3 | (extend ((?sum ??.0) (?count ?.1)) 4 | (group (?a) ((??.0 (sum ?val)) (?.1 (count ?a))) 5 | (bgp (triple ?a rdf:value ?val)))))) 6 | -------------------------------------------------------------------------------- /examples/in_with_uris.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path("../../lib", __FILE__)) 3 | require 'rubygems' 4 | require "bundler/setup" 5 | require 'rdf' 6 | require 'sparql' 7 | require 'rdf/turtle' 8 | 9 | sse = %q( 10 | (prefix ( 11 | (rdf: ) 12 | (rdfs: )) 13 | (project (?o) 14 | (filter (in ?o rdf:Property rdfs:Class rdf:Datatype) 15 | (bgp (triple ?s ?p ?o))))) 16 | ) 17 | 18 | rep = RDF::Repository.new << RDF::Turtle::Reader.new(%{ 19 | @prefix rdf: . 20 | @prefix rdfs: . 21 | _:a a rdf:Property . 22 | _:b a rdfs:Class . 23 | _:c a rdf:Datatype . 24 | }) 25 | query = SPARQL::Algebra.parse(sse) 26 | 27 | solutions = query.execute(rep, debug: true) 28 | 29 | solutions.each_solution do |s| 30 | puts s.to_h 31 | end 32 | -------------------------------------------------------------------------------- /examples/issue-21.sparql: -------------------------------------------------------------------------------- 1 | PREFIX ext: 2 | PREFIX rdf: 3 | 4 | SELECT * 5 | WHERE {?x a ext:Subject; ?prop ?obj} 6 | -------------------------------------------------------------------------------- /examples/issue-25.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdf: 2 | PREFIX owl: 3 | 4 | SELECT ?class (group_concat(DISTINCT ?item;separator=",") as ?keys) WHERE { 5 | ?class rdf:type owl:Class . 6 | ?class owl:hasKey ?key . 7 | ?key rdf:rest*/rdf:first ?item . 8 | } 9 | GROUP BY ?class ?key 10 | -------------------------------------------------------------------------------- /examples/issue11.rb: -------------------------------------------------------------------------------- 1 | require 'linkeddata' 2 | rep = RDF::Repository.new 3 | rep.load("https://raw.github.com/mwkuster/eli-budabe/master/sparql/source.ttl") 4 | query = %{ 5 | PREFIX cdm: 6 | 7 | SELECT DISTINCT ?number ?typedoc ?is_corrigendum (GROUP_CONCAT(?lang_code; separator="-") AS ?langs) 8 | WHERE 9 | { 10 | ?manif cdm:manifestation_official-journal_part_information_number ?number . 11 | ?manif cdm:manifestation_official-journal_part_typedoc_printer ?typedoc . 12 | ?manif cdm:manifestation_official-journal_part_is_corrigendum_printer ?is_corrigendum . 13 | ?manif cdm:manifestation_manifests_expression ?expr . 14 | ?expr cdm:expression_uses_language ?lang . 15 | BIND(lcase(replace(str(?lang), ".*/([A-Z]{3})", "$1")) AS ?lang_code) 16 | } 17 | GROUP BY ?number ?typedoc ?is_corrigendum 18 | } 19 | s = SPARQL.execute(query, rep) 20 | -------------------------------------------------------------------------------- /examples/issue27.rq: -------------------------------------------------------------------------------- 1 | PREFIX simc: 2 | PREFIX rdf: 3 | 4 | SELECT ?r ?f WHERE { 5 | simc:Collection rdf:rest* ?r . 6 | ?r rdf:first ?f 7 | } 8 | -------------------------------------------------------------------------------- /examples/issue3.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rdf' 4 | require 'sparql' 5 | 6 | class TestRepo < RDF::Repository 7 | def query(pattern, &block) 8 | #puts "received pattern #{pattern.inspect}" 9 | 10 | statements = [] 11 | if pattern[:predicate].path == '/attribute_types/first_name' 12 | statements << RDF::Statement.new( 13 | :subject => RDF::URI.new('http://localhost/people/1'), 14 | predicate: RDF::URI.new('http://localhost/attribute_types/first_name'), 15 | :object => RDF::Literal.new('joe')) 16 | elsif pattern[:predicate].path == '/attribute_types/last_name' 17 | statements << RDF::Statement.new( 18 | :subject => RDF::URI.new('http://localhost/people/1'), 19 | predicate: RDF::URI.new('http://localhost/attribute_types/last_name'), 20 | :object => RDF::Literal.new('smith')) 21 | elsif pattern[:predicate].path == '/attribute_types/middle_name' 22 | statements << RDF::Statement.new( 23 | :subject => RDF::URI.new('http://localhost/people/2'), 24 | predicate: RDF::URI.new('http://localhost/attribute_types/middle_name'), 25 | :object => RDF::Literal.new('blah')) 26 | 27 | statements << RDF::Statement.new( 28 | :subject => RDF::URI.new('http://localhost/people/1'), 29 | predicate: RDF::URI.new('http://localhost/attribute_types/middle_name'), 30 | :object => RDF::Literal.new('blah')) 31 | 32 | end 33 | 34 | statements.each(&block) 35 | end 36 | end 37 | 38 | 39 | query = %q( 40 | PREFIX a: 41 | SELECT ?entity 42 | WHERE { 43 | ?entity a:first_name 'joe' . 44 | ?entity a:last_name 'smith' . 45 | OPTIONAL { 46 | ?entity a:middle_name 'blah' 47 | } 48 | } 49 | ) 50 | 51 | rep = TestRepo.new(base_url: 'http://localhost') 52 | sse = SPARQL.parse(query) 53 | puts sse.to_sse 54 | 55 | solutions = sse.execute(rep, debug: true) 56 | 57 | solutions.each_solution do |s| 58 | puts s.to_h 59 | end 60 | -------------------------------------------------------------------------------- /examples/issue33.rq: -------------------------------------------------------------------------------- 1 | CONSTRUCT { 2 | ?uri ?anotherURI . 3 | } 4 | WHERE 5 | { 6 | ?uri a ?type ; 7 | / ?anotherURI 8 | } 9 | -------------------------------------------------------------------------------- /examples/issue42-simple.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT * { 3 | ?a :b/:b [ :c :d] . 4 | } 5 | 6 | # => 7 | # (join 8 | # (path ?a (seq ) ??0) 9 | # (bgp (triple ??0 ))) 10 | -------------------------------------------------------------------------------- /examples/issue42.rq: -------------------------------------------------------------------------------- 1 | PREFIX p14: 2 | PREFIX up: 3 | PREFIX rdf: 4 | PREFIX rdfs: 5 | SELECT DISTINCT ?taxonomy ?node_43 ?gene ?node_37 ?structured_name ?node_27 ?journal_citation ?catalytic_activity_annotation ?function ?node_0 6 | WHERE { 7 | ?node_0 rdfs:seeAlso ?taxonomy . 8 | ?node_0 rdfs:seeAlso/rdfs:seeAlso [ 9 | up:recommendedName ?structured_name ; 10 | up:mnemonic ?node_27 ; 11 | up:encodedBy ?gene ; 12 | up:citation ?journal_citation ; 13 | up:annotation ?catalytic_activity_annotation ; 14 | up:annotation ?function 15 | ] . 16 | ?gene rdf:type up:Gene . 17 | ?gene up:orfName ?node_37 . 18 | ?taxonomy p14:genbankCommonName ?node_43 . 19 | } 20 | LIMIT 100 -------------------------------------------------------------------------------- /examples/issue7.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | require 'rdf' 5 | require 'rdf/turtle' 6 | require 'sparql' 7 | 8 | graph = RDF::Graph.new 'urn:graph1' 9 | reader = RDF::Turtle::Reader.new <<'TTL' 10 | @prefix ex: . 11 | 12 | ex:Subject1 a ex:MyClass; 13 | ex:pred1 'Predicate 1'; 14 | ex:pred2 'Predicate 2'; 15 | ex:pred3 'Predicate 3'; 16 | ex:pred4 'Predicate 4'; 17 | ex:pred5 'Predicate 5'; 18 | ex:pred6 'Predicate 6'; 19 | ex:pred7 'Predicate 7'. 20 | TTL 21 | reader.each_statement {|stmt| graph << stmt } 22 | 23 | solutions = ::SPARQL.execute(<<-'QRY', graph) 24 | PREFIX ex: 25 | 26 | SELECT ?subj ?pred ?obj 27 | WHERE { 28 | ?subj ?pred ?obj . { 29 | { ?subj ex:pred1 ?obj } UNION 30 | { ?subj ex:pred2 ?obj } UNION 31 | { ?subj ex:pred3 ?obj } UNION 32 | { ?subj ex:pred4 ?obj } UNION 33 | { ?subj ex:pred5 ?obj } UNION 34 | { ?subj ex:pred6 ?obj } UNION 35 | { ?subj ex:pred7 ?obj } 36 | } 37 | } 38 | QRY 39 | 40 | # Print out solutions 41 | solutions.each do |sln| 42 | puts sln.to_h.inspect 43 | end 44 | 45 | ### Output 46 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 1", :pred=>urn:example#pred1 47 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 2", :pred=>urn:example#pred2 48 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 3", :pred=>urn:example#pred3 49 | 50 | ### Output expected 51 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 1", :pred=>urn:example#pred1 52 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 2", :pred=>urn:example#pred2 53 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 3", :pred=>urn:example#pred3 54 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 4", :pred=>urn:example#pred4 55 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 5", :pred=>urn:example#pred5 56 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 6", :pred=>urn:example#pred6 57 | #> :subj=>urn:example#Subject1, :obj=>"Predicate 7", :pred=>urn:example#pred7 -------------------------------------------------------------------------------- /examples/issue7.rq: -------------------------------------------------------------------------------- 1 | PREFIX ex: 2 | 3 | SELECT ?subj ?pred ?obj 4 | WHERE { 5 | ?subj ?pred ?obj . { 6 | { ?subj ex:pred1 ?obj } UNION 7 | { ?subj ex:pred2 ?obj } UNION 8 | { ?subj ex:pred3 ?obj } UNION 9 | { ?subj ex:pred4 ?obj } UNION 10 | { ?subj ex:pred5 ?obj } UNION 11 | { ?subj ex:pred6 ?obj } UNION 12 | { ?subj ex:pred7 ?obj } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/issue9.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", 'lib'))) 3 | require 'rubygems' 4 | require "bundler/setup" 5 | require 'linkeddata' 6 | 7 | rq = Kernel.open("https://raw.github.com/mwkuster/eli-budabe/master/sparql/eli_md.rq").read 8 | repo = RDF::Repository.load("https://raw.github.com/mwkuster/eli-budabe/master/sparql/source.ttl", format: :ttl) 9 | query = SPARQL.parse(rq) 10 | begin 11 | results = query.execute(repo) 12 | puts results.dump(:ttl, prefixes: { 13 | cdm: "http://publications.europa.eu/ontology/cdm#", 14 | eli: "http://eurlex.europa.eu/eli#", 15 | xsd: "http://www.w3.org/2001/XMLSchema#", 16 | owl: "http://www.w3.org/2002/07/owl#" 17 | }) 18 | rescue Exception => e 19 | puts "Raised error: #{e.inspect}" 20 | end 21 | -------------------------------------------------------------------------------- /examples/issue9_filter.rq: -------------------------------------------------------------------------------- 1 | SELECT * WHERE { 2 | { 3 | ?subj ?p ?o . 4 | FILTER isIRI(?o) 5 | } 6 | { 7 | ?o ?p1 "foo" . 8 | FILTER isIRI(?p1) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/issue9_short.rq: -------------------------------------------------------------------------------- 1 | SELECT * WHERE { 2 | ?subj ?p ?o . 3 | BIND (?o AS ?v) 4 | ?o ?p1 "foo" . 5 | BIND (?p1 AS ?v2) 6 | } 7 | -------------------------------------------------------------------------------- /examples/issue9_short2.rq: -------------------------------------------------------------------------------- 1 | SELECT * WHERE { 2 | ?subj ?p ?o . 3 | OPTIONAL { ?subj ?p2 ?o2 } 4 | BIND ( AS ?eli) 5 | BIND (str(?eli) AS ?eli_str) 6 | ?o ?p1 "foo" . 7 | BIND (IRI(?eli) AS ?expr_eli) 8 | OPTIONAL { 9 | ?manif_cellar_id ?p ?manif . 10 | } 11 | BIND (?p1 AS ?v2) 12 | } 13 | -------------------------------------------------------------------------------- /examples/jcarrol-exists.sparql: -------------------------------------------------------------------------------- 1 | select * 2 | { BIND (1 as ?x) 3 | FILTER EXISTS { 4 | # Uncomment optional block to correct 5 | # OPTIONAL { 6 | # ?x 7 | # } 8 | FILTER EXISTS { 9 | FILTER (?x = 1) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/json-ld-example.sparql: -------------------------------------------------------------------------------- 1 | EFIX dc11: 2 | PREFIX rdf: 3 | PREFIX xsd: 4 | 5 | CONSTRUCT { 6 | ?lib a ex:Library; ex:contains ?book . 7 | ?book a ex:Book; dc:creator ?creator; ?bp ?bo . 8 | ?chapter a ex:Chapter; ?cp ?co . 9 | } 10 | WHERE { 11 | ?lib a ex:Library; ex:contains ?book . 12 | ?book a ex:Book; dc:creator ?creator; ?bp ?bo . 13 | ?chapter a ex:Chapter; ?cp ?co . 14 | } 15 | 16 | -------------------------------------------------------------------------------- /examples/laksa-path-expression.sparql: -------------------------------------------------------------------------------- 1 | PREFIX rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# 2 | PREFIX owl: http://www.w3.org/2002/07/owl# 3 | 4 | SELECT ?class (group_concat(DISTINCT ?item;separator=",") as ?keys) WHERE { 5 | ?class rdf:type owl:Class . 6 | ?class owl:hasKey ?key . 7 | ?key rdf:rest*/rdf:first ?item . 8 | } 9 | GROUP BY ?class ?key 10 | -------------------------------------------------------------------------------- /examples/multiple_joins.rq: -------------------------------------------------------------------------------- 1 | PREFIX ex: 2 | 3 | SELECT ?subj ?pred ?obj 4 | WHERE { 5 | ?subj ?pred ?obj . { 6 | { ?subj ex:pred1 ?obj } 7 | { ?subj ex:pred2 ?obj } 8 | { ?subj ex:pred3 ?obj } 9 | { ?subj ex:pred4 ?obj } 10 | { ?subj ex:pred5 ?obj } 11 | { ?subj ex:pred6 ?obj } 12 | { ?subj ex:pred7 ?obj } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/multiple_options.rq: -------------------------------------------------------------------------------- 1 | PREFIX ex: 2 | 3 | SELECT ?subj ?pred ?obj 4 | WHERE { 5 | ?subj ?pred ?obj . { 6 | { ?subj ex:pred1 ?obj } OPTIONAL 7 | { ?subj ex:pred2 ?obj } OPTIONAL 8 | { ?subj ex:pred3 ?obj } OPTIONAL 9 | { ?subj ex:pred4 ?obj } OPTIONAL 10 | { ?subj ex:pred5 ?obj } OPTIONAL 11 | { ?subj ex:pred6 ?obj } OPTIONAL 12 | { ?subj ex:pred7 ?obj } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/path2.rq: -------------------------------------------------------------------------------- 1 | SELECT * { 2 | ?x :rdfs:subClassOf*/rdf:type ?T . 3 | } 4 | -------------------------------------------------------------------------------- /examples/patteren-involving-bind.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | 3 | SELECT * WHERE 4 | { 5 | ?s :p ?v . 6 | BIND (2*?v AS ?v2) . 7 | ?s :p1 ?v2 . 8 | } 9 | -------------------------------------------------------------------------------- /examples/pp12.rq: -------------------------------------------------------------------------------- 1 | SELECT * { 2 | ?x foaf:mbox . 3 | ?x foaf:knows{1,2}/foaf:name ?name . 4 | } 5 | -------------------------------------------------------------------------------- /examples/pp2.rq: -------------------------------------------------------------------------------- 1 | SELECT * { 2 | ?x foaf:mbox . 3 | ?x foaf:knows{2}/foaf:name ?name . 4 | } 5 | -------------------------------------------------------------------------------- /examples/pp2_.rq: -------------------------------------------------------------------------------- 1 | SELECT * { 2 | ?x foaf:mbox . 3 | ?x foaf:knows{2,}/foaf:name ?name . 4 | } 5 | -------------------------------------------------------------------------------- /examples/pp_2.rq: -------------------------------------------------------------------------------- 1 | SELECT * { 2 | ?x foaf:mbox . 3 | ?x foaf:knows{,2}/foaf:name ?name . 4 | } 5 | -------------------------------------------------------------------------------- /examples/query_bench.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path("../../lib", __FILE__)) 3 | 4 | require "bundler/setup" 5 | require 'benchmark' 6 | require 'sparql' 7 | require 'rdf/turtle' 8 | graph = RDF::Graph.load("etc/doap.ttl") 9 | query = nil 10 | 11 | Benchmark.bmbm do |bench| 12 | bench.report("sparql parse") do 13 | 100_000.times do 14 | query = SPARQL.parse %( 15 | PREFIX foaf: <> 16 | SELECT ?name 17 | WHERE { 18 | [foaf:name ?name] 19 | } 20 | ) 21 | end 22 | end 23 | end 24 | 25 | Benchmark.bmbm do |bench| 26 | bench.report("sparql execute") do 27 | 100_000.times do 28 | graph.query(query) {} 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /examples/query_prof.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path("../../lib", __FILE__)) 3 | 4 | require "bundler/setup" 5 | require 'ruby-prof' 6 | require 'sparql' 7 | require 'rdf/turtle' 8 | require 'fileutils' 9 | graph = RDF::Graph.load("etc/doap.ttl") 10 | query = nil 11 | 12 | output_dir = File.expand_path("../../doc/profiles/#{File.basename __FILE__, ".rb"}", __FILE__) 13 | FileUtils.mkdir_p(output_dir) 14 | 15 | result = RubyProf.profile do 16 | 1000.times do 17 | query = SPARQL.parse %( 18 | PREFIX foaf: <> 19 | SELECT ?name 20 | WHERE { 21 | [foaf:name ?name] 22 | } 23 | ) 24 | end 25 | end 26 | 27 | # Print a graph profile to text 28 | printer = RubyProf::MultiPrinter.new(result) 29 | printer.print(path: output_dir, profile: "profile") 30 | puts "output saved in #{output_dir}" 31 | -------------------------------------------------------------------------------- /examples/rdfa-test-0272.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", 'lib'))) 3 | require 'rubygems' 4 | require "bundler/setup" 5 | require 'linkeddata' 6 | 7 | query = SPARQL.parse %{ 8 | PREFIX rdf: 9 | PREFIX xsd: 10 | ASK WHERE { 11 | [ rdf:value "2012-03-18"^^xsd:date ] . 12 | } 13 | } 14 | 15 | repo = RDF::Graph.new << RDF::Turtle::Reader.new(%{ 16 | PREFIX rdf: 17 | PREFIX xsd: 18 | rdf:value "2012-03-18"^^xsd:date . 19 | }) 20 | 21 | begin 22 | results = query.execute(repo, debug: true) 23 | puts "Returned #{results.inspect}" 24 | rescue Exception => e 25 | puts "Raised error: #{e.inspect}" 26 | end 27 | -------------------------------------------------------------------------------- /examples/rdfstar-bob-bind.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | PREFIX foaf: 3 | 4 | SELECT ?a ?b ?c WHERE { 5 | ?bob foaf:name "Bob" . 6 | BIND( <> AS ?a ) . 7 | ?a ?b ?c . 8 | } 9 | -------------------------------------------------------------------------------- /examples/rdfstar-bob-construct.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | PREFIX foaf: 3 | PREFIX ex: 4 | 5 | CONSTRUCT { 6 | ?bob foaf:name "Bob" . 7 | <> ?b ?c . 8 | } 9 | WHERE { 10 | ?bob foaf:name "Bob" . 11 | <> ?b ?c . 12 | } 13 | -------------------------------------------------------------------------------- /examples/rdfstar-bob-data.nt: -------------------------------------------------------------------------------- 1 | "Bob" . 2 | _:bn <<( "23"^^)>> 3 | _:bn "0.9"^^ . 4 | -------------------------------------------------------------------------------- /examples/rdfstar-bob-data.ttl: -------------------------------------------------------------------------------- 1 | @prefix : . 2 | @prefix foaf: . 3 | @prefix ex: . 4 | 5 | :bob foaf:name "Bob" . 6 | <<:bob foaf:age 23>> 0.9 . 7 | -------------------------------------------------------------------------------- /examples/rdfstar-bob.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | PREFIX foaf: 3 | PREFIX ex: 4 | 5 | SELECT ?age ?c WHERE { 6 | ?bob foaf:name "Bob" . 7 | <> ex:certainty ?c . 8 | } 9 | -------------------------------------------------------------------------------- /examples/rdfstar-issue76.rq: -------------------------------------------------------------------------------- 1 | SELECT * 2 | WHERE { 3 | << ?s:marriedTo :Beth >> :at :Kelby . 4 | FILTER (?s = :Peter) . 5 | BIND (<< ?s:marriedTo :Beth >> as ?r) . 6 | } 7 | -------------------------------------------------------------------------------- /examples/reto-shared-variables.sparql: -------------------------------------------------------------------------------- 1 | select ?x ?y where { 2 | values ?x { 1 2 } 3 | OPTIONAL { 4 | select ?y where { 5 | { 6 | values ?y { 5 6 } 7 | } UNION { 8 | bind (?x as ?y) 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/star-example.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | PREFIX foaf: 3 | PREFIX ex: 4 | 5 | SELECT ?age ?c WHERE { 6 | ?bob foaf:name "Bob" . 7 | <> ex:certainty ?c . 8 | } 9 | -------------------------------------------------------------------------------- /lib/rack/sparql.rb: -------------------------------------------------------------------------------- 1 | require 'rack' 2 | begin 3 | require 'linkeddata' 4 | rescue LoadError => e 5 | require 'rdf/ntriples' 6 | end 7 | require 'sparql' 8 | 9 | module Rack 10 | module SPARQL 11 | autoload :ContentNegotiation, 'rack/sparql/conneg' 12 | 13 | ## 14 | # Registers all known RDF formats with Rack's MIME types registry. 15 | # 16 | # Registers both known file extensions and format symbols. 17 | # 18 | # @param [Hash{Symbol => Object}] options 19 | # @option options [Boolean] :overwrite (false) 20 | # @return [void] 21 | def self.register_mime_types!(**options) 22 | if defined?(Rack::Mime::MIME_TYPES) 23 | RDF::Format.each do |format| 24 | if !Rack::Mime::MIME_TYPES.has_key?(file_ext = ".#{format.to_sym}") || options[:overwrite] 25 | Rack::Mime::MIME_TYPES.merge!(file_ext => format.content_type.first) 26 | end 27 | end 28 | RDF::Format.file_extensions.each do |file_ext, formats| 29 | if !Rack::Mime::MIME_TYPES.has_key?(file_ext = ".#{file_ext}") || options[:overwrite] 30 | Rack::Mime::MIME_TYPES.merge!(file_ext => formats.first.content_type.first) 31 | end 32 | end 33 | end 34 | end 35 | end 36 | end 37 | 38 | Rack::SPARQL.register_mime_types! 39 | -------------------------------------------------------------------------------- /lib/sinatra/sparql/extensions.rb: -------------------------------------------------------------------------------- 1 | # Patch Sinatra::Response#finish to not calculate Content-Length unless 2 | # all members of an array are strings 3 | class Sinatra::Response 4 | def finish 5 | if status.to_i / 100 == 1 6 | headers.delete "Content-Length" 7 | headers.delete "Content-Type" 8 | elsif RDF::Query::Solutions === body 9 | # Don't calculate content-length here 10 | end 11 | 12 | # Rack::Response#finish sometimes returns self as response body. We don't want that. 13 | status, headers, result = super 14 | result = body if self == result 15 | [status, headers, result] 16 | end 17 | end -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/abs.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `abs` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'ABS' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT * WHERE { 11 | # ?s :num ?num 12 | # FILTER(ABS(?num) >= 2) 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix ((: )) 17 | # (filter (>= (abs ?num) 2) 18 | # (bgp (triple ?s :num ?num)))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#func-abs 21 | # @see https://www.w3.org/TR/xpath-functions/#func-abs 22 | class Abs < Operator::Unary 23 | include Evaluatable 24 | 25 | NAME = [:abs] 26 | 27 | ## 28 | # Returns the absolute value of `arg`. An error is raised if `arg` is not a numeric value. 29 | # 30 | # @param [RDF::Literal] operand 31 | # the operand 32 | # @return [RDF::Literal] literal of same type 33 | # @raise [TypeError] if the operand is not a numeric value 34 | def apply(operand, **options) 35 | case operand 36 | when RDF::Literal::Numeric then operand.abs 37 | else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}" 38 | end 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "ABS(#{operands.first.to_sparql(**options)})" 48 | end 49 | end # Abs 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/asc.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL ascending sort operator. 5 | # 6 | # [24] OrderCondition ::= ( ( 'ASC' | 'DESC' ) BrackettedExpression ) | ( Constraint | Var ) 7 | # 8 | # @example SPARQL Query 9 | # PREFIX foaf: 10 | # SELECT ?name 11 | # WHERE { ?x foaf:name ?name } 12 | # ORDER BY ASC(?name) 13 | # 14 | # @example SSE 15 | # (prefix ((foaf: )) 16 | # (project (?name) 17 | # (order ((asc ?name)) 18 | # (bgp (triple ?x foaf:name ?name))))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#modOrderBy 21 | class Asc < Operator::Unary 22 | include Evaluatable 23 | 24 | NAME = :asc 25 | 26 | ## 27 | # Returns the evaluation of its operand. Default comparison is in 28 | # ascending order. Ordering is applied in {Order}. 29 | # 30 | # @param [RDF::Query::Solution] bindings 31 | # a query solution containing zero or more variable bindings 32 | # @param [Hash{Symbol => Object}] options ({}) 33 | # options passed from query 34 | # @return [RDF::Term] 35 | def evaluate(bindings, **options) 36 | operand(0).evaluate(bindings, **options.merge(depth: options[:depth].to_i + 1)) 37 | end 38 | 39 | ## 40 | # 41 | # Returns a partial SPARQL grammar for this operator. 42 | # 43 | # Provides order to descendant query. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "ASC(#{operands.last.to_sparql(**options)})" 48 | end 49 | end # Asc 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/ask.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL GraphPattern `ask` operator. 5 | # 6 | # Applications can use the ASK form to test whether or not a query pattern has a solution. No information is returned about the possible query solutions, just whether or not a solution exists. 7 | # 8 | # [12] AskQuery ::= 'ASK' DatasetClause* WhereClause ValuesClause 9 | # 10 | # @example SPARQL Query 11 | # PREFIX : 12 | # ASK { :x :p ?x } 13 | # 14 | # @example SSE 15 | # (prefix ((: )) 16 | # (ask 17 | # (bgp (triple :x :p ?x)))) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-query/#ask 20 | class Ask < Operator::Unary 21 | include Query 22 | 23 | NAME = [:ask] 24 | 25 | ## 26 | # Executes this query on the given `queryable` graph or repository. 27 | # Returns true if any solutions are found, false otherwise. 28 | # 29 | # @param [RDF::Queryable] queryable 30 | # the graph or repository to query 31 | # @param [Hash{Symbol => Object}] options 32 | # any additional keyword options 33 | # @yield [RDF::Literal::Boolean] 34 | # @yieldparam [RDF::Query::Solution] solution 35 | # @yieldreturn [void] ignored 36 | # @return [RDF::Literal::Boolean]\ 37 | # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra 38 | def execute(queryable, **options) 39 | debug(options) {"Ask #{operands.first}"} 40 | res = boolean(!queryable.query(operands.last, **options.merge(depth: options[:depth].to_i + 1)).empty?) 41 | yield res if block_given? 42 | res 43 | end 44 | 45 | # Query results in a boolean result (e.g., ASK) 46 | # @return [Boolean] 47 | def query_yields_boolean? 48 | true 49 | end 50 | 51 | ## 52 | # 53 | # Returns a partial SPARQL grammar for this term. 54 | # 55 | # @return [String] 56 | def to_sparql(**options) 57 | "ASK\n" + 58 | operands.first.to_sparql(top_level: true, project: nil, **options) 59 | end 60 | end # Ask 61 | end # Operator 62 | end; end # SPARQL::Algebra 63 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/avg.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `avg` set function. 5 | # 6 | # [127] Aggregate::= ... | 'AVG' '(' 'DISTINCT'? Expression ')' 7 | # 8 | # @example SPARQL Query 9 | # PREFIX : 10 | # SELECT (AVG(?o) AS ?avg) 11 | # WHERE { ?s :dec ?o } 12 | # 13 | # @example SSE 14 | # (prefix ((: )) 15 | # (project (?avg) 16 | # (extend ((?avg ??.0)) 17 | # (group () ((??.0 (avg ?o))) 18 | # (bgp (triple ?s :dec ?o)))))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#defn_aggAvg 21 | class Avg < Operator 22 | include Aggregate 23 | 24 | NAME = :avg 25 | 26 | def initialize(*operands, **options) 27 | raise ArgumentError, 28 | "avg operator accepts at most one argument with an optional :distinct" if 29 | (operands - %i{distinct}).length != 1 30 | super 31 | end 32 | 33 | ## 34 | # The Avg set function calculates the average value for an expression over a group. It is defined in terms of Sum and Count. 35 | # 36 | # @param [Enumerable>] enum 37 | # enum of evaluated operand 38 | # @return [RDF::Literal::Numeric] The numeric average of the terms 39 | def apply(enum, **options) 40 | # FIXME: we don't actually do anything with distinct 41 | operands.shift if distinct = (operands.first == :distinct) 42 | if enum.empty? 43 | RDF::Literal(0) 44 | elsif enum.flatten.all? {|n| n.is_a?(RDF::Literal::Numeric)} 45 | enum.flatten.reduce(:+) / RDF::Literal::Decimal.new(enum.length) 46 | else 47 | raise TypeError, "Averaging non-numeric types: #{enum.flatten}" 48 | end 49 | end 50 | 51 | ## 52 | # 53 | # Returns a partial SPARQL grammar for this operator. 54 | # 55 | # @return [String] 56 | def to_sparql(**options) 57 | distinct = operands.first == :distinct 58 | args = distinct ? operands[1..-1] : operands 59 | "AVG(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})" 60 | end 61 | end # Avg 62 | end # Operator 63 | end; end # SPARQL::Algebra 64 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/bgp.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL GraphPattern `bgp` operator. 5 | # 6 | # Query with `graph_name` set to false. 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT * { ?s ?p ?o } 11 | # 12 | # @example SSE 13 | # (prefix ((: )) 14 | # (bgp (triple ?s ?p ?o))) 15 | # 16 | # @example SPARQL Grammar (sparql-star) 17 | # PREFIX : 18 | # SELECT * {<< :a :b :c ~ :r >> :p1 :o1.} 19 | # 20 | # @example SSE (sparql-star) 21 | # (prefix 22 | # ((: )) 23 | # (bgp 24 | # (triple :r 25 | # (qtriple :a :b :c)) 26 | # (triple :r :p1 :o1))) 27 | # 28 | # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra 29 | class BGP < Operator 30 | NAME = [:bgp] 31 | ## 32 | # A `graph` is an RDF::Query with a graph_name. 33 | # 34 | # @overload self.new(*patterns) 35 | # @param [Array] patterns 36 | # @yield [solution] 37 | # each matching solution 38 | # @yieldparam [RDF::Query::Solution] solution 39 | # @yieldreturn [void] ignored 40 | # @return [RDF::Query] 41 | def self.new(*patterns, **options, &block) 42 | RDF::Query.new(*patterns, graph_name: false, &block) 43 | end 44 | end # BGP 45 | end # Operator 46 | end; end # SPARQL::Algebra 47 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/ceil.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `ceil` operator. 5 | # 6 | # [121] BuiltInCall ::= ... 'CEIL' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # PREFIX xsd: 11 | # SELECT ?s ?num (CEIL(?num) AS ?ceil) WHERE { 12 | # ?s :num ?num 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix 17 | # ((: ) 18 | # (xsd: )) 19 | # (project (?s ?num ?ceil) 20 | # (extend ((?ceil (ceil ?num))) 21 | # (bgp (triple ?s :num ?num))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-ceil 24 | # @see https://www.w3.org/TR/xpath-functions/#func-ceil 25 | class Ceil < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = [:ceil] 29 | 30 | ## 31 | # Returns the smallest (closest to negative infinity) number with no fractional part that is not less than the value of `arg`. An error is raised if `arg` is not a numeric value. 32 | # 33 | # @param [RDF::Literal] operand 34 | # the operand 35 | # @return [RDF::Literal] literal of same type 36 | # @raise [TypeError] if the operand is not a numeric value 37 | def apply(operand, **options) 38 | case operand 39 | when RDF::Literal::Numeric then operand.ceil 40 | else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}" 41 | end 42 | end 43 | 44 | ## 45 | # 46 | # Returns a partial SPARQL grammar for this operator. 47 | # 48 | # @return [String] 49 | def to_sparql(**options) 50 | "CEIL(#{operands.to_sparql(**options)})" 51 | end 52 | end # Ceil 53 | end # Operator 54 | end; end # SPARQL::Algebra 55 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/create.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | 4 | ## 5 | # The SPARQL UPDATE `create` operator. 6 | # 7 | # This operation creates a graph in the Graph Store 8 | # 9 | # This is a no-op for RDF.rb implementations, unless the graph exists 10 | # 11 | # [34] Create ::= 'CREATE' 'SILENT'? GraphRef 12 | # 13 | # @example SPARQL Grammar 14 | # CREATE SILENT GRAPH 15 | # 16 | # @example SSE 17 | # (update (create silent )) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-update/#create 20 | class Create < Operator 21 | include SPARQL::Algebra::Update 22 | 23 | NAME = [:create] 24 | 25 | ## 26 | # Executes this upate on the given `writable` graph or repository. 27 | # 28 | # @param [RDF::Queryable] queryable 29 | # the graph or repository to write 30 | # @param [Hash{Symbol => Object}] options 31 | # any additional keyword options 32 | # @option options [Boolean] debug 33 | # Query execution debugging 34 | # @return [RDF::Queryable] 35 | # Returns queryable. 36 | # @raise [IOError] 37 | # If `from` does not exist, unless the `silent` operator is present 38 | # @see https://www.w3.org/TR/sparql11-update/ 39 | def execute(queryable, **options) 40 | debug(options) {"Create"} 41 | silent = operands.first == :silent 42 | operands.shift if silent 43 | 44 | iri = operands.first 45 | raise ArgumentError, "clear expected a single IRI" if operands.length != 1 || !iri.is_a?(RDF::URI) 46 | if queryable.has_graph?(iri) 47 | raise IOError, "create operation graph #{iri.to_ntriples} exists" unless silent 48 | end 49 | queryable 50 | end 51 | 52 | ## 53 | # 54 | # Returns a partial SPARQL grammar for this operator. 55 | # 56 | # @return [String] 57 | def to_sparql(**options) 58 | silent = operands.first == :silent 59 | str = "CREATE " 60 | str << "SILENT " if operands.first == :silent 61 | str << "GRAPH " if operands.last.is_a?(RDF::URI) 62 | str << operands.last.to_sparql(**options) 63 | end 64 | end # Create 65 | end # Operator 66 | end; end # SPARQL::Algebra 67 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/datatype.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `datatype` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'DATATYPE' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX xsd: 10 | # PREFIX : 11 | # SELECT ?x ?v 12 | # WHERE 13 | # { ?x :p ?v . 14 | # FILTER ( datatype(?v) = xsd:double ) . 15 | # } 16 | # 17 | # @example SSE 18 | # (prefix ((xsd: ) 19 | # (: )) 20 | # (project (?x ?v) 21 | # (filter (= (datatype ?v) xsd:double) 22 | # (bgp (triple ?x :p ?v))))) 23 | # 24 | # @see https://www.w3.org/TR/sparql11-query/#func-datatype 25 | class Datatype < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = :datatype 29 | 30 | ## 31 | # Returns the datatype IRI of the operand. 32 | # 33 | # If the operand is a simple literal, returns a datatype of 34 | # `xsd:string`. 35 | # 36 | # @param [RDF::Literal] literal 37 | # a typed or simple literal 38 | # @return [RDF::URI] the datatype IRI, or `xsd:string` for simple literals 39 | # @raise [TypeError] if the operand is not a typed or simple literal 40 | def apply(literal, **options) 41 | case literal 42 | when RDF::Literal then literal.datatype 43 | else raise TypeError, "expected an RDF::Literal, but got #{literal.inspect}" 44 | end 45 | end 46 | 47 | ## 48 | # 49 | # Returns a partial SPARQL grammar for this operator. 50 | # 51 | # @return [String] 52 | def to_sparql(**options) 53 | "DATATYPE(#{operands.last.to_sparql(**options)})" 54 | end 55 | end # Datatype 56 | end # Operator 57 | end; end # SPARQL::Algebra 58 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/day.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `day` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'DAY' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?s (DAY(?date) AS ?x) WHERE { 11 | # ?s :date ?date 12 | # } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((: )) 17 | # (project (?s ?x) 18 | # (extend ((?x (day ?date))) 19 | # (bgp (triple ?s :date ?date))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#func-day 22 | class Day < Operator::Unary 23 | include Evaluatable 24 | 25 | NAME = :day 26 | 27 | ## 28 | # Returns the day part of `arg` as an integer. 29 | # 30 | # @param [RDF::Literal] operand 31 | # the operand 32 | # @return [RDF::Literal::Temporal] 33 | # @raise [TypeError] if the operand is not a simple literal 34 | def apply(operand, **options) 35 | raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal) 36 | RDF::Literal(operand.object.day) 37 | end 38 | 39 | ## 40 | # 41 | # Returns a partial SPARQL grammar for this operator. 42 | # 43 | # @return [String] 44 | def to_sparql(**options) 45 | "DAY(#{operands.last.to_sparql(**options)})" 46 | end 47 | end # Day 48 | end # Operator 49 | end; end # SPARQL::Algebra 50 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/delete_data.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | 4 | ## 5 | # The SPARQL UPDATE `deleteData` operator. 6 | # 7 | # The DELETE DATA operation removes some triples, given inline in the request, if the respective graphs in the Graph Store contain those 8 | # 9 | # [39] DeleteData ::= 'DELETE DATA' QuadData 10 | # 11 | # @example SPARQL Grammar 12 | # PREFIX : 13 | # PREFIX foaf: 14 | # DELETE DATA { 15 | # :a foaf:knows :b . 16 | # } 17 | # 18 | # @example SSE 19 | # (prefix ((: ) (foaf: )) 20 | # (update (deleteData ((triple :a foaf:knows :b))))) 21 | # 22 | # @see https://www.w3.org/TR/sparql11-update/#deleteData 23 | class DeleteData < Operator::Unary 24 | include SPARQL::Algebra::Update 25 | 26 | NAME = [:deleteData] 27 | 28 | ## 29 | # Executes this upate on the given `writable` graph or repository. 30 | # 31 | # @param [RDF::Queryable] queryable 32 | # the graph or repository to write 33 | # @param [Hash{Symbol => Object}] options 34 | # any additional keyword options 35 | # @option options [Boolean] debug 36 | # Query execution debugging 37 | # @return [RDF::Queryable] 38 | # Returns queryable. 39 | # @raise [IOError] 40 | # If `from` does not exist, unless the `silent` operator is present 41 | # @see https://www.w3.org/TR/sparql11-update/ 42 | def execute(queryable, **options) 43 | operand.each do |op| 44 | debug(options) {"DeleteData #{op.to_sxp}"} 45 | queryable.delete(op) 46 | end 47 | queryable 48 | end 49 | 50 | ## 51 | # 52 | # Returns a partial SPARQL grammar for this term. 53 | # 54 | # @return [String] 55 | def to_sparql(**options) 56 | "DELETE DATA {\n" + 57 | operands.first.to_sparql(top_level: false, delimiter: ". \n", **options) + 58 | "\n}" 59 | end 60 | end # DeleteData 61 | end # Operator 62 | end; end # SPARQL::Algebra 63 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/desc.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL descending sort operator. 5 | # 6 | # [24] OrderCondition ::= ( ( 'ASC' | 'DESC' ) BrackettedExpression ) | ( Constraint | Var ) 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX foaf: 10 | # SELECT ?name 11 | # WHERE { ?x foaf:name ?name } 12 | # ORDER BY DESC(?name) 13 | # 14 | # @example SSE 15 | # (prefix ((foaf: )) 16 | # (project (?name) 17 | # (order ((desc ?name)) 18 | # (bgp (triple ?x foaf:name ?name))))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#modOrderBy 21 | class Desc < Operator::Asc 22 | NAME = :desc 23 | 24 | ## 25 | # 26 | # Returns a partial SPARQL grammar for this operator. 27 | # 28 | # Provides order to descendant query. 29 | # 30 | # @return [String] 31 | def to_sparql(**options) 32 | "DESC(#{operands.last.to_sparql(**options)})" 33 | end 34 | end # Desc 35 | end # Operator 36 | end; end # SPARQL::Algebra 37 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/distinct.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL GraphPattern `distinct` operator. 5 | # 6 | # [9] SelectClause ::= 'SELECT' ( 'DISTINCT' | 'REDUCED' )? ( ( Var | ( '(' Expression 'AS' Var ')' ) )+ | '*' ) 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # PREFIX xsd: 11 | # SELECT DISTINCT ?v 12 | # WHERE { ?x ?p ?v } 13 | # 14 | # @example SSE 15 | # (prefix ((: ) 16 | # (xsd: )) 17 | # (distinct 18 | # (project (?v) 19 | # (bgp (triple ?x ?p ?v))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#sparqlDistinct 22 | class Distinct < Operator::Unary 23 | include Query 24 | 25 | NAME = [:distinct] 26 | 27 | ## 28 | # Executes this query on the given `queryable` graph or repository. 29 | # Removes duplicate solutions from the solution set. 30 | # 31 | # @param [RDF::Queryable] queryable 32 | # the graph or repository to query 33 | # @param [Hash{Symbol => Object}] options 34 | # any additional keyword options 35 | # @yield [solution] 36 | # each matching solution 37 | # @yieldparam [RDF::Query::Solution] solution 38 | # @yieldreturn [void] ignored 39 | # @return [RDF::Query::Solutions] 40 | # the resulting solution sequence 41 | # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra 42 | def execute(queryable, **options, &block) 43 | @solutions = queryable.query(operands.last, **options.merge(depth: options[:depth].to_i + 1)).distinct 44 | @solutions.each(&block) if block_given? 45 | @solutions 46 | end 47 | 48 | ## 49 | # 50 | # Returns a partial SPARQL grammar for this operator. 51 | # 52 | # @return [String] 53 | def to_sparql(**options) 54 | operands.first.to_sparql(distinct: true, **options) 55 | end 56 | end # Distinct 57 | end # Operator 58 | end; end # SPARQL::Algebra 59 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/encode_for_uri.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # The SPARQL logical `abs` operator. 7 | # 8 | # [121] BuiltInCall ::= ... | 'ENCODE_FOR_URI' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT ?s ?str (ENCODE_FOR_URI(?str) AS ?encoded) WHERE { 13 | # ?s :str ?str 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix ((: )) 18 | # (project (?s ?str ?encoded) 19 | # (extend ((?encoded (encode_for_uri ?str))) 20 | # (bgp (triple ?s :str ?str))))) 21 | # 22 | # @see https://www.w3.org/TR/sparql11-query/#func-encode 23 | # @see https://www.w3.org/TR/xpath-functions/#func-abs 24 | class EncodeForURI < Operator::Unary 25 | include Evaluatable 26 | 27 | NAME = :encode_for_uri 28 | 29 | ## 30 | # The `ENCODE_FOR_URI` function corresponds to the XPath fn:encode-for-uri function. It returns a simple literal with the lexical form obtained from the lexical form of its input after translating reserved characters according to the fn:encode-for-uri function. 31 | # 32 | # @example 33 | # encode_for_uri("Los Angeles") "Los%20Angeles" 34 | # encode_for_uri("Los Angeles"@en) "Los%20Angeles" 35 | # encode_for_uri("Los Angeles"^^xsd:string) "Los%20Angeles" 36 | # 37 | # @param [RDF::Literal] operand 38 | # the operand 39 | # @return [RDF::Literal] literal of same type 40 | # @raise [TypeError] if the operand is not a literal value 41 | def apply(operand, **options) 42 | case operand 43 | when RDF::Literal then RDF::Literal(CGI.escape(operand.to_s)) 44 | else raise TypeError, "expected an RDF::Literal, but got #{operand.inspect}" 45 | end 46 | end 47 | 48 | ## 49 | # 50 | # Returns a partial SPARQL grammar for this operator. 51 | # 52 | # @return [String] 53 | def to_sparql(**options) 54 | "ENCODE_FOR_URI(#{operands.last.to_sparql(**options)})" 55 | end 56 | end # EncodeForURI 57 | end # Operator 58 | end; end # SPARQL::Algebra 59 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/equal.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL relational `=` (equal) comparison operator. 5 | # 6 | # [114] RelationalExpression ::= NumericExpression ('=' NumericExpression)? 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX xsd: 10 | # PREFIX : 11 | # SELECT ?x 12 | # WHERE { ?x :p ?v . FILTER ( ?v = 1 ) } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((xsd: ) (: )) 17 | # (project (?x) (filter (= ?v 1) (bgp (triple ?x :p ?v))))) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-query/#OperatorMapping 20 | # @see https://www.w3.org/TR/sparql11-query/#func-RDFterm-equal 21 | class Equal < Compare 22 | NAME = :'=' 23 | 24 | ## 25 | # Returns TRUE if `term1` and `term2` are the same RDF term as defined in Resource Description Framework (RDF): Concepts and Abstract Syntax [CONCEPTS]; produces a type error if the arguments are both literal but are not the same RDF term *; returns FALSE otherwise. `term1` and `term2` are the same if any of the following is true: 26 | # 27 | # * term1 and term2 are equivalent IRIs as defined in 6.4 RDF URI References of [CONCEPTS]. 28 | # * term1 and term2 are equivalent literals as defined in 6.5.1 Literal Equality of [CONCEPTS]. 29 | # * term1 and term2 are the same blank node as described in 6.6 Blank Nodes of [CONCEPTS]. 30 | # 31 | # @param [RDF::Term] term1 32 | # an RDF term 33 | # @param [RDF::Term] term2 34 | # an RDF term 35 | # @return [RDF::Literal::Boolean] `true` or `false` 36 | # @raise [TypeError] if either operand is not an RDF term or operands are not comperable 37 | # 38 | # @see RDF::Term#== 39 | def apply(term1, term2, **options) 40 | term1 = term1.dup.extend(RDF::TypeCheck) 41 | term2 = term2.dup.extend(RDF::TypeCheck) 42 | RDF::Literal(term1 == term2) 43 | end 44 | end # Equal 45 | end # Operator 46 | end; end # SPARQL::Algebra 47 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/exprlist.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL GraphPattern `exprlist` operator. 5 | # 6 | # Used for filters with more than one expression. 7 | # 8 | # [72] ExpressionList ::= NIL | '(' Expression ( ',' Expression )* ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # 13 | # SELECT ?v ?w 14 | # { 15 | # FILTER (?v = 2) 16 | # FILTER (?w = 3) 17 | # ?s :p ?v . 18 | # ?s :q ?w . 19 | # } 20 | # 21 | # @example SSE 22 | # (prefix ((: )) 23 | # (project (?v ?w) 24 | # (filter (exprlist (= ?v 2) (= ?w 3)) 25 | # (bgp 26 | # (triple ?s :p ?v) 27 | # (triple ?s :q ?w) 28 | # )))) 29 | # 30 | # @see https://www.w3.org/TR/sparql11-query/#evaluation 31 | class Exprlist < Operator 32 | include Evaluatable 33 | 34 | NAME = [:exprlist] 35 | 36 | ## 37 | # Returns `true` if all operands evaluate to `true`. 38 | # 39 | # Note that this operator operates on the effective boolean value 40 | # (EBV) of its operands. 41 | # 42 | # @example 43 | # 44 | # (exprlist (= 1 1) (!= 1 0)) 45 | # 46 | # @param [RDF::Query::Solution] bindings 47 | # a query solution containing zero or more variable bindings 48 | # @param [Hash{Symbol => Object}] options ({}) 49 | # options passed from query 50 | # @return [RDF::Literal::Boolean] `true` or `false` 51 | # @raise [TypeError] if the operands could not be coerced to a boolean literal 52 | def evaluate(bindings, **options) 53 | res = operands.all? {|op| boolean(op.evaluate(bindings, **options.merge(depth: options[:depth].to_i + 1))).true? } 54 | RDF::Literal(res) # FIXME: error handling 55 | end 56 | end # Exprlist 57 | end # Operator 58 | end; end # SPARQL::Algebra 59 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/floor.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `floor` operator. 5 | # 6 | # [121] BuiltInCall ::= ... 'FLOOR' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # PREFIX xsd: 11 | # SELECT ?s ?num (FLOOR(?num) AS ?floor) WHERE { 12 | # ?s :num ?num 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix 17 | # ((: ) 18 | # (xsd: )) 19 | # (project (?s ?num ?floor) 20 | # (extend ((?floor (floor ?num))) 21 | # (bgp (triple ?s :num ?num))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-floor 24 | # @see https://www.w3.org/TR/xpath-functions/#func-floor 25 | class Floor < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = [:floor] 29 | 30 | ## 31 | # Returns the largest (closest to positive infinity) number with no fractional part that is not greater than the value of `arg`. An error is raised if `arg` is not a numeric value. 32 | # 33 | # If type of $arg is one of the four numeric types xs:float, xs:double, xs:decimal or xs:integer the type of the result is the same as the type of $arg. If the type of $arg is a type derived from one of the numeric types, the result is an instance of the base numeric type. 34 | # 35 | # For float and double arguments, if the argument is positive zero, then positive zero is returned. If the argument is negative zero, then negative zero is returned. 36 | # 37 | # @param [RDF::Literal] operand 38 | # the operand 39 | # @return [RDF::Literal] literal of same type 40 | # @raise [TypeError] if the operand is not a numeric value 41 | def apply(operand, **options) 42 | case operand 43 | when RDF::Literal::Numeric then operand.floor 44 | else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}" 45 | end 46 | end 47 | 48 | ## 49 | # 50 | # Returns a partial SPARQL grammar for this operator. 51 | # 52 | # @return [String] 53 | def to_sparql(**options) 54 | "FLOOR(#{operands.to_sparql(**options)})" 55 | end 56 | end # Floor 57 | end # Operator 58 | end; end # SPARQL::Algebra 59 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/function_call.rb: -------------------------------------------------------------------------------- 1 | 2 | module SPARQL; module Algebra 3 | class Operator 4 | ## 5 | # The SPARQL `function_call` operator. 6 | # 7 | # [70] FunctionCall ::= iri ArgList 8 | # 9 | # @example SPARQL Grammar 10 | # PREFIX xsd: 11 | # SELECT * 12 | # WHERE { ?s ?p ?o . FILTER xsd:integer(?o) } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((xsd: )) 17 | # (filter (xsd:integer ?o) 18 | # (bgp (triple ?s ?p ?o)))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#funcex-regex 21 | # @see https://www.w3.org/TR/xpath-functions/#func-matches 22 | class FunctionCall < Operator 23 | include Evaluatable 24 | 25 | NAME = :function_call 26 | 27 | ## 28 | # Invokes the function with the passed arguments. 29 | # 30 | # @param [RDF::IRI] iri 31 | # Identifies the function 32 | # @param [Array] args 33 | # @return [RDF::Term] 34 | def apply(iri, *args, **options) 35 | args = RDF.nil == args.last ? args[0..-2] : args 36 | SPARQL::Algebra::Expression.extension(iri, *args, **options) 37 | end 38 | 39 | ## 40 | # Returns the SPARQL S-Expression (SSE) representation of this expression. 41 | # 42 | # Remove the optional argument. 43 | # 44 | # @return [Array] `self` 45 | # @see https://openjena.org/wiki/SSE 46 | def to_sxp_bin 47 | @operands.map(&:to_sxp_bin) 48 | end 49 | 50 | ## 51 | # 52 | # Returns a partial SPARQL grammar for this operator. 53 | # 54 | # @return [String] 55 | def to_sparql(**options) 56 | iri, args = operands 57 | iri.to_sparql(**options) + 58 | '(' + 59 | args.to_sparql(delimiter: ', ', **options) + 60 | ')' 61 | end 62 | end # FunctionCall 63 | end # Operator 64 | end; end # SPARQL::Algebra 65 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/greater_than.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL relational `>` (greater than) comparison operator. 5 | # 6 | # [114] RelationalExpression ::= NumericExpression ('>' NumericExpression)? 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX xsd: 10 | # PREFIX : 11 | # SELECT ?x 12 | # WHERE { ?x :p ?v . FILTER ( ?v > 1 ) } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((xsd: ) (: )) 17 | # (project (?x) (filter (> ?v 1) (bgp (triple ?x :p ?v))))) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-query/#OperatorMapping 20 | # @see https://www.w3.org/TR/xpath-functions/#func-compare 21 | # @see https://www.w3.org/TR/xpath-functions/#func-numeric-greater-than 22 | # @see https://www.w3.org/TR/xpath-functions/#func-boolean-greater-than 23 | # @see https://www.w3.org/TR/xpath-functions/#func-dateTime-greater-than 24 | class GreaterThan < Compare 25 | NAME = :> 26 | 27 | ## 28 | # Returns `true` if the first operand is greater than the second 29 | # operand; returns `false` otherwise. 30 | # 31 | # @param [RDF::Literal] left 32 | # a literal 33 | # @param [RDF::Literal] right 34 | # a literal 35 | # @return [RDF::Literal::Boolean] `true` or `false` 36 | # @raise [TypeError] if either operand is not a literal 37 | def apply(left, right, **options) 38 | RDF::Literal(super == RDF::Literal(1)) 39 | end 40 | end # GreaterThan 41 | end # Operator 42 | end; end # SPARQL::Algebra 43 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/greater_than_or_equal.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL relational `>=` (greater than or equal) comparison 5 | # operator. 6 | # 7 | # [114] RelationalExpression ::= NumericExpression ('>=' NumericExpression)? 8 | # 9 | # @example SPARQL Grammar 10 | # PREFIX xsd: 11 | # PREFIX : 12 | # SELECT ?x 13 | # WHERE { ?x :p ?v . FILTER ( ?v >= 1 ) } 14 | # 15 | # @example SSE 16 | # (prefix 17 | # ((xsd: ) (: )) 18 | # (project (?x) (filter (>= ?v 1) (bgp (triple ?x :p ?v))))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#OperatorMapping 21 | # @see https://www.w3.org/TR/xpath-functions/#func-compare 22 | # @see https://www.w3.org/TR/xpath-functions/#func-numeric-greater-than 23 | # @see https://www.w3.org/TR/xpath-functions/#func-boolean-greater-than 24 | # @see https://www.w3.org/TR/xpath-functions/#func-dateTime-greater-than 25 | class GreaterThanOrEqual < Compare 26 | NAME = :>= 27 | 28 | ## 29 | # Returns `true` if the first operand is greater than or equal to the 30 | # second operand; returns `false` otherwise. 31 | # 32 | # @param [RDF::Literal] left 33 | # a literal 34 | # @param [RDF::Literal] right 35 | # a literal 36 | # @return [RDF::Literal::Boolean] `true` or `false` 37 | # @raise [TypeError] if either operand is not a literal 38 | def apply(left, right, **options) 39 | RDF::Literal(super >= RDF::Literal(0)) 40 | end 41 | end # GreaterThanOrEqual 42 | end # Operator 43 | end; end # SPARQL::Algebra 44 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/hours.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `hours` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'HOURS' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?s (HOURS(?date) AS ?x) WHERE { 11 | # ?s :date ?date 12 | # } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((: )) 17 | # (project (?s ?x) 18 | # (extend ((?x (hours ?date))) 19 | # (bgp (triple ?s :date ?date))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#func-hours 22 | class Hours < Operator::Unary 23 | include Evaluatable 24 | 25 | NAME = :hours 26 | 27 | ## 28 | # Returns the hours part of `arg` as an integer. The value is as given in the lexical form of the XSD dateTime. 29 | # 30 | # @param [RDF::Literal] operand 31 | # the operand 32 | # @return [RDF::Literal::Temporal] 33 | # @raise [TypeError] if the operand is not a simple literal 34 | def apply(operand, **options) 35 | raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal) 36 | RDF::Literal(operand.object.hour) 37 | end 38 | 39 | ## 40 | # 41 | # Returns a partial SPARQL grammar for this operator. 42 | # 43 | # @return [String] 44 | def to_sparql(**options) 45 | "HOURS(#{operands.last.to_sparql(**options)})" 46 | end 47 | end # Hours 48 | end # Operator 49 | end; end # SPARQL::Algebra 50 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/insert_data.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | 4 | ## 5 | # The SPARQL UPDATE `insertData` operator. 6 | # 7 | # The INSERT DATA operation adds some triples, given inline in the request, into the Graph Store 8 | # 9 | # [38] InsertData ::= 'INSERT DATA' QuadData 10 | # 11 | # @example SPARQL Grammar 12 | # PREFIX : 13 | # INSERT DATA { GRAPH { :s :p :o } } 14 | # 15 | # @example SSE 16 | # (prefix 17 | # ((: )) 18 | # (update 19 | # (insertData ( 20 | # (graph ((triple :s :p :o))))))) 21 | # 22 | # @see https://www.w3.org/TR/sparql11-update/#insertData 23 | class InsertData < Operator::Unary 24 | include SPARQL::Algebra::Update 25 | 26 | NAME = [:insertData] 27 | 28 | ## 29 | # Executes this upate on the given `writable` graph or repository. 30 | # 31 | # @param [RDF::Queryable] queryable 32 | # the graph or repository to write 33 | # @param [Hash{Symbol => Object}] options 34 | # any additional keyword options 35 | # @option options [Boolean] debug 36 | # Query execution debugging 37 | # @return [RDF::Queryable] 38 | # Returns queryable. 39 | # @raise [IOError] 40 | # If `from` does not exist, unless the `silent` operator is present 41 | # @see https://www.w3.org/TR/sparql11-update/ 42 | def execute(queryable, **options) 43 | operand.each do |op| 44 | debug(options) {"InsertData #{op.to_sxp}"} 45 | queryable.insert(op) 46 | end 47 | queryable 48 | end 49 | 50 | ## 51 | # 52 | # Returns a partial SPARQL grammar for this term. 53 | # 54 | # @return [String] 55 | def to_sparql(**options) 56 | "INSERT DATA {\n" + 57 | operands.first.to_sparql(top_level: false, delimiter: ". \n", **options) + 58 | "\n}" 59 | end 60 | end # InsertData 61 | end # Operator 62 | end; end # SPARQL::Algebra 63 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/iri.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `iri` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'IRI' '(' Expression ')' | 'URI' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # BASE 10 | # SELECT (URI("uri") AS ?uri) (IRI("iri") AS ?iri) 11 | # WHERE {} 12 | # 13 | # @example SSE 14 | # (base 15 | # (project (?uri ?iri) 16 | # (extend ((?uri (iri "uri")) (?iri (iri "iri"))) 17 | # (bgp)))) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-query/#func-iri 20 | class IRI < Operator::Unary 21 | include Evaluatable 22 | 23 | NAME = [:iri, :uri] 24 | 25 | ## 26 | # The IRI function constructs an IRI by resolving the string argument (see RFC 3986 and RFC 3987 or any later RFC that superceeds RFC 3986 or RFC 3987). The IRI is resolved against the base IRI of the query and must result in an absolute IRI. 27 | # 28 | # The URI function is a synonym for IRI. 29 | # 30 | # If the function is passed an IRI, it returns the IRI unchanged. 31 | # 32 | # Passing any RDF term other than a simple literal, xsd:string or an IRI is an error. 33 | # 34 | # An implementation may normalize the IRI. 35 | # 36 | # @param [RDF::Term] literal 37 | # a simple literal 38 | # @return [RDF::URI] 39 | # @raise [TypeError] if the operand is not a simple literal 40 | def apply(literal, **options) 41 | raise TypeError, "expected an simple literal, but got #{literal.inspect}" unless literal.literal? && literal.simple? 42 | base = Operator.base_uri || RDF::URI("") 43 | base.join(literal.to_s) 44 | end 45 | 46 | ## 47 | # 48 | # Returns a partial SPARQL grammar for this operator. 49 | # 50 | # @return [String] 51 | def to_sparql(**options) 52 | "IRI(" + operands.last.to_sparql(**options) + ")" 53 | end 54 | 55 | Operator::URI = IRI 56 | end # IRI 57 | end # Operator 58 | end; end # SPARQL::Algebra 59 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/is_blank.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `isBlank` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'isBlank' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?x ?v WHERE { 11 | # ?x :p ?v . 12 | # FILTER isBlank(?v) . 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix ((: )) 17 | # (project (?x ?v) 18 | # (filter (isBlank ?v) 19 | # (bgp (triple ?x :p ?v))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#func-isBlank 22 | class IsBlank < Operator::Unary 23 | include Evaluatable 24 | 25 | NAME = :isBlank 26 | 27 | ## 28 | # Returns `true` if the operand is an `RDF::Node`, `false` otherwise. 29 | # 30 | # @param [RDF::Term] term 31 | # an RDF term 32 | # @return [RDF::Literal::Boolean] `true` or `false` 33 | # @raise [TypeError] if the operand is not an RDF term 34 | def apply(term, **options) 35 | case term 36 | when RDF::Node then RDF::Literal::TRUE 37 | when RDF::Term then RDF::Literal::FALSE 38 | else raise TypeError, "expected an RDF::Term, but got #{term.inspect}" 39 | end 40 | end 41 | 42 | ## 43 | # 44 | # Returns a partial SPARQL grammar for this operator. 45 | # 46 | # @return [String] 47 | def to_sparql(**options) 48 | "isBlank(" + operands.first.to_sparql(**options) + ")" 49 | end 50 | end # IsBlank 51 | end # Operator 52 | end; end # SPARQL::Algebra 53 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/is_iri.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `isIRI`/`isURI` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'isIRI' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?x ?v WHERE { 11 | # ?x :p ?v . 12 | # FILTER isIRI(?v) . 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix ((: )) 17 | # (project (?x ?v) 18 | # (filter (isIRI ?v) 19 | # (bgp (triple ?x :p ?v))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#func-isIRI 22 | class IsIRI < Operator::Unary 23 | include Evaluatable 24 | 25 | NAME = [:isIRI, :isURI] 26 | 27 | ## 28 | # Returns `true` if the operand is an `RDF::URI`, `false` otherwise. 29 | # 30 | # @param [RDF::Term] term 31 | # an RDF term 32 | # @return [RDF::Literal::Boolean] `true` or `false` 33 | # @raise [TypeError] if the operand is not an RDF term 34 | def apply(term, **options) 35 | case term 36 | when RDF::URI then RDF::Literal::TRUE 37 | when RDF::Term then RDF::Literal::FALSE 38 | else raise TypeError, "expected an RDF::Term, but got #{term.inspect}" 39 | end 40 | end 41 | 42 | Operator::IsURI = IsIRI 43 | 44 | ## 45 | # 46 | # Returns a partial SPARQL grammar for this operator. 47 | # 48 | # @return [String] 49 | def to_sparql(**options) 50 | "isIRI(" + operands.first.to_sparql(**options) + ")" 51 | end 52 | end # IsIRI 53 | end # Operator 54 | end; end # SPARQL::Algebra 55 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/is_literal.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `isLiteral` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'isLiteral' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?x ?v WHERE { 11 | # ?x :p ?v . 12 | # FILTER isLiteral(?v) . 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix ((: )) 17 | # (project (?x ?v) 18 | # (filter (isLiteral ?v) 19 | # (bgp (triple ?x :p ?v))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#func-isLiteral 22 | class IsLiteral < Operator::Unary 23 | include Evaluatable 24 | 25 | NAME = :isLiteral 26 | 27 | ## 28 | # Returns `true` if the operand is an `RDF::Literal`, `false` 29 | # otherwise. 30 | # 31 | # @param [RDF::Term] term 32 | # an RDF term 33 | # @return [RDF::Literal::Boolean] `true` or `false` 34 | # @raise [TypeError] if the operand is not an RDF term 35 | def apply(term, **options) 36 | case term 37 | when RDF::Literal then RDF::Literal::TRUE 38 | when RDF::Term then RDF::Literal::FALSE 39 | else raise TypeError, "expected an RDF::Term, but got #{term.inspect}" 40 | end 41 | end 42 | 43 | ## 44 | # 45 | # Returns a partial SPARQL grammar for this operator. 46 | # 47 | # @return [String] 48 | def to_sparql(**options) 49 | "isLiteral(" + operands.first.to_sparql(**options) + ")" 50 | end 51 | end # IsLiteral 52 | end # Operator 53 | end; end # SPARQL::Algebra 54 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/is_numeric.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `isNumeric` operator. 5 | # 6 | # Note numeric denotes typed literals with datatypes `xsd:integer`, `xsd:decimal`, `xsd:float`, and `xsd:double`, not derived types. 7 | # 8 | # [121] BuiltInCall ::= ... | 'isNumeric' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT ?x ?v WHERE { 13 | # ?x :p ?v . 14 | # FILTER isNumeric(?v) . 15 | # } 16 | # 17 | # @example SSE 18 | # (prefix ((: )) 19 | # (project (?x ?v) 20 | # (filter (isNumeric ?v) 21 | # (bgp (triple ?x :p ?v))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-isNumeric 24 | class IsNumeric < Operator::Unary 25 | include Evaluatable 26 | 27 | NAME = :isNumeric 28 | 29 | ## 30 | # Returns `true` if the operand is an `RDF::Literal::Numeric`, `false` 31 | # otherwise. 32 | # 33 | # @param [RDF::Term] term 34 | # an RDF term 35 | # @return [RDF::Literal::Boolean] `true` or `false` 36 | # @raise [TypeError] if the operand is not an RDF term 37 | def apply(term, **options) 38 | case term 39 | when RDF::Literal::NonPositiveInteger then RDF::Literal::FALSE 40 | when RDF::Literal::NonNegativeInteger then RDF::Literal::FALSE 41 | when RDF::Literal::Long then RDF::Literal::FALSE 42 | when RDF::Literal::Numeric then RDF::Literal::TRUE 43 | when RDF::Term then RDF::Literal::FALSE 44 | else raise TypeError, "expected an RDF::Term, but got #{term.inspect}" 45 | end 46 | end 47 | 48 | ## 49 | # 50 | # Returns a partial SPARQL grammar for this operator. 51 | # 52 | # @return [String] 53 | def to_sparql(**options) 54 | "isNumeric(" + operands.first.to_sparql(**options) + ")" 55 | end 56 | end # IsNumeric 57 | end # Operator 58 | end; end # SPARQL::Algebra 59 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/is_triple.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `isTRIPLE` operator. 5 | # 6 | # Returns true if term is an RDF-star triple. Returns false otherwise. 7 | # 8 | # [121] BuiltInCall ::= ... | 'isTreiple' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT * { 13 | # ?t :source :g 14 | # FILTER(isTriple(?t)) 15 | # FILTER(SUBJECT(?t) = :s) 16 | # FILTER(PREDICATE(?t) = :p) 17 | # FILTER(OBJECT(?t) = :o) 18 | # } 19 | # 20 | # @example SSE 21 | # (prefix 22 | # ((: )) 23 | # (filter 24 | # (exprlist 25 | # (isTRIPLE ?t) 26 | # (= (subject ?t) :s) 27 | # (= (predicate ?t) :p) 28 | # (= (object ?t) :o)) 29 | # (bgp (triple ?t :source :g))) ) 30 | # 31 | # @see https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#istriple 32 | class IsTriple < Operator::Unary 33 | include Evaluatable 34 | 35 | NAME = :isTRIPLE 36 | 37 | ## 38 | # Returns `true` if the operand is an `RDF::Statement`, `false` otherwise. 39 | # 40 | # @param [RDF::Term] term 41 | # an RDF term 42 | # @return [RDF::Literal::Boolean] `true` or `false` 43 | # @raise [TypeError] if the operand is not an RDF term 44 | def apply(term, **options) 45 | case term 46 | when RDF::Statement then RDF::Literal::TRUE 47 | when RDF::Term then RDF::Literal::FALSE 48 | else raise TypeError, "expected an RDF::Term, but got #{term.inspect}" 49 | end 50 | end 51 | 52 | ## 53 | # 54 | # Returns a partial SPARQL grammar for this operator. 55 | # 56 | # @return [String] 57 | def to_sparql(**options) 58 | "isTRIPLE(" + operands.first.to_sparql(**options) + ")" 59 | end 60 | end # IsTriple 61 | end # Operator 62 | end; end # SPARQL::Algebra 63 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/lang.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `lang` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'LANG' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # 11 | # SELECT ?x 12 | # { ?x :p ?v . 13 | # FILTER ( lang(?v) != '@NotALangTag@' ) 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix ((: )) 18 | # (project (?x) 19 | # (filter (!= (lang ?v) "@NotALangTag@") 20 | # (bgp (triple ?x :p ?v))))) 21 | # 22 | # @see https://www.w3.org/TR/sparql11-query/#func-lang 23 | class Lang < Operator::Unary 24 | include Evaluatable 25 | 26 | NAME = :lang 27 | 28 | ## 29 | # Returns the language tag of the operand, if it has one. 30 | # 31 | # If the operand has no language tag, returns `""`. 32 | # 33 | # @param [RDF::Literal] literal 34 | # a literal 35 | # @return [RDF::Literal] a simple literal 36 | # @raise [TypeError] if the operand is not a literal 37 | def apply(literal, **options) 38 | case literal 39 | when RDF::Literal then RDF::Literal(literal.language.to_s) 40 | else raise TypeError, "expected an RDF::Literal, but got #{literal.inspect}" 41 | end 42 | end 43 | 44 | ## 45 | # 46 | # Returns a partial SPARQL grammar for this operator. 47 | # 48 | # @return [String] 49 | def to_sparql(**options) 50 | "lang(" + operands.first.to_sparql(**options) + ")" 51 | end 52 | end # Lang 53 | end # Operator 54 | end; end # SPARQL::Algebra 55 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/lcase.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `lcase` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'LCASE' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?s (LCASE(?str) AS ?lstr) WHERE { 11 | # ?s :str ?str 12 | # } 13 | # 14 | # @example SSE 15 | # (prefix ((: )) 16 | # (project (?s ?lstr) 17 | # (extend ((?lstr (lcase ?str))) 18 | # (bgp (triple ?s :str ?str))))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#func-lcase 21 | # @see https://www.w3.org/TR/xpath-functions/#func-lcase 22 | class LCase < Operator::Unary 23 | include Evaluatable 24 | 25 | NAME = :lcase 26 | 27 | ## 28 | # The LCASE function corresponds to the XPath fn:lower-case function. It returns a string literal whose lexical form is the lower case of the lexcial form of the argument. 29 | # 30 | # @param [RDF::Literal] operand 31 | # the operand 32 | # @return [RDF::Literal] literal of same type 33 | # @raise [TypeError] if the operand is not a literal value 34 | def apply(operand, **options) 35 | case operand 36 | when RDF::Literal then RDF::Literal(operand.to_s.downcase, datatype: operand.datatype, language: operand.language) 37 | else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}" 38 | end 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "LCASE(" + operands.last.to_sparql(**options) + ")" 48 | end 49 | end # LCase 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/less_than.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL relational `<` (less than) comparison operator. 5 | # 6 | # [114] RelationalExpression ::= NumericExpression ('<' NumericExpression)? 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX xsd: 10 | # PREFIX : 11 | # SELECT ?x 12 | # WHERE { ?x :p ?v . FILTER ( ?v < 1 ) } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((xsd: ) (: )) 17 | # (project (?x) (filter (< ?v 1) (bgp (triple ?x :p ?v))))) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-query/#OperatorMapping 20 | # @see https://www.w3.org/TR/xpath-functions/#func-compare 21 | # @see https://www.w3.org/TR/xpath-functions/#func-numeric-less-than 22 | # @see https://www.w3.org/TR/xpath-functions/#func-boolean-less-than 23 | # @see https://www.w3.org/TR/xpath-functions/#func-dateTime-less-than 24 | class LessThan < Compare 25 | NAME = :< 26 | 27 | ## 28 | # Returns `true` if the first operand is less than the second 29 | # operand; returns `false` otherwise. 30 | # 31 | # @param [RDF::Literal] left 32 | # a literal 33 | # @param [RDF::Literal] right 34 | # a literal 35 | # @return [RDF::Literal::Boolean] `true` or `false` 36 | # @raise [TypeError] if either operand is not a literal 37 | def apply(left, right, **options) 38 | RDF::Literal(super == RDF::Literal(-1)) 39 | end 40 | end # LessThan 41 | end # Operator 42 | end; end # SPARQL::Algebra 43 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/less_than_or_equal.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL relational `<=` (less than or equal) comparison operator. 5 | # 6 | # [114] RelationalExpression ::= NumericExpression ('<=' NumericExpression)? 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX xsd: 10 | # PREFIX : 11 | # SELECT ?x 12 | # WHERE { ?x :p ?v . FILTER ( ?v <= 1 ) } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((xsd: ) (: )) 17 | # (project (?x) (filter (<= ?v 1) (bgp (triple ?x :p ?v))))) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-query/#OperatorMapping 20 | # @see https://www.w3.org/TR/xpath-functions/#func-compare 21 | # @see https://www.w3.org/TR/xpath-functions/#func-numeric-less-than 22 | # @see https://www.w3.org/TR/xpath-functions/#func-boolean-less-than 23 | # @see https://www.w3.org/TR/xpath-functions/#func-dateTime-less-than 24 | class LessThanOrEqual < Compare 25 | NAME = :<= 26 | 27 | ## 28 | # Returns `true` if the first operand is less than or equal to the 29 | # second operand; returns `false` otherwise. 30 | # 31 | # @param [RDF::Literal] left 32 | # a literal 33 | # @param [RDF::Literal] right 34 | # a literal 35 | # @return [RDF::Literal::Boolean] `true` or `false` 36 | # @raise [TypeError] if either operand is not a literal 37 | def apply(left, right, **options) 38 | RDF::Literal(super <= RDF::Literal(0)) 39 | end 40 | end # LessThanOrEqual 41 | end # Operator 42 | end; end # SPARQL::Algebra 43 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/max.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `max` set function. 5 | # 6 | # [127] Aggregate::= ... | 'MAX' '(' 'DISTINCT'? Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT (MAX(?o) AS ?max) 11 | # WHERE { ?s ?p ?o } 12 | # 13 | # @example SSE 14 | # (prefix ((: )) 15 | # (project (?max) 16 | # (extend ((?max ??.0)) 17 | # (group () ((??.0 (max ?o))) 18 | # (bgp (triple ?s ?p ?o)))))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#defn_aggMax 21 | class Max < Operator 22 | include Aggregate 23 | 24 | NAME = :max 25 | 26 | def initialize(*operands, **options) 27 | raise ArgumentError, 28 | "max operator accepts at most one argument with an optional :distinct" if 29 | (operands - %i{distinct}).length != 1 30 | super 31 | end 32 | 33 | ## 34 | # Max is a SPARQL set function that return the maximum value from a group respectively. 35 | # 36 | # It makes use of the SPARQL ORDER BY ordering definition, to allow ordering over arbitrarily typed expressions. 37 | # 38 | # @param [Enumerable>] enum 39 | # enum of evaluated operand 40 | # @return [RDF::Literal] The maximum value of the terms 41 | def apply(enum, **options) 42 | # FIXME: we don't actually do anything with distinct 43 | operands.shift if distinct = (operands.first == :distinct) 44 | if enum.empty? 45 | raise TypeError, "Maximum of an empty multiset" 46 | elsif enum.flatten.all? {|n| n.literal?} 47 | RDF::Literal(enum.flatten.max) 48 | else 49 | raise TypeError, "Maximum of non-literals: #{enum.flatten}" 50 | end 51 | end 52 | 53 | ## 54 | # 55 | # Returns a partial SPARQL grammar for this operator. 56 | # 57 | # @return [String] 58 | def to_sparql(**options) 59 | distinct = operands.first == :distinct 60 | args = distinct ? operands[1..-1] : operands 61 | "MAX(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})" 62 | end 63 | end # Max 64 | end # Operator 65 | end; end # SPARQL::Algebra 66 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/md5.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # The SPARQL logical `md5` operator. 7 | # 8 | # Returns the MD5 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the `xsd:string`. Hex digits `SHOULD` be in lower case. 9 | # 10 | # [121] BuiltInCall ::= ... | 'MD5' '(' Expression ')' 11 | # 12 | # @example SPARQL Grammar 13 | # PREFIX : 14 | # SELECT (MD5(?l) AS ?hash) WHERE { 15 | # :s1 :str ?l 16 | # } 17 | # 18 | # @example SSE 19 | # (prefix ((: )) 20 | # (project (?hash) 21 | # (extend ((?hash (md5 ?l))) 22 | # (bgp (triple :s1 :str ?l))))) 23 | # 24 | # @see https://www.w3.org/TR/sparql11-query/#func-md5 25 | class MD5 < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = :md5 29 | 30 | ## 31 | # Returns the MD5 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the xsd:string. Hex digits should be in lower case. 32 | # 33 | # @param [RDF::Literal] operand 34 | # the operand 35 | # @return [RDF::Literal] 36 | # @raise [TypeError] if the operand is not a simple literal 37 | def apply(operand, **options) 38 | raise TypeError, "expected an RDF::Literal, but got #{operand.inspect}" unless operand.literal? 39 | raise TypeError, "expected simple literal or xsd:string, but got #{operand.inspect}" unless (operand.datatype || RDF::XSD.string) == RDF::XSD.string 40 | RDF::Literal(Digest::MD5.new.hexdigest(operand.to_s)) 41 | end 42 | 43 | ## 44 | # 45 | # Returns a partial SPARQL grammar for this operator. 46 | # 47 | # @return [String] 48 | def to_sparql(**options) 49 | "MD5(" + operands.to_sparql(**options) + ")" 50 | end 51 | end # MD5 52 | end # Operator 53 | end; end # SPARQL::Algebra 54 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/min.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `min` set function. 5 | # 6 | # [127] Aggregate::= ... | 'MIN' '(' 'DISTINCT'? Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT (MIN(?o) AS ?min) 11 | # WHERE { ?s :dec ?o } 12 | # 13 | # @example SSE 14 | # (prefix ((: )) 15 | # (project (?min) 16 | # (extend ((?min ??.0)) 17 | # (group () ((??.0 (min ?o))) 18 | # (bgp (triple ?s :dec ?o)))))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#defn_aggMin 21 | class Min < Operator 22 | include Aggregate 23 | 24 | NAME = :min 25 | 26 | def initialize(*operands, **options) 27 | raise ArgumentError, 28 | "min operator accepts at most one argument with an optional :distinct" if 29 | (operands - %i{distinct}).length != 1 30 | super 31 | end 32 | 33 | ## 34 | # Min is a SPARQL set function that return the minimum value from a group respectively. 35 | # 36 | # It makes use of the SPARQL ORDER BY ordering definition, to allow ordering over arbitrarily typed expressions. 37 | # 38 | # @param [Enumerable>] enum 39 | # enum of evaluated operand 40 | # @return [RDF::Literal] The maximum value of the terms 41 | def apply(enum, **options) 42 | # FIXME: we don't actually do anything with distinct 43 | operands.shift if distinct = (operands.first == :distinct) 44 | if enum.empty? 45 | raise TypeError, "Minumuim of an empty multiset" 46 | elsif enum.flatten.all? {|n| n.literal?} 47 | RDF::Literal(enum.flatten.min) 48 | else 49 | raise TypeError, "Minumuim of non-literals: #{enum.flatten}" 50 | end 51 | end 52 | 53 | ## 54 | # 55 | # Returns a partial SPARQL grammar for this operator. 56 | # 57 | # @return [String] 58 | def to_sparql(**options) 59 | distinct = operands.first == :distinct 60 | args = distinct ? operands[1..-1] : operands 61 | "MIN(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})" 62 | end 63 | end # Min 64 | end # Operator 65 | end; end # SPARQL::Algebra 66 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/minutes.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `minutes` operator. 5 | # 6 | # Returns the minutes part of the lexical form of `arg`. The value is as given in the lexical form of the XSD dateTime. 7 | # 8 | # [121] BuiltInCall ::= ... | 'MINUTES' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT ?s (MINUTES(?date) AS ?x) WHERE { 13 | # ?s :date ?date 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: )) 19 | # (project (?s ?x) 20 | # (extend ((?x (minutes ?date))) 21 | # (bgp (triple ?s :date ?date))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-minutes 24 | class Minutes < Operator::Unary 25 | include Evaluatable 26 | 27 | NAME = :minutes 28 | 29 | ## 30 | # Returns the minutes part of arg as an integer. 31 | # 32 | # @param [RDF::Literal] operand 33 | # the operand 34 | # @return [RDF::Literal::Temporal] 35 | # @raise [TypeError] if the operand is not a simple literal 36 | def apply(operand, **options) 37 | raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal) 38 | RDF::Literal(operand.object.minute) 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "MINUTES(#{operands.last.to_sparql(**options)})" 48 | end 49 | end # Minutes 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/month.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `month` operator. 5 | # 6 | # Returns the month part of `arg` as an integer. 7 | # 8 | # [121] BuiltInCall ::= ... | 'MONTH' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT ?s (MONTH(?date) AS ?x) WHERE { 13 | # ?s :date ?date 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: )) 19 | # (project (?s ?x) 20 | # (extend ((?x (month ?date))) 21 | # (bgp (triple ?s :date ?date))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-month 24 | class Month < Operator::Unary 25 | include Evaluatable 26 | 27 | NAME = :month 28 | 29 | ## 30 | # Returns the month part of arg as an integer. 31 | # 32 | # @param [RDF::Literal] operand 33 | # the operand 34 | # @return [RDF::Literal::Temporal] 35 | # @raise [TypeError] if the operand is not a simple literal 36 | def apply(operand, **options) 37 | raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal) 38 | RDF::Literal(operand.object.month) 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "MONTH(#{operands.last.to_sparql(**options)})" 48 | end 49 | end # Month 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/multiply.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL numeric `multiply` operator. 5 | # 6 | # [117] MultiplicativeExpression::= UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )* 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?s WHERE { 11 | # ?s :p ?o . 12 | # ?s2 :p ?o2 . 13 | # FILTER(?o * ?o2 = 4) . 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix ((: )) 18 | # (project (?s) 19 | # (filter (= (* ?o ?o2) 4) 20 | # (bgp 21 | # (triple ?s :p ?o) 22 | # (triple ?s2 :p ?o2))))) 23 | # 24 | # @see https://www.w3.org/TR/xpath-functions/#func-numeric-multiply 25 | class Multiply < Operator::Binary 26 | include Evaluatable 27 | 28 | NAME = [:*, :multiply] 29 | 30 | ## 31 | # Returns the arithmetic product of the operands. 32 | # 33 | # @param [RDF::Literal::Numeric] left 34 | # a numeric literal 35 | # @param [RDF::Literal::Numeric] right 36 | # a numeric literal 37 | # @return [RDF::Literal::Numeric] 38 | # @raise [TypeError] if either operand is not a numeric literal 39 | def apply(left, right, **options) 40 | case 41 | when left.is_a?(RDF::Literal::Numeric) && right.is_a?(RDF::Literal::Numeric) 42 | left * right 43 | else raise TypeError, "expected two RDF::Literal::Numeric operands, but got #{left.inspect} and #{right.inspect}" 44 | end 45 | end 46 | 47 | ## 48 | # 49 | # Returns a partial SPARQL grammar for this operator. 50 | # 51 | # @return [String] 52 | def to_sparql(**options) 53 | "(#{operands.first.to_sparql(**options)} * #{operands.last.to_sparql(**options)})" 54 | end 55 | end # Multiply 56 | end # Operator 57 | end; end # SPARQL::Algebra 58 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/negate.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL numeric unary `-` (negation) operator. 5 | # 6 | # [118] UnaryExpression ::= ... | '-' PrimaryExpression 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?s WHERE { 11 | # ?s :p ?o . 12 | # FILTER(-?o = -2) . 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix ((: )) 17 | # (project (?s) 18 | # (filter (= (- ?o) -2) 19 | # (bgp (triple ?s :p ?o))))) 20 | # 21 | # @see https://www.w3.org/TR/xpath-functions/#func-numeric-unary-minus 22 | class Negate < Operator::Unary 23 | include Evaluatable 24 | 25 | NAME = [:-, :negate] 26 | 27 | ## 28 | # Returns the operand with its sign reversed. 29 | # 30 | # @param [RDF::Literal::Numeric] term 31 | # a numeric literal 32 | # @return [RDF::Literal::Numeric] 33 | # @raise [TypeError] if the operand is not a numeric literal 34 | def apply(term, **options) 35 | case term 36 | when RDF::Literal::Numeric then -term 37 | else raise TypeError, "expected an RDF::Literal::Numeric, but got #{term.inspect}" 38 | end 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "(-#{operands.to_sparql(**options)})" 48 | end 49 | end # Negate 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/not.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `not` operator. 5 | # 6 | # [118] UnaryExpression ::= ... | '!' PrimaryExpression 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?a 11 | # WHERE { 12 | # ?a :p ?v . 13 | # FILTER ( ! ?v ) . 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix ((: )) 18 | # (project (?a) 19 | # (filter (! ?v) 20 | # (bgp (triple ?a :p ?v))))) 21 | # 22 | # @see https://www.w3.org/TR/xpath-functions/#func-not 23 | class Not < Operator::Unary 24 | include Evaluatable 25 | 26 | NAME = [:'!', :not] 27 | 28 | ## 29 | # Returns the logical `NOT` (inverse) of the operand. 30 | # 31 | # Note that this operator operates on the effective boolean value 32 | # (EBV) of its operand. 33 | # 34 | # @param [RDF::Literal::Boolean] operand 35 | # the operand 36 | # @return [RDF::Literal::Boolean] `true` or `false` 37 | # @raise [TypeError] if the operand could not be coerced to a boolean literal 38 | def apply(operand, **options) 39 | case bool = boolean(operand) 40 | when RDF::Literal::Boolean 41 | RDF::Literal(bool.false?) 42 | else super 43 | end 44 | end 45 | 46 | ## 47 | # 48 | # Returns a partial SPARQL grammar for this operator. 49 | # 50 | # @return [String] 51 | def to_sparql(**options) 52 | "(!" + operands.first.to_sparql(**options) + ")" 53 | end 54 | end # Not 55 | end # Operator 56 | end; end # SPARQL::Algebra 57 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/not_equal.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL relational `!=` (not equal) comparison operator. 5 | # 6 | # [114] RelationalExpression ::= NumericExpression ('!=' NumericExpression)? 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX xsd: 10 | # PREFIX : 11 | # SELECT ?x 12 | # WHERE { ?x :p ?v . FILTER ( ?v != 1 ) } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((xsd: ) (: )) 17 | # (project (?x) (filter (!= ?v 1) (bgp (triple ?x :p ?v))))) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-query/#OperatorMapping 20 | # @see https://www.w3.org/TR/sparql11-query/#func-RDFterm-equal 21 | class NotEqual < Equal 22 | NAME = :'!=' 23 | 24 | ## 25 | # Returns `true` if the operands are not equal; returns `false` 26 | # otherwise. 27 | # 28 | # Comparing unknown datatypes might have different lexical forms but be the same value. 29 | # 30 | # @param [RDF::Term] term1 31 | # an RDF term 32 | # @param [RDF::Term] term2 33 | # an RDF term 34 | # @return [RDF::Literal::Boolean] `true` or `false` 35 | # @raise [TypeError] if either operand is not an RDF term 36 | def apply(term1, term2, **options) 37 | RDF::Literal(super.false?) 38 | end 39 | end # NotEqual 40 | end # Operator 41 | end; end # SPARQL::Algebra 42 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/now.rb: -------------------------------------------------------------------------------- 1 | require 'time' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # The SPARQL logical `now` operator. 7 | # 8 | # Returns an XSD dateTime value for the current query execution. All calls to this function in any one query execution must return the same value. The exact moment returned is not specified. 9 | # 10 | # [121] BuiltInCall ::= ... | 'NOW' NIL 11 | # 12 | # @example SPARQL Grammar 13 | # PREFIX xsd: 14 | # ASK { 15 | # BIND(NOW() AS ?n) 16 | # FILTER(DATATYPE(?n) = xsd:dateTime) 17 | # } 18 | # 19 | # @example SSE 20 | # (prefix ((xsd: )) 21 | # (ask 22 | # (filter (= (datatype ?n) xsd:dateTime) 23 | # (extend ((?n (now))) 24 | # (bgp))))) 25 | # 26 | # @see https://www.w3.org/TR/sparql11-query/#func-now 27 | class Now < Operator::Nullary 28 | include Evaluatable 29 | 30 | NAME = :now 31 | 32 | ## 33 | # Returns an XSD dateTime value for the current query execution. All calls to this function in any one query execution must return the same value. The exact moment returned is not specified. 34 | # 35 | # @return [RDF::Literal::Double] random value 36 | def apply(**options) 37 | RDF::Literal(DateTime.now) 38 | end 39 | 40 | ## 41 | # 42 | # Returns a partial SPARQL grammar for this operator. 43 | # 44 | # @return [String] 45 | def to_sparql(**options) 46 | "NOW()" 47 | end 48 | end # Now 49 | end # Operator 50 | end; end # SPARQL::Algebra 51 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/object.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `OBJECT` operator. 5 | # 6 | # If triple is an RDF-star triple, the function returns the object of this triple. Passing anything other than an RDF-star triple is an error. 7 | # 8 | # [121] BuiltInCall ::= ... | 'OBJECT' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT * { 13 | # ?t :source :g 14 | # FILTER(isTriple(?t)) 15 | # FILTER(SUBJECT(?t) = :s) 16 | # FILTER(PREDICATE(?t) = :p) 17 | # FILTER(OBJECT(?t) = :o) 18 | # } 19 | # 20 | # @example SSE 21 | # (prefix 22 | # ((: )) 23 | # (filter 24 | # (exprlist 25 | # (isTRIPLE ?t) 26 | # (= (subject ?t) :s) 27 | # (= (predicate ?t) :p) 28 | # (= (object ?t) :o)) 29 | # (bgp (triple ?t :source :g))) ) 30 | # 31 | # @see https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#object 32 | class Object < Operator::Unary 33 | include Evaluatable 34 | 35 | NAME = :object 36 | 37 | ## 38 | # Returns the object part of arg. 39 | # 40 | # @param [RDF::Statement] operand 41 | # the operand 42 | # @return [RDF::Literal] 43 | # @raise [TypeError] if the operand is not a statement 44 | def apply(operand, **options) 45 | raise TypeError, "expected an RDF::Statement, but got #{operand.inspect}" unless operand.is_a?(RDF::Statement) 46 | operand.object 47 | end 48 | 49 | ## 50 | # 51 | # Returns a partial SPARQL grammar for this operator. 52 | # 53 | # @return [String] 54 | def to_sparql(**options) 55 | "OBJECT(" + operands.last.to_sparql(**options) + ")" 56 | end 57 | end # Object 58 | end # Operator 59 | end; end # SPARQL::Algebra 60 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/path_star.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL Property Path `path*` (ZeroOrMorePath) operator. 5 | # 6 | # [91] PathElt ::= PathPrimary PathMod? 7 | # [93] PathMod ::= '*' | '?' | '+' | '{' INTEGER? (',' INTEGER?)? '}' 8 | 9 | # @example SPARQL Grammar 10 | # PREFIX : 11 | # SELECT * WHERE { 12 | # :a :p* ?z 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix ((: )) 17 | # (path :a (path* :p) ?z)) 18 | # 19 | # @see https://www.w3.org/TR/sparql11-query/#defn_evalPP_ZeroOrMorePath 20 | class PathStar < Operator::Unary 21 | include Query 22 | 23 | NAME = :"path*" 24 | 25 | ## 26 | # Path including at zero length: 27 | # 28 | # (path :a (path* :p) :b) 29 | # 30 | # into 31 | # 32 | # (path :a (path? (path+ :p)) :b) 33 | # 34 | # @param [RDF::Queryable] queryable 35 | # the graph or repository to query 36 | # @param [Hash{Symbol => Object}] options 37 | # any additional keyword options 38 | # @option options [RDF::Term, RDF::Variable] :subject 39 | # @option options [RDF::Term, RDF::Variable] :object 40 | # @yield [solution] 41 | # each matching solution 42 | # @yieldparam [RDF::Query::Solution] solution 43 | # @yieldreturn [void] ignored 44 | # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra 45 | def execute(queryable, **options, &block) 46 | subject, object = options[:subject], options[:object] 47 | debug(options) {"Path* #{[subject, operands, object].to_sse}"} 48 | 49 | # (:x :p* :y) => (:x (:p+)? :y) 50 | query = PathOpt.new(PathPlus.new(*operands)) 51 | query.execute(queryable, **options.merge(depth: options[:depth].to_i + 1), &block) 52 | end 53 | ## 54 | # 55 | # Returns a partial SPARQL grammar for this operator. 56 | # 57 | # @return [String] 58 | def to_sparql(**options) 59 | "(#{operands.first.to_sparql(**options)})*" 60 | end 61 | end # PathStar 62 | end # Operator 63 | end; end # SPARQL::Algebra 64 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/predicate.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `PREDICATE` operator. 5 | # 6 | # If triple is an RDF-star triple, the function returns the predicate of this triple. Passing anything other than an RDF-star triple is an error. 7 | # 8 | # [121] BuiltInCall ::= ... | 'PREDICATE' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT * { 13 | # ?t :source :g 14 | # FILTER(isTriple(?t)) 15 | # FILTER(SUBJECT(?t) = :s) 16 | # FILTER(PREDICATE(?t) = :p) 17 | # FILTER(OBJECT(?t) = :o) 18 | # } 19 | # 20 | # @example SSE 21 | # (prefix 22 | # ((: )) 23 | # (filter 24 | # (exprlist 25 | # (isTRIPLE ?t) 26 | # (= (subject ?t) :s) 27 | # (= (predicate ?t) :p) 28 | # (= (object ?t) :o)) 29 | # (bgp (triple ?t :source :g))) ) 30 | # 31 | # @see https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#predicate 32 | class Predicate < Operator::Unary 33 | include Evaluatable 34 | 35 | NAME = :predicate 36 | 37 | ## 38 | # Returns the predicate part of arg. 39 | # 40 | # @param [RDF::Statement] operand 41 | # the operand 42 | # @return [RDF::Literal] 43 | # @raise [TypeError] if the operand is not a statement 44 | def apply(operand, **options) 45 | raise TypeError, "expected an RDF::Statement, but got #{operand.inspect}" unless operand.is_a?(RDF::Statement) 46 | operand.predicate 47 | end 48 | 49 | ## 50 | # 51 | # Returns a partial SPARQL grammar for this operator. 52 | # 53 | # @return [String] 54 | def to_sparql(**options) 55 | "PREDICATE(" + operands.last.to_sparql(**options) + ")" 56 | end 57 | end # Predicate 58 | end # Operator 59 | end; end # SPARQL::Algebra 60 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/rand.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `rand` operator. 5 | # 6 | # Returns a pseudo-random number between 0 (inclusive) and 1.0e0 (exclusive). Different numbers can be produced every time this function is invoked. Numbers should be produced with approximately equal probability. 7 | # 8 | # [121] BuiltInCall ::= ... | 'RAND' NIL 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX xsd: 12 | # ASK { 13 | # BIND(RAND() AS ?r) 14 | # FILTER(DATATYPE(?r) = xsd:double && ?r >= 0.0 && ?r < 1.0) 15 | # } 16 | # 17 | # @example SSE 18 | # (prefix 19 | # ((xsd: )) 20 | # (ask 21 | # (filter 22 | # (&& 23 | # (&& (= (datatype ?r) xsd:double) (>= ?r 0.0)) 24 | # (< ?r 1.0)) 25 | # (extend ((?r (rand))) 26 | # (bgp))))) 27 | # 28 | # @see https://www.w3.org/TR/sparql11-query/#idp2130040 29 | class Rand < Operator::Nullary 30 | include Evaluatable 31 | 32 | NAME = :rand 33 | 34 | ## 35 | # Returns a pseudo-random number between 0 (inclusive) and 1.0e0 (exclusive). Different numbers can be produced every time this function is invoked. Numbers should be produced with approximately equal probability. 36 | # 37 | # @return [RDF::Literal::Double] random value 38 | def apply(**options) 39 | RDF::Literal::Double.new(Random.rand) 40 | end 41 | 42 | ## 43 | # 44 | # Returns a partial SPARQL grammar for this operator. 45 | # 46 | # Extracts projections 47 | # 48 | # @return [String] 49 | def to_sparql(**options) 50 | "RAND()" 51 | end 52 | end # Rand 53 | end # Operator 54 | end; end # SPARQL::Algebra 55 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/reduced.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL GraphPattern `reduced` operator. 5 | # 6 | # [9] SelectClause ::= 'SELECT' ( 'DISTINCT' | 'REDUCED' )? ( ( Var | ( '(' Expression 'AS' Var ')' ) )+ | '*' ) 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # PREFIX xsd: 11 | # SELECT REDUCED ?v 12 | # WHERE { ?x ?p ?v } 13 | # 14 | # @example SSE 15 | # (prefix ((: ) 16 | # (xsd: )) 17 | # (reduced 18 | # (project (?v) 19 | # (bgp (triple ?x ?p ?v))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra 22 | class Reduced < Operator::Unary 23 | include Query 24 | 25 | NAME = [:reduced] 26 | 27 | ## 28 | # Executes this query on the given `queryable` graph or repository. 29 | # Removes duplicate solutions from the solution set. 30 | # 31 | # @param [RDF::Queryable] queryable 32 | # the graph or repository to query 33 | # @param [Hash{Symbol => Object}] options 34 | # any additional keyword options 35 | # @yield [solution] 36 | # each matching solution 37 | # @yieldparam [RDF::Query::Solution] solution 38 | # @yieldreturn [void] ignored 39 | # @return [RDF::Query::Solutions] 40 | # the resulting solution sequence 41 | # @see https://www.w3.org/TR/sparql11-query/#sparqlAlgebra 42 | def execute(queryable, **options, &block) 43 | @solutions = operands.last. 44 | execute(queryable, **options.merge(depth: options[:depth].to_i + 1)).reduced 45 | @solutions.each(&block) if block_given? 46 | @solutions 47 | end 48 | 49 | ## 50 | # 51 | # Returns a partial SPARQL grammar for this operator. 52 | # 53 | # @return [String] 54 | def to_sparql(**options) 55 | operands.first.to_sparql(reduced: true, **options) 56 | end 57 | end # Reduced 58 | end # Operator 59 | end; end # SPARQL::Algebra 60 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/round.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `round` operator. 5 | # 6 | # Returns the number with no fractional part that is closest to the argument. If there are two such numbers, then the one that is closest to positive infinity is returned. An error is raised if `arg` is not a numeric value. 7 | # 8 | # [121] BuiltInCall ::= ... 'ROUND' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # PREFIX xsd: 13 | # SELECT ?s ?num (ROUND(?num) AS ?round) WHERE { 14 | # ?s :num ?num 15 | # } 16 | # 17 | # @example SSE 18 | # (prefix 19 | # ((: ) 20 | # (xsd: )) 21 | # (project (?s ?num ?round) 22 | # (extend ((?round (round ?num))) 23 | # (bgp (triple ?s :num ?num))))) 24 | # 25 | # @see https://www.w3.org/TR/sparql11-query/#func-round 26 | # @see https://www.w3.org/TR/xpath-functions/#func-round 27 | class Round < Operator::Unary 28 | include Evaluatable 29 | 30 | NAME = [:round] 31 | 32 | ## 33 | # Returns the number with no fractional part that is closest to the argument. If there are two such numbers, then the one that is closest to positive infinity is returned. An error is raised if arg is not a numeric value. 34 | # 35 | # @param [RDF::Literal] operand 36 | # the operand 37 | # @return [RDF::Literal] literal of same type 38 | # @raise [TypeError] if the operand is not a numeric value 39 | def apply(operand, **options) 40 | case operand 41 | when RDF::Literal::Numeric then operand.round 42 | else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}" 43 | end 44 | end 45 | end # Round 46 | 47 | ## 48 | # 49 | # Returns a partial SPARQL grammar for this operator. 50 | # 51 | # @return [String] 52 | def to_sparql(**options) 53 | "ROUND(#{operands.to_sparql(**options)})" 54 | end 55 | end # Operator 56 | end; end # SPARQL::Algebra 57 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/same_term.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `sameTerm` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'sameTerm' '(' Expression ',' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT * { 11 | # ?x1 :p ?v1 . 12 | # ?x2 :p ?v2 . 13 | # FILTER sameTerm(?v1, ?v2) 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: )) 19 | # (filter (sameTerm ?v1 ?v2) 20 | # (bgp (triple ?x1 :p ?v1) (triple ?x2 :p ?v2)))) 21 | # 22 | # @see https://www.w3.org/TR/sparql11-query/#func-sameTerm 23 | class SameTerm < Operator::Binary 24 | include Evaluatable 25 | 26 | NAME = :sameTerm 27 | 28 | ## 29 | # Returns `true` if the operands are the same RDF term; returns 30 | # `false` otherwise. 31 | # 32 | # @param [RDF::Term] term1 33 | # an RDF term 34 | # @param [RDF::Term] term2 35 | # an RDF term 36 | # @return [RDF::Literal::Boolean] `true` or `false` 37 | # @raise [TypeError] if either operand is unbound 38 | def apply(term1, term2, **options) 39 | RDF::Literal(term1.eql?(term2)) 40 | end 41 | 42 | ## 43 | # Returns an optimized version of this expression. 44 | # 45 | # Return true if variable operand1 is a bound variable and equals operand2 46 | # 47 | # @return [SameTerm] a copy of `self` 48 | # @see SPARQL::Algebra::Expression#optimize 49 | def optimize(**options) 50 | if operand(0).is_a?(Variable) && operand(0).bound? && operand(0).eql?(operand(1)) 51 | RDF::Literal::TRUE 52 | else 53 | super # @see Operator#optimize! 54 | end 55 | end 56 | 57 | ## 58 | # 59 | # Returns a partial SPARQL grammar for this operator. 60 | # 61 | # @return [String] 62 | def to_sparql(**options) 63 | "sameTerm(#{operands.to_sparql(delimiter: ', ', **options)})" 64 | end 65 | end # SameTerm 66 | end # Operator 67 | end; end # SPARQL::Algebra 68 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/sample.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `sample` set function. 5 | # 6 | # [127] Aggregate::= ... | 'SAMPLE' '(' 'DISTINCT'? Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # 11 | # SELECT ?w (SAMPLE(?v) AS ?S) 12 | # { 13 | # ?s :p ?v . 14 | # OPTIONAL { ?s :q ?w } 15 | # } 16 | # GROUP BY ?w 17 | # 18 | # @example SSE 19 | # (prefix ((: )) 20 | # (project (?w ?S) 21 | # (extend ((?S ??.0)) 22 | # (group (?w) ((??.0 (sample ?v))) 23 | # (leftjoin 24 | # (bgp (triple ?s :p ?v)) 25 | # (bgp (triple ?s :q ?w))))) )) 26 | # 27 | # @see https://www.w3.org/TR/sparql11-query/#defn_aggSample 28 | class Sample < Operator 29 | include Aggregate 30 | 31 | NAME = :sample 32 | 33 | def initialize(*operands, **options) 34 | raise ArgumentError, 35 | "sample operator accepts at most one argument with an optional :distinct" if 36 | (operands - %i{distinct}).length != 1 37 | super 38 | end 39 | 40 | ## 41 | # Sample is a set function which returns an arbitrary value from the multiset passed to it. 42 | # 43 | # @param [Enumerable>] enum 44 | # enum of evaluated operand 45 | # @return [RDF::Term] An arbitrary term 46 | # @raise [TypeError] If enum is empty 47 | def apply(enum, **options) 48 | enum.detect(lambda {raise TypeError, "Sampling an empty multiset"}) {|e| e.first}.first 49 | end 50 | 51 | ## 52 | # 53 | # Returns a partial SPARQL grammar for this operator. 54 | # 55 | # @return [String] 56 | def to_sparql(**options) 57 | distinct = operands.first == :distinct 58 | args = distinct ? operands[1..-1] : operands 59 | "SAMPLE(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})" 60 | end 61 | end # Sample 62 | end # Operator 63 | end; end # SPARQL::Algebra 64 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/seconds.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `seconds` operator. 5 | # 6 | # Returns the seconds part of the lexical form of `arg`. 7 | # 8 | # [121] BuiltInCall ::= ... | 'SECONDS' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT ?s (SECONDS(?date) AS ?x) WHERE { 13 | # ?s :date ?date 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: )) 19 | # (project (?s ?x) 20 | # (extend ((?x (seconds ?date))) 21 | # (bgp (triple ?s :date ?date))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-seconds 24 | class Seconds < Operator::Unary 25 | include Evaluatable 26 | 27 | NAME = :seconds 28 | 29 | ## 30 | # Returns the seconds part of arg as an integer. 31 | # 32 | # @param [RDF::Literal] operand 33 | # the operand 34 | # @return [RDF::Literal::Temporal] 35 | # @raise [TypeError] if the operand is not a simple literal 36 | def apply(operand, **options) 37 | raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal) 38 | RDF::Literal(operand.object.second) 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "SECONDS(#{operands.last.to_sparql(**options)})" 48 | end 49 | end # Seconds 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/sha1.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # The SPARQL logical `sha1` operator. 7 | # 8 | # Returns the SHA1 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the `xsd:string`. Hex digits `SHOULD` be in lower case. 9 | # 10 | # [121] BuiltInCall ::= ... | 'SHA1' '(' Expression ')' 11 | # 12 | # @example SPARQL Grammar 13 | # PREFIX : 14 | # SELECT (SHA1(?l) AS ?hash) WHERE { 15 | # :s1 :str ?l 16 | # } 17 | # 18 | # @example SSE 19 | # (prefix ((: )) 20 | # (project (?hash) 21 | # (extend ((?hash (sha1 ?l))) 22 | # (bgp (triple :s1 :str ?l))))) 23 | # 24 | # @see https://www.w3.org/TR/sparql11-query/#func-sha1 25 | class SHA1 < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = :sha1 29 | 30 | ## 31 | # Returns the SHA1 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the xsd:string. Hex digits should be in lower case. 32 | # 33 | # @param [RDF::Literal] operand 34 | # the operand 35 | # @return [RDF::Literal] 36 | # @raise [TypeError] if the operand is not a simple literal 37 | def apply(operand, **options) 38 | raise TypeError, "expected an RDF::Literal, but got #{operand.inspect}" unless operand.literal? 39 | raise TypeError, "expected simple literal or xsd:string, but got #{operand.inspect}" unless (operand.datatype || RDF::XSD.string) == RDF::XSD.string 40 | RDF::Literal(Digest::SHA1.new.hexdigest(operand.to_s)) 41 | end 42 | 43 | ## 44 | # 45 | # Returns a partial SPARQL grammar for this operator. 46 | # 47 | # @return [String] 48 | def to_sparql(**options) 49 | "SHA1(" + operands.to_sparql(**options) + ")" 50 | end 51 | end # SHA1 52 | end # Operator 53 | end; end # SPARQL::Algebra 54 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/sha256.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # The SPARQL logical `sha256` operator. 7 | # 8 | # Returns the SHA256 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the `xsd:string`. Hex digits `SHOULD` be in lower case. 9 | # 10 | # [121] BuiltInCall ::= ... | 'SHA256' '(' Expression ')' 11 | # 12 | # @example SPARQL Grammar 13 | # PREFIX : 14 | # SELECT (SHA256(?l) AS ?hash) WHERE { 15 | # :s1 :str ?l 16 | # } 17 | # 18 | # @example SSE 19 | # (prefix ((: )) 20 | # (project (?hash) 21 | # (extend ((?hash (sha256 ?l))) 22 | # (bgp (triple :s1 :str ?l))))) 23 | # 24 | # @see https://www.w3.org/TR/sparql11-query/#func-sha256 25 | class SHA256 < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = :sha256 29 | 30 | ## 31 | # Returns the SHA256 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the xsd:string. Hex digits should be in lower case. 32 | # 33 | # @param [RDF::Literal] operand 34 | # the operand 35 | # @return [RDF::Literal] 36 | # @raise [TypeError] if the operand is not a simple literal 37 | def apply(operand, **options) 38 | raise TypeError, "expected an RDF::Literal, but got #{operand.inspect}" unless operand.literal? 39 | raise TypeError, "expected simple literal or xsd:string, but got #{operand.inspect}" unless (operand.datatype || RDF::XSD.string) == RDF::XSD.string 40 | RDF::Literal(Digest::SHA256.new.hexdigest(operand.to_s)) 41 | end 42 | 43 | ## 44 | # 45 | # Returns a partial SPARQL grammar for this operator. 46 | # 47 | # @return [String] 48 | def to_sparql(**options) 49 | "SHA256(" + operands.to_sparql(**options) + ")" 50 | end 51 | end # SHA256 52 | end # Operator 53 | end; end # SPARQL::Algebra 54 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/sha384.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # The SPARQL logical `sha1` operator. 7 | # 8 | # Returns the SHA384 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the `xsd:string`. Hex digits `SHOULD` be in lower case. 9 | # 10 | # [121] BuiltInCall ::= ... | 'SHA384' '(' Expression ')' 11 | # 12 | # @example SPARQL Grammar 13 | # PREFIX : 14 | # SELECT (SHA384(?l) AS ?hash) WHERE { 15 | # :s1 :str ?l 16 | # } 17 | # 18 | # @example SSE 19 | # (prefix ((: )) 20 | # (project (?hash) 21 | # (extend ((?hash (sha384 ?l))) 22 | # (bgp (triple :s1 :str ?l))))) 23 | # 24 | # @see https://www.w3.org/TR/sparql11-query/#func-sha384 25 | class SHA384 < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = :sha384 29 | 30 | ## 31 | # Returns the SHA384 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the xsd:string. Hex digits should be in lower case. 32 | # 33 | # @param [RDF::Literal] operand 34 | # the operand 35 | # @return [RDF::Literal] 36 | # @raise [TypeError] if the operand is not a simple literal 37 | def apply(operand, **options) 38 | raise TypeError, "expected an RDF::Literal, but got #{operand.inspect}" unless operand.literal? 39 | raise TypeError, "expected simple literal or xsd:string, but got #{operand.inspect}" unless (operand.datatype || RDF::XSD.string) == RDF::XSD.string 40 | RDF::Literal(Digest::SHA384.new.hexdigest(operand.to_s)) 41 | end 42 | 43 | ## 44 | # 45 | # Returns a partial SPARQL grammar for this operator. 46 | # 47 | # @return [String] 48 | def to_sparql(**options) 49 | "SHA384(" + operands.to_sparql(**options) + ")" 50 | end 51 | end # SHA384 52 | end # Operator 53 | end; end # SPARQL::Algebra 54 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/sha512.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # The SPARQL logical `sha512` operator. 7 | # 8 | # Returns the SHA512 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the `xsd:string`. Hex digits `SHOULD` be in lower case. 9 | # 10 | # [121] BuiltInCall ::= ... | 'SHA512' '(' Expression ')' 11 | # 12 | # @example SPARQL Grammar 13 | # PREFIX : 14 | # SELECT (SHA512(?l) AS ?hash) WHERE { 15 | # :s1 :str ?l 16 | # } 17 | # 18 | # @example SSE 19 | # (prefix ((: )) 20 | # (project (?hash) 21 | # (extend ((?hash (sha512 ?l))) 22 | # (bgp (triple :s1 :str ?l))))) 23 | # 24 | # @see https://www.w3.org/TR/sparql11-query/#func-sha512 25 | class SHA512 < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = :sha512 29 | 30 | ## 31 | # Returns the SHA512 checksum, as a hex digit string, calculated on the UTF-8 representation of the simple literal or lexical form of the xsd:string. Hex digits should be in lower case. 32 | # 33 | # @param [RDF::Literal] operand 34 | # the operand 35 | # @return [RDF::Literal] 36 | # @raise [TypeError] if the operand is not a simple literal 37 | def apply(operand, **options) 38 | raise TypeError, "expected an RDF::Literal, but got #{operand.inspect}" unless operand.literal? 39 | raise TypeError, "expected simple literal or xsd:string, but got #{operand.inspect}" unless (operand.datatype || RDF::XSD.string) == RDF::XSD.string 40 | RDF::Literal(Digest::SHA512.new.hexdigest(operand.to_s)) 41 | end 42 | 43 | ## 44 | # 45 | # Returns a partial SPARQL grammar for this operator. 46 | # 47 | # @return [String] 48 | def to_sparql(**options) 49 | "SHA512(" + operands.to_sparql(**options) + ")" 50 | end 51 | end # SHA512 52 | end # Operator 53 | end; end # SPARQL::Algebra 54 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/str.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `str` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'STR' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX xsd: 10 | # PREFIX : 11 | # SELECT ?x ?v 12 | # WHERE 13 | # { ?x :p ?v . 14 | # FILTER ( str(?v) = "1" ) . 15 | # } 16 | # 17 | # @example SSE 18 | # (prefix ((xsd: ) 19 | # (: )) 20 | # (project (?x ?v) 21 | # (filter (= (str ?v) "1") 22 | # (bgp (triple ?x :p ?v))))) 23 | # 24 | # @see https://www.w3.org/TR/sparql11-query/#func-str 25 | class Str < Operator::Unary 26 | include Evaluatable 27 | 28 | NAME = :str 29 | 30 | ## 31 | # Returns the string form of the operand. 32 | # 33 | # @param [RDF::Literal, RDF::URI] term 34 | # a literal or IRI 35 | # @return [RDF::Literal] a simple literal 36 | # @raise [TypeError] if the operand is not a literal or IRI 37 | def apply(term, **options) 38 | case term 39 | when RDF::Literal then RDF::Literal(term.value) 40 | when RDF::URI then RDF::Literal(term.to_s) 41 | else raise TypeError, "expected an RDF::Literal or RDF::URI, but got #{term.inspect}" 42 | end 43 | end 44 | 45 | ## 46 | # 47 | # Returns a partial SPARQL grammar for this operator. 48 | # 49 | # @return [String] 50 | def to_sparql(**options) 51 | "str(" + operands.first.to_sparql(**options) + ")" 52 | end 53 | end # Str 54 | end # Operator 55 | end; end # SPARQL::Algebra 56 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/strdt.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `strdt` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'STRDT' '(' Expression ',' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # PREFIX xsd: 11 | # SELECT ?s (STRDT(?str,xsd:string) AS ?str1) WHERE { 12 | # ?s :str ?str 13 | # FILTER(LANGMATCHES(LANG(?str), "en")) 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: ) (xsd: )) 19 | # (project (?s ?str1) 20 | # (extend ((?str1 (strdt ?str xsd:string))) 21 | # (filter (langMatches (lang ?str) "en") 22 | # (bgp (triple ?s :str ?str)))))) 23 | # 24 | # @see https://www.w3.org/TR/sparql11-query/#func-strdt 25 | class StrDT < Operator::Binary 26 | include Evaluatable 27 | 28 | NAME = :strdt 29 | 30 | ## 31 | # Constructs a literal with lexical form and type as specified by the arguments. 32 | # 33 | # @param [RDF::Literal] value 34 | # a literal 35 | # @param [RDF::URI] datatypeIRI 36 | # datatype 37 | # @return [RDF::Literal] a datatyped literal 38 | # @see https://www.w3.org/TR/sparql11-query/#func-strdt 39 | def apply(value, datatypeIRI, **options) 40 | raise TypeError, "Literal #{value.inspect} is not simple" unless value.simple? 41 | RDF::Literal.new(value.to_s, datatype: datatypeIRI) 42 | end 43 | 44 | ## 45 | # 46 | # Returns a partial SPARQL grammar for this operator. 47 | # 48 | # @return [String] 49 | def to_sparql(**options) 50 | "STRDT(" + operands.to_sparql(delimiter: ', ', **options) + ")" 51 | end 52 | end # StrDT 53 | end # Operator 54 | end; end # SPARQL::Algebra 55 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/strlang.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `strlang` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'STRLANG' '(' Expression ',' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?s (STRLANG(?str,"en-US") AS ?s2) WHERE { 11 | # ?s :str ?str 12 | # FILTER(LANGMATCHES(LANG(?str), "en")) 13 | # } 14 | # 15 | # @example SSE 16 | # (prefix ((: )) 17 | # (project (?s ?s2) 18 | # (extend ((?s2 (strlang ?str "en-US"))) 19 | # (filter (langMatches (lang ?str) "en") 20 | # (bgp (triple ?s :str ?str)))))) 21 | # 22 | # @see https://www.w3.org/TR/sparql11-query/#func-strlang 23 | class StrLang < Operator::Binary 24 | include Evaluatable 25 | 26 | NAME = :strlang 27 | 28 | ## 29 | # Constructs a literal with lexical form and type as specified by the arguments. 30 | # 31 | # @param [RDF::Literal] value 32 | # a literal 33 | # @param [RDF::Literal] langTag 34 | # datatype 35 | # @return [RDF::Literal] a datatyped literal 36 | # @see https://www.w3.org/TR/sparql11-query/#func-strlang 37 | def apply(value, langTag, **options) 38 | raise TypeError, "Literal #{value.inspect} is not simple" unless value.simple? 39 | RDF::Literal.new(value.to_s, language: langTag.to_s) 40 | end 41 | 42 | ## 43 | # 44 | # Returns a partial SPARQL grammar for this operator. 45 | # 46 | # @return [String] 47 | def to_sparql(**options) 48 | "STRLANG(" + operands.to_sparql(delimiter: ', ', **options) + ")" 49 | end 50 | end # StrLang 51 | end # Operator 52 | end; end # SPARQL::Algebra 53 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/strlen.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `strlen` operator. 5 | # 6 | # [121] BuiltInCall ::= ... 'STRLEN' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?str (STRLEN(?str) AS ?len) WHERE { 11 | # ?s :str ?str 12 | # } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((: )) 17 | # (project (?str ?len) 18 | # (extend ((?len (strlen ?str))) 19 | # (bgp (triple ?s :str ?str))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#func-strlen 22 | # @see https://www.w3.org/TR/xpath-functions/#func-string-length 23 | class StrLen < Operator::Unary 24 | include Evaluatable 25 | 26 | NAME = :strlen 27 | 28 | ## 29 | # The strlen function corresponds to the XPath fn:string-length function and returns an xsd:integer equal to the length in characters of the lexical form of the literal. 30 | # 31 | # @example 32 | # strlen("chat") 4 33 | # strlen("chat"@en) 4 34 | # strlen("chat"^^xsd:string) 4 35 | # 36 | # @param [RDF::Literal] operand 37 | # the operand 38 | # @return [RDF::Literal::Integer] length of string 39 | # @raise [TypeError] if the operand is not a numeric value 40 | def apply(operand, **options) 41 | raise TypeError, "expected a plain RDF::Literal, but got #{operand.inspect}" unless operand.literal? && operand.plain? 42 | RDF::Literal(operand.to_s.length) 43 | end 44 | 45 | ## 46 | # 47 | # Returns a partial SPARQL grammar for this operator. 48 | # 49 | # @return [String] 50 | def to_sparql(**options) 51 | "STRLEN(" + operands.to_sparql(delimiter: ', ', **options) + ")" 52 | end 53 | end # StrLen 54 | end # Operator 55 | end; end # SPARQL::Algebra 56 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/struuid.rb: -------------------------------------------------------------------------------- 1 | require 'securerandom' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # [121] BuiltInCall ::= ... | 'STRUUID' NIL 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # PREFIX xsd: 11 | # SELECT (STRLEN(?uuid) AS ?length) 12 | # WHERE { 13 | # BIND(STRUUID() AS ?uuid) 14 | # FILTER(ISLITERAL(?uuid) && REGEX(?uuid, "^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$", "i")) 15 | # } 16 | # 17 | # @example SSE 18 | # (prefix ((: ) (xsd: )) 19 | # (project (?length) 20 | # (extend ((?length (strlen ?uuid))) 21 | # (filter 22 | # (&& 23 | # (isLiteral ?uuid) 24 | # (regex ?uuid "^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$" "i")) 25 | # (extend ((?uuid (struuid))) 26 | # (bgp)))))) 27 | # 28 | # @see https://www.w3.org/TR/sparql11-query/#func-struuid 29 | class StrUUID < Operator::Nullary 30 | include Evaluatable 31 | 32 | NAME = :struuid 33 | 34 | ## 35 | # Return a string that is the scheme specific part of UUID. That is, as a simple literal, the result of generating a UUID, converting to a simple literal and removing the initial urn:uuid:. 36 | # 37 | # @return [RDF::URI] 38 | def apply(**options) 39 | RDF::Literal(SecureRandom.uuid) 40 | end 41 | 42 | ## 43 | # 44 | # Returns a partial SPARQL grammar for this operator. 45 | # 46 | # @return [String] 47 | def to_sparql(**options) 48 | "STRUUID(" + operands.to_sparql(delimiter: ', ', **options) + ")" 49 | end 50 | end # StrUUID 51 | end # Operator 52 | end; end # SPARQL::Algebra 53 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/subject.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `SUBJECT` operator. 5 | # 6 | # Returns the subject part of `arg` as a term. 7 | # 8 | # If triple is an RDF-star triple, the function returns the subject of this triple. Passing anything other than an RDF-star triple is an error. 9 | # 10 | # [121] BuiltInCall ::= ... | 'SUBJECT' '(' Expression ')' 11 | # 12 | # @example SPARQL Grammar 13 | # PREFIX : 14 | # SELECT * { 15 | # ?t :source :g 16 | # FILTER(isTriple(?t)) 17 | # FILTER(SUBJECT(?t) = :s) 18 | # FILTER(PREDICATE(?t) = :p) 19 | # FILTER(OBJECT(?t) = :o) 20 | # } 21 | # 22 | # @example SSE 23 | # (prefix 24 | # ((: )) 25 | # (filter 26 | # (exprlist 27 | # (isTRIPLE ?t) 28 | # (= (subject ?t) :s) 29 | # (= (predicate ?t) :p) 30 | # (= (object ?t) :o)) 31 | # (bgp (triple ?t :source :g))) ) 32 | # 33 | # @see https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#subject 34 | class Subject < Operator::Unary 35 | include Evaluatable 36 | 37 | NAME = :subject 38 | 39 | ## 40 | # Returns the subject part of arg. 41 | # 42 | # @param [RDF::Statement] operand 43 | # the operand 44 | # @return [RDF::Literal] 45 | # @raise [TypeError] if the operand is not a statement 46 | def apply(operand, **options) 47 | raise TypeError, "expected an RDF::Statement, but got #{operand.inspect}" unless operand.is_a?(RDF::Statement) 48 | operand.subject 49 | end 50 | 51 | ## 52 | # 53 | # Returns a partial SPARQL grammar for this operator. 54 | # 55 | # @return [String] 56 | def to_sparql(**options) 57 | "SUBJECT(" + operands.last.to_sparql(**options) + ")" 58 | end 59 | end # Subject 60 | end # Operator 61 | end; end # SPARQL::Algebra 62 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/subtract.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL numeric `subtract` operator. 5 | # 6 | # [116] AdditiveExpression ::= MultiplicativeExpression ( '-' MultiplicativeExpression )? 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?s WHERE { 11 | # ?s :p ?o . 12 | # ?s2 :p ?o2 . 13 | # FILTER(?o - ?o2 = 3) . 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: )) 19 | # (project (?s) 20 | # (filter (= (- ?o ?o2) 3) 21 | # (bgp 22 | # (triple ?s :p ?o) 23 | # (triple ?s2 :p ?o2))))) 24 | # 25 | # @see https://www.w3.org/TR/xpath-functions/#func-numeric-subtract 26 | class Subtract < Operator::Binary 27 | include Evaluatable 28 | 29 | NAME = [:-, :subtract] 30 | 31 | ## 32 | # Returns the arithmetic difference of the operands. 33 | # 34 | # @param [RDF::Literal::Numeric, RDF::Literal::Temporal] left 35 | # a numeric literal 36 | # @param [RDF::Literal::Numeric, RDF::Literal::Temporal, RDF::Literal::Duration] right 37 | # a numeric literal 38 | # @return [RDF::Literal::Numeric, RDF::Literal::Temporal, RDF::Literal::Duration] 39 | # @raise [TypeError] if either operand is neither a numeric nor a temporal literal 40 | def apply(left, right, **options) 41 | case 42 | when left.is_a?(RDF::Literal::Numeric) && right.is_a?(RDF::Literal::Numeric) 43 | left - right 44 | when left.is_a?(RDF::Literal::Temporal) && right.is_a?(RDF::Literal::Temporal) 45 | left - right 46 | when left.is_a?(RDF::Literal::Temporal) && right.is_a?(RDF::Literal::Duration) 47 | left - right 48 | else raise TypeError, "expected two RDF::Literal::Numeric, Temporal operands, or a Temporal and a Duration, but got #{left.inspect} and #{right.inspect}" 49 | end 50 | end 51 | 52 | ## 53 | # 54 | # Returns a partial SPARQL grammar for this operator. 55 | # 56 | # @return [String] 57 | def to_sparql(**options) 58 | "(#{operands.first.to_sparql(**options)} - #{operands.last.to_sparql(**options)})" 59 | end 60 | end # Subtract 61 | end # Operator 62 | end; end # SPARQL::Algebra 63 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/sum.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `sum` set function. 5 | # 6 | # [127] Aggregate::= ... | 'SUM' '(' 'DISTINCT'? Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT (SUM(?o) AS ?sum) 11 | # WHERE { ?s :dec ?o } 12 | # 13 | # @example SSE 14 | # (prefix ((: )) 15 | # (project (?sum) 16 | # (extend ((?sum ??.0)) 17 | # (group () ((??.0 (sum ?o))) 18 | # (bgp (triple ?s :dec ?o)))))) 19 | # 20 | # @see https://www.w3.org/TR/sparql11-query/#defn_aggSum 21 | class Sum < Operator 22 | include Aggregate 23 | 24 | NAME = :sum 25 | 26 | ## 27 | # Sum is a SPARQL set function that will return the numeric value obtained by summing the values within the aggregate group. Type promotion happens as per the op:numeric-add function, applied transitively, (see definition below) so the value of SUM(?x), in an aggregate group where ?x has values 1 (integer), 2.0e0 (float), and 3.0 (decimal) will be 6.0 (float). 28 | # 29 | # @param [Enumerable>] enum 30 | # enum of evaluated operand 31 | # @return [RDF::Literal::Numeric] The sum of the terms 32 | def apply(enum, **options) 33 | # FIXME: we don't actually do anything with distinct 34 | operands.shift if distinct = (operands.first == :distinct) 35 | if enum.empty? 36 | RDF::Literal(0) 37 | elsif enum.flatten.all? {|n| n.is_a?(RDF::Literal::Numeric)} 38 | enum.flatten.reduce(:+) 39 | else 40 | raise TypeError, "Averaging non-numeric types: #{enum.flatten}" 41 | end 42 | end 43 | 44 | ## 45 | # 46 | # Returns a partial SPARQL grammar for this operator. 47 | # 48 | # @return [String] 49 | def to_sparql(**options) 50 | distinct = operands.first == :distinct 51 | args = distinct ? operands[1..-1] : operands 52 | "SUM(#{'DISTINCT ' if distinct}#{args.to_sparql(**options)})" 53 | end 54 | end # Sum 55 | end # Operator 56 | end; end # SPARQL::Algebra 57 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/timezone.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `timezone` operator. 5 | # 6 | # Returns the timezone part of `arg` as an xsd:dayTimeDuration. Raises an error if there is no timezone. 7 | # 8 | # [121] BuiltInCall ::= ... | 'TIMEZONE' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT ?s (TIMEZONE(?date) AS ?x) WHERE { 13 | # ?s :date ?date 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: )) 19 | # (project (?s ?x) 20 | # (extend ((?x (timezone ?date))) 21 | # (bgp (triple ?s :date ?date))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-timezone 24 | class Timezone < Operator::Unary 25 | include Evaluatable 26 | 27 | NAME = :timezone 28 | 29 | ## 30 | # Returns the timezone part of arg as an xsd:dayTimeDuration. Raises an error if there is no timezone. 31 | # 32 | # This function corresponds to fn:timezone-from-dateTime except for the treatment of literals with no timezone. 33 | # 34 | # @param [RDF::Literal] operand 35 | # the operand 36 | # @return [RDF::Literal::Temporal] 37 | # @raise [TypeError] if the operand is not a simple literal 38 | def apply(operand, **options) 39 | raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal) 40 | raise TypeError, "literal has no timezone" unless res = operand.timezone 41 | res 42 | end 43 | 44 | ## 45 | # 46 | # Returns a partial SPARQL grammar for this operator. 47 | # 48 | # @return [String] 49 | def to_sparql(**options) 50 | "TIMEZONE(" + operands.to_sparql(**options) + ")" 51 | end 52 | end # Timezone 53 | end # Operator 54 | end; end # SPARQL::Algebra 55 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/triple.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL `triple` operator. 5 | # 6 | # If the 3-tuple (term1, term2, term3) is an RDF-star triple, the function returns this triple. If the 3-tuple is not an RDF-star triple, then the function raises an error. 7 | # 8 | # [121] BuiltInCall ::= ... | 'TRIPLE' '(' Expression ',' Expression ',' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT * { 13 | # ?s ?p ?o . 14 | # BIND(TRIPLE(?s, ?p, ?o) AS ?t1) 15 | # } 16 | # 17 | # @example SSE 18 | # (prefix ((: )) 19 | # (extend ((?t1 (triple ?s ?p ?o))) 20 | # (bgp (triple ?s ?p ?o)))) 21 | # 22 | # @note This operator overlaps with RDF::Query::Pattern as used as an operand to a BGP. 23 | # @see https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#triple 24 | class Triple < Operator::Ternary 25 | include Evaluatable 26 | 27 | NAME = :triple 28 | 29 | ## 30 | # @param [RDF::Term] subject 31 | # @param [RDF::Term] predicate 32 | # @param [RDF::Term] object 33 | # @return [RDF::URI] 34 | # @raise [TypeError] if the operand is not a simple literal 35 | def apply(subject, predicate, object, **options) 36 | triple = RDF::Statement(subject, predicate, object) 37 | raise TypeError, "valid components, but got #{triple.inspect}" unless triple.valid? 38 | triple 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "TRIPLE(#{operands.to_sparql(delimiter: ', ', **options)})" 48 | end 49 | end # Triple 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/tz.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `tz` operator. 5 | # 6 | # Returns the timezone part of `arg` as a simple literal. Returns the empty string if there is no timezone. 7 | # 8 | # [121] BuiltInCall ::= ... | 'TZ' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT ?s (TZ(?date) AS ?x) WHERE { 13 | # ?s :date ?date 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: )) 19 | # (project (?s ?x) 20 | # (extend ((?x (tz ?date))) 21 | # (bgp (triple ?s :date ?date))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-tz 24 | class TZ < Operator::Unary 25 | include Evaluatable 26 | 27 | NAME = :tz 28 | 29 | ## 30 | # Returns the timezone part of arg as a simple literal. Returns the empty string if there is no timezone. 31 | # 32 | # @param [RDF::Literal::Temporal] operand 33 | # the operand 34 | # @return [RDF::Literal] 35 | # @raise [TypeError] if the operand is not a simple literal 36 | def apply(operand, **options) 37 | raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal) 38 | operand.tz 39 | end 40 | ## 41 | # 42 | # Returns a partial SPARQL grammar for this operator. 43 | # 44 | # @return [String] 45 | def to_sparql(**options) 46 | "TZ(" + operands.to_sparql(**options) + ")" 47 | end 48 | end # TZ 49 | end # Operator 50 | end; end # SPARQL::Algebra 51 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/ucase.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `ucase` operator. 5 | # 6 | # [121] BuiltInCall ::= ... | 'UCASE' '(' Expression ')' 7 | # 8 | # @example SPARQL Grammar 9 | # PREFIX : 10 | # SELECT ?s (UCASE(?str) AS ?ustr) WHERE { 11 | # ?s :str ?str 12 | # } 13 | # 14 | # @example SSE 15 | # (prefix 16 | # ((: )) 17 | # (project (?s ?ustr) 18 | # (extend ((?ustr (ucase ?str))) 19 | # (bgp (triple ?s :str ?str))))) 20 | # 21 | # @see https://www.w3.org/TR/sparql11-query/#func-ucase 22 | # @see https://www.w3.org/TR/xpath-functions/#func-ucase 23 | class UCase < Operator::Unary 24 | include Evaluatable 25 | 26 | NAME = :ucase 27 | 28 | ## 29 | # The LCASE function corresponds to the XPath fn:lower-case function. It returns a string literal whose lexical form is the lower case of the lexcial form of the argument. 30 | # 31 | # @param [RDF::Literal] operand 32 | # the operand 33 | # @return [RDF::Literal] literal of same type 34 | # @raise [TypeError] if the operand is not a literal value 35 | def apply(operand, **options) 36 | case operand 37 | when RDF::Literal then RDF::Literal(operand.to_s.upcase, datatype: operand.datatype, language: operand.language) 38 | else raise TypeError, "expected an RDF::Literal::Numeric, but got #{operand.inspect}" 39 | end 40 | end 41 | 42 | ## 43 | # 44 | # Returns a partial SPARQL grammar for this operator. 45 | # 46 | # @return [String] 47 | def to_sparql(**options) 48 | "UCASE(" + operands.last.to_sparql(**options) + ")" 49 | end 50 | end # UCase 51 | end # Operator 52 | end; end # SPARQL::Algebra 53 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/uuid.rb: -------------------------------------------------------------------------------- 1 | require 'securerandom' 2 | 3 | module SPARQL; module Algebra 4 | class Operator 5 | ## 6 | # The SPARQL `uuid` function. 7 | # 8 | # [121] BuiltInCall ::= ... | 'UUID' NIL 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # PREFIX xsd: 13 | # ASK { 14 | # BIND(UUID() AS ?u1) 15 | # BIND(UUID() AS ?u2) 16 | # FILTER(?u1 != ?u2) 17 | # } 18 | # 19 | # @example SSE 20 | # (prefix 21 | # ((: ) (xsd: )) 22 | # (ask 23 | # (filter (!= ?u1 ?u2) 24 | # (extend ((?u1 (uuid)) (?u2 (uuid))) 25 | # (bgp))))) 26 | # 27 | # @see https://www.w3.org/TR/sparql11-query/#func-uuid 28 | class UUID < Operator::Nullary 29 | include Evaluatable 30 | 31 | NAME = :uuid 32 | 33 | ## 34 | # Return a fresh IRI from the UUID URN scheme. Each call of UUID() returns a different UUID. It must not be the "nil" UUID (all zeroes). The variant and version of the UUID is implementation dependent. 35 | # 36 | # @return [RDF::URI] 37 | def apply(**options) 38 | RDF::URI("urn:uuid:#{SecureRandom.uuid}") 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "UUID(" + operands.to_sparql(delimiter: ', ', **options) + ")" 48 | end 49 | end # UUID 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/operator/year.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | class Operator 3 | ## 4 | # The SPARQL logical `year` operator. 5 | # 6 | # Returns the year part of `arg` as an integer. 7 | # 8 | # [121] BuiltInCall ::= ... | 'YEAR' '(' Expression ')' 9 | # 10 | # @example SPARQL Grammar 11 | # PREFIX : 12 | # SELECT ?s (YEAR(?date) AS ?x) WHERE { 13 | # ?s :date ?date 14 | # } 15 | # 16 | # @example SSE 17 | # (prefix 18 | # ((: )) 19 | # (project (?s ?x) 20 | # (extend ((?x (year ?date))) 21 | # (bgp (triple ?s :date ?date))))) 22 | # 23 | # @see https://www.w3.org/TR/sparql11-query/#func-year 24 | class Year < Operator::Unary 25 | include Evaluatable 26 | 27 | NAME = :year 28 | 29 | ## 30 | # Returns the year part of arg as an integer. 31 | # 32 | # @param [RDF::Literal] operand 33 | # the operand 34 | # @return [RDF::Literal::Temporal] 35 | # @raise [TypeError] if the operand is not a simple literal 36 | def apply(operand, **options) 37 | raise TypeError, "expected an RDF::Literal::Temporal, but got #{operand.inspect}" unless operand.is_a?(RDF::Literal::Temporal) 38 | RDF::Literal(operand.object.year) 39 | end 40 | 41 | ## 42 | # 43 | # Returns a partial SPARQL grammar for this operator. 44 | # 45 | # @return [String] 46 | def to_sparql(**options) 47 | "YEAR(#{operands.last.to_sparql(**options)})" 48 | end 49 | end # Year 50 | end # Operator 51 | end; end # SPARQL::Algebra 52 | -------------------------------------------------------------------------------- /lib/sparql/algebra/sxp_extensions.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # Extensions for Ruby's `NilClass` class. 3 | class NilClass 4 | ## 5 | # Returns the SXP representation of this object. 6 | # 7 | # @return [String] 8 | def to_sxp(**options) 9 | RDF.nil.to_s 10 | end 11 | end 12 | 13 | ## 14 | # Extensions for Ruby's `FalseClass` class. 15 | class FalseClass 16 | ## 17 | # Returns the SXP representation of this object. 18 | # 19 | # @return [String] 20 | def to_sxp(**options) 21 | 'false' 22 | end 23 | end 24 | 25 | ## 26 | # Extensions for Ruby's `TrueClass` class. 27 | class TrueClass 28 | ## 29 | # Returns the SXP representation of this object. 30 | # 31 | # @return [String] 32 | def to_sxp(**options) 33 | 'true' 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/sparql/algebra/update.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | ## 3 | # A SPARQL algebra Update can make modifications to it's dataset 4 | # 5 | # Mixin with SPARQL::Algebra::Operator to provide update-like operations on graphs 6 | # 7 | # @abstract 8 | module Update 9 | ## 10 | # Prepends an operator. 11 | # 12 | # @param [RDF::Query] query 13 | # a query 14 | # @return [void] self 15 | def unshift(query) 16 | @operands.unshift(query) 17 | self 18 | end 19 | 20 | ## 21 | # The variables used in this update. 22 | # 23 | # @return [Hash{Symbol => RDF::Query::Variable}] 24 | def variables 25 | operands.inject({}) {|hash, o| o.executable? ? hash.merge(o.variables) : hash} 26 | end 27 | 28 | ## 29 | # Executes this upate on the given `queryable` graph or repository. 30 | # 31 | # @param [RDF::Writable] queryable 32 | # the graph or repository to write 33 | # @param [Hash{Symbol => Object}] options 34 | # any additional keyword options 35 | # @option options [Boolean] debug 36 | # Query execution debugging 37 | # @return [RDF::Writable] 38 | # Returns the dataset. 39 | # @raise [NotImplementedError] 40 | # If an attempt is made to perform an unsupported operation 41 | # @see https://www.w3.org/TR/sparql11-update/ 42 | def execute(queryable, **options, &block) 43 | raise NotImplementedError, "#{self.class}#execute(#{queryable})" 44 | end 45 | 46 | # Add graph_name to sub-items, unless they already have a graph_name 47 | # @param [RDF::URI, RDF::Query::Variable] value 48 | # @return [RDF::URI, RDF::Query::Variable] 49 | def graph_name=(value) 50 | operands.each do |operand| 51 | operand.graph_name = value if operand.respond_to?(:graph_name) && operand.graph_name != false 52 | end 53 | value 54 | end 55 | end # Query 56 | end; end # SPARQL::Algebra 57 | -------------------------------------------------------------------------------- /lib/sparql/algebra/version.rb: -------------------------------------------------------------------------------- 1 | module SPARQL; module Algebra 2 | module VERSION 3 | VERSION_FILE = File.expand_path("../../../../VERSION", __FILE__) 4 | MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chop.split(".") 5 | 6 | STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.') 7 | 8 | ## 9 | # @return [String] 10 | def self.to_s() STRING end 11 | 12 | ## 13 | # @return [String] 14 | def self.to_str() STRING end 15 | 16 | ## 17 | # @return [Array(Integer, Integer, Integer)] 18 | def self.to_a() [MAJOR, MINOR, TINY] end 19 | end 20 | end; end 21 | -------------------------------------------------------------------------------- /lib/sparql/version.rb: -------------------------------------------------------------------------------- 1 | module SPARQL 2 | module VERSION 3 | VERSION_FILE = File.expand_path("../../../VERSION", __FILE__) 4 | MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chomp.split(".") 5 | 6 | STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.') 7 | 8 | ## 9 | # @return [String] 10 | def self.to_s() STRING end 11 | 12 | ## 13 | # @return [String] 14 | def self.to_str() STRING end 15 | 16 | ## 17 | # @return [Array(Integer, Integer, Integer)] 18 | def self.to_a() STRING.split(".") end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /sinatra_rackup.rb: -------------------------------------------------------------------------------- 1 | # Sinatra example 2 | # 3 | # Call as http://localhost:4567/sparql?query=uri, 4 | # where `uri` is the URI of a SPARQL query, or 5 | # a URI-escaped SPARQL query, for example: 6 | # http://localhost:4567/?query=SELECT%20?s%20?p%20?o%20WHERE%20%7B?s%20?p%20?o%7D 7 | require 'sinatra' 8 | require 'sinatra/sparql' 9 | require 'uri' 10 | 11 | get '/' do 12 | settings.sparql_options.merge!(standard_prefixes: true) 13 | repository = RDF::Repository.new do |graph| 14 | graph << [RDF::Node.new, RDF::Vocab::DC.title, "Hello, world!"] 15 | end 16 | if params["query"] 17 | query = query.to_s =~ /^\w:/ ? RDF::Util::File.open_file(params["query"]) : CGI.unescape(params["query"].to_s) 18 | SPARQL.execute(query, repository) 19 | else 20 | service_description(repo: repository) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/.gitignore: -------------------------------------------------------------------------------- 1 | /rdf-star 2 | /rdf-tests 3 | -------------------------------------------------------------------------------- /spec/algebra/algebra_helper.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'sxp' # @see https://rubygems.org/gems/sxp 3 | rescue LoadError 4 | abort "SPARQL::Algebra specs require the SXP gem (hint: `gem install sxp')." 5 | end 6 | 7 | def sse_examples(filename) 8 | input = File.read(File.join(File.dirname(__FILE__), filename)) 9 | input.gsub!(/\s\-INF/, " \"-INF\"^^<#{RDF::XSD.double}>") # Shorthand 10 | input.gsub!(/\s\+INF/, " \"INF\"^^<#{RDF::XSD.double}>") # Shorthand 11 | input.gsub!(/\sNaN/, " \"NaN\"^^<#{RDF::XSD.double}>") # Shorthand 12 | datatypes = { 13 | 'rdf:langString' => RDF.langString, 14 | 'xsd:double' => RDF::XSD.double, 15 | 'xsd:float' => RDF::XSD.float, 16 | 'xsd:integer' => RDF::XSD.integer, 17 | 'xsd:int' => RDF::XSD.int, 18 | 'xsd:decimal' => RDF::XSD.decimal, 19 | 'xsd:string' => RDF::XSD.string, 20 | 'xsd:token' => RDF::XSD.token, 21 | 'xsd:boolean' => RDF::XSD.boolean, 22 | 'xsd:dateTime' => RDF::XSD.dateTime, 23 | } 24 | datatypes.each { |qname, uri| input.gsub!(qname, "<#{uri}>") } # Shorthand 25 | examples = SXP::Reader::SPARQL.read_all(input) 26 | examples.inject({}) do |result, (tag, input, output)| 27 | output = case output 28 | when :TypeError then TypeError 29 | when :ZeroDivisionError then ZeroDivisionError 30 | else output 31 | end 32 | result.merge(input => output) 33 | end 34 | end 35 | 36 | shared_examples "Evaluate" do |examples| 37 | examples.each do |input, output| 38 | if output.is_a?(Class) 39 | it "raises #{output.inspect} for .evaluate(#{input.to_sse})" do 40 | expect { described_class.evaluate(*input[1..-1]) }.to raise_error(output) 41 | end 42 | else 43 | it "returns #{repr(output)} for .evaluate(#{input.to_sse})" do 44 | result = described_class.evaluate(*input[1..-1]) 45 | if output.is_a?(RDF::Literal::Double) && output.nan? 46 | expect(result).to be_nan 47 | else 48 | expect(result).to eq output 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/algebra/ask_spec.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("../..", __FILE__) 2 | require 'spec_helper' 3 | require 'algebra/algebra_helper' 4 | require 'sparql/client' 5 | 6 | include SPARQL::Algebra 7 | 8 | describe SPARQL::Algebra::Query do 9 | EX = RDF::EX = RDF::Vocabulary.new('http://example.org/') unless const_defined?(:EX) 10 | 11 | context "ask" do 12 | { 13 | "data-r2/as/ask-1" => [ 14 | %q{ 15 | @prefix : . 16 | @prefix xsd: . 17 | 18 | :x :p "1"^^xsd:integer . 19 | :x :p "2"^^xsd:integer . 20 | :x :p "3"^^xsd:integer . 21 | 22 | :y :p :a . 23 | :a :q :r . 24 | }, 25 | %q{ 26 | (prefix ((: )) 27 | (ask 28 | (bgp (triple :x :p 1)))) 29 | }, 30 | ], 31 | }.each do |example, (source, query)| 32 | it "passes #{example}" do 33 | expect( 34 | sparql_query( 35 | form: :ask, sse: true, 36 | graphs: {default: {data: source, format: :ttl}}, 37 | query: query) 38 | ).to eq RDF::Literal::TRUE 39 | end 40 | 41 | it "passes #{example} (with optimimzation)" do 42 | expect( 43 | sparql_query( 44 | form: :ask, sse: true, optimize: true, 45 | graphs: {default: {data: source, format: :ttl}}, 46 | query: query) 47 | ).to eq RDF::Literal::TRUE 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/algebra/extensions_spec.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("../..", __FILE__) 2 | require 'spec_helper' 3 | require 'algebra/algebra_helper' 4 | require 'sparql/algebra/sxp_extensions' 5 | 6 | describe "Core objects #to_sxp" do 7 | [ 8 | [nil, RDF.nil], 9 | [false, 'false'], 10 | [true, 'true'], 11 | ].each do |(value, result)| 12 | it "returns #{result.inspect} for #{value.inspect}" do 13 | expect(value.to_sxp).to eq result 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/algebra/operator/and/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # logical-and(EBV(A), EBV(B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#ebv 6 | # @see http://www.w3.org/TR/sparql11-query/#func-logical-and 7 | 8 | (equal (and true true) true) 9 | (equal (and true false) false) 10 | (equal (and false true) false) 11 | (equal (and false false) false) 12 | -------------------------------------------------------------------------------- /spec/algebra/operator/bound/variable.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # bound(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-bound 6 | 7 | (error (bound _:foobar) TypeError) 8 | (error (bound ) TypeError) 9 | (error (bound true) TypeError) 10 | -------------------------------------------------------------------------------- /spec/algebra/operator/datatype/literal.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # datatype(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-datatype 6 | 7 | (equal (datatype "Hello") xsd:string) 8 | 9 | (equal (datatype "Hello"^^xsd:string) xsd:string) 10 | (equal (datatype "Hello"^^xsd:token) xsd:token) 11 | (equal (datatype "Hello"@en) rdf:langString) 12 | 13 | (error (datatype _:foobar) TypeError) 14 | (error (datatype ) TypeError) 15 | -------------------------------------------------------------------------------- /spec/algebra/operator/divide/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-divide(A, B) 3 | # 4 | # Returns the arithmetic quotient of the operands. 5 | # 6 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 7 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-divide 8 | 9 | (error (/ 1 +0) ZeroDivisionError) 10 | (error (/ 1 +0.0) ZeroDivisionError) 11 | (error (/ 1 -0.0) ZeroDivisionError) 12 | 13 | (error (/ 1.0 +0) ZeroDivisionError) 14 | (error (/ 1.0 +0.0) ZeroDivisionError) 15 | (error (/ 1.0 -0.0) ZeroDivisionError) 16 | (error (/ +1.0 +0) ZeroDivisionError) 17 | (error (/ +1.0 +0.0) ZeroDivisionError) 18 | (error (/ +1.0 -0.0) ZeroDivisionError) 19 | (error (/ -1.0 +0) ZeroDivisionError) 20 | (error (/ -1.0 +0.0) ZeroDivisionError) 21 | (error (/ -1.0 -0.0) ZeroDivisionError) 22 | 23 | (equal (/ +1.0e0 +0.0) +INF) 24 | (equal (/ +1.0e0 -0.0) -INF) 25 | (equal (/ -1.0e0 +0.0) -INF) 26 | (equal (/ -1.0e0 -0.0) +INF) 27 | (equal (/ +0.0e0 +0.0) NaN) 28 | (equal (/ +0.0e0 -0.0) NaN) 29 | (equal (/ -0.0e0 +0.0) NaN) 30 | (equal (/ -0.0e0 -0.0) NaN) 31 | (equal (/ +INF +INF) NaN) 32 | (equal (/ +INF -INF) NaN) 33 | (equal (/ -INF +INF) NaN) 34 | (equal (/ -INF -INF) NaN) 35 | 36 | (equal (/ 42 7) "6.0"^^xsd:decimal) # promoted to xsd:decimal 37 | (equal (/ 42.0 7.0) 6.0) 38 | (equal (/ "42"^^xsd:decimal "7"^^xsd:decimal) "6.0"^^xsd:decimal) 39 | 40 | (error (/ true false) TypeError) 41 | -------------------------------------------------------------------------------- /spec/algebra/operator/equal/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:boolean-equal(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-boolean-equal 6 | 7 | (equal (= true true) true) 8 | (equal (= true false) false) 9 | (equal (= false true) false) 10 | (equal (= false false) true) 11 | -------------------------------------------------------------------------------- /spec/algebra/operator/equal/datetime.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:dateTime-equal(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-dateTime-equal 6 | 7 | (equal (= "2002-04-02T12:00:00-01:00"^^xsd:dateTime "2002-04-02T17:00:00+04:00"^^xsd:dateTime) true) 8 | (equal (= "2002-04-02T12:00:00-05:00"^^xsd:dateTime "2002-04-02T23:00:00+06:00"^^xsd:dateTime) true) 9 | (equal (= "2002-04-02T12:00:00-05:00"^^xsd:dateTime "2002-04-02T17:00:00-05:00"^^xsd:dateTime) false) 10 | (equal (= "2002-04-02T12:00:00-05:00"^^xsd:dateTime "2002-04-02T12:00:00-05:00"^^xsd:dateTime) true) 11 | (equal (= "2002-04-02T23:00:00-04:00"^^xsd:dateTime "2002-04-03T02:00:00-01:00"^^xsd:dateTime) true) 12 | (equal (= "1999-12-31T24:00:00-05:00"^^xsd:dateTime "2000-01-01T00:00:00-05:00"^^xsd:dateTime) true) 13 | (equal (= "2005-04-04T24:00:00-05:00"^^xsd:dateTime "2005-04-04T00:00:00-05:00"^^xsd:dateTime) false) 14 | -------------------------------------------------------------------------------- /spec/algebra/operator/equal/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-equal(A, B) 3 | # 4 | # Returns `true` if and only if the value of `A` is equal to the value of 5 | # `B`. 6 | # 7 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 8 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 9 | 10 | # Equal 11 | (equal (= 1 1) true) 12 | (equal (= 1.0 1.0) true) 13 | (equal (= "1"^^xsd:decimal "1"^^xsd:decimal) true) 14 | 15 | # Equivalent 16 | (equal (= 1 1.0) true) 17 | (equal (= 1 "1"^^xsd:decimal) true) 18 | (equal (= "1"^^xsd:decimal 1) true) 19 | (equal (= 1 "1"^^xsd:int) true) 20 | (equal (= "1"^^xsd:int 1) true) 21 | 22 | # more from expr-equals eq-1 23 | (equal (= 1 "01"^^xsd:integer) true) 24 | (equal (= 1 "1.0e0"^^xsd:double) true) 25 | (equal (= 1 "1.0"^^xsd:double) true) 26 | (equal (= 1 "1"^^xsd:double) true) 27 | 28 | # more from expr-equals eq-2-1 29 | (equal (= "1.0e0"^^xsd:double "1.0"^^xsd:double) true) 30 | (equal (= "1.0e0"^^xsd:double "1"^^xsd:double) true) 31 | (equal (= "1"^^xsd:double "1"^^xsd:integer) true) 32 | (equal (= "1"^^xsd:double "1.0"^^xsd:double) true) 33 | (equal (= "01"^^xsd:integer "1"^^xsd:integer) true) 34 | 35 | # Not equal 36 | (equal (= 1 2) false) 37 | (equal (= 1.0 2.0) false) 38 | (equal (= "1"^^xsd:decimal "2"^^xsd:decimal) false) 39 | 40 | # For `xsd:float` and `xsd:double` values, positive zero and negative zero 41 | # compare equal. `INF` equals `INF` and `-INF` equals `-INF`. `NaN` does 42 | # not equal itself. 43 | (equal (= +0.0 +0.0) true) 44 | (equal (= +0.0 -0.0) true) 45 | (equal (= -0.0 +0.0) true) 46 | (equal (= -0.0 -0.0) true) 47 | (equal (= +INF +INF) true) 48 | (equal (= +INF -INF) false) 49 | (equal (= -INF +INF) false) 50 | (equal (= -INF -INF) true) 51 | (equal (= NaN NaN) false) 52 | 53 | # From open-eq-10 54 | (equal (= "xyz"^^xsd:integer "xyz"^^xsd:integer) true) 55 | -------------------------------------------------------------------------------- /spec/algebra/operator/equal/string.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-equal(fn:compare(A, B), 0) 3 | # op:numeric-equal(fn:compare(STR(A), STR(B)), 0) 4 | # 5 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 6 | # @see http://www.w3.org/TR/xpath-functions/#func-compare 7 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 8 | 9 | (equal (= "foo" "foo") true) 10 | (equal (= "foo" "bar") false) 11 | 12 | (equal (= "foo"^^xsd:string "foo"^^xsd:string) true) 13 | (equal (= "foo"^^xsd:string "bar"^^xsd:string) false) 14 | -------------------------------------------------------------------------------- /spec/algebra/operator/equal/term.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # RDFterm-equal(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-RDFterm-equal 6 | 7 | (equal (= "Hello"@en "Hello"@en) true) 8 | (equal (= "Hello"@en "Hello"@EN) true) 9 | (equal (= "Hello"^^xsd:token "Hello"^^xsd:token) true) 10 | 11 | (error (= "Hello"@en "Hello") false) 12 | (error (= "Hello"@en "Hello"@en-US) false) 13 | (error (= "Hello"@en "Hello"^^xsd:token) false) 14 | (error (= "true" true) TypeError) 15 | (error (= "0" 0) TypeError) 16 | 17 | (equal (= ) true) 18 | (equal (= _:foobar _:foobar) true) 19 | 20 | (equal (= ) false) 21 | (equal (= _:foobar _:barfoo) false) 22 | 23 | # more from expr-equals eq-2-1 24 | (equal (= "zzz"^^ "zzz"^^) true) 25 | 26 | # From open-eq-07 27 | (equal (= "xyz" "xyz"^^xsd:string) true) 28 | (equal (= "xyz"^^xsd:string "xyz") true) 29 | 30 | # From open-eq-08 31 | (equal (= "xyz" "xyz"^^xsd:integer) TypeError) 32 | (equal (= "xyz" "xyz"^^) TypeError) 33 | (equal (= "xyz"@en "xyz"^^xsd:string) false) 34 | #(equal (= "xyz"@en "xyz"^^xsd:integer) false) 35 | (equal (= "xyz"@en "xyz"^^) false) 36 | (equal (= "xyz"@en "xyz") false) 37 | (equal (= "xyz"@EN "xyz"^^xsd:string) false) 38 | (equal (= _:xyz "xyz"^^xsd:integer) false) 39 | (equal (= "xyz"^^xsd:integer) false) 40 | (equal (= "xyz" _:xyz) false) 41 | 42 | # From open-eq-09 43 | (equal (= "xyz"^^xsd:integer "abc"^^xsd:integer) TypeError) 44 | -------------------------------------------------------------------------------- /spec/algebra/operator/greater_than/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:boolean-greater-than(A, B) 3 | # 4 | # Returns `true` if the left operand is `true` and the right operand is 5 | # `false`. Otherwise, returns `false`. 6 | # 7 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 8 | # @see http://www.w3.org/TR/xpath-functions/#func-boolean-greater-than 9 | 10 | (equal (> true true) false) 11 | (equal (> true false) true) 12 | (equal (> false true) false) 13 | (equal (> false false) false) 14 | -------------------------------------------------------------------------------- /spec/algebra/operator/greater_than/datetime.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:dateTime-greater-than(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-dateTime-greater-than 6 | -------------------------------------------------------------------------------- /spec/algebra/operator/greater_than/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-greater-than(A, B) 3 | # 4 | # Returns `true` if and only if `A` is greater than `B`. 5 | # 6 | # For `xsd:float` and `xsd:double` values, positive infinity is greater than 7 | # all other non-`NaN` values; negative infinity is less than all other 8 | # non-`NaN` values. If `A` or `B` is `NaN`, the function returns `false`. 9 | # 10 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 11 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-greater-than 12 | 13 | (equal (> +0.0 -1.0) true) 14 | (equal (> +1.0 +0.0) true) 15 | 16 | (equal (> 0.0 -INF) true) 17 | (equal (> +INF 0.0) true) 18 | 19 | #(equal (> +INF NaN) false) # FIXME in RDF.rb 20 | #(equal (> -INF NaN) false) # FIXME in RDF.rb 21 | #(equal (> NaN NaN) false) # FIXME in RDF.rb 22 | -------------------------------------------------------------------------------- /spec/algebra/operator/greater_than/string.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-equal(fn:compare(A, B), 1) 3 | # op:numeric-equal(fn:compare(STR(A), STR(B)), 1) 4 | # 5 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 6 | # @see http://www.w3.org/TR/xpath-functions/#func-compare 7 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 8 | 9 | (equal (> "" "a") false) 10 | (equal (> "a" "") true) 11 | (equal (> "aaa" "bbb") false) 12 | (equal (> "bbb" "aaa") true) 13 | 14 | (equal (> ""^^xsd:string "a"^^xsd:string) false) 15 | (equal (> "a"^^xsd:string ""^^xsd:string) true) 16 | (equal (> "aaa"^^xsd:string "bbb"^^xsd:string) false) 17 | (equal (> "bbb"^^xsd:string "aaa"^^xsd:string) true) 18 | -------------------------------------------------------------------------------- /spec/algebra/operator/greater_than_or_equal/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:boolean-less-than(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-boolean-less-than 6 | # @see http://www.w3.org/TR/xpath-functions/#func-not 7 | 8 | (equal (>= true true) true) 9 | (equal (>= true false) true) 10 | (equal (>= false true) false) 11 | (equal (>= false false) true) 12 | -------------------------------------------------------------------------------- /spec/algebra/operator/greater_than_or_equal/datetime.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:dateTime-less-than(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-dateTime-less-than 6 | # @see http://www.w3.org/TR/xpath-functions/#func-not 7 | -------------------------------------------------------------------------------- /spec/algebra/operator/greater_than_or_equal/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # logical-or(op:numeric-greater-than(A, B), op:numeric-equal(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-greater-than 6 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 7 | # @see http://www.w3.org/TR/sparql11-query/#func-logical-or 8 | -------------------------------------------------------------------------------- /spec/algebra/operator/greater_than_or_equal/string.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:numeric-equal(fn:compare(A, B), -1)) 3 | # fn:not(op:numeric-equal(fn:compare(STR(A), STR(B)), -1)) 4 | # 5 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 6 | # @see http://www.w3.org/TR/xpath-functions/#func-compare 7 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 8 | # @see http://www.w3.org/TR/xpath-functions/#func-not 9 | -------------------------------------------------------------------------------- /spec/algebra/operator/is_blank/term.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # isBlank(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-isBlank 6 | 7 | (equal (isBlank _:foobar) true) 8 | 9 | (equal (isBlank ) false) 10 | (equal (isBlank 42) false) 11 | (equal (isBlank "foobar") false) 12 | -------------------------------------------------------------------------------- /spec/algebra/operator/is_iri/term.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # isIRI(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-isIRI 6 | 7 | (equal (isIRI ) true) 8 | 9 | (equal (isIRI _:foobar) false) 10 | (equal (isIRI 42) false) 11 | (equal (isIRI "foobar") false) 12 | -------------------------------------------------------------------------------- /spec/algebra/operator/is_literal/term.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # isLiteral(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-isLiteral 6 | 7 | (equal (isLiteral "foobar") true) 8 | (equal (isLiteral "Hello"@en) true) 9 | (equal (isLiteral 42) true) 10 | 11 | (equal (isLiteral _:foobar) false) 12 | (equal (isLiteral ) false) 13 | -------------------------------------------------------------------------------- /spec/algebra/operator/is_uri/term.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # isIRI(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-isIRI 6 | 7 | (equal (isIRI ) true) 8 | 9 | (equal (isIRI _:foobar) false) 10 | (equal (isIRI 42) false) 11 | (equal (isIRI "foobar") false) 12 | -------------------------------------------------------------------------------- /spec/algebra/operator/lang/literal.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # lang(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-lang 6 | 7 | (equal (lang "Hello") "") 8 | 9 | (equal (lang "Hello"@en) "en") 10 | (equal (lang "Hello"@EN) "en") 11 | 12 | (error (lang _:foobar) TypeError) 13 | (error (lang ) TypeError) 14 | -------------------------------------------------------------------------------- /spec/algebra/operator/lang_matches/literal.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # langMatches(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-langMatches 6 | 7 | (equal (langMatches "" "*") false) 8 | (equal (langMatches "en" "*") true) 9 | (equal (langMatches "en" "en") true) 10 | (equal (langMatches "en" "EN") true) 11 | (equal (langMatches "EN" "en") true) 12 | (equal (langMatches "en-US" "en") true) 13 | 14 | # @see http://www.w3.org/TR/sparql11-query/#func-langMatches 15 | (equal (langMatches "en" "FR") false) 16 | (equal (langMatches "fr" "FR") true) 17 | (equal (langMatches "fr-BE" "FR") true) 18 | (equal (langMatches "" "FR") false) 19 | 20 | # @see http://tools.ietf.org/html/rfc4647#section-3.3.1 21 | (equal (langMatches "de-DE-1996" "de-de") true) 22 | (equal (langMatches "de-Deva" "de-de") false) 23 | (equal (langMatches "de-Latn-DE" "de-de") false) 24 | 25 | (error (langMatches "en" _:foobar) TypeError) 26 | (error (langMatches "en" "*"@en) TypeError) 27 | (error (langMatches "en" "*"^^xsd:token) TypeError) 28 | (error (langMatches "en" 42) TypeError) 29 | (error (langMatches _:foobar "*") TypeError) 30 | (error (langMatches "*") TypeError) 31 | -------------------------------------------------------------------------------- /spec/algebra/operator/less_than/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:boolean-less-than(A, B) 3 | # 4 | # Returns `true` if the left operand is `false` and the right operand is 5 | # `true`. Otherwise, returns `false`. 6 | # 7 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 8 | # @see http://www.w3.org/TR/xpath-functions/#func-boolean-less-than 9 | 10 | (equal (< true true) false) 11 | (equal (< true false) false) 12 | (equal (< false true) true) 13 | (equal (< false false) false) 14 | -------------------------------------------------------------------------------- /spec/algebra/operator/less_than/datetime.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:dateTime-less-than(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-dateTime-less-than 6 | -------------------------------------------------------------------------------- /spec/algebra/operator/less_than/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-less-than(A, B) 3 | # 4 | # Returns `true` if and only if `A` is less than `B`. 5 | # 6 | # For `xsd:float` and `xsd:double` values, positive infinity is greater than 7 | # all other non-`NaN` values; negative infinity is less than all other 8 | # non-`NaN` values. If `A` or `B` is `NaN`, the function returns `false`. 9 | # 10 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 11 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-less-than 12 | 13 | (equal (< -1.0 +0.0) true) 14 | (equal (< +0.0 +1.0) true) 15 | 16 | (equal (< -INF 0.0) true) 17 | (equal (< 0.0 +INF) true) 18 | 19 | #(equal (< +INF NaN) false) # FIXME in RDF.rb 20 | #(equal (< -INF NaN) false) # FIXME in RDF.rb 21 | #(equal (< NaN NaN) false) # FIXME in RDF.rb 22 | -------------------------------------------------------------------------------- /spec/algebra/operator/less_than/string.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-equal(fn:compare(A, B), -1) 3 | # op:numeric-equal(fn:compare(STR(A), STR(B)), -1) 4 | # 5 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 6 | # @see http://www.w3.org/TR/xpath-functions/#func-compare 7 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 8 | 9 | (equal (< "" "a") true) 10 | (equal (< "a" "") false) 11 | (equal (< "aaa" "bbb") true) 12 | (equal (< "bbb" "aaa") false) 13 | 14 | (equal (< ""^^xsd:string "a"^^xsd:string) true) 15 | (equal (< "a"^^xsd:string ""^^xsd:string) false) 16 | (equal (< "aaa"^^xsd:string "bbb"^^xsd:string) true) 17 | (equal (< "bbb"^^xsd:string "aaa"^^xsd:string) false) 18 | -------------------------------------------------------------------------------- /spec/algebra/operator/less_than_or_equal/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:boolean-greater-than(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-boolean-greater-than 6 | # @see http://www.w3.org/TR/xpath-functions/#func-not 7 | 8 | (equal (<= true true) true) 9 | (equal (<= true false) false) 10 | (equal (<= false true) true) 11 | (equal (<= false false) true) 12 | -------------------------------------------------------------------------------- /spec/algebra/operator/less_than_or_equal/datetime.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:dateTime-greater-than(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-dateTime-greater-than 6 | # @see http://www.w3.org/TR/xpath-functions/#func-not 7 | -------------------------------------------------------------------------------- /spec/algebra/operator/less_than_or_equal/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # logical-or(op:numeric-less-than(A, B), op:numeric-equal(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-less-than 6 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 7 | # @see http://www.w3.org/TR/sparql11-query/#func-logical-or 8 | -------------------------------------------------------------------------------- /spec/algebra/operator/less_than_or_equal/string.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:numeric-equal(fn:compare(A, B), 1)) 3 | # fn:not(op:numeric-equal(fn:compare(STR(A), STR(B)), 1)) 4 | # 5 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 6 | # @see http://www.w3.org/TR/xpath-functions/#func-compare 7 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 8 | # @see http://www.w3.org/TR/xpath-functions/#func-not 9 | -------------------------------------------------------------------------------- /spec/algebra/operator/multiply/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-multiply(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-multiply 6 | 7 | (equal (* 0.0e0 +INF) NaN) 8 | (equal (* 0.0e0 -INF) NaN) 9 | (equal (* +INF 0.0) NaN) 10 | (equal (* -INF 0.0) NaN) 11 | (equal (* 1.0e0 +INF) +INF) 12 | (equal (* 1.0e0 -INF) -INF) 13 | (equal (* +INF 1.0) +INF) 14 | (equal (* -INF 1.0) -INF) 15 | (equal (* +INF +INF) +INF) 16 | (equal (* -INF -INF) +INF) 17 | (equal (* +INF -INF) -INF) 18 | (equal (* -INF +INF) -INF) 19 | 20 | (equal (* 6 7) 42) 21 | (equal (* 6.0 7.0) 42.0) 22 | (equal (* "6"^^xsd:decimal "7"^^xsd:decimal) "42.0"^^xsd:decimal) 23 | 24 | (error (* true false) TypeError) 25 | -------------------------------------------------------------------------------- /spec/algebra/operator/negate/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-unary-minus(A) 3 | # 4 | # Returns its operand with the sign reversed: `(- A)`. 5 | # 6 | # If `A` is positive, its negative is returned; if it is negative, its 7 | # positive is returned. 8 | # 9 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 10 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-unary-minus 11 | 12 | (equal (- 0) 0) 13 | (equal (- +1) -1) 14 | (equal (- -1) +1) 15 | (equal (+ +0.0) -0.0) 16 | (equal (+ -0.0) +0.0) 17 | (equal (+ +1.0) -1.0) 18 | (equal (+ -1.0) +1.0) 19 | (equal (+ +INF) -INF) 20 | (equal (+ -INF) +INF) 21 | 22 | # For `xsd:integer` and `xsd:decimal` arguments, `0` and `0.0` return `0` 23 | # and `0.0`, respectively. 24 | (equal (- "0"^^xsd:integer) "0"^^xsd:integer) 25 | (equal (- "0.0"^^xsd:decimal) "0.0"^^xsd:decimal) 26 | 27 | # For `xsd:float` and `xsd:double` arguments, `NaN` returns `NaN`, `0.0E0` 28 | # returns `-0.0E0` and vice versa. `INF` returns `-INF`. `-INF` returns 29 | # `INF`. 30 | (equal (- "NaN"^^xsd:float) "NaN"^^xsd:float) 31 | (equal (- "NaN"^^xsd:double) "NaN"^^xsd:double) 32 | (equal (- "0.0E0"^^xsd:float) "-0.0E0"^^xsd:float) 33 | (equal (- "0.0E0"^^xsd:double) "-0.0E0"^^xsd:double) 34 | (equal (- "-0.0E0"^^xsd:float) "0.0E0"^^xsd:float) 35 | (equal (- "-0.0E0"^^xsd:double) "0.0E0"^^xsd:double) 36 | (equal (- "INF"^^xsd:float) "-INF"^^xsd:float) 37 | (equal (- "INF"^^xsd:double) "-INF"^^xsd:double) 38 | (equal (- "-INF"^^xsd:float) "INF"^^xsd:float) 39 | (equal (- "-INF"^^xsd:double) "INF"^^xsd:double) 40 | 41 | (error (- "") TypeError) 42 | -------------------------------------------------------------------------------- /spec/algebra/operator/not/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(EBV(A)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#ebv 6 | # @see http://www.w3.org/TR/xpath-functions/#func-not 7 | 8 | (equal (not true) false) 9 | (equal (not false) true) 10 | 11 | # The inverse EBV of any literal whose type is `xsd:boolean` or numeric is 12 | # `true` if the lexical form is not valid for that datatype. 13 | (equal (not "abc"^^xsd:integer) true) 14 | 15 | # If the operand is a typed literal with a datatype of `xsd:boolean`, the 16 | # inverse EBV is the logical inverse of the value of that operand. 17 | (equal (not "1"^^xsd:boolean) false) 18 | (equal (not "0"^^xsd:boolean) true) 19 | 20 | # If the operand is a plain literal or a typed literal with a datatype of 21 | # `xsd:string`, the inverse EBV is `true` if the operand value has zero 22 | # length; otherwise the inverse EBV is `false`. 23 | (equal (not "") true) 24 | (equal (not ""^^xsd:string) true) 25 | (equal (not "foo") false) 26 | (equal (not "foo"^^xsd:string) false) 27 | 28 | # If the operand is a numeric type or a typed literal with a datatype 29 | # derived from a numeric type, the inverse EBV is `true` if the operand 30 | # value is `NaN` or is numerically equal to zero; otherwise the inverse EBV 31 | # is `false`. 32 | (equal (not NaN) true) 33 | (equal (not 0) true) 34 | (equal (not 0.0) true) 35 | (equal (not 42) false) 36 | (equal (not 3.1415) false) 37 | 38 | # All other arguments, including unbound arguments, produce a type error. 39 | # TODO 40 | -------------------------------------------------------------------------------- /spec/algebra/operator/not_equal/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:boolean-equal(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-boolean-equal 6 | # @see http://www.w3.org/TR/xpath-functions/#func-not 7 | 8 | (equal (!= true true) false) 9 | (equal (!= true false) true) 10 | (equal (!= false true) true) 11 | (equal (!= false false) false) 12 | -------------------------------------------------------------------------------- /spec/algebra/operator/not_equal/datetime.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:dateTime-equal(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-dateTime-equal 6 | # @see http://www.w3.org/TR/xpath-functions/#func-not 7 | -------------------------------------------------------------------------------- /spec/algebra/operator/not_equal/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:numeric-equal(A, B)) 3 | # 4 | # Returns `true` if and only if the value of `A` is not equal to the value 5 | # of `B`. 6 | # 7 | # For `xsd:float` and `xsd:double` values, positive zero and negative zero 8 | # compare equal. `INF` equals `INF` and `-INF` equals `-INF`. `NaN` does 9 | # not equal itself. 10 | # 11 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 12 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 13 | # @see http://www.w3.org/TR/xpath-functions/#func-not 14 | 15 | (equal (!= 1 1) false) 16 | (equal (!= 1.0 1.0) false) 17 | (equal (!= "1"^^xsd:decimal "1"^^xsd:decimal) false) 18 | 19 | (equal (!= 1 2) true) 20 | (equal (!= 1.0 2.0) true) 21 | (equal (!= "1"^^xsd:decimal "2"^^xsd:decimal) true) 22 | -------------------------------------------------------------------------------- /spec/algebra/operator/not_equal/string.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(op:numeric-equal(fn:compare(A, B), 0)) 3 | # fn:not(op:numeric-equal(fn:compare(STR(A), STR(B)), 0)) 4 | # 5 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 6 | # @see http://www.w3.org/TR/xpath-functions/#func-compare 7 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-equal 8 | # @see http://www.w3.org/TR/xpath-functions/#func-not 9 | 10 | (equal (!= "foo" "foo") false) 11 | (equal (!= "foo" "bar") true) 12 | 13 | (equal (!= "foo"^^xsd:string "foo"^^xsd:string) false) 14 | (equal (!= "foo"^^xsd:string "bar"^^xsd:string) true) 15 | -------------------------------------------------------------------------------- /spec/algebra/operator/not_equal/term.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # fn:not(RDFterm-equal(A, B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-RDFterm-equal 6 | # @see http://www.w3.org/TR/xpath-functions/#func-not 7 | 8 | (equal (!= "Hello"@en "Hello"@en) false) 9 | (equal (!= "Hello"^^xsd:token "Hello"^^xsd:token) false) 10 | 11 | (error (!= "Hello"@en "Hello") true) 12 | (error (!= "Hello"@en "Hello"@en-US) true) 13 | (error (!= "Hello"@en "Hello"^^xsd:token) true) 14 | (error (!= "true" true) TypeError) 15 | (error (!= "0" 0) TypeError) 16 | 17 | (equal (!= ) false) 18 | (equal (!= _:foobar _:foobar) false) 19 | 20 | (equal (!= ) true) 21 | (equal (!= _:foobar _:barfoo) true) 22 | -------------------------------------------------------------------------------- /spec/algebra/operator/or/boolean.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # logical-or(EBV(A), EBV(B)) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#ebv 6 | # @see http://www.w3.org/TR/sparql11-query/#func-logical-or 7 | 8 | (equal (or true true) true) 9 | (equal (or true false) true) 10 | (equal (or false true) true) 11 | (equal (or false false) false) 12 | -------------------------------------------------------------------------------- /spec/algebra/operator/plus/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-unary-plus(A) 3 | # 4 | # Returns its operand with the sign unchanged: (+ A). 5 | # 6 | # Semantically, this operation performs no operation. 7 | # 8 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 9 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-unary-plus 10 | 11 | (equal (+ 0) 0) 12 | (equal (+ +1) +1) 13 | (equal (+ -1) -1) 14 | (equal (+ +0.0) +0.0) 15 | (equal (+ -0.0) -0.0) 16 | (equal (+ +1.0) +1.0) 17 | (equal (+ -1.0) -1.0) 18 | (equal (+ +INF) +INF) 19 | (equal (+ -INF) -INF) 20 | 21 | (error (+ "") TypeError) 22 | 23 | ## 24 | # Now implemented as part of plus 25 | # op:numeric-add(A, B) 26 | # 27 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 28 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-add 29 | 30 | (equal (+ 0.0 +INF) +INF) 31 | (equal (+ 0.0 -INF) -INF) 32 | (equal (+ +INF 0.0) +INF) 33 | (equal (+ -INF 0.0) -INF) 34 | (equal (+ +INF +INF) +INF) 35 | (equal (+ -INF -INF) -INF) 36 | (equal (+ +INF -INF) NaN) 37 | (equal (+ -INF +INF) NaN) 38 | 39 | (equal (+ 29 13) 42) 40 | (equal (+ 29.0 13.0) 42.0) 41 | (equal (+ "29"^^xsd:decimal "13"^^xsd:decimal) "42.0"^^xsd:decimal) 42 | 43 | (error (+ true false) TypeError) 44 | -------------------------------------------------------------------------------- /spec/algebra/operator/same_term/term.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # sameTerm(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-sameTerm 6 | 7 | # @see http://www.w3.org/TR/rdf-concepts/#section-blank-nodes 8 | (equal (sameTerm _:foo _:foo) true) 9 | (equal (sameTerm _:foo _:bar) false) 10 | 11 | # @see http://www.w3.org/TR/rdf-concepts/#section-Graph-URIref 12 | (equal (sameTerm ) true) 13 | (equal (sameTerm ) false) 14 | 15 | # @see http://www.w3.org/TR/rdf-concepts/#section-Literal-Equality 16 | (equal (sameTerm "foo" "foo") true) 17 | (equal (sameTerm "foo" "bar") false) 18 | (equal (sameTerm true true) true) 19 | (equal (sameTerm true false) false) 20 | (equal (sameTerm 1 1.0) false) 21 | -------------------------------------------------------------------------------- /spec/algebra/operator/str/iri.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # str(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-str 6 | -------------------------------------------------------------------------------- /spec/algebra/operator/str/literal.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # str(A) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/sparql11-query/#func-str 6 | 7 | (equal (str "Hello") "Hello") 8 | (equal (str 3.1415) "3.1415") 9 | 10 | (equal (str ) "mailto:alice@example.org") 11 | 12 | (error (str _:foobar) TypeError) 13 | -------------------------------------------------------------------------------- /spec/algebra/operator/subtract/numeric.sse: -------------------------------------------------------------------------------- 1 | ## 2 | # op:numeric-subtract(A, B) 3 | # 4 | # @see http://www.w3.org/TR/sparql11-query/#OperatorMapping 5 | # @see http://www.w3.org/TR/xpath-functions/#func-numeric-subtract 6 | 7 | (equal (- 0.0 +INF) -INF) 8 | (equal (- 0.0 -INF) +INF) 9 | (equal (- +INF 0.0) +INF) 10 | (equal (- -INF 0.0) -INF) 11 | (equal (- +INF +INF) NaN) 12 | (equal (- -INF -INF) NaN) 13 | (equal (- +INF -INF) +INF) 14 | (equal (- -INF +INF) -INF) 15 | 16 | (equal (- 42 13) 29) 17 | (equal (- 42.0 13.0) 29.0) 18 | (equal (- "42"^^xsd:decimal "13"^^xsd:decimal) "29.0"^^xsd:decimal) 19 | 20 | (error (- true false) TypeError) 21 | -------------------------------------------------------------------------------- /spec/fixtures/data0.rdf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /spec/fixtures/data1.rdf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /spec/fixtures/data2.rdf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /spec/fixtures/data3.rdf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /spec/module_spec.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("..", __FILE__) 2 | require 'spec_helper' 3 | 4 | describe SPARQL do 5 | describe "#parse" do 6 | it "returns an operator" do 7 | query = "query" 8 | parser = double("Parser") 9 | operator = double("Operator") 10 | expect(SPARQL::Grammar::Parser).to receive(:new).with(query, any_args).and_return(parser) 11 | expect(parser).to receive(:parse).and_return(operator) 12 | 13 | expect(SPARQL.parse(query)).to eq operator 14 | end 15 | end 16 | 17 | describe "#execute" do 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --format specdoc 2 | --colour -------------------------------------------------------------------------------- /spec/support/extensions/comparitors.rb: -------------------------------------------------------------------------------- 1 | require 'rdf/isomorphic' 2 | 3 | module RDF::Isomorphic 4 | alias_method :==, :isomorphic_with? 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/matchers/be_one_of.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/matchers' 2 | require 'amazing_print' 3 | 4 | ::RSpec::Matchers.define :be_one_of do |expected| 5 | match do |actual| 6 | expected.include?(actual) 7 | end 8 | 9 | failure_message do |actual| 10 | "expected one of #{expected}, got #{actual}" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/matchers/generate.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/matchers' 2 | require 'amazing_print' 3 | 4 | RSpec::Matchers.define :generate do |expected, options| 5 | def parser(**options) 6 | Proc.new do |query| 7 | parser = SPARQL::Grammar::Parser.new(query, resolve_iris: true, **options) 8 | options[:production] ? parser.parse(options[:production]) : parser.parse 9 | end 10 | end 11 | 12 | def normalize(obj) 13 | if obj.is_a?(String) 14 | obj.gsub(/\s+/m, ' '). 15 | gsub(/\s+\)/m, ')'). 16 | gsub(/\(\s+/m, '('). 17 | strip 18 | else 19 | obj 20 | end 21 | end 22 | 23 | match do |input| 24 | @input = input 25 | @actual = input.is_a?(String) ? parser(**options).call(input) : input 26 | case 27 | when options[:last] 28 | # Only look at end of production 29 | @actual = @actual.last 30 | if expected.is_a?(String) 31 | normalize(@actual.to_sxp) == normalize(expected) 32 | else 33 | @actual == expected 34 | end 35 | when options[:shift] 36 | @actual = @actual[1..-1] 37 | @actual == expected 38 | when expected.nil? 39 | @actual.nil? 40 | when expected.is_a?(String) 41 | @actual = SXP::Generator.string(@actual.to_sxp_bin) 42 | normalize(@actual) == normalize(expected) 43 | when expected.is_a?(Symbol) 44 | @actual.to_sxp == expected.to_s 45 | else 46 | @actual == expected 47 | end 48 | rescue 49 | @exception = $! 50 | expected == EBNF::LL1::Parser::Error 51 | end 52 | 53 | failure_message do |input| 54 | "Input:\n#{@input}\n" + 55 | case expected 56 | when String 57 | "Expected:\n#{expected}\n" 58 | else 59 | "Expected:\n#{expected.ai}\n" + 60 | "Expected(sse):\n#{expected.to_sse}\n" 61 | end + 62 | case input 63 | when String 64 | "Actual:\n#{actual}\n" 65 | else 66 | "Actual:\n#{actual.ai}\n" + 67 | "Actual(sse):\n#{actual.to_sse}\n" 68 | end + 69 | (@exception ? "Exception: #{@exception}" : "") + 70 | "Processing results:\n#{options[:logger].to_s}" 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/support/matchers/have_result_set.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/matchers' 2 | require 'amazing_print' 3 | 4 | ::RSpec::Matchers.define :have_result_set do |expected| 5 | def normalize(soln) 6 | soln.to_h.inject({}) do |c, (k, v)| 7 | c.merge(k => v.to_base) 8 | end 9 | end 10 | 11 | match do |result| 12 | @actual = result.map {|s| normalize(s)} 13 | @expected = expected.map {|s| normalize(s)} 14 | expect(@actual).to contain_exactly *@expected 15 | end 16 | 17 | failure_message do |input| 18 | "Expected : #{@expected.ai}\n" + 19 | "Actual : #{@actual.ai}\n" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/support/matchers/produces.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/matchers' 2 | 3 | RSpec::Matchers.define :produce do |expected, info| 4 | match do |actual| 5 | expect(actual).to eq expected 6 | end 7 | 8 | failure_message do |actual| 9 | log = case info 10 | when Hash then info[:logger].to_s 11 | when Array then info.join("\n") 12 | else info.to_s 13 | end 14 | case expected 15 | when String 16 | "Expected : #{expected.inspect}\n" 17 | else 18 | "Expected : #{expected.inspect}\n" + 19 | "Expected(sse): #{expected.to_sxp}\n" 20 | end + 21 | case actual 22 | when String 23 | "Actual : #{actual.inspect}\n" 24 | else 25 | "Actual : #{actual.inspect}\n" + 26 | "Actual(sse) : #{actual.to_sxp}\n" 27 | end + 28 | "Processing results:\n#{log}" 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/support/matchers/xpath_matcher.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/matchers' 2 | require 'nokogiri' 3 | require 'amazing_print' 4 | 5 | RSpec::Matchers.define :have_xpath do |xpath, value| 6 | match do |actual| 7 | @doc = Nokogiri::XML.parse(actual) 8 | expect(@doc).to be_a(Nokogiri::XML::Document) 9 | expect(@doc.root).to be_a(Nokogiri::XML::Element) 10 | @namespaces = @doc.namespaces.merge( 11 | "xml" => "http://www.w3.org/XML/1998/namespace", 12 | "sr" => "http://www.w3.org/2005/sparql-results#") 13 | case value 14 | when false 15 | expect(@doc.root.at_xpath(xpath, @namespaces)).to be_nil 16 | when true 17 | expect(@doc.root.at_xpath(xpath, @namespaces)).not_to be_nil 18 | when Array 19 | expect(@doc.root.xpath(xpath, @namespaces).map(&:to_s)).to include(*value) 20 | when Regexp 21 | expect(@doc.root.at_xpath(xpath, @namespaces).to_s).to match value 22 | else 23 | expect(@doc.root.at_xpath(xpath, @namespaces).to_s).to eq value 24 | end 25 | end 26 | 27 | failure_message do |actual| 28 | msg = "expected that #{xpath.ai} would be #{value.ai} in:\n" + actual.to_s 29 | msg += "was: #{@doc.root.at_xpath(xpath, @namespaces)}" 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/version_spec.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("..", __FILE__) 2 | require 'spec_helper' 3 | 4 | describe 'SPARQL::VERSION' do 5 | it "matches the VERSION file" do 6 | expect(SPARQL::VERSION.to_s).to eq File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).chomp 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/w3c-sparql-dev: -------------------------------------------------------------------------------- 1 | ../../w3c-sparql-dev --------------------------------------------------------------------------------