├── .github └── workflows │ ├── deploy.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── deps.edn ├── dev-resources └── test-fixtures │ ├── inputs │ ├── query │ │ ├── ask │ │ │ ├── ask-1.edn │ │ │ └── ask-2.edn │ │ ├── construct │ │ │ ├── construct-1.edn │ │ │ ├── construct-2.edn │ │ │ ├── construct-3.edn │ │ │ ├── construct-4.edn │ │ │ └── construct-5.edn │ │ ├── describe │ │ │ ├── describe-1.edn │ │ │ ├── describe-2.edn │ │ │ ├── describe-3.edn │ │ │ ├── describe-4.edn │ │ │ └── describe-5.edn │ │ ├── select-agg │ │ │ ├── select-agg-1.edn │ │ │ ├── select-agg-2.edn │ │ │ ├── select-agg-3.edn │ │ │ ├── select-agg-4.edn │ │ │ └── select-agg-5.edn │ │ ├── select-bind │ │ │ ├── select-bind-1.edn │ │ │ └── select-bind-2.edn │ │ ├── select-filter │ │ │ ├── select-filter-1.edn │ │ │ ├── select-filter-2.edn │ │ │ ├── select-filter-3.edn │ │ │ ├── select-filter-4.edn │ │ │ ├── select-filter-5.edn │ │ │ └── select-filter-6.edn │ │ ├── select-from │ │ │ ├── select-from-1.edn │ │ │ ├── select-from-2.edn │ │ │ ├── select-from-3.edn │ │ │ ├── select-from-4.edn │ │ │ ├── select-from-5.edn │ │ │ └── select-from-6.edn │ │ ├── select-limit │ │ │ ├── select-limit-1.edn │ │ │ └── select-limit-2.edn │ │ ├── select-minus │ │ │ ├── select-minus-1.edn │ │ │ └── select-minus-2.edn │ │ ├── select-optional │ │ │ ├── select-optional-1.edn │ │ │ ├── select-optional-2.edn │ │ │ └── select-optional-3.edn │ │ ├── select-order │ │ │ ├── select-order-1.edn │ │ │ ├── select-order-2.edn │ │ │ └── select-order-3.edn │ │ ├── select-path │ │ │ ├── select-path-1.edn │ │ │ ├── select-path-2.edn │ │ │ ├── select-path-3.edn │ │ │ └── select-path-4.edn │ │ ├── select-service │ │ │ ├── select-service-1.edn │ │ │ ├── select-service-2.edn │ │ │ └── select-service-3.edn │ │ ├── select-subquery │ │ │ └── select-subquery-1.edn │ │ ├── select-union │ │ │ ├── select-union-1.edn │ │ │ └── select-union-2.edn │ │ ├── select-values │ │ │ ├── select-values-1.edn │ │ │ ├── select-values-2.edn │ │ │ ├── select-values-3.edn │ │ │ └── select-values-4.edn │ │ └── select │ │ │ ├── select-1.edn │ │ │ ├── select-10.edn │ │ │ ├── select-11.edn │ │ │ ├── select-12.edn │ │ │ ├── select-13.edn │ │ │ ├── select-14.edn │ │ │ ├── select-2.edn │ │ │ ├── select-3.edn │ │ │ ├── select-4.edn │ │ │ ├── select-5.edn │ │ │ ├── select-6.edn │ │ │ ├── select-7.edn │ │ │ ├── select-8.edn │ │ │ └── select-9.edn │ ├── update-seq │ │ ├── updates-1.edn │ │ ├── updates-2.edn │ │ ├── updates-3.edn │ │ ├── updates-4.edn │ │ ├── updates-5.edn │ │ └── updates-6.edn │ └── update │ │ ├── delete-data-1.edn │ │ ├── delete-where-1.edn │ │ ├── delete-where-2.edn │ │ ├── graph-add-1.edn │ │ ├── graph-copy-1.edn │ │ ├── graph-move-1.edn │ │ ├── insert-data-1.edn │ │ ├── insert-data-2.edn │ │ ├── modify-1.edn │ │ ├── modify-2.edn │ │ ├── modify-3.edn │ │ ├── modify-4.edn │ │ └── modify-5.edn │ └── outputs │ ├── query │ ├── ask │ │ ├── ask-1.rq │ │ └── ask-2.rq │ ├── construct │ │ ├── construct-1.rq │ │ ├── construct-2.rq │ │ ├── construct-3.rq │ │ ├── construct-4.rq │ │ └── construct-5.rq │ ├── describe │ │ ├── describe-1.rq │ │ ├── describe-2.rq │ │ ├── describe-3.rq │ │ ├── describe-4.rq │ │ └── describe-5.rq │ ├── select-agg │ │ ├── select-agg-1.rq │ │ ├── select-agg-2.rq │ │ ├── select-agg-3.rq │ │ ├── select-agg-4.rq │ │ └── select-agg-5.rq │ ├── select-bind │ │ ├── select-bind-1.rq │ │ └── select-bind-2.rq │ ├── select-filter │ │ ├── select-filter-1.rq │ │ ├── select-filter-2.rq │ │ ├── select-filter-3.rq │ │ ├── select-filter-4.rq │ │ ├── select-filter-5.rq │ │ └── select-filter-6.rq │ ├── select-from │ │ ├── select-from-1.rq │ │ ├── select-from-2.rq │ │ ├── select-from-3.rq │ │ ├── select-from-4.rq │ │ ├── select-from-5.rq │ │ └── select-from-6.rq │ ├── select-limit │ │ ├── select-limit-1.rq │ │ └── select-limit-2.rq │ ├── select-minus │ │ ├── select-minus-1.rq │ │ └── select-minus-2.rq │ ├── select-optional │ │ ├── select-optional-1.rq │ │ ├── select-optional-2.rq │ │ └── select-optional-3.rq │ ├── select-order │ │ ├── select-order-1.rq │ │ ├── select-order-2.rq │ │ └── select-order-3.rq │ ├── select-path │ │ ├── select-path-1.rq │ │ ├── select-path-2.rq │ │ ├── select-path-3.rq │ │ └── select-path-4.rq │ ├── select-service │ │ ├── select-service-1.rq │ │ ├── select-service-2.rq │ │ └── select-service-3.rq │ ├── select-subquery │ │ └── select-subquery-1.rq │ ├── select-union │ │ ├── select-union-1.rq │ │ └── select-union-2.rq │ ├── select-values │ │ ├── select-values-1.rq │ │ ├── select-values-2.rq │ │ ├── select-values-3.rq │ │ └── select-values-4.rq │ └── select │ │ ├── select-1.rq │ │ ├── select-10.rq │ │ ├── select-11.rq │ │ ├── select-12.rq │ │ ├── select-13.rq │ │ ├── select-14.rq │ │ ├── select-2.rq │ │ ├── select-3.rq │ │ ├── select-4.rq │ │ ├── select-5.rq │ │ ├── select-6.rq │ │ ├── select-7.rq │ │ ├── select-8.rq │ │ └── select-9.rq │ ├── update-seq │ ├── updates-1.rq │ ├── updates-2.rq │ ├── updates-3.rq │ ├── updates-4.rq │ ├── updates-5.rq │ └── updates-6.rq │ └── update │ ├── delete-data-1.rq │ ├── delete-where-1.rq │ ├── delete-where-2.rq │ ├── graph-add-1.rq │ ├── graph-copy-1.rq │ ├── graph-move-1.rq │ ├── insert-data-1.rq │ ├── insert-data-2.rq │ ├── modify-1.rq │ ├── modify-2.rq │ ├── modify-3.rq │ ├── modify-4.rq │ └── modify-5.rq ├── doc ├── axiom.md ├── cljdoc.edn ├── expr.md ├── graph.md ├── modifier.md ├── prologue.md ├── query.md ├── triple.md ├── update.md └── where.md ├── logo ├── logo.png └── logo.svg └── src ├── dev └── com │ └── yetanalytics │ └── flint │ └── sparql.clj ├── main └── com │ └── yetanalytics │ ├── flint.cljc │ └── flint │ ├── axiom │ ├── impl.cljc │ ├── impl │ │ ├── format.cljc │ │ └── validation.cljc │ ├── iri.cljc │ └── protocol.cljc │ ├── error.cljc │ ├── format.cljc │ ├── format │ ├── axiom.cljc │ ├── expr.cljc │ ├── modifier.cljc │ ├── path.cljc │ ├── prologue.cljc │ ├── query.cljc │ ├── select.cljc │ ├── triple.cljc │ ├── update.cljc │ ├── values.cljc │ └── where.cljc │ ├── spec.cljc │ ├── spec │ ├── axiom.cljc │ ├── expr.cljc │ ├── modifier.cljc │ ├── path.cljc │ ├── prologue.cljc │ ├── query.cljc │ ├── select.cljc │ ├── triple.cljc │ ├── update.cljc │ ├── values.cljc │ └── where.cljc │ ├── util.cljc │ ├── validate.cljc │ └── validate │ ├── aggregate.cljc │ ├── bnode.cljc │ ├── prefix.cljc │ ├── scope.cljc │ ├── util.cljc │ └── variable.cljc └── test └── com └── yetanalytics ├── flint ├── axiom_test.cljc ├── error_test.cljc ├── format │ ├── axiom_test.cljc │ ├── expr_test.cljc │ ├── modifier_test.cljc │ ├── path_test.cljc │ ├── prologue_test.cljc │ ├── query_test.cljc │ ├── triple_test.cljc │ ├── update_test.cljc │ ├── values_test.cljc │ └── where_test.cljc ├── spec │ ├── axiom_test.cljc │ ├── expr_test.cljc │ ├── modifier_test.cljc │ ├── path_test.cljc │ ├── prologue_test.cljc │ ├── query_test.cljc │ ├── select_test.cljc │ ├── triple_test.cljc │ ├── update_test.cljc │ ├── values_test.cljc │ └── where_test.cljc ├── spec_test.cljc └── validate │ ├── aggregate_test.cljc │ ├── bnode_test.cljc │ ├── prefix_test.cljc │ └── scope_test.cljc └── flint_test.cljc /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: CD 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' # Enforce Semantic Versioning 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup CD Environment 17 | uses: yetanalytics/actions/setup-env@v0.0.4 18 | 19 | - name: Extract version 20 | id: version 21 | run: echo version=${GITHUB_REF#refs\/tags\/v} >> $GITHUB_OUTPUT 22 | 23 | - name: Build and deploy to Clojars 24 | uses: yetanalytics/action-deploy-clojars@v1 25 | with: 26 | artifact-id: 'flint' 27 | resource-dirs: '[]' 28 | version: ${{ steps.version.outputs.version }} 29 | clojars-username: ${{ secrets.CLOJARS_USERNAME }} 30 | clojars-deploy-token: ${{ secrets.CLOJARS_PASSWORD }} 31 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | target: [test-clj, test-cljs] 12 | 13 | steps: 14 | - name: Checkout project 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup CI Environment 18 | uses: yetanalytics/actions/setup-env@v0.0.4 19 | 20 | - name: Run Makefile target ${{ matrix.target }} 21 | run: make ${{ matrix.target }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | pom.xml.asc 3 | *.jar 4 | *.class 5 | /lib/ 6 | /classes/ 7 | /target/ 8 | /checkouts/ 9 | .lein-deps-sum 10 | .lein-repl-history 11 | .lein-plugins/ 12 | .lein-failures 13 | .nrepl-port 14 | .cpcache/ 15 | 16 | # VSCode 17 | .clj-kondo/.cache/ 18 | .calva/ 19 | .lsp/ 20 | .vscode/ 21 | 22 | # ClojureScript 23 | .cljs_node_repl/ 24 | cljs-test-runner-out/ 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.3.0 4 | 5 | - Add support for SPARQL collections syntactic sugar: 6 | - Add support for RDF list syntax. 7 | - Add support for blank node vector syntax. 8 | - Modify the AST tree for triples to support the aforementioned features and to remove redundant nodes in the tree. 9 | - Rework blank node validation to make the implementation simpler (this results in minor changes to the error output). 10 | - Disallow syntax-quoting for symbols. 11 | - Fix bug where timestamps with zeroed-out seconds have the seconds omitted. 12 | 13 | ## v0.2.1 14 | 15 | - Update GitHub Actions CI and CD to remove deprecation warnings. 16 | 17 | ## v0.2.0 18 | 19 | - Rework IRI, variable, blank node and literal implementations and add support for additional Java(Script) types. 20 | - Implement protocols to define validation and formatting behavior IRIs, variables, blank nodes and literals, and apply `extend-type`/`extend-protocol` to default types. 21 | - Add support for `java.net.URI` and `js/URL` IRI instances. 22 | - Add support for `java.time.Temporal` timestamps (e.g. `LocalDateTime` and `ZonedDateTime`). 23 | - Refine datatypes for numeric literals, e.g. Clojure integers are associated with `xsd:long` by default. 24 | - Refine datatypes for date- and time-only timestamps, e.g. `java.sql.Date` and `java.sql.Time` are now associated with `xsd:date` and `xsd:time`, respectively. **(Breaking!)** 25 | - Add `:force-iris?` optional arg in order to force datatype IRIs to be appended when formatting literals (with the exception of language-tagged strings). 26 | - Add support for Unicode characters and percent encoding. 27 | - Unicode characters are now supported in symbols and keywords. 28 | - Percent encoding is allowed in prefixed IRI keyword names. 29 | - (Clojure-only) Optimize string validation. 30 | - Replace certain uses of `s/or` with multi-specs in order to simplify error messages. 31 | 32 | ## v0.1.2 33 | 34 | - Fix a bug where certain SPARQL Update clauses - `LOAD`, `CLEAR`, `CREATE`, and `DROP` - were not being correctly formatted. 35 | - The form `[:graph iri]` is now mandatory for the aforementioned Update clauses and optional for others (`ADD`, `MOVE`, AND `COPY`). 36 | - Fix validation of strings containing escaped char sequences such as `\\n` or `\\r`. 37 | - Special thanks to [@quoll](https://github.com/quoll) for their assistance with this bugfix. 38 | 39 | ## v0.1.1 40 | 41 | Fix a number of bugs discovered in v0.1.0 (see [Pull Request #20](https://github.com/yetanalytics/flint/pull/20)): 42 | - Allow IRIs and prefixed IRIs to be used in expressions. 43 | - Fix zipper traversal not working correctly with deletion or insertion quads. 44 | - Fix `java.time.Instant` instances not being formatted correctly. 45 | - Fix `a` not being valid in DELETE or INSERT queries. 46 | - Fix incorrect predicate specs for triples that restrict both blank nodes and variables. 47 | - Ensure parentheses are properly added around negated property paths. 48 | - Ensure parentheses around expressions are added for certain clauses (e.g. `FILTER`). 49 | 50 | Apply updates to the documentation: 51 | - Fix incorrect documentation on the `CONSTRUCT WHERE` query. 52 | - General grammar and cleanup fixes. 53 | 54 | ## v0.1.0 55 | 56 | Initial release of Flint! 57 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Yet Analytics Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | [team@yetanalytics.com](mailto:team@yetanalytics.com). 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available 126 | at [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | 134 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Yet Analytics Open Source Contribution Guidelines 2 | 3 | ## Welcome to the Yet Analytics Open Source Community! 4 | 5 | Thank you for your interest in contributing to Yet Analytics Open Source projects. It is our goal in maintaining these Open Source projects to provide useful tools for the entire xAPI Community. We welcome feedback and contributions from users, stakeholders and engineers alike. 6 | 7 | The following document outlines the policies, methodology, and guidelines for contributing to our open source projects. 8 | 9 | ## Code of Conduct 10 | 11 | The Yet Analytics Open Source Community has a [Code of Conduct](CODE_OF_CONDUCT.md) which should be read and followed when contributing in any way. 12 | 13 | ## Issue Reporting 14 | 15 | Yet Analytics encourages users to contribute by reporting any issues or enhancement suggestions via [GitHub Issues](https://github.com/yetanalytics/flint/issues). 16 | 17 | Before submission, we encourage you to read through the existing [Documentation](https://cljdoc.org/d/com.yetanalytics/flint) to ensure that the issue has not been addressed or explained. 18 | 19 | ### Issue Templates 20 | 21 | If the repository has an Issue Template, please follow the template as much as possible in your submission as this helps our team more quickly triage and understand the issues you are seeing or enhancements you are suggesting. 22 | 23 | ### Security Issues 24 | 25 | If you believe you have found a potential security issue in the codebase of a Yet Analytics project, please do NOT open an issue. Email [team@yetanalytics.com](mailto:team@yetanalytics.com) directly instead. 26 | 27 | ## Code Contributions 28 | 29 | ### Methodology 30 | 31 | For community contribution to the codebase of a Yet Analytics project we ask that you follow the [Fork and Pull](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) methodology for proposing changes. In short, this method requires you to do the following: 32 | 33 | - Fork the repository 34 | - Clone your Fork and perform the appropriate changes on a branch 35 | - Push the changes back to your Fork 36 | - Make sure your Fork is up to date with the latest `main` branch in the central repository (merge upstream changes) 37 | - Submit a Pull Request using your fork's branch 38 | 39 | The Yet Analytics team will then review the changes, and may make suggestions on GitHub before a final decision is made. The Yet team reviews Pull Requests regularly and we will be notified of its creation and all updates immediately. 40 | 41 | ### Style 42 | 43 | For contributions in Clojure, we would suggest you read this [Clojure Style Guide](https://github.com/bbatsov/clojure-style-guide) as it is one that we generally follow in our codebases. 44 | 45 | ### Tests 46 | 47 | In order for us to merge a Pull Request it must pass the `make ci` Makefile target. This target runs a set of unit, integration and/or conformance tests which verify the build's behavior. Please run this target and remediate any issues before submitting a Pull Request. 48 | 49 | We ask that when adding or changing functionality in the system that you examine whether it is a candidate for additional or modified test coverage and add it if so. You can see what sort of tests are in place currently by exploring the namespaces in `src/test`. 50 | 51 | ## License and Copyright 52 | 53 | By contributing to a Yet Analytics Open Source project you agree that your contributions will be licensed under its [Apache License 2.0](LICENSE). 54 | 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .phony: test-clj test-cov 2 | 3 | test-clj: 4 | clojure -X:test:run-clj 5 | 6 | test-cljs: 7 | clojure -M:test:run-cljs 8 | 9 | test-cov: 10 | clojure -X:test:run-cov 11 | 12 | ci: test-clj test-cljs test-cov 13 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src/main"] 2 | :deps {org.clojure/clojure {:mvn/version "1.10.3"} 3 | org.clojure/clojurescript {:mvn/version "1.10.914"}} 4 | :aliases 5 | {:dev {:extra-paths ["src/dev"] 6 | :extra-deps {org.apache.jena/jena-arq {:mvn/version "4.3.2"} 7 | criterium/criterium {:mvn/version "0.4.6"}}} 8 | :test {:extra-paths ["src/test" "dev-resources"] 9 | :extra-deps {org.clojure/test.check {:mvn/version "1.1.1"} 10 | cloverage/cloverage {:mvn/version "1.2.2"} 11 | olical/cljs-test-runner {:mvn/version "3.8.0" 12 | :exclusions [org.clojure/clojurescript]} 13 | io.github.cognitect-labs/test-runner 14 | {:git/tag "v0.5.0" 15 | :git/sha "b3fd0d2"}}} 16 | :run-clj {:exec-fn cognitect.test-runner.api/test 17 | :exec-args {:dirs ["src/test"]}} 18 | :run-cljs {;; -X is not supported here yet 19 | :main-opts ["-m" "cljs-test-runner.main" 20 | "-d" "src/test"]} 21 | :run-cov {:exec-fn cloverage.coverage/run-project 22 | :exec-args {:src-ns-path ["src/main"] 23 | :test-ns-path ["src/test"]}}}} 24 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/ask/ask-1.edn: -------------------------------------------------------------------------------- 1 | ;; Basic ASK 2 | {:prefixes {:foaf ""} 3 | :ask [] 4 | :where [[?x :foaf/name "Alice"]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/ask/ask-2.edn: -------------------------------------------------------------------------------- 1 | ;; ASK with shared subject 2 | {:prefixes {:foaf ""} 3 | :ask [] 4 | :where [{?x {:foaf/name #{"Alice"} 5 | :foaf/mbox #{"mailto:alice@work.example"}}}]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/construct/construct-1.edn: -------------------------------------------------------------------------------- 1 | ;; CONSTRUCT query 2 | {:prefixes {:foaf "" 3 | :vcard ""} 4 | :construct [["" :vcard/FN ?name]] 5 | :where [[?x :foaf/name ?name]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/construct/construct-2.edn: -------------------------------------------------------------------------------- 1 | ;; CONSTRUCT with UNION 2 | {:prefixes {:foaf "" 3 | :vcard ""} 4 | :construct [[?x :vcard/N _v] 5 | [_v :vcard/givenName ?gname] 6 | [_v :vcard/familyName ?fname]] 7 | :where [[:union 8 | [[?x :foaf/firstname ?gname]] 9 | [[?x :foaf/givenname ?gname]]] 10 | [:union 11 | [[?x :foaf/surname ?fname]] 12 | [[?x :foaf/family_name ?fname]]]]} 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/construct/construct-3.edn: -------------------------------------------------------------------------------- 1 | ;; CONSTRUCT with GRAPH and FILTER 2 | {:prefixes {:dc "" 3 | :app "" 4 | :xsd ""} 5 | :construct [[?s ?p ?o]] 6 | :where [[:graph ?g [[?s ?p ?o]]] 7 | [?g :dc/publisher ""] 8 | [?g :dc/date ?date] 9 | [:filter (> (:app/customDate ?date) 10 | #inst "2005-02-28T00:00:00.005Z")]]} 11 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/construct/construct-4.edn: -------------------------------------------------------------------------------- 1 | ;; CONSTRUCT with ORDER BY and LIMIT 2 | {:prefixes {:foaf "" 3 | :site ""} 4 | :construct [[_ :foaf/name ?name]] 5 | :where [{_ {:foaf/name #{?name} 6 | :site/hits #{?hits}}}] 7 | :order-by [(desc ?hits)] 8 | :limit 2} 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/construct/construct-5.edn: -------------------------------------------------------------------------------- 1 | ;; CONSTRUCT WHERE 2 | {:prefixes {:foaf ""} 3 | :construct [] 4 | :where [[?x :foaf/name ?name]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/describe/describe-1.edn: -------------------------------------------------------------------------------- 1 | ;; Basic DESCRIBE 2 | {:describe [""]} 3 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/describe/describe-2.edn: -------------------------------------------------------------------------------- 1 | ;; DESCRIBE with WHERE 2 | {:prefixes {:foaf ""} 3 | :describe [?x] 4 | :where [[?x :foaf/mbox "mailto:alice@org"]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/describe/describe-3.edn: -------------------------------------------------------------------------------- 1 | ;; DESCRIBE with WHERE and string literal 2 | {:prefixes {:foaf ""} 3 | :describe [?x] 4 | :where [[?x :foaf/name "Alice"]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/describe/describe-4.edn: -------------------------------------------------------------------------------- 1 | ;; DESCRIBE with multiple vars and iris 2 | {:prefixes {:foaf ""} 3 | :describe [?x ?y ""] 4 | :where [[?x :foaf/knows ?y]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/describe/describe-5.edn: -------------------------------------------------------------------------------- 1 | ;; DESCRIBE with numerical string literal 2 | {:prefixes {:ent ""} 3 | :describe [?x] 4 | :where [[?x :ent/employeeId "1234"]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-agg/select-agg-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with AVG and GROUP BY 2 | {:prefixes {:$ ""} 3 | :select [[(avg ?y) ?avg]] 4 | :where [{?a {:x #{?x} 5 | :y #{?y}}}] 6 | :group-by [?x]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-agg/select-agg-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with AVG, GROUP BY and HAVING 2 | {:prefixes {:$ ""} 3 | :select [[(avg ?size) ?asize]] 4 | :where [[?x :size ?size]] 5 | :group-by [?x] 6 | :having [(> (avg ?size) 10)]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-agg/select-agg-3.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with MIN and STR expression in GROUP BY 2 | {:prefixes {:$ ""} 3 | :select [?x [(* (min ?y) 2) ?min]] 4 | :where [[?x :p ?y] [?x :q ?z]] 5 | :group-by [?x (str ?z)]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-agg/select-agg-4.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with AVG, MIN, and GROUP BY 2 | {:prefixes {:$ ""} 3 | :select [?g [(avg ?p) ?avg] [(/ (+ (min ?p) (max ?p)) 2) ?c]] 4 | :where [[?g :p ?p]] 5 | :group-by [?g]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-agg/select-agg-5.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with SUM, GROUP BY and HAVING 2 | {:prefixes {:$ ""} 3 | :select [[(sum ?lprice) ?totalPrice]] 4 | :where [[?org :affiliates ?auth] 5 | [?auth :writesBook ?book] 6 | [?book :price ?lprice]] 7 | :group-by [?org] 8 | :having [(> (sum ?lprice) 10)]} 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-bind/select-bind-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with BIND 2 | {:prefixes {:dc "" 3 | :ns ""} 4 | :select [?title ?price] 5 | :where [[?x :ns/price ?p] 6 | [?x :ns/discount ?discount] 7 | [:bind [(* ?p (- 1 ?discount)) ?price]] 8 | [:filter (< ?price 20)] 9 | [?x :dc/title ?title]]} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-bind/select-bind-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with nested BIND 2 | {:prefixes {:dc "" 3 | :ns ""} 4 | :select [?title ?price] 5 | :where [[:where [[?x :ns/price ?p] 6 | [?x :ns/discount ?discount] 7 | [:bind [(* ?p (- 1 ?discount)) ?price]]]] 8 | [:filter (< ?price 20)] 9 | [?x :dc/title ?title]]} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-filter/select-filter-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FILTER (`regex` expression) 2 | {:prefixes {:dc ""} 3 | :select [?title] 4 | :where [[?x :dc/title ?title] 5 | [:filter (regex ?title "^SPARQL")]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-filter/select-filter-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FILTER (`<` expression) 2 | {:prefixes {:dc "" 3 | :ns ""} 4 | :select [?title ?price] 5 | :where [[?x :ns/price ?price] 6 | [:filter (< ?price 30.5)] 7 | [?x :dc/title ?title]]} 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-filter/select-filter-3.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FILTER EXISTS 2 | {:prefixes {:rdf "" 3 | :foaf ""} 4 | :select [?person] 5 | :where [[?person :rdf/type :foaf/Person] 6 | [:filter (exists [[?person :foaf/name ?name]])]]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-filter/select-filter-4.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FILTER NOT EXISTS 2 | {:prefixes {:rdf "" 3 | :foaf ""} 4 | :select [?person] 5 | :where [[?person :rdf/type :foaf/Person] 6 | [:filter (not-exists [[?person :foaf/name ?name]])]]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-filter/select-filter-5.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with nested FILTERs 2 | {:prefixes {:$ ""} 3 | :select * 4 | :where [[?x :p ?n] 5 | [:filter (not-exists [[?x :q ?m] 6 | [:filter (= ?n ?m)]])]]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-filter/select-filter-6.edn: -------------------------------------------------------------------------------- 1 | ;; FILTER with isBlank and boolean literal 2 | {:prefixes {:a "" 3 | :dc "" 4 | :foaf ""} 5 | :select [?given ?family] 6 | :where [[?annot :a/annotates ""] 7 | [?annot :dc/creator ?c] 8 | [:optional [{?c {:foaf/given #{?given} 9 | :foaf/family #{?family}}}]] 10 | [:filter (= (blank? ?c) true)]]} 11 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-from/select-from-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT using GRAPH 2 | {:prefixes {:foaf "" 3 | :dc ""} 4 | :select [?name ?mbox ?date] 5 | :where [{?g {:dc/publisher #{?name} 6 | :dc/date #{?date}}} 7 | [:graph ?g [{?person {:foaf/name #{?name} 8 | :foaf/mbox #{?mbox}}}]]]} 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-from/select-from-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FROM 2 | {:prefixes {:foaf ""} 3 | :select [?name] 4 | :from "" 5 | :where [[?x :foaf/name ?name]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-from/select-from-3.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT using FROM NAMED and variable GRAPH iri 2 | {:prefixes {:foaf ""} 3 | :select [?src ?bobNick] 4 | :from-named ["" 5 | ""] 6 | :where [[:graph ?src [[?x :foaf/mbox "mailto:bob@work.example"] 7 | [?x :foaf/name ?bobNick]]] 8 | [?x :foaf/name ?name]]} 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-from/select-from-4.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FROM NAMED and fixed GRAPH iri 2 | {:prefixes {:foaf "" 3 | :data ""} 4 | :select [?nick] 5 | :from-named ["" 6 | ""] 7 | :where [[:graph 8 | :data/bobFoaf 9 | [[?x :foaf/mbox "mailto:bob@work.example"] 10 | [?x :foaf/name ?nick]]]]} 11 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-from/select-from-5.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FROM NAMED and multiple GRAPHs 2 | {:prefixes {:foaf "" 3 | :data "" 4 | :rdfs ""} 5 | :select [?mbox ?nick ?ppd] 6 | :from-named ["" 7 | ""] 8 | :where [[:graph 9 | :data/aliceFoaf 10 | [{?alice {:foaf/mbox #{"mailto:alice@work.example"} 11 | :foaf/knows #{?whom}} 12 | ?whom {:foaf/mbox #{?mbox} 13 | :rdfs/seeAlso #{?ppd}} 14 | ?ppd {a #{:foaf/PersonalProfileDocument}}}]] 15 | [:graph 16 | ?ppd 17 | [{?w {:foaf/mbox #{?mbox} 18 | :foaf/nick #{?nick}}}]]]} 19 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-from/select-from-6.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with both FROM and FROM NAMED 2 | {:prefixes {:foaf "" 3 | :dc ""} 4 | :select [?who ?g ?mbox] 5 | :from [""] 6 | :from-named ["" 7 | ""] 8 | :where [[?g :dc/publisher ?who] 9 | [:graph ?g [[?x :foaf/mbox ?mbox]]]]} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-limit/select-limit-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with LIMIT 2 | {:prefixes {:foaf ""} 3 | :select [?name] 4 | :where [[?x :foaf/name ?name]] 5 | :limit 20} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-limit/select-limit-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with LIMIT OFFSET 2 | {:prefixes {:foaf ""} 3 | :select [?name] 4 | :where [[?x :foaf/name ?name]] 5 | :order-by [?name] 6 | :limit 5 7 | :offset 10} 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-minus/select-minus-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with MINUS 2 | {:prefixes {:$ "" 3 | :foaf ""} 4 | :select-distinct [?s] 5 | :where [[?s ?p ?o] 6 | [:minus [[?s :foaf/givenName "Bob"]]]]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-minus/select-minus-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FILTER nested inside of MINUS 2 | {:prefixes {:$ ""} 3 | :select * 4 | :where [[?x :p ?n] 5 | [:minus [[?x :q ?m] 6 | [:filter (= ?n ?m)]]]]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-optional/select-optional-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with OPTIONAL 2 | {:prefixes {:foaf ""} 3 | :select [?name ?mbox] 4 | :where [[?x :foaf/name ?name] 5 | [:optional [[?x :foaf/mbox ?mbox]]]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-optional/select-optional-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with multiple OPTIONALs 2 | {:prefixes {:foaf ""} 3 | :select [?name ?mbox ?hpage] 4 | :where [[?x :foaf/name ?name] 5 | [:optional [[?x :foaf/mbox ?mbox]]] 6 | [:optional [[?x :foaf/homepage ?hpage]]]]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-optional/select-optional-3.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with FILTER nested inside of OPTIONAL 2 | {:prefixes {:dc "" 3 | :ns ""} 4 | :select [?title ?price] 5 | :where [[?x :dc/title ?title] 6 | [:optional [[?x :ns/price ?price] 7 | [:filter (< ?price 30)]]]]} 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-order/select-order-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with ORDER BY 2 | {:prefixes {:foaf ""} 3 | :select [?name] 4 | :where [[?x :foaf/name ?name]] 5 | :order-by [?name]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-order/select-order-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with ORDER BY DESC 2 | {:prefixes {:foaf "" 3 | :$ ""} 4 | :select [?name] 5 | :where [{?x {:foaf/name #{?name} :empId #{?emp}}}] 6 | :order-by [(desc ?name)]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-order/select-order-3.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with ORDER BY ?var DESC 2 | {:prefixes {:foaf "" 3 | :$ ""} 4 | :select [?name] 5 | :where [{?x {:foaf/name #{?name} :empId #{?emp}}}] 6 | :order-by [?name (desc ?emp)]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-path/select-path-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with sequential property path 2 | {:prefixes {:$ ""} 3 | :select * 4 | :where [[:s (cat :item :price) ?x]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-path/select-path-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with SUM and sequential property path 2 | {:prefixes {:$ ""} 3 | :select [[(sum ?x) ?total]] 4 | :where [[:s (cat :item :price) ?x]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-path/select-path-3.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with sequential and Kleene star property path 2 | {:prefixes {:rdfs "" 3 | :rdf ""} 4 | :select [?x ?type] 5 | :where [[?x (cat :rdf/type (* :rdfs/subClassOf)) ?type]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-path/select-path-4.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with Kleene plus property path 2 | {:prefixes {:foaf "" 3 | :$ ""} 4 | :select [?person] 5 | :where [[:x (+ :foaf/knows) ?person]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-service/select-service-1.edn: -------------------------------------------------------------------------------- 1 | ;; SERVICE call 2 | {:prefixes {:foaf ""} 3 | :select [?name] 4 | :from "" 5 | :where [["" :foaf/knows ?person] 6 | [:service "" 7 | [[?person :foaf/name ?name]]]]} 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-service/select-service-2.edn: -------------------------------------------------------------------------------- 1 | ;; Nested SERVICE call 2 | {:prefixes {:foaf ""} 3 | :select [?person ?interest ?known] 4 | :where [[:service "" 5 | [[?person :foaf/name ?name] 6 | [:optional [[?person :foaf/interest ?interest] 7 | [:service 8 | "" 9 | [[?person :foaf/knows ?known]]]]]]]]} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-service/select-service-3.edn: -------------------------------------------------------------------------------- 1 | ;; SERVICE SILENTcall 2 | {:prefixes {:foaf ""} 3 | :select [?name] 4 | :where [[:service-silent "" 5 | [["" :foaf/name ?name]]]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-subquery/select-subquery-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with sub-SELECT query 2 | {:prefixes {:$ ""} 3 | :select [?y ?minName] 4 | :where [[:alice :knows ?y] 5 | [:where {:select [?y [(min ?name) ?minName]] 6 | :where [[?y :name ?name]] 7 | :group-by [?y]}]]} 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-union/select-union-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with UNION (same object) 2 | {:prefixes {:dc10 "" 3 | :dc11 ""} 4 | :select [?title] 5 | :where [[:union 6 | [[?book :dc10/title ?title]] 7 | [[?book :dc11/title ?title]]]]} 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-union/select-union-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with UNION (different object) 2 | {:prefixes {:dc10 "" 3 | :dc11 ""} 4 | :select [?x ?y] 5 | :where [[:union 6 | [[?book :dc10/title ?x]] 7 | [[?book :dc11/title ?y]]]]} 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-values/select-values-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with VALUES 2 | {:prefixes {:dc "" 3 | :ns "" 4 | :$ ""} 5 | :select [?book ?title ?price] 6 | :where [[:values {?book [:book1 :book3]}] 7 | {?book {:dc/title #{?title} 8 | :ns/price #{?price}}}]} 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-values/select-values-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with null VALUES 2 | {:prefixes {:dc "" 3 | :ns "" 4 | :$ ""} 5 | :select [?book ?title ?price] 6 | :where [[:values {?book [nil :book2] 7 | ?title ["SPARQL Tutorial" nil]}] 8 | {?book {:dc/title #{?title} 9 | :ns/price #{?price}}}]} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-values/select-values-3.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with null VALUES (original syntax) 2 | {:prefixes {:dc "" 3 | :ns "" 4 | :$ ""} 5 | :select [?book ?title ?price] 6 | :where [[:values {[?book ?title] [[nil "SPARQL Tutorial"] 7 | [:book2 nil]]}] 8 | {?book {:dc/title #{?title} 9 | :ns/price #{?price}}}]} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select-values/select-values-4.edn: -------------------------------------------------------------------------------- 1 | ;; VALUES clause as a solution modifier 2 | {:prefixes {:dc "" 3 | :ns "" 4 | :$ ""} 5 | :select [?book ?title ?price] 6 | :where [{?book {:dc/title #{?title} 7 | :ns/price #{?price}}}] 8 | :values {[?book ?title] [[nil "SPARQL Tutorial"] 9 | [:book2 nil]]}} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-1.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with no WHERE 2 | {:select [?x] 3 | :where []} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-10.edn: -------------------------------------------------------------------------------- 1 | ;; BASE in prologue 2 | {:base "" 3 | :prefixes {:dc ""} 4 | :select [?title] 5 | :where [["" :dc/title ?title]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-11.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with all the escaped chars 2 | {:select [?v] 3 | :where [[?v ?p "\\\"foo\\\"\\n\\'bar\\'\\r"]]} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-12.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with lists 2 | {:select [?x] 3 | :where [[?s ?p (1 ?x 3 4)]]} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-13.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with blank node vectors 2 | {:prefixes {:$ "" 3 | :foaf ""} 4 | :select [?x ?name] 5 | :where [[[:p1 "v"] :q1 "w"] 6 | [:x :q2 [:p2 "v"]] 7 | [[:foaf/name ?name :foaf/mbox ""]]]} 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-14.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with both lists and blank node vectors 2 | {:prefixes {:$ ""} 3 | :select [?x] 4 | :where [{(1 [:p :q] (2 ?x)) {} 5 | () {:r #{[]}}}]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-2.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with var + iri WHERE 2 | {:select [?title] 3 | :where [["" 4 | "" 5 | ?title]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-3.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with var + string literal WHERE 2 | {:select [?v] 3 | :where [[?v ?p "cat: \\\"meow\\\""]]} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-4.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with var + lang tag WHERE 2 | {:select [?v] 3 | :where [[?v ?p {:en "cat: \\\"meow\\\""}]]} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-5.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with var + num literal WHERE 2 | {:select [?v] 3 | :where [[?v ?p 42]]} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-6.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with prefixed IRI 2 | {:prefixes {:foaf ""} 3 | :select [?name ?mbox] 4 | :where [[?x :foaf/name ?name] 5 | [?x :foaf/mbox ?mbox]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-7.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT DISTINCT with prefixed IRI 2 | {:prefixes {:foaf ""} 3 | :select-distinct [?name] 4 | :where [[?x :foaf/name ?name]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-8.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT REDUCED with prefixed IRI 2 | {:prefixes {:foaf ""} 3 | :select-reduced [?name] 4 | :where [[?x :foaf/name ?name]]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/query/select/select-9.edn: -------------------------------------------------------------------------------- 1 | ;; SELECT with shared subject in WHERE 2 | {:prefixes {:foaf ""} 3 | :select [[(concat ?G " " ?S) ?name]] 4 | :where [{?P {:foaf/givenName #{?G} 5 | :foaf/surname #{?S}}}]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update-seq/updates-1.edn: -------------------------------------------------------------------------------- 1 | ;; DELETE DATA followed by INSERT DATA 2 | [{:prefixes {:dc ""} 3 | :delete-data [[:graph "" 4 | [["" 5 | :dc/title 6 | "Fundamentals of Compiler Desing"]]]]} 7 | {:prefixes {:dc ""} 8 | :insert-data [[:graph "" 9 | [["" 10 | :dc/title 11 | "Fundamentals of Compiler Design"]]]]}] 12 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update-seq/updates-2.edn: -------------------------------------------------------------------------------- 1 | ;; DELETE DATA with different PREFIXes 2 | [{:prefixes {:dc0 ""} 3 | :delete-data [[:graph "" 4 | [["" 5 | :dc0/title 6 | "Fundamentals of Compiler Desing"]]]]} 7 | {:prefixes {:dc1 ""} 8 | :delete-data [[:graph "" 9 | [["" 10 | :dc1/title 11 | "Fundamentals of Compiler Desing"]]]]}] 12 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update-seq/updates-3.edn: -------------------------------------------------------------------------------- 1 | [{:prefixes {:dc "" 2 | :dcmitype "" 3 | :xsd ""} 4 | :insert [[:graph "" 5 | [[?book ?p ?v]]]] 6 | :where [[:graph "" 7 | [[?book :dc/date ?date] 8 | [:filter (< ?date #inst "2000-01-01T00:00:00.001Z")] 9 | [?book ?p ?v]]]]} 10 | {:with "" 11 | :delete [[?book ?p ?v]] 12 | :where [{?book {:dc/date #{?date} 13 | :dc/type #{:dcmitype/PhysicalObject}}} 14 | [:filter (< ?date #inst "2000-01-01T00:00:00.002Z")] 15 | [?book ?p ?v]]}] 16 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update-seq/updates-4.edn: -------------------------------------------------------------------------------- 1 | ;; All the graph management updates 2 | [{:create [:graph ""]} 3 | {:load "" 4 | :into [:graph ""]} 5 | {:copy "" 6 | :to ""} 7 | {:copy :default 8 | :to [:graph ""]} 9 | {:copy [:graph ""] 10 | :to :default} 11 | {:add [:graph ""] 12 | :to [:graph ""]} 13 | {:add :default 14 | :to ""} 15 | {:add [:graph ""] 16 | :to :default} 17 | {:move [:graph ""] 18 | :to [:graph ""]} 19 | {:move :default 20 | :to [:graph ""]} 21 | {:move "" 22 | :to :default} 23 | {:clear [:graph ""]} 24 | {:clear :default} 25 | {:clear :named} 26 | {:clear :all} 27 | {:drop [:graph ""]} 28 | {:drop :default} 29 | {:drop :named} 30 | {:drop :all}] 31 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update-seq/updates-5.edn: -------------------------------------------------------------------------------- 1 | ;; All the graph management updates, silent mode 2 | [{:create-silent [:graph ""]} 3 | {:load-silent "" 4 | :into [:graph ""]} 5 | {:copy-silent "" 6 | :to ""} 7 | {:copy-silent :default 8 | :to [:graph ""]} 9 | {:copy-silent [:graph ""] 10 | :to :default} 11 | {:add-silent [:graph ""] 12 | :to [:graph ""]} 13 | {:add-silent :default 14 | :to ""} 15 | {:add-silent [:graph ""] 16 | :to :default} 17 | {:move-silent [:graph ""] 18 | :to [:graph ""]} 19 | {:move-silent :default 20 | :to [:graph ""]} 21 | {:move-silent "" 22 | :to :default} 23 | {:clear-silent [:graph ""]} 24 | {:clear-silent :default} 25 | {:clear-silent :named} 26 | {:clear-silent :all} 27 | {:drop-silent [:graph ""]} 28 | {:drop-silent :default} 29 | {:drop-silent :named} 30 | {:drop-silent :all}] 31 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update-seq/updates-6.edn: -------------------------------------------------------------------------------- 1 | ;; All the graph management updates, as prefixes 2 | [{:prefixes {:graphs "" 3 | :src ""} 4 | :create [:graph :graphs/one]} 5 | {:load :src/source 6 | :into [:graph :graphs/two]} 7 | {:copy :graphs/one 8 | :to :graphs/two} 9 | {:copy :default 10 | :to [:graph :graphs/two]} 11 | {:copy [:graph :graphs/one] 12 | :to :default} 13 | {:add [:graph :graphs/one] 14 | :to [:graph :graphs/two]} 15 | {:add :default 16 | :to :graphs/two} 17 | {:add [:graph :graphs/one] 18 | :to :default} 19 | {:move [:graph :graphs/one] 20 | :to [:graph :graphs/two]} 21 | {:move :default 22 | :to [:graph :graphs/two]} 23 | {:move :graphs/one 24 | :to :default} 25 | {:clear [:graph :graphs/two]} 26 | {:clear :default} 27 | {:clear :named} 28 | {:clear :all} 29 | {:drop [:graph :graphs/one]} 30 | {:drop :default} 31 | {:drop :named} 32 | {:drop :all}] 33 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/delete-data-1.edn: -------------------------------------------------------------------------------- 1 | ;; DELETE DATA 2 | {:prefixes {:dc ""} 3 | :delete-data [{"" 4 | {:dc/title #{"David Copperfield"} 5 | :dc/creator #{"Edmund Wells"}}}]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/delete-where-1.edn: -------------------------------------------------------------------------------- 1 | ;; DELETE WHERE 2 | {:prefixes {:foaf ""} 3 | :delete-where [{?person {:foaf/givenName #{"Fred"} 4 | ?property #{?value}}}]} 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/delete-where-2.edn: -------------------------------------------------------------------------------- 1 | ;; DELETE WHERE with GRAPH 2 | {:prefixes {:foaf ""} 3 | :delete-where [[:graph 4 | "" 5 | [{?person {:foaf/givenName #{"Fred"} 6 | ?property1 #{?value1}}}]] 7 | [:graph 8 | "" 9 | [[?person ?property2 ?value2]]]]} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/graph-add-1.edn: -------------------------------------------------------------------------------- 1 | ;; ADD update 2 | {:add :default 3 | :to ""} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/graph-copy-1.edn: -------------------------------------------------------------------------------- 1 | ;; COPY update 2 | {:copy :default 3 | :to ""} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/graph-move-1.edn: -------------------------------------------------------------------------------- 1 | ;; MOVE update 2 | {:move :default 3 | :to ""} 4 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/insert-data-1.edn: -------------------------------------------------------------------------------- 1 | ;; INSERT DATA 2 | {:prefixes {:dc ""} 3 | :insert-data [{"" 4 | {:dc/title #{"A new book"} 5 | :dc/creator #{"A. N. Other"}}}]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/insert-data-2.edn: -------------------------------------------------------------------------------- 1 | ;; INSERT DATA with GRAPH 2 | {:prefixes {:dc "" 3 | :ns ""} 4 | :insert-data [[:graph "" 5 | [["" :ns/price 42]]]]} 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/modify-1.edn: -------------------------------------------------------------------------------- 1 | ;; DELETE with WHERE 2 | {:prefixes {:foaf ""} 3 | :with "" 4 | :delete [[?person :foaf/givenName "Bill"] 5 | [?person :foaf/givenName "William"]] 6 | :where [[?person :foaf/givenName "Bill"]]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/modify-2.edn: -------------------------------------------------------------------------------- 1 | ;; DELETE with WHERE (normal form) 2 | {:prefixes {:foaf ""} 3 | :with "" 4 | :delete [[?person ?property ?value]] 5 | :where [{?person {?property #{?value} 6 | :foaf/givenName #{"Fred"}}}]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/modify-3.edn: -------------------------------------------------------------------------------- 1 | ;; INSERT with WHERE (and FILTER on dateTime literal) 2 | {:prefixes {:dc "" 3 | :xsd ""} 4 | :insert [[:graph "" [[?book ?p ?v]]]] 5 | :where [[:graph 6 | "" 7 | [[?book :dc/date ?date] 8 | [:filter (> ?date #inst "1970-01-01T00:00:00.005Z")] 9 | [?book ?p ?v]]]]} 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/modify-4.edn: -------------------------------------------------------------------------------- 1 | ;; INSERT with GRAPH and WHERE 2 | {:prefixes {:foaf "" 3 | :rdf ""} 4 | :insert [[:graph 5 | "" 6 | [[?person :foaf/name ?name] 7 | [?person :foaf/mbox ?email]]]] 8 | :where [[:graph 9 | "" 10 | [[?person :foaf/name ?name] 11 | [:optional [[?person :foaf/mbox ?email]]]]]]} 12 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/inputs/update/modify-5.edn: -------------------------------------------------------------------------------- 1 | ;; DELETE followed by INSERT 2 | {:prefixes {:foaf ""} 3 | :with "" 4 | :delete [[?person :foaf/givenName "Bill"]] 5 | :insert [[?person :foaf/givenName "William"]] 6 | :where [[?person :foaf/givenName "Bill"]]} 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/ask/ask-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | ASK 3 | WHERE { 4 | ?x foaf:name "Alice" . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/ask/ask-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | ASK 3 | WHERE { 4 | ?x foaf:name "Alice" ; 5 | foaf:mbox "mailto:alice@work.example" . 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/construct/construct-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX vcard: 3 | CONSTRUCT { 4 | vcard:FN ?name . 5 | } 6 | WHERE { 7 | ?x foaf:name ?name . 8 | } 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/construct/construct-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX vcard: 3 | CONSTRUCT { 4 | ?x vcard:N _:v . 5 | _:v vcard:givenName ?gname . 6 | _:v vcard:familyName ?fname . 7 | } 8 | WHERE { 9 | { 10 | ?x foaf:firstname ?gname . 11 | } 12 | UNION 13 | { 14 | ?x foaf:givenname ?gname . 15 | } 16 | { 17 | ?x foaf:surname ?fname . 18 | } 19 | UNION 20 | { 21 | ?x foaf:family_name ?fname . 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/construct/construct-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX app: 3 | PREFIX xsd: 4 | CONSTRUCT { 5 | ?s ?p ?o . 6 | } 7 | WHERE { 8 | GRAPH ?g { 9 | ?s ?p ?o . 10 | } 11 | ?g dc:publisher . 12 | ?g dc:date ?date . 13 | FILTER (app:customDate(?date) > "2005-02-28T00:00:00.005Z"^^xsd:dateTime) 14 | } 15 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/construct/construct-4.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX site: 3 | CONSTRUCT { 4 | [] foaf:name ?name . 5 | } 6 | WHERE { 7 | [] foaf:name ?name ; 8 | site:hits ?hits . 9 | } 10 | ORDER BY DESC(?hits) 11 | LIMIT 2 12 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/construct/construct-5.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | CONSTRUCT 3 | WHERE { 4 | ?x foaf:name ?name . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/describe/describe-1.rq: -------------------------------------------------------------------------------- 1 | DESCRIBE 2 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/describe/describe-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | DESCRIBE ?x 3 | WHERE { 4 | ?x foaf:mbox "mailto:alice@org" . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/describe/describe-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | DESCRIBE ?x 3 | WHERE { 4 | ?x foaf:name "Alice" . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/describe/describe-4.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | DESCRIBE ?x ?y 3 | WHERE { 4 | ?x foaf:knows ?y . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/describe/describe-5.rq: -------------------------------------------------------------------------------- 1 | PREFIX ent: 2 | DESCRIBE ?x 3 | WHERE { 4 | ?x ent:employeeId "1234" . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-agg/select-agg-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT (AVG(?y) AS ?avg) 3 | WHERE { 4 | ?a :x ?x ; 5 | :y ?y . 6 | } 7 | GROUP BY ?x 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-agg/select-agg-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT (AVG(?size) AS ?asize) 3 | WHERE { 4 | ?x :size ?size . 5 | } 6 | GROUP BY ?x 7 | HAVING (AVG(?size) > 10) 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-agg/select-agg-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT ?x ((MIN(?y) * 2) AS ?min) 3 | WHERE { 4 | ?x :p ?y . 5 | ?x :q ?z . 6 | } 7 | GROUP BY ?x STR(?z) 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-agg/select-agg-4.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT ?g (AVG(?p) AS ?avg) (((MIN(?p) + MAX(?p)) / 2) AS ?c) 3 | WHERE { 4 | ?g :p ?p . 5 | } 6 | GROUP BY ?g 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-agg/select-agg-5.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT (SUM(?lprice) AS ?totalPrice) 3 | WHERE { 4 | ?org :affiliates ?auth . 5 | ?auth :writesBook ?book . 6 | ?book :price ?lprice . 7 | } 8 | GROUP BY ?org 9 | HAVING (SUM(?lprice) > 10) 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-bind/select-bind-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | SELECT ?title ?price 4 | WHERE { 5 | ?x ns:price ?p . 6 | ?x ns:discount ?discount . 7 | BIND ((?p * (1 - ?discount)) AS ?price) 8 | FILTER (?price < 20) 9 | ?x dc:title ?title . 10 | } 11 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-bind/select-bind-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | SELECT ?title ?price 4 | WHERE { 5 | { 6 | ?x ns:price ?p . 7 | ?x ns:discount ?discount . 8 | BIND ((?p * (1 - ?discount)) AS ?price) 9 | } 10 | FILTER (?price < 20) 11 | ?x dc:title ?title . 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-filter/select-filter-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | SELECT ?title 3 | WHERE { 4 | ?x dc:title ?title . 5 | FILTER REGEX(?title, "^SPARQL") 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-filter/select-filter-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | SELECT ?title ?price 4 | WHERE { 5 | ?x ns:price ?price . 6 | FILTER (?price < 30.5) 7 | ?x dc:title ?title . 8 | } 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-filter/select-filter-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdf: 2 | PREFIX foaf: 3 | SELECT ?person 4 | WHERE { 5 | ?person rdf:type foaf:Person . 6 | FILTER EXISTS { 7 | ?person foaf:name ?name . 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-filter/select-filter-4.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdf: 2 | PREFIX foaf: 3 | SELECT ?person 4 | WHERE { 5 | ?person rdf:type foaf:Person . 6 | FILTER NOT EXISTS { 7 | ?person foaf:name ?name . 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-filter/select-filter-5.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT * 3 | WHERE { 4 | ?x :p ?n . 5 | FILTER NOT EXISTS { 6 | ?x :q ?m . 7 | FILTER (?n = ?m) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-filter/select-filter-6.rq: -------------------------------------------------------------------------------- 1 | PREFIX a: 2 | PREFIX dc: 3 | PREFIX foaf: 4 | SELECT ?given ?family 5 | WHERE { 6 | ?annot a:annotates . 7 | ?annot dc:creator ?c . 8 | OPTIONAL { 9 | ?c foaf:given ?given ; 10 | foaf:family ?family . 11 | } 12 | FILTER (isBLANK(?c) = true) 13 | } 14 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-from/select-from-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX dc: 3 | SELECT ?name ?mbox ?date 4 | WHERE { 5 | ?g dc:publisher ?name ; 6 | dc:date ?date . 7 | GRAPH ?g { 8 | ?person foaf:name ?name ; 9 | foaf:mbox ?mbox . 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-from/select-from-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name 3 | FROM 4 | WHERE { 5 | ?x foaf:name ?name . 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-from/select-from-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?src ?bobNick 3 | FROM NAMED 4 | FROM NAMED 5 | WHERE { 6 | GRAPH ?src { 7 | ?x foaf:mbox "mailto:bob@work.example" . 8 | ?x foaf:name ?bobNick . 9 | } 10 | ?x foaf:name ?name . 11 | } 12 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-from/select-from-4.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX data: 3 | SELECT ?nick 4 | FROM NAMED 5 | FROM NAMED 6 | WHERE { 7 | GRAPH data:bobFoaf { 8 | ?x foaf:mbox "mailto:bob@work.example" . 9 | ?x foaf:name ?nick . 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-from/select-from-5.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX data: 3 | PREFIX rdfs: 4 | SELECT ?mbox ?nick ?ppd 5 | FROM NAMED 6 | FROM NAMED 7 | WHERE { 8 | GRAPH data:aliceFoaf { 9 | ?alice foaf:mbox "mailto:alice@work.example" ; 10 | foaf:knows ?whom . 11 | ?whom foaf:mbox ?mbox ; 12 | rdfs:seeAlso ?ppd . 13 | ?ppd a foaf:PersonalProfileDocument . 14 | } 15 | GRAPH ?ppd { 16 | ?w foaf:mbox ?mbox ; 17 | foaf:nick ?nick . 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-from/select-from-6.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX dc: 3 | SELECT ?who ?g ?mbox 4 | FROM 5 | FROM NAMED 6 | FROM NAMED 7 | WHERE { 8 | ?g dc:publisher ?who . 9 | GRAPH ?g { 10 | ?x foaf:mbox ?mbox . 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-limit/select-limit-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name 3 | WHERE { 4 | ?x foaf:name ?name . 5 | } 6 | LIMIT 20 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-limit/select-limit-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name 3 | WHERE { 4 | ?x foaf:name ?name . 5 | } 6 | ORDER BY ?name 7 | LIMIT 5 8 | OFFSET 10 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-minus/select-minus-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | PREFIX foaf: 3 | SELECT DISTINCT ?s 4 | WHERE { 5 | ?s ?p ?o . 6 | MINUS { 7 | ?s foaf:givenName "Bob" . 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-minus/select-minus-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT * 3 | WHERE { 4 | ?x :p ?n . 5 | MINUS { 6 | ?x :q ?m . 7 | FILTER (?n = ?m) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-optional/select-optional-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name ?mbox 3 | WHERE { 4 | ?x foaf:name ?name . 5 | OPTIONAL { 6 | ?x foaf:mbox ?mbox . 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-optional/select-optional-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name ?mbox ?hpage 3 | WHERE { 4 | ?x foaf:name ?name . 5 | OPTIONAL { 6 | ?x foaf:mbox ?mbox . 7 | } 8 | OPTIONAL { 9 | ?x foaf:homepage ?hpage . 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-optional/select-optional-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | SELECT ?title ?price 4 | WHERE { 5 | ?x dc:title ?title . 6 | OPTIONAL { 7 | ?x ns:price ?price . 8 | FILTER (?price < 30) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-order/select-order-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name 3 | WHERE { 4 | ?x foaf:name ?name . 5 | } 6 | ORDER BY ?name 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-order/select-order-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX : 3 | SELECT ?name 4 | WHERE { 5 | ?x foaf:name ?name ; 6 | :empId ?emp . 7 | } 8 | ORDER BY DESC(?name) 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-order/select-order-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX : 3 | SELECT ?name 4 | WHERE { 5 | ?x foaf:name ?name ; 6 | :empId ?emp . 7 | } 8 | ORDER BY ?name DESC(?emp) 9 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-path/select-path-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT * 3 | WHERE { 4 | :s (:item / :price) ?x . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-path/select-path-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT (SUM(?x) AS ?total) 3 | WHERE { 4 | :s (:item / :price) ?x . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-path/select-path-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX rdfs: 2 | PREFIX rdf: 3 | SELECT ?x ?type 4 | WHERE { 5 | ?x (rdf:type / rdfs:subClassOf*) ?type . 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-path/select-path-4.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX : 3 | SELECT ?person 4 | WHERE { 5 | :x foaf:knows+ ?person . 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-service/select-service-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name 3 | FROM 4 | WHERE { 5 | foaf:knows ?person . 6 | SERVICE { 7 | ?person foaf:name ?name . 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-service/select-service-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?person ?interest ?known 3 | WHERE { 4 | SERVICE { 5 | ?person foaf:name ?name . 6 | OPTIONAL { 7 | ?person foaf:interest ?interest . 8 | SERVICE { 9 | ?person foaf:knows ?known . 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-service/select-service-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name 3 | WHERE { 4 | SERVICE SILENT { 5 | foaf:name ?name . 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-subquery/select-subquery-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT ?y ?minName 3 | WHERE { 4 | :alice :knows ?y . 5 | { 6 | SELECT ?y (MIN(?name) AS ?minName) 7 | WHERE { 8 | ?y :name ?name . 9 | } 10 | GROUP BY ?y 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-union/select-union-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc10: 2 | PREFIX dc11: 3 | SELECT ?title 4 | WHERE { 5 | { 6 | ?book dc10:title ?title . 7 | } 8 | UNION 9 | { 10 | ?book dc11:title ?title . 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-union/select-union-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc10: 2 | PREFIX dc11: 3 | SELECT ?x ?y 4 | WHERE { 5 | { 6 | ?book dc10:title ?x . 7 | } 8 | UNION 9 | { 10 | ?book dc11:title ?y . 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-values/select-values-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | PREFIX : 4 | SELECT ?book ?title ?price 5 | WHERE { 6 | VALUES ?book { 7 | :book1 8 | :book3 9 | } 10 | ?book dc:title ?title ; 11 | ns:price ?price . 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-values/select-values-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | PREFIX : 4 | SELECT ?book ?title ?price 5 | WHERE { 6 | VALUES (?book ?title) { 7 | (UNDEF "SPARQL Tutorial") 8 | (:book2 UNDEF) 9 | } 10 | ?book dc:title ?title ; 11 | ns:price ?price . 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-values/select-values-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | PREFIX : 4 | SELECT ?book ?title ?price 5 | WHERE { 6 | VALUES (?book ?title) { 7 | (UNDEF "SPARQL Tutorial") 8 | (:book2 UNDEF) 9 | } 10 | ?book dc:title ?title ; 11 | ns:price ?price . 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select-values/select-values-4.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | PREFIX : 4 | SELECT ?book ?title ?price 5 | WHERE { 6 | ?book dc:title ?title ; 7 | ns:price ?price . 8 | } 9 | VALUES (?book ?title) { 10 | (UNDEF "SPARQL Tutorial") 11 | (:book2 UNDEF) 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-1.rq: -------------------------------------------------------------------------------- 1 | SELECT ?x 2 | WHERE {} 3 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-10.rq: -------------------------------------------------------------------------------- 1 | BASE 2 | PREFIX dc: 3 | SELECT ?title 4 | WHERE { 5 | dc:title ?title . 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-11.rq: -------------------------------------------------------------------------------- 1 | SELECT ?v 2 | WHERE { 3 | ?v ?p "\"foo\"\n\'bar\'\r" . 4 | } 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-12.rq: -------------------------------------------------------------------------------- 1 | SELECT ?x 2 | WHERE { 3 | ?s ?p ( 1 ?x 3 4 ) . 4 | } 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-13.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | PREFIX foaf: 3 | SELECT ?x ?name 4 | WHERE { 5 | [ :p1 "v" ] :q1 "w" . 6 | :x :q2 [ :p2 "v" ] . 7 | [ foaf:name ?name ; 8 | foaf:mbox ] . 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-14.rq: -------------------------------------------------------------------------------- 1 | PREFIX : 2 | SELECT ?x 3 | WHERE { 4 | ( 1 [ :p :q ] ( 2 ?x ) ) . 5 | () :r [] . 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-2.rq: -------------------------------------------------------------------------------- 1 | SELECT ?title 2 | WHERE { 3 | ?title . 4 | } 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-3.rq: -------------------------------------------------------------------------------- 1 | SELECT ?v 2 | WHERE { 3 | ?v ?p "cat: \"meow\"" . 4 | } 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-4.rq: -------------------------------------------------------------------------------- 1 | SELECT ?v 2 | WHERE { 3 | ?v ?p "cat: \"meow\""@en . 4 | } 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-5.rq: -------------------------------------------------------------------------------- 1 | SELECT ?v 2 | WHERE { 3 | ?v ?p 42 . 4 | } 5 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-6.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT ?name ?mbox 3 | WHERE { 4 | ?x foaf:name ?name . 5 | ?x foaf:mbox ?mbox . 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-7.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT DISTINCT ?name 3 | WHERE { 4 | ?x foaf:name ?name . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-8.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT REDUCED ?name 3 | WHERE { 4 | ?x foaf:name ?name . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/query/select/select-9.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | SELECT (CONCAT(?G, " ", ?S) AS ?name) 3 | WHERE { 4 | ?P foaf:givenName ?G ; 5 | foaf:surname ?S . 6 | } 7 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update-seq/updates-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | DELETE DATA { 3 | GRAPH { 4 | dc:title "Fundamentals of Compiler Desing" . 5 | } 6 | }; 7 | PREFIX dc: 8 | INSERT DATA { 9 | GRAPH { 10 | dc:title "Fundamentals of Compiler Design" . 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update-seq/updates-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc0: 2 | DELETE DATA { 3 | GRAPH { 4 | dc0:title "Fundamentals of Compiler Desing" . 5 | } 6 | }; 7 | PREFIX dc1: 8 | DELETE DATA { 9 | GRAPH { 10 | dc1:title "Fundamentals of Compiler Desing" . 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update-seq/updates-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX dcmitype: 3 | PREFIX xsd: 4 | INSERT { 5 | GRAPH { 6 | ?book ?p ?v . 7 | } 8 | } 9 | WHERE { 10 | GRAPH { 11 | ?book dc:date ?date . 12 | FILTER (?date < "2000-01-01T00:00:00.001Z"^^xsd:dateTime) 13 | ?book ?p ?v . 14 | } 15 | }; 16 | WITH 17 | DELETE { 18 | ?book ?p ?v . 19 | } 20 | WHERE { 21 | ?book dc:date ?date ; 22 | dc:type dcmitype:PhysicalObject . 23 | FILTER (?date < "2000-01-01T00:00:00.002Z"^^xsd:dateTime) 24 | ?book ?p ?v . 25 | } 26 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update-seq/updates-4.rq: -------------------------------------------------------------------------------- 1 | CREATE GRAPH ; 2 | LOAD 3 | INTO GRAPH ; 4 | COPY 5 | TO ; 6 | COPY DEFAULT 7 | TO GRAPH ; 8 | COPY GRAPH 9 | TO DEFAULT; 10 | ADD GRAPH 11 | TO GRAPH ; 12 | ADD DEFAULT 13 | TO ; 14 | ADD GRAPH 15 | TO DEFAULT; 16 | MOVE GRAPH 17 | TO GRAPH ; 18 | MOVE DEFAULT 19 | TO GRAPH ; 20 | MOVE 21 | TO DEFAULT; 22 | CLEAR GRAPH ; 23 | CLEAR DEFAULT; 24 | CLEAR NAMED; 25 | CLEAR ALL; 26 | DROP GRAPH ; 27 | DROP DEFAULT; 28 | DROP NAMED; 29 | DROP ALL 30 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update-seq/updates-5.rq: -------------------------------------------------------------------------------- 1 | CREATE SILENT GRAPH ; 2 | LOAD SILENT 3 | INTO GRAPH ; 4 | COPY SILENT 5 | TO ; 6 | COPY SILENT DEFAULT 7 | TO GRAPH ; 8 | COPY SILENT GRAPH 9 | TO DEFAULT; 10 | ADD SILENT GRAPH 11 | TO GRAPH ; 12 | ADD SILENT DEFAULT 13 | TO ; 14 | ADD SILENT GRAPH 15 | TO DEFAULT; 16 | MOVE SILENT GRAPH 17 | TO GRAPH ; 18 | MOVE SILENT DEFAULT 19 | TO GRAPH ; 20 | MOVE SILENT 21 | TO DEFAULT; 22 | CLEAR SILENT GRAPH ; 23 | CLEAR SILENT DEFAULT; 24 | CLEAR SILENT NAMED; 25 | CLEAR SILENT ALL; 26 | DROP SILENT GRAPH ; 27 | DROP SILENT DEFAULT; 28 | DROP SILENT NAMED; 29 | DROP SILENT ALL 30 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update-seq/updates-6.rq: -------------------------------------------------------------------------------- 1 | PREFIX graphs: 2 | PREFIX src: 3 | CREATE GRAPH graphs:one; 4 | LOAD src:source 5 | INTO GRAPH graphs:two; 6 | COPY graphs:one 7 | TO graphs:two; 8 | COPY DEFAULT 9 | TO GRAPH graphs:two; 10 | COPY GRAPH graphs:one 11 | TO DEFAULT; 12 | ADD GRAPH graphs:one 13 | TO GRAPH graphs:two; 14 | ADD DEFAULT 15 | TO graphs:two; 16 | ADD GRAPH graphs:one 17 | TO DEFAULT; 18 | MOVE GRAPH graphs:one 19 | TO GRAPH graphs:two; 20 | MOVE DEFAULT 21 | TO GRAPH graphs:two; 22 | MOVE graphs:one 23 | TO DEFAULT; 24 | CLEAR GRAPH graphs:two; 25 | CLEAR DEFAULT; 26 | CLEAR NAMED; 27 | CLEAR ALL; 28 | DROP GRAPH graphs:one; 29 | DROP DEFAULT; 30 | DROP NAMED; 31 | DROP ALL 32 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/delete-data-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | DELETE DATA { 3 | dc:title "David Copperfield" ; 4 | dc:creator "Edmund Wells" . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/delete-where-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | DELETE WHERE { 3 | ?person foaf:givenName "Fred" ; 4 | ?property ?value . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/delete-where-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | DELETE WHERE { 3 | GRAPH { 4 | ?person foaf:givenName "Fred" ; 5 | ?property1 ?value1 . 6 | } 7 | GRAPH { 8 | ?person ?property2 ?value2 . 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/graph-add-1.rq: -------------------------------------------------------------------------------- 1 | ADD DEFAULT 2 | TO 3 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/graph-copy-1.rq: -------------------------------------------------------------------------------- 1 | COPY DEFAULT 2 | TO 3 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/graph-move-1.rq: -------------------------------------------------------------------------------- 1 | MOVE DEFAULT 2 | TO 3 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/insert-data-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | INSERT DATA { 3 | dc:title "A new book" ; 4 | dc:creator "A. N. Other" . 5 | } 6 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/insert-data-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX ns: 3 | INSERT DATA { 4 | GRAPH { 5 | ns:price 42 . 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/modify-1.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | WITH 3 | DELETE { 4 | ?person foaf:givenName "Bill" . 5 | ?person foaf:givenName "William" . 6 | } 7 | WHERE { 8 | ?person foaf:givenName "Bill" . 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/modify-2.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | WITH 3 | DELETE { 4 | ?person ?property ?value . 5 | } 6 | WHERE { 7 | ?person ?property ?value ; 8 | foaf:givenName "Fred" . 9 | } 10 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/modify-3.rq: -------------------------------------------------------------------------------- 1 | PREFIX dc: 2 | PREFIX xsd: 3 | INSERT { 4 | GRAPH { 5 | ?book ?p ?v . 6 | } 7 | } 8 | WHERE { 9 | GRAPH { 10 | ?book dc:date ?date . 11 | FILTER (?date > "1970-01-01T00:00:00.005Z"^^xsd:dateTime) 12 | ?book ?p ?v . 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/modify-4.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | PREFIX rdf: 3 | INSERT { 4 | GRAPH { 5 | ?person foaf:name ?name . 6 | ?person foaf:mbox ?email . 7 | } 8 | } 9 | WHERE { 10 | GRAPH { 11 | ?person foaf:name ?name . 12 | OPTIONAL { 13 | ?person foaf:mbox ?email . 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dev-resources/test-fixtures/outputs/update/modify-5.rq: -------------------------------------------------------------------------------- 1 | PREFIX foaf: 2 | WITH 3 | DELETE { 4 | ?person foaf:givenName "Bill" . 5 | } 6 | INSERT { 7 | ?person foaf:givenName "William" . 8 | } 9 | WHERE { 10 | ?person foaf:givenName "Bill" . 11 | } 12 | -------------------------------------------------------------------------------- /doc/cljdoc.edn: -------------------------------------------------------------------------------- 1 | {:cljdoc.doc/tree [["Readme" {:file "README.md"}] 2 | ["Changelog" {:file "CHANGELOG.md"}] 3 | ["Queries and Updates" 4 | ["Queries" {:file "doc/query.md"}] 5 | ["Updates" {:file "doc/update.md"}]] 6 | ["Clauses and Subforms" 7 | ["Graph IRIs" {:file "doc/graph.md"}] 8 | ["Graph Patterns" {:file "doc/where.md"}] 9 | ["Modifiers" {:file "doc/modifier.md"}] 10 | ["Prologue" {:file "doc/prologue.md"}]] 11 | ["Expressions" {:file "doc/expr.md"}] 12 | ["Triples" {:file "doc/triple.md"}] 13 | ["RDF Terms" {:file "doc/axiom.md"}]]} 14 | -------------------------------------------------------------------------------- /doc/graph.md: -------------------------------------------------------------------------------- 1 | # Graph IRIs 2 | 3 | SPARQL contains a number of clauses that can be used to specify default and named graphs in the query via IRIs. 4 | 5 | ## Graph IRI Clauses 6 | 7 | ### `:from` 8 | 9 | Used in queries, `:from` denotes the _default_ graph that the query operates on. Syntactically, the `:from` clause can be an IRI or prefixed IRI, or a vector of **one** such IRI. 10 | 11 | The example: 12 | ```clojure 13 | {:prefixes {:foaf ""} 14 | :select [?x] 15 | :from [""] 16 | :where [[?x :foaf/name "Armin Arlet"]]} 17 | ``` 18 | becomes: 19 | ```sparql 20 | PREFIX foaf: 21 | SELECT ?x 22 | FROM 23 | WHERE { 24 | ?x foaf:name "Armin Arlet" 25 | } 26 | ``` 27 | 28 | ### `:from-named` 29 | 30 | Used in queries, `:from-named` denotes the _named_ graphs that the query can operate on. Syntactically, the `:from-named` clause is a vector of one or more IRIs or prefixed IRIs. 31 | 32 | The example: 33 | ```clojure 34 | {:prefixes {:foaf ""} 35 | :select [?x] 36 | :from-named ["" 37 | ""] 38 | :where [[:union [[:graph "" 39 | [[?x :foaf/name "Armin Arlet"]]]] 40 | [[:graph "" 41 | [[?x :foaf/name "Armin Arlet"]]]]]]} 42 | ``` 43 | becomes: 44 | ```sparql 45 | PREFIX foaf: 46 | SELECT ?x 47 | FROM NAMED 48 | FROM NAMED 49 | WHERE { 50 | { 51 | GRAPH { 52 | ?x foaf:name "Armin Arlet" . 53 | } 54 | } 55 | UNION 56 | { 57 | GRAPH { 58 | ?x foaf:name "Armin Arlet" . 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | ### `:using` 65 | 66 | Used only in `:delete`/`:insert` updates, `:using` specifies the graph used in the `:where` clause. Syntactically, it consists of either: 67 | - An IRI or prefixed IRI, to specify the default graph. 68 | - A `[:named iri]` tuple, to specify a named graph. 69 | 70 | The example: 71 | ```clojure 72 | {:prefixes {:foaf ""} 73 | :delete [[:graph "" 74 | [[?x :foaf/familyName "Brown"]]]] 75 | :insert [[:graph "" 76 | [[?x :foaf/familyName "Braun"]]]] 77 | :using "" 78 | :where [[?x :foaf/familyName "Brown"]]} 79 | ``` 80 | becomes: 81 | ```sparql 82 | PREFIX foaf: 83 | DELETE { 84 | GRAPH { 85 | ?x foaf:familyName "Brown" . 86 | } 87 | } 88 | INSERT { 89 | GRAPH { 90 | ?x foaf:familyName "Braun" . 91 | } 92 | } 93 | USING 94 | WHERE { 95 | ?x foaf:familyName "Brown" . 96 | } 97 | ``` 98 | 99 | ### `:with` 100 | 101 | Used only in `:delete`/`:insert` updates, `:with` specifies the graph being mutated by the update; it is similar to `:from` for queries. Syntactically, it consists an IRI or prefixed IRI that specifies the graph. 102 | 103 | The example: 104 | ```clojure 105 | {:prefixes {:foaf ""} 106 | :with "" 107 | :delete [[?x :foaf/familyName "Brown"]] 108 | :insert [[?x :foaf/familyName "Braun"]] 109 | :where [[?x :foaf/familyName "Brown"]]} 110 | ``` 111 | becomes: 112 | ```sparql 113 | PREFIX foaf: 114 | WITH 115 | DELETE { 116 | ?x foaf:familyName "Brown" . 117 | } 118 | INSERT { 119 | ?x foaf:familyName "Braun" . 120 | } 121 | WHERE { 122 | ?x foaf:familyName "Brown" . 123 | } 124 | ``` 125 | -------------------------------------------------------------------------------- /doc/prologue.md: -------------------------------------------------------------------------------- 1 | # Prologue 2 | 3 | The SPARQL prologue allows users to list IRI prefixes and base IRIs, which is very useful for shortening IRIs in the query or update body. In Flint, there are two elements in the prologue, both of which are optional: the base IRI and the prefix map. 4 | 5 | ## Prologue clauses 6 | 7 | ### `:base` 8 | 9 | The `:base` IRI sets the base IRI that subsequent IRIs are relative to. In the following example: 10 | ```clojure 11 | {:base "" 12 | :select [?x] 13 | :where [[?x "" ?y]]} 14 | ``` 15 | `` expands to ``. During SPARQL translation, the above query becomes: 16 | ```sparql 17 | BASE 18 | SELECT ?x 19 | WHERE { 20 | ?x ?y 21 | } 22 | ``` 23 | 24 | **NOTE:** The SPARQL spec allows for multiple base IRIs to be defined, but this is not allowed in Flint. Having multiple base IRIs means that each set of IRIs after a base will use a different base IRI. This is mainly useful for defining different bases for IRI prefixes, which clashes with Flint's approach of one `:prefixes` map per query or update. 25 | 26 | ### `:prefixes` 27 | 28 | The `:prefixes` map associates prefix keywords to IRI prefixes. In the following example: 29 | ```clojure 30 | {:prefixes {:foaf "" 31 | :$ ""} 32 | :select [?x] 33 | :where [[?x :foaf/name "Dr. X"] 34 | [?y :bar ?z]]} 35 | ``` 36 | both `:foaf/name` and `:bar` are prefixed IRIs that are resolvable thanks to prefixes defined in the `:prefixes` map. Note the special prefix `:$`, which represents the "null" prefix; `:$` allows for prefixed IRIs like `:bar` to be written without a namespace, while ensuring that they are still expandable. The above query translates to: 37 | ```sparql 38 | PREFIX foaf: 39 | PREFIX : 40 | SELECT ?x 41 | WHERE { 42 | ?x foaf:name "Dr. X" 43 | ?y :bar ?z 44 | } 45 | ``` 46 | While in SPARQL each prefix-IRI pair is separately preceded by `PREFIX`, the `:prefixes` map approach is more idiomatic to Clojure. 47 | -------------------------------------------------------------------------------- /logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yetanalytics/flint/d9468a6e640bbeb5aad1f3bde612b617235f328b/logo/logo.png -------------------------------------------------------------------------------- /logo/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 32 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/axiom/impl/format.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.axiom.impl.format 2 | (:require [com.yetanalytics.flint.axiom.iri :as iri] 3 | [com.yetanalytics.flint.axiom.protocol :as p])) 4 | 5 | ;; IRIs, Vars, and Blank Nodes 6 | 7 | (defn unwrap-iri-string 8 | "Given a string `s` of the form ``, return `iri`." 9 | [^String s] 10 | (.substring s 1 (dec (count s)))) 11 | 12 | (defn format-prefix-keyword 13 | "Return the string of the keyword `k`, or the empty string if `k` is `:$`." 14 | [k] 15 | (if (= :$ k) "" (name k))) 16 | 17 | (defn format-prefix-iri-keyword 18 | "Given a potentially-qualified keyword `k`, return a prefixed IRI in the 19 | form `prefix:name`." 20 | [k] 21 | (let [kns (namespace k) 22 | kname (name k)] 23 | (str kns ":" kname))) 24 | 25 | (defn format-var-symbol 26 | "Return the var `v-sym` as a string of the form `?var`." 27 | [v-sym] 28 | (str v-sym)) 29 | 30 | (defn format-bnode-symbol 31 | "Return the bnode `b-sym` as a string of the form `_:bnode`. Returns 32 | `[]` if `b-sym` is a single underscore." 33 | [b-sym] 34 | (if (not= '_ b-sym) 35 | (let [^String s (str b-sym) 36 | sub-string (.substring s 1 (count s))] 37 | (str "_:" sub-string)) 38 | "[]")) 39 | 40 | ;; Lang Map Literals 41 | 42 | (defn format-lang-map-tag 43 | "Return the lang tag string from `lang-map`." 44 | [lang-map] 45 | (-> lang-map keys first name)) 46 | 47 | (defn format-lang-map-val 48 | "Return the string literal value from `lang-map`." 49 | [lang-map] 50 | (-> lang-map vals first)) 51 | 52 | (defn format-lang-map-literal 53 | "Format `lang-map` into a string of the form `\"value\"@lang-tag`." 54 | [lang-map] 55 | (let [ltag (format-lang-map-tag lang-map) 56 | lval (format-lang-map-val lang-map)] 57 | (str "\"" lval "\"@" ltag))) 58 | 59 | ;; Common format functions 60 | 61 | (defn format-xsd-iri 62 | "Create an XSD datatype IRI of the form `(str xsd-prefix xsd-suffix)`, 63 | where `xsd-suffix` should be a string like `\"boolean\"` or `\"dateTime\"`. 64 | If `iri-prefix-m` is provided, it will use the prefix associated with 65 | the XSD IRI prefix." 66 | [xsd-suffix {:keys [iri-prefix-m]}] 67 | (if-some [xsd-prefix (get iri-prefix-m iri/xsd-iri-prefix)] 68 | (str (name xsd-prefix) ":" xsd-suffix) 69 | (str "<" iri/xsd-iri-prefix xsd-suffix ">"))) 70 | 71 | (defn format-rdf-iri 72 | "Similar to `format-xsd-iri`, but for the RDF datatype IRI." 73 | [rdf-suffix {:keys [iri-prefix-m]}] 74 | (if-some [rdf-prefix (get iri-prefix-m iri/rdf-iri-prefix)] 75 | (str (name rdf-prefix) ":" rdf-suffix) 76 | (str "<" iri/rdf-iri-prefix rdf-suffix ">"))) 77 | 78 | ;; This could cause fowrard declaration problems, but in practice the impls 79 | ;; should have already been defined when `format-literal` is called. 80 | (defn format-literal 81 | "Create a literal of the form `\"strval^^iri\"`. If `force-iri?` is `true` 82 | then the datatype IRI will be appended, and `iri-prefix-m` will map any 83 | IRI string prefix to a keyword prefix. `literal` should extend 84 | `p/Literal` and as such implement `p/-format-literal-strval` and 85 | `p/-format-literal-url`." 86 | [literal {:keys [force-iri? _iri-prefix-m] :as opts}] 87 | (let [strval (p/-format-literal-strval literal)] 88 | (if force-iri? 89 | (str "\"" strval "\"^^" (p/-format-literal-url literal opts)) 90 | strval))) 91 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/axiom/iri.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.axiom.iri) 2 | 3 | (def xsd-iri-prefix 4 | "http://www.w3.org/2001/XMLSchema#") 5 | 6 | (def rdf-iri-prefix 7 | "http://www.w3.org/1999/02/22-rdf-syntax-ns#") 8 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/axiom/protocol.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.axiom.protocol) 2 | 3 | ;; We want to keep IRI and PrefixedIRI as seprate protocols since we want 4 | ;; them to be distinguished with different tags in the AST, for the sake 5 | ;; of prefix validation. 6 | 7 | (defprotocol IRI 8 | "A SPARQL full IRI (e.g. ``)." 9 | (-valid-iri? [this] 10 | "Return `true` if `this` is a valid full IRI of its type.") 11 | (-format-iri [this] 12 | "Convert the full IRI `this` into its SPARQL string representation.") 13 | (-unwrap-iri [this] 14 | "Return the underlying IRI string of `this`, without angle bracket 15 | wrapping.")) 16 | 17 | (defprotocol Prefix 18 | "A SPARQL prefix (e.g. `foo` in `PREFIX foo`)." 19 | (-valid-prefix? [this] 20 | "Return `true` if `this` is a valid prefix of its type.") 21 | (-format-prefix [this] 22 | "Convert the prefix `this` into its string representation.")) 23 | 24 | (defprotocol PrefixedIRI 25 | "A SPARQL prefixed IRI (e.g. `foo:bar`)." 26 | (-valid-prefix-iri? [this] 27 | "Return `true` if `this` is a valid prefixed IRI of its type.") 28 | (-format-prefix-iri [this] 29 | "Convert the prefixed IRI `this` into its string representation.")) 30 | 31 | (defprotocol Variable 32 | "A SPARQL variable (e.g. `?var`)." 33 | (-valid-variable? [this] 34 | "Return `true` if `this` is a valid variable of its type.") 35 | (-format-variable [this] 36 | "Convert the variable `this` into its string representation.")) 37 | 38 | (defprotocol BlankNode 39 | "A SPARQL blank node (e.g. `_:b0`)." 40 | (-valid-bnode? [this] 41 | "Return `true` if `this` is a valid blank node of its type.") 42 | (-format-bnode [this] 43 | "Convert the blank node `this` into its string representation.")) 44 | 45 | (defprotocol Wildcard 46 | "A SPARQL wildcard representation (i.e. `*`)." 47 | (-valid-wildcard? [this] 48 | "Return `true` if `this` is a valid wildcard.") 49 | (-format-wildcard [this] 50 | "Convert the wildcard `this` into its string representation.")) 51 | 52 | (defprotocol RDFType 53 | "A SPARQL shorthand for `rdf:type` (i.e. `a`)." 54 | (-valid-rdf-type? [this] 55 | "Return `true` if `this` is a valid `rdf:type` shorthand.") 56 | (-format-rdf-type [this] 57 | "Convert the `rdf:type` shorthand `this` into its string representation.")) 58 | 59 | (defprotocol Literal 60 | "A SPARQL literal (e.g. `\"foo\"`, `\"bar\"@en`, `2`, and `true`)." 61 | (-valid-literal? [this] 62 | "Return `true` if `this` is a valid literal of its type.") 63 | (-format-literal [this] [this opts] 64 | "Convert the literal `this` into its string representation. 65 | The `opts` arg map is implementation-specific, but two common args 66 | are `:force-iri?` to force the appending of the datatype IRI suffix 67 | and `:iri-prefix-m` to map full datatype IRI strings to prefixes.") 68 | (-format-literal-strval [this] 69 | "Return the string value reprentation of `this` literal.") 70 | (-format-literal-lang-tag [this] 71 | "Return the language tag string associated with `this` literal. 72 | Returns `nil` if the literal does not have any language tags 73 | or is a typed literal.") 74 | (-format-literal-url [this] [this opts] 75 | "Return the RDF datatype URL associated with `this` literal. 76 | The `opts` arg map is implementation-specific, but a common arg is 77 | `:iri-prefix-m` to map full datatype IRI strings to prefixes.")) 78 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format 2 | (:require [clojure.string :as cstr] 3 | [clojure.walk :as w] 4 | [com.yetanalytics.flint.util :as u])) 5 | 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | ;; Helpers 8 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 9 | 10 | (defn bracketted-expr-str? 11 | [s] 12 | (boolean (re-matches #"\(.*\)" s))) 13 | 14 | (defn bracketted-or-fn-expr-str? 15 | "Determine if a expression string represents a bracketted expression or 16 | a function call." 17 | [s] 18 | (boolean (or (re-matches #"\(.*\)" s) 19 | ;; Built-ins, IRI, and prefixed IRI functions 20 | (re-matches #"[\<\w\-].*\(.*\)" s) 21 | ;; (NOT) EXISTS doesn't use parens 22 | (cstr/starts-with? s "EXISTS") 23 | (cstr/starts-with? s "NOT EXISTS")))) 24 | 25 | (defn indent-str 26 | "Add 4 spaces after each line break (including at the beginning)." 27 | [s] 28 | (str " " (cstr/replace s #"\n" "\n "))) 29 | 30 | (defn wrap-in-braces 31 | "Wrap the `clause` string in curly braces. If `pretty?` is true, 32 | also add line breaks and indent `clause`." 33 | [clause pretty?] 34 | (if pretty? 35 | (str "{\n" (indent-str clause) "\n}") 36 | (str "{ " clause " }"))) 37 | 38 | (defn join-clauses 39 | "Join the `clauses` coll. If `pretty?` is true, separate by line 40 | breaks; otherwise separate by space." 41 | [clauses pretty?] 42 | (if pretty? 43 | (cstr/join "\n" clauses) 44 | (cstr/join " " clauses))) 45 | 46 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 47 | ;; Formatting 48 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 49 | 50 | (defmulti format-ast-node 51 | "Convert the AST node into a string." 52 | (fn [_ x] (if-some [k (u/get-keyword x)] k :default))) 53 | 54 | (defmethod format-ast-node :default [_ ast-node] ast-node) 55 | 56 | (defn format-ast 57 | "Convert `ast` into a string, with `opts` including: 58 | - `:xsd-prefix` the prefix of the XSD IRI, used in RDF literals. 59 | - `:pretty?` whether to add linebreaks or indents." 60 | [ast opts] 61 | (w/postwalk (partial format-ast-node opts) ast)) 62 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/axiom.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.axiom 2 | (:require [com.yetanalytics.flint.format :as f] 3 | [com.yetanalytics.flint.axiom.protocol :as p] 4 | [com.yetanalytics.flint.axiom.impl])) 5 | 6 | (defmethod f/format-ast-node :ax/iri [_ [_ iri]] 7 | (p/-format-iri iri)) 8 | 9 | (defmethod f/format-ast-node :ax/prefix [_ [_ prefix]] 10 | (p/-format-prefix prefix)) 11 | 12 | (defmethod f/format-ast-node :ax/prefix-iri [_ [_ prefix-iri]] 13 | (p/-format-prefix-iri prefix-iri)) 14 | 15 | (defmethod f/format-ast-node :ax/var [_ [_ variable]] 16 | (p/-format-variable variable)) 17 | 18 | (defmethod f/format-ast-node :ax/bnode [_ [_ bnode]] 19 | (p/-format-bnode bnode)) 20 | 21 | (defmethod f/format-ast-node :ax/wildcard [_ [_ wildcard]] 22 | (p/-format-wildcard wildcard)) 23 | 24 | (defmethod f/format-ast-node :ax/rdf-type [_ [_ rdf-type]] 25 | (p/-format-rdf-type rdf-type)) 26 | 27 | (defmethod f/format-ast-node :ax/literal [opts [_ literal]] 28 | (p/-format-literal literal opts)) 29 | 30 | ;; Technically literals in the SPARQL spec but they behave differently in Flint 31 | (defmethod f/format-ast-node :ax/numeric [_ [_ num]] 32 | (str num)) 33 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/expr.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.expr 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom])) 5 | 6 | ;; NOTE: Any deps that call this namespace should also call the format.where 7 | ;; ns, to properly format EXISTS and NOT EXISTS. 8 | ;; format.where cannot directly be called here since it would cause a cyclic 9 | ;; dependency. 10 | 11 | (defn- elist-op? 12 | [op] 13 | (#{'in 'not-in} op)) 14 | 15 | (defn- infix-op? 16 | [op] 17 | (#{'= 'not= '< '> '<= '>= 'and 'or '+ '- '* '/} op)) 18 | 19 | (defn- unary-op? 20 | [op] 21 | (#{'not} op)) 22 | 23 | (defn- where-op? 24 | [op] 25 | (#{'exists 'not-exists} op)) 26 | 27 | (defn- op->str 28 | [op] 29 | (let [op-name (str op)] 30 | (case op-name 31 | "-" "-" ; Otherwise will be removed below 32 | "not=" "!=" 33 | "and" "&&" 34 | "or" "||" 35 | "in" "IN" 36 | "not-in" "NOT IN" 37 | "not" "!" 38 | ;; Function names with unique hyphen rules 39 | "encode-for-uri" "ENCODE_FOR_URI" 40 | "group-concat" "GROUP_CONCAT" 41 | "not-exists" "NOT EXISTS" 42 | (if-not (or (cstr/includes? op-name "<") 43 | (cstr/includes? op-name ":")) 44 | ;; Hyphenate and convert `pred?` to `isPred` 45 | (if-some [pred-prefix (second (re-matches #"(.+)\?" op-name))] 46 | (str "is" (cstr/upper-case pred-prefix)) 47 | (cstr/upper-case (cstr/replace op-name #"-" ""))) 48 | ;; Custom name 49 | op-name)))) 50 | 51 | (defn- parens-if-nests 52 | "Super-basic precedence comparison to wrap parens if there's an inner 53 | unary op, since expressions like `!!true` are illegal." 54 | [arg] 55 | (if (re-matches #"(\!|\+|\-).*" arg) 56 | (str "(" arg ")") 57 | arg)) 58 | 59 | (defmethod f/format-ast-node :expr/kwarg [_ [_ [[_ k] [_ v]]]] 60 | (str (cstr/upper-case (name k)) " = '" v "'")) 61 | 62 | (defmethod f/format-ast-node :expr/op [_ [_ op]] op) 63 | 64 | (defmethod f/format-ast-node :expr/args [_ [_ args]] args) 65 | 66 | ;; Keywords - don't convert to string 67 | 68 | (defmethod f/format-ast-node :expr/kwargs [_ [_ kwargs]] 69 | (into {} kwargs)) 70 | 71 | (defmethod f/format-ast-node :distinct? [_ x] x) 72 | 73 | (defmethod f/format-ast-node :separator [_ x] x) 74 | 75 | (defmethod f/format-ast-node :expr/branch [_ [_ [op args ?kwargs]]] 76 | (let [op-str (op->str op)] 77 | (cond 78 | ?kwargs ; ops with kwargs all have regular fn syntax 79 | (let [{?dist :distinct? 80 | ?sep :separator} ?kwargs 81 | ?dist-str (when ?dist "DISTINCT ") 82 | ?sep-str (when ?sep (str "; SEPARATOR = \"" ?sep "\""))] 83 | (str op-str "(" ?dist-str (cstr/join ", " args) ?sep-str ")")) 84 | (elist-op? op) (str "(" (first args) " " op-str " (" (cstr/join ", " (rest args)) "))") 85 | (infix-op? op) (str "(" (cstr/join (str " " op-str " ") args) ")") 86 | (unary-op? op) (str op-str (-> args first parens-if-nests)) 87 | (where-op? op) (str op-str " " (first args)) 88 | :else (str op-str "(" (cstr/join ", " args) ")")))) 89 | 90 | (defmethod f/format-ast-node :expr/terminal [_ [_ terminal]] 91 | terminal) 92 | 93 | (defmethod f/format-ast-node :expr/as-var [_ [_ [expr var]]] 94 | (str expr " AS " var)) 95 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/modifier.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.modifier 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f])) 4 | 5 | (defmethod f/format-ast-node :mod/op [_ [_ op]] 6 | (str op)) 7 | 8 | (defmethod f/format-ast-node :mod/asc-desc [_ [_ [op sub-expr]]] 9 | (let [op-name (cstr/upper-case op)] 10 | (str op-name sub-expr))) 11 | 12 | (defmethod f/format-ast-node :mod/asc-desc-expr [_ [_ expr]] 13 | (if (f/bracketted-expr-str? expr) 14 | expr 15 | (str "(" expr ")"))) 16 | 17 | (defmethod f/format-ast-node :mod/group-expr [_ [_ expr]] 18 | expr) 19 | 20 | (defmethod f/format-ast-node :mod/order-expr [_ [_ expr]] 21 | (if (f/bracketted-or-fn-expr-str? expr) 22 | expr 23 | (str "(" expr ")"))) 24 | 25 | (defmethod f/format-ast-node :mod/expr-as-var [_ [_ expr-as-var]] 26 | (str "(" expr-as-var ")")) 27 | 28 | (defmethod f/format-ast-node :group-by [_ [_ group-bys]] 29 | (str "GROUP BY " (cstr/join " " group-bys))) 30 | 31 | (defmethod f/format-ast-node :order-by [_ [_ order-bys]] 32 | (str "ORDER BY " (cstr/join " " order-bys))) 33 | 34 | (defmethod f/format-ast-node :having [_ [_ exprs]] 35 | (->> exprs 36 | (map (fn [e] (if (f/bracketted-or-fn-expr-str? e) e (str "(" e ")")))) 37 | (cstr/join " ") 38 | (str "HAVING "))) 39 | 40 | (defmethod f/format-ast-node :limit [_ [_ limit-val]] 41 | (str "LIMIT " limit-val)) 42 | 43 | (defmethod f/format-ast-node :offset [_ [_ offset-val]] 44 | (str "OFFSET " offset-val)) 45 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/path.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.path 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom])) 5 | 6 | (defmethod f/format-ast-node :path/op [_ [_ op]] (keyword op)) 7 | 8 | (defmethod f/format-ast-node :path/paths [_ [_ paths]] paths) 9 | 10 | (defn- parens-if-nests 11 | "Super-basic precedence comparison to wrap parens if there's an inner 12 | unary regex op, since paths like `a?*+` are illegal." 13 | [arg] 14 | (if (or (re-matches #".*(\?|\*|\+)" arg) 15 | (re-matches #"\!.*" arg)) 16 | (str "(" arg ")") 17 | arg)) 18 | 19 | (defn- parens-if-nests-neg 20 | "Similar to `parens-if-nests` but speficially for `!`." 21 | [arg] 22 | (if (re-matches #"\!.*" arg) 23 | (str "(" arg ")") 24 | arg)) 25 | 26 | (defmethod f/format-ast-node :path/branch [_ [_ [op paths]]] 27 | (case op 28 | :alt (str "(" (cstr/join " | " paths) ")") 29 | :cat (str "(" (cstr/join " / " paths) ")") 30 | :inv (str "^" (first paths)) 31 | :? (-> paths first parens-if-nests (str "?")) 32 | :* (-> paths first parens-if-nests (str "*")) 33 | :+ (-> paths first parens-if-nests (str "+")) 34 | :not (->> paths first parens-if-nests-neg (str "!")))) 35 | 36 | (defmethod f/format-ast-node :path/terminal [_ [_ value]] 37 | value) 38 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/prologue.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.prologue 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom])) 5 | 6 | (defmethod f/format-ast-node :base [_ [_ value]] 7 | (str "BASE " value)) 8 | 9 | (defmethod f/format-ast-node :prologue/prefix [_ [_ [prefix iri]]] 10 | (str "PREFIX " prefix ": " iri)) 11 | 12 | (defn- align-prefixes 13 | [prefixes] 14 | (let [pre-groups (map (fn [pre] (re-matches #"(PREFIX (.*)\:) \<.*\>" pre)) 15 | prefixes) 16 | pre-lens (map (fn [grps] (count (get grps 2))) 17 | pre-groups) 18 | longest-len (apply max pre-lens) 19 | paddings (map (fn [len] (cstr/join "" (repeat (- longest-len len) " "))) 20 | pre-lens) 21 | pre-strs (map second pre-groups)] 22 | (map (fn [pre pre-str pad] 23 | (cstr/replace-first pre #"PREFIX[^\<\>]*\:" (str pre-str pad))) 24 | prefixes 25 | pre-strs 26 | paddings))) 27 | 28 | (defmethod f/format-ast-node :prefixes [{:keys [pretty?]} [_ prefixes]] 29 | (if pretty? 30 | (cstr/join "\n" (align-prefixes prefixes)) 31 | (cstr/join " " prefixes))) 32 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/query.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.query 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom] 5 | [com.yetanalytics.flint.format.prologue] 6 | [com.yetanalytics.flint.format.triple] 7 | [com.yetanalytics.flint.format.modifier] 8 | [com.yetanalytics.flint.format.select] 9 | [com.yetanalytics.flint.format.where] 10 | [com.yetanalytics.flint.format.values])) 11 | 12 | (defmethod f/format-ast-node :construct [{:keys [pretty?]} [_ construct]] 13 | (if (not-empty construct) 14 | (str "CONSTRUCT " (-> construct 15 | (f/join-clauses pretty?) 16 | (f/wrap-in-braces pretty?))) 17 | "CONSTRUCT")) 18 | 19 | (defmethod f/format-ast-node :describe/vars-or-iris [_ [_ var-or-iris]] 20 | (cstr/join " " var-or-iris)) 21 | 22 | (defmethod f/format-ast-node :describe [_ [_ describe]] 23 | (str "DESCRIBE " describe)) 24 | 25 | (defmethod f/format-ast-node :ask [_ _] 26 | "ASK") 27 | 28 | (defmethod f/format-ast-node :from [_ [_ iri]] 29 | (str "FROM " iri)) 30 | 31 | (defmethod f/format-ast-node :from-named [{:keys [pretty?]} [_ iri-coll]] 32 | (-> (map (fn [iri] (str "FROM NAMED " iri)) iri-coll) 33 | (f/join-clauses pretty?))) 34 | 35 | (defmethod f/format-ast-node :query/select [{:keys [pretty?]} [_ select-query]] 36 | (f/join-clauses select-query pretty?)) 37 | 38 | (defmethod f/format-ast-node :query/construct [{:keys [pretty?]} [_ construct-query]] 39 | (f/join-clauses construct-query pretty?)) 40 | 41 | (defmethod f/format-ast-node :query/describe [{:keys [pretty?]} [_ describe-query]] 42 | (f/join-clauses describe-query pretty?)) 43 | 44 | (defmethod f/format-ast-node :query/ask [{:keys [pretty?]} [_ ask-query]] 45 | (f/join-clauses ask-query pretty?)) 46 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/select.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.select 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom] 5 | [com.yetanalytics.flint.format.expr])) 6 | 7 | (defmethod f/format-ast-node :select/var-or-exprs [_ [_ value]] 8 | (cstr/join " " value)) 9 | 10 | (defmethod f/format-ast-node :select/expr-as-var [_ [_ expr-as-var]] 11 | (str "(" expr-as-var ")")) 12 | 13 | (defmethod f/format-ast-node :select [_ [_ select]] 14 | (str "SELECT " select)) 15 | 16 | (defmethod f/format-ast-node :select-distinct [_ [_ select-distinct]] 17 | (str "SELECT DISTINCT " select-distinct)) 18 | 19 | (defmethod f/format-ast-node :select-reduced [_ [_ select-reduced]] 20 | (str "SELECT REDUCED " select-reduced)) 21 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/triple.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.triple 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom] 5 | [com.yetanalytics.flint.format.path])) 6 | 7 | (defmethod f/format-ast-node :triple/path [_ [_ path]] 8 | path) 9 | 10 | (defmethod f/format-ast-node :triple/list [_ [_ list]] 11 | (if (empty? list) 12 | "()" ; Special case for empty lists 13 | (str "( " (cstr/join " " list) " )"))) 14 | 15 | (defmethod f/format-ast-node :triple/bnodes [{:keys [pretty?]} [_ po-pairs]] 16 | (if (empty? po-pairs) 17 | "[]" ; Treat as a scalar blank node 18 | (let [join-sep (if pretty? " ;\n " " ; ") 19 | po-strs (mapv (fn [[p-str o-str]] (str p-str " " o-str)) po-pairs)] 20 | (str "[ " (cstr/join join-sep po-strs) " ]")))) 21 | 22 | (defmethod f/format-ast-node :triple.vec/spo [_ [_ [s-str p-str o-str]]] 23 | (str s-str " " p-str " " o-str " .")) 24 | 25 | (defmethod f/format-ast-node :triple.vec/s [_ [_ [s-str]]] 26 | (str s-str " .")) 27 | 28 | (defn- format-spo-pretty [s-str po-str] 29 | (let [indent (->> (repeat (inc (count s-str)) " ") 30 | (cstr/join "") 31 | (str "\n"))] 32 | (str s-str " " (cstr/replace po-str #"\n" indent)))) 33 | 34 | (defn- format-spo [s-str po-str] 35 | (str s-str " " po-str)) 36 | 37 | (defmethod f/format-ast-node :triple.nform/spo [{:keys [pretty?]} [_ spo-pairs]] 38 | (let [format-spo (if pretty? format-spo-pretty format-spo) 39 | join-sep (if pretty? " .\n" " . ")] 40 | (str (->> spo-pairs 41 | (map (fn [[s-str po-str]] 42 | (if (empty? po-str) s-str (format-spo s-str po-str)))) 43 | (cstr/join join-sep)) 44 | " ."))) 45 | 46 | (defmethod f/format-ast-node :triple.nform/po [{:keys [pretty?]} [_ po-strs]] 47 | (let [join-sep (if pretty? " ;\n" " ; ")] 48 | (->> po-strs 49 | (map (fn [[p o]] (str p " " o))) 50 | (cstr/join join-sep)))) 51 | 52 | (defmethod f/format-ast-node :triple.nform/po-empty [_ _] 53 | "") 54 | 55 | (defmethod f/format-ast-node :triple.nform/o [_ [_ o-strs]] 56 | (->> o-strs (cstr/join " , "))) 57 | 58 | (defn format-quads [quads pretty?] 59 | (-> quads 60 | (f/join-clauses pretty?) 61 | (f/wrap-in-braces pretty?))) 62 | 63 | (defmethod f/format-ast-node :triple.quad/gspo 64 | [_ [_ [graph-str spo-str]]] 65 | (str "GRAPH " graph-str " " spo-str)) 66 | 67 | (defmethod f/format-ast-node :triple.quad/spo 68 | [{:keys [pretty?]} [_ spo-strs]] 69 | (format-quads spo-strs pretty?)) 70 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/update.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.update 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom] 5 | [com.yetanalytics.flint.format.prologue] 6 | [com.yetanalytics.flint.format.triple :as tf] 7 | [com.yetanalytics.flint.format.where])) 8 | 9 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 10 | ;; Graph Management 11 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | 13 | (defmethod f/format-ast-node :update/graph [_ [_ [_graph-kw graph-iri]]] 14 | (str "GRAPH " graph-iri)) 15 | 16 | (defmethod f/format-ast-node :update/graph-notag [_ [_ graph-iri]] 17 | graph-iri) 18 | 19 | (defmethod f/format-ast-node :update/default [_ _] 20 | "DEFAULT") 21 | 22 | (defmethod f/format-ast-node :update/named [_ _] 23 | "NAMED") 24 | 25 | (defmethod f/format-ast-node :update/all [_ _] 26 | "ALL") 27 | 28 | (defmethod f/format-ast-node :into [_ [_ in]] 29 | (str "INTO " in)) 30 | 31 | (defmethod f/format-ast-node :to [_ [_ to]] 32 | (str "TO " to)) 33 | 34 | (defmethod f/format-ast-node :load [_ [_ ld]] 35 | (str "LOAD " ld)) 36 | 37 | (defmethod f/format-ast-node :load-silent [_ [_ ld-silent]] 38 | (str "LOAD SILENT " ld-silent)) 39 | 40 | (defmethod f/format-ast-node :clear [_ [_ clr]] 41 | (str "CLEAR " clr)) 42 | 43 | (defmethod f/format-ast-node :clear-silent [_ [_ clr-silent]] 44 | (str "CLEAR SILENT " clr-silent)) 45 | 46 | (defmethod f/format-ast-node :drop [_ [_ drp]] 47 | (str "DROP " drp)) 48 | 49 | (defmethod f/format-ast-node :drop-silent [_ [_ drp-silent]] 50 | (str "DROP SILENT " drp-silent)) 51 | 52 | (defmethod f/format-ast-node :create [_ [_ create]] 53 | (str "CREATE " create)) 54 | 55 | (defmethod f/format-ast-node :create-silent [_ [_ create-silent]] 56 | (str "CREATE SILENT " create-silent)) 57 | 58 | (defmethod f/format-ast-node :add [_ [_ add]] 59 | (str "ADD " add)) 60 | 61 | (defmethod f/format-ast-node :add-silent [_ [_ add-silent]] 62 | (str "ADD SILENT " add-silent)) 63 | 64 | (defmethod f/format-ast-node :move [_ [_ move]] 65 | (str "MOVE " move)) 66 | 67 | (defmethod f/format-ast-node :move-silent [_ [_ move-silent]] 68 | (str "MOVE SILENT " move-silent)) 69 | 70 | (defmethod f/format-ast-node :copy [_ [_ copy]] 71 | (str "COPY " copy)) 72 | 73 | (defmethod f/format-ast-node :copy-silent [_ [_ copy-silent]] 74 | (str "COPY SILENT " copy-silent)) 75 | 76 | (defmethod f/format-ast-node :update/load [{:keys [pretty?]} [_ load-update]] 77 | (f/join-clauses load-update pretty?)) 78 | 79 | (defmethod f/format-ast-node :update/clear [{:keys [pretty?]} [_ clear-update]] 80 | (f/join-clauses clear-update pretty?)) 81 | 82 | (defmethod f/format-ast-node :update/drop [{:keys [pretty?]} [_ drop-update]] 83 | (f/join-clauses drop-update pretty?)) 84 | 85 | (defmethod f/format-ast-node :update/create [{:keys [pretty?]} [_ create-update]] 86 | (f/join-clauses create-update pretty?)) 87 | 88 | (defmethod f/format-ast-node :update/add [{:keys [pretty?]} [_ add-update]] 89 | (f/join-clauses add-update pretty?)) 90 | 91 | (defmethod f/format-ast-node :update/move [{:keys [pretty?]} [_ move-update]] 92 | (f/join-clauses move-update pretty?)) 93 | 94 | (defmethod f/format-ast-node :update/copy [{:keys [pretty?]} [_ copy-update]] 95 | (f/join-clauses copy-update pretty?)) 96 | 97 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 98 | ;; Graph Update 99 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 100 | 101 | (defmethod f/format-ast-node :update/iri [_ [_ iri]] 102 | iri) 103 | 104 | (defmethod f/format-ast-node :update/named-iri [_ [_ [_named-kw iri]]] 105 | (str "NAMED " iri)) 106 | 107 | (defmethod f/format-ast-node :using [_ [_ using]] 108 | (str "USING " using)) 109 | 110 | (defmethod f/format-ast-node :with [_ [_ with]] 111 | (str "WITH " with)) 112 | 113 | (defmethod f/format-ast-node :insert-data [{:keys [pretty?]} [_ insert-data]] 114 | (str "INSERT DATA " (tf/format-quads insert-data pretty?))) 115 | 116 | (defmethod f/format-ast-node :delete-data [{:keys [pretty?]} [_ delete-data]] 117 | (str "DELETE DATA " (tf/format-quads delete-data pretty?))) 118 | 119 | (defmethod f/format-ast-node :delete-where [{:keys [pretty?]} [_ delete-where]] 120 | (str "DELETE WHERE " (tf/format-quads delete-where pretty?))) 121 | 122 | (defmethod f/format-ast-node :delete [{:keys [pretty?]} [_ delete]] 123 | (str "DELETE " (tf/format-quads delete pretty?))) 124 | 125 | (defmethod f/format-ast-node :insert [{:keys [pretty?]} [_ insert]] 126 | (str "INSERT " (tf/format-quads insert pretty?))) 127 | 128 | (defmethod f/format-ast-node :update/insert-data [{:keys [pretty?]} [_ id-update]] 129 | (f/join-clauses id-update pretty?)) 130 | 131 | (defmethod f/format-ast-node :update/delete-data [{:keys [pretty?]} [_ dd-update]] 132 | (f/join-clauses dd-update pretty?)) 133 | 134 | (defmethod f/format-ast-node :update/delete-where [{:keys [pretty?]} [_ dw-update]] 135 | (f/join-clauses dw-update pretty?)) 136 | 137 | (defmethod f/format-ast-node :update/modify [{:keys [pretty?]} [_ mod-update]] 138 | (f/join-clauses mod-update pretty?)) 139 | 140 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 141 | ;; Updates 142 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 143 | 144 | (defn join-updates 145 | "Given a coll of `updates`, return a semicolon-joined UpdateRequest string." 146 | [updates pretty?] 147 | (if pretty? 148 | (cstr/join ";\n" updates) 149 | (cstr/join "; " updates))) 150 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/values.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.values 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom])) 5 | 6 | (defmulti format-values-clause 7 | (fn [vars _ _] (if (= 1 (count vars)) :values/single :values/default))) 8 | 9 | (defmethod format-values-clause :values/single 10 | [var values pretty?] 11 | (let [kstr (first var) 12 | vstrs (map first values) 13 | vstr (-> vstrs 14 | (f/join-clauses pretty?) 15 | (f/wrap-in-braces pretty?))] 16 | (str kstr " " vstr))) 17 | 18 | (defmethod format-values-clause :values/default 19 | [vars values pretty?] 20 | (let [kstr (str "(" (cstr/join " " vars) ")") 21 | vstrs (map #(str "(" (cstr/join " " %) ")") values) 22 | vstr (-> vstrs 23 | (f/join-clauses pretty?) 24 | (f/wrap-in-braces pretty?))] 25 | (str kstr " " vstr))) 26 | 27 | (defmethod f/format-ast-node :values/undef [_ _] 28 | "UNDEF") 29 | 30 | (defmethod f/format-ast-node :values/map [{:keys [pretty?]} [_ [vars values]]] 31 | (format-values-clause vars values pretty?)) 32 | 33 | (defmethod f/format-ast-node :values [_ [_ values-map]] 34 | (str "VALUES " values-map)) 35 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/format/where.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.where 2 | (:require [clojure.string :as cstr] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.axiom] 5 | [com.yetanalytics.flint.format.expr] 6 | [com.yetanalytics.flint.format.modifier] 7 | [com.yetanalytics.flint.format.select] 8 | [com.yetanalytics.flint.format.triple] 9 | [com.yetanalytics.flint.format.values])) 10 | 11 | (defmethod f/format-ast-node :where-sub/select [{:keys [pretty?]} [_ sub-select]] 12 | (-> sub-select 13 | (f/join-clauses pretty?) 14 | (f/wrap-in-braces pretty?))) 15 | 16 | (defmethod f/format-ast-node :where-sub/where [{:keys [pretty?]} [_ sub-where]] 17 | (-> sub-where 18 | (f/join-clauses pretty?) 19 | (f/wrap-in-braces pretty?))) 20 | 21 | (defmethod f/format-ast-node :where-sub/empty [_ _] 22 | "{}") 23 | 24 | (defmethod f/format-ast-node :where/recurse [_ [_ pattern]] 25 | pattern) 26 | 27 | (defmethod f/format-ast-node :where/union [{:keys [pretty?]} [_ patterns]] 28 | (if pretty? 29 | (cstr/join "\nUNION\n" patterns) 30 | (cstr/join " UNION " patterns))) 31 | 32 | (defmethod f/format-ast-node :where/optional [_ [_ pattern]] 33 | (str "OPTIONAL " pattern)) 34 | 35 | (defmethod f/format-ast-node :where/minus [_ [_ pattern]] 36 | (str "MINUS " pattern)) 37 | 38 | (defmethod f/format-ast-node :where/graph [_ [_ [iri pattern]]] 39 | (str "GRAPH " iri " " pattern)) 40 | 41 | (defmethod f/format-ast-node :where/service [_ [_ [iri pattern]]] 42 | (str "SERVICE " iri " " pattern)) 43 | 44 | (defmethod f/format-ast-node :where/service-silent [_ [_ [iri pattern]]] 45 | (str "SERVICE SILENT " iri " " pattern)) 46 | 47 | (defmethod f/format-ast-node :where/filter [_ [_ expr]] 48 | (if (f/bracketted-or-fn-expr-str? expr) 49 | (str "FILTER " expr) 50 | (str "FILTER (" expr ")"))) 51 | 52 | (defmethod f/format-ast-node :where/bind [_ [_ expr-as-var]] 53 | (str "BIND (" expr-as-var ")")) 54 | 55 | (defmethod f/format-ast-node :where/values [_ [_ values]] 56 | (str "VALUES " values)) 57 | 58 | (defmethod f/format-ast-node :where/special [_ [_ where-form]] 59 | where-form) 60 | 61 | (defmethod f/format-ast-node :where/triple [_ [_ triples]] 62 | triples) 63 | 64 | (defmethod f/format-ast-node :where [_ [_ where]] 65 | (str "WHERE " where)) 66 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec 2 | #?(:clj (:require [clojure.spec.alpha :as s])) 3 | #?(:cljs (:require-macros 4 | [com.yetanalytics.flint.spec :refer [sparql-keys]]))) 5 | 6 | ;; Need these helpers to deal with `(or ::kspec-1 ::kspec-2 ...)` 7 | 8 | #?(:clj 9 | (defn- collect-keys 10 | [x] 11 | (cond 12 | (coll? x) (->> x flatten (filter keyword?)) 13 | (keyword? x) [x] 14 | :else nil))) 15 | 16 | #?(:clj 17 | (defn- collect-unq-keys 18 | [x] 19 | (map (comp keyword name) (collect-keys x)))) 20 | 21 | #?(:clj 22 | (defmacro sparql-keys 23 | "A variant of `s/keys` that automatically conforms the map into 24 | a kv-pair vector sorted by `key-comp-fn`. In addition, keys 25 | are restricted to those in the spec." 26 | [& {:keys [key-comp-fn req opt req-un opt-un] 27 | :or {key-comp-fn compare}}] 28 | (let [keys-set# (set (concat (collect-keys req) 29 | (collect-keys opt) 30 | (collect-unq-keys req-un) 31 | (collect-unq-keys opt-un))) 32 | keys-spec# (cond-> [`s/keys] 33 | req (conj :req req) 34 | opt (conj :opt opt) 35 | req-un (conj :req-un req-un) 36 | opt-un (conj :opt-un opt-un) 37 | true seq)] 38 | `(s/and map? 39 | ;; `restrict-keys` taken from xapi-schema.spec 40 | #(every? ~keys-set# (keys %)) 41 | ~keys-spec# 42 | (s/conformer #(into [] %)) 43 | (s/conformer #(sort-by first ~key-comp-fn %)))))) 44 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec/axiom.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.axiom 2 | (:require [clojure.spec.alpha :as s] 3 | [com.yetanalytics.flint.axiom.protocol :as p] 4 | [com.yetanalytics.flint.axiom.impl])) 5 | 6 | ;; Axiom specs 7 | ;; We need to wrap them in functions in order to ensure that the functions 8 | ;; are called dynamically, rather than have fixed definitions. Otherwise 9 | ;; `extend-protocol` will not work. 10 | 11 | (def iri-spec 12 | #(p/-valid-iri? %)) 13 | 14 | (def prefix-spec 15 | #(p/-valid-prefix? %)) 16 | 17 | (def prefix-iri-spec 18 | #(p/-valid-prefix-iri? %)) 19 | 20 | (def variable-spec 21 | #(p/-valid-variable? %)) 22 | 23 | (def bnode-spec 24 | #(p/-valid-bnode? %)) 25 | 26 | (def wildcard-spec 27 | #(p/-valid-wildcard? %)) 28 | 29 | (def rdf-type-spec 30 | #(p/-valid-rdf-type? %)) 31 | 32 | (def literal-spec 33 | #(p/-valid-literal? %)) 34 | 35 | ;; Composite specs 36 | 37 | (def iri-or-prefixed-spec 38 | "Spec for both prefixed and full IRIs." 39 | (s/or :ax/iri iri-spec 40 | :ax/prefix-iri prefix-iri-spec)) 41 | 42 | (def iri-or-var-spec 43 | "Spec for both prefixed IRIs, full IRIs, and variables." 44 | (s/or :ax/var variable-spec 45 | :ax/iri iri-spec 46 | :ax/prefix-iri prefix-iri-spec)) 47 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec/modifier.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.modifier 2 | (:require [clojure.spec.alpha :as s] 3 | [com.yetanalytics.flint.spec.axiom :as ax] 4 | [com.yetanalytics.flint.spec.expr :as es])) 5 | 6 | ;; Technically a single variable is also an expression, but it's already 7 | ;; distinguished in the context-free grammar so why not also reflect that here. 8 | (s/def ::group-by 9 | (s/coll-of (s/or :ax/var ax/variable-spec 10 | :mod/group-expr ::es/expr 11 | :mod/expr-as-var ::es/expr-as-var) 12 | :min-count 1 13 | :kind vector?)) 14 | 15 | (s/def ::order-by 16 | (s/coll-of (s/or :ax/var ax/variable-spec 17 | :mod/asc-desc (s/& (s/cat :mod/op #{'asc 'desc} 18 | :mod/asc-desc-expr ::es/agg-expr) 19 | (s/conformer #(into [] %))) 20 | :mod/order-expr ::es/agg-expr) 21 | :min-count 1 22 | :kind vector?)) 23 | 24 | (s/def ::having 25 | (s/coll-of ::es/agg-expr 26 | :min-count 1 27 | :kind vector?)) 28 | 29 | ;; single-branch `s/or`s are used to conform values 30 | 31 | (s/def ::limit (s/or :ax/numeric nat-int?)) 32 | 33 | (s/def ::offset (s/or :ax/numeric int?)) 34 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec/path.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.path 2 | (:require [clojure.spec.alpha :as s] 3 | [com.yetanalytics.flint.spec.axiom :as ax])) 4 | 5 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 6 | ;; Path Terminal 7 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 8 | 9 | (def path-terminal-spec 10 | (s/and 11 | (comp not list?) 12 | (s/or :ax/iri ax/iri-spec 13 | :ax/prefix-iri ax/prefix-iri-spec 14 | :ax/rdf-type ax/rdf-type-spec))) 15 | 16 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 17 | ;; Negated Path 18 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 19 | 20 | ;; A "negated path" (PathNegatedPropertySet in the SPARQL grammar) can 21 | ;; only contain a bunch of alternates of more negated paths. 22 | 23 | ;; Multi-spec is kind of pointless given that there's only one choice here, 24 | ;; but this makes it symmetrical to the main path spec. 25 | 26 | (defmulti path-neg-spec-mm first) 27 | 28 | (defmethod path-neg-spec-mm 'alt [_] 29 | (s/cat :path/op #{'alt} 30 | :path/paths (s/* ::path-neg))) 31 | 32 | (def path-neg-multi-spec 33 | (s/multi-spec path-neg-spec-mm identity)) 34 | 35 | (s/def ::path-neg 36 | (s/or :path/terminal 37 | path-terminal-spec 38 | :path/branch 39 | (s/and list? 40 | path-neg-multi-spec 41 | ;; Since we never get a singular `:path/path` there is 42 | ;; no need for extra conforming. 43 | (s/conformer #(into [] %))))) 44 | 45 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 46 | ;; Path 47 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 48 | 49 | ;; Specs 50 | 51 | (def varardic-spec 52 | (s/cat :path/op #{'alt 'cat} 53 | :path/paths (s/* ::path))) 54 | 55 | (def unary-spec 56 | (s/cat :path/op #{'inv '? '* '+} 57 | :path/path ::path)) 58 | 59 | (def unary-neg-spec 60 | (s/cat :path/op #{'not} 61 | :path/path ::path-neg)) 62 | 63 | ;; Multimethods 64 | ;; Note: we could use expr/defexprspec here, but given the limited number of 65 | ;; ops that would be a bit overkill. 66 | 67 | (defmulti path-spec-mm first) 68 | 69 | (defmethod path-spec-mm 'alt [_] varardic-spec) 70 | (defmethod path-spec-mm 'cat [_] varardic-spec) 71 | 72 | (defmethod path-spec-mm 'inv [_] unary-spec) 73 | (defmethod path-spec-mm '? [_] unary-spec) 74 | (defmethod path-spec-mm '* [_] unary-spec) 75 | (defmethod path-spec-mm '+ [_] unary-spec) 76 | 77 | (defmethod path-spec-mm 'not [_] unary-neg-spec) 78 | 79 | (def path-multi-spec 80 | (s/multi-spec path-spec-mm identity)) 81 | 82 | ;; Putting it all together 83 | 84 | (defn- path-conformer 85 | "Conform the result of a regex spec by converting any `:path/path` keys 86 | into `:path/paths`." 87 | [{op :path/op 88 | path :path/path 89 | paths :path/paths}] 90 | [[:path/op op] 91 | [:path/paths (if path [path] paths)]]) 92 | 93 | (s/def ::path 94 | (s/or :path/terminal 95 | path-terminal-spec 96 | :path/branch 97 | (s/and list? 98 | path-multi-spec 99 | (s/conformer path-conformer)))) 100 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec/prologue.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.prologue 2 | (:require [clojure.spec.alpha :as s] 3 | [com.yetanalytics.flint.spec.axiom :as ax] 4 | [com.yetanalytics.flint.axiom.impl.validation :refer []])) 5 | 6 | ;; single `s/or` used to conform 7 | 8 | (s/def ::base 9 | (s/or :ax/iri ax/iri-spec)) 10 | 11 | (s/def ::prefixes 12 | (s/and (s/map-of ax/prefix-spec ax/iri-spec) 13 | (s/conformer 14 | (partial map 15 | (fn [[pre iri]] 16 | [:prologue/prefix [[:ax/prefix pre] 17 | [:ax/iri iri]]]))))) 18 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec/query.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.query 2 | (:require [clojure.spec.alpha :as s] 3 | [com.yetanalytics.flint.spec.axiom :as ax] 4 | [com.yetanalytics.flint.spec.modifier :as ms] 5 | [com.yetanalytics.flint.spec.prologue :as ps] 6 | [com.yetanalytics.flint.spec.select :as ss] 7 | [com.yetanalytics.flint.spec.triple :as ts] 8 | [com.yetanalytics.flint.spec.where :as ws] 9 | [com.yetanalytics.flint.spec.values :as vs]) 10 | #?(:clj (:require 11 | [com.yetanalytics.flint.spec :refer [sparql-keys]]) 12 | :cljs (:require-macros 13 | [com.yetanalytics.flint.spec :refer [sparql-keys]]))) 14 | 15 | (def key-order-map 16 | {:base 0 17 | :prefixes 1 18 | :select 2 19 | :select-distinct 2 20 | :select-reduced 2 21 | :construct 2 22 | :describe 2 23 | :ask 2 24 | :from 3 25 | :from-named 4 26 | :where 5 27 | :group-by 6 28 | :order-by 7 29 | :having 8 30 | :limit 9 31 | :offset 10 32 | :values 11}) 33 | 34 | (defn- key-comp 35 | [k1 k2] 36 | (let [n1 (get key-order-map k1 100) 37 | n2 (get key-order-map k2 100)] 38 | (- n1 n2))) 39 | 40 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 41 | ;; Dataset Clause specs 42 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 43 | 44 | (s/def ::from 45 | (s/and (s/or :from/single ax/iri-or-prefixed-spec 46 | :from/coll (s/and (s/coll-of ax/iri-or-prefixed-spec 47 | :count 1 48 | :kind vector?) 49 | (s/conformer first))) 50 | (s/conformer second))) 51 | 52 | (s/def ::from-named 53 | (s/coll-of ax/iri-or-prefixed-spec 54 | :min-count 1 55 | :kind vector?)) 56 | 57 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 58 | ;; Query 59 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 60 | 61 | ;; Cannot use `s/merge` since conformance does not work properly with it 62 | 63 | (def select-query-spec 64 | (sparql-keys :req-un [(or ::ss/select 65 | ::ss/select-distinct 66 | ::ss/select-reduced) 67 | ::ws/where] 68 | :opt-un [::ps/base ::ps/prefixes 69 | ::from ::from-named 70 | ::ms/group-by 71 | ::ms/order-by 72 | ::ms/having 73 | ::ms/limit 74 | ::ms/offset 75 | ::vs/values] 76 | :key-comp-fn key-comp)) 77 | 78 | (s/def ::construct ts/triple-coll-nopath-spec) 79 | 80 | (def construct-query-spec 81 | (sparql-keys :req-un [::construct ::ws/where] 82 | :opt-un [::ps/base ::ps/prefixes 83 | ::from ::from-named 84 | ::ms/group-by 85 | ::ms/order-by 86 | ::ms/having 87 | ::ms/limit 88 | ::ms/offset] 89 | :key-comp-fn key-comp)) 90 | 91 | (s/def ::describe 92 | (s/or :describe/vars-or-iris (s/coll-of ax/iri-or-var-spec 93 | :min-count 1 94 | :kind vector?) 95 | :ax/wildcard ax/wildcard-spec)) 96 | 97 | (def describe-query-spec 98 | (sparql-keys :req-un [::describe] 99 | :opt-un [::ps/base ::ps/prefixes 100 | ::from ::from-named 101 | ::ws/where 102 | ::ms/group-by 103 | ::ms/order-by 104 | ::ms/having 105 | ::ms/limit 106 | ::ms/offset] 107 | :key-comp-fn key-comp)) 108 | 109 | (s/def ::ask empty?) 110 | 111 | (def ask-query-spec 112 | (sparql-keys :req-un [::ask ::ws/where] 113 | :opt-un [::ps/base ::ps/prefixes 114 | ::from ::from-named 115 | ::ms/group-by 116 | ::ms/order-by 117 | ::ms/having 118 | ::ms/limit 119 | ::ms/offset] 120 | :key-comp-fn key-comp)) 121 | 122 | (def query-spec 123 | (s/or :query/select select-query-spec 124 | :query/construct construct-query-spec 125 | :query/describe describe-query-spec 126 | :query/ask ask-query-spec)) 127 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec/select.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.select 2 | (:require [clojure.spec.alpha :as s] 3 | [com.yetanalytics.flint.spec.axiom :as ax] 4 | [com.yetanalytics.flint.spec.expr :as es])) 5 | 6 | (defn- no-duplicate-vars? 7 | [var-or-exprs] 8 | (boolean (reduce (fn [seen [k x]] 9 | (case k 10 | :ax/var 11 | (if (contains? seen x) 12 | (reduced false) 13 | (conj seen x)) 14 | :select/expr-as-var 15 | (let [v (-> x second second second)] 16 | (if (contains? seen v) 17 | (reduced false) 18 | (conj seen v))))) 19 | #{} 20 | var-or-exprs))) 21 | 22 | (def select-spec 23 | (s/or :select/var-or-exprs 24 | (s/and (s/* (s/alt :ax/var ax/variable-spec 25 | :select/expr-as-var ::es/agg-expr-as-var)) 26 | no-duplicate-vars?) 27 | :ax/wildcard ax/wildcard-spec)) 28 | 29 | (s/def ::select select-spec) 30 | (s/def ::select-distinct select-spec) 31 | (s/def ::select-reduced select-spec) 32 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec/values.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.values 2 | (:require [clojure.spec.alpha :as s] 3 | [com.yetanalytics.flint.spec.axiom :as ax])) 4 | 5 | (def value-spec 6 | (s/or :ax/iri ax/iri-spec 7 | :ax/prefix-iri ax/prefix-iri-spec 8 | :ax/literal ax/literal-spec 9 | :values/undef nil?)) 10 | 11 | (defn- matching-val-lengths* 12 | [m] 13 | (let [vars (first (keys m)) 14 | values (first (vals m)) 15 | nv (count vars)] 16 | (every? #(= nv (count %)) values))) 17 | 18 | (defn- matching-val-lengths 19 | [m] 20 | (let [values (vals m) 21 | nv (count (first values))] 22 | (every? #(= nv (count %)) values))) 23 | 24 | (defn- clojure->sparql 25 | [m] 26 | (let [mfn (fn [& ks] (vec ks)) 27 | vars (->> m keys vec) 28 | values (->> m vals (apply map mfn) vec)] 29 | {vars values})) 30 | 31 | ;; For some reason `s/map-of` doesn't conform the keys properly 32 | ;; so we have to do it manually. 33 | (defn- conform-vars 34 | [m] 35 | (let [k (first (keys m)) 36 | v (first (vals m))] 37 | [(mapv (fn [vr] [:ax/var vr]) k) v])) 38 | 39 | (def values-clause-spec 40 | (s/and 41 | (s/or :values/sparql-format 42 | (s/and (s/map-of any? any? :min-count 1 :max-count 1) 43 | (s/map-of (s/coll-of ax/variable-spec) 44 | (s/coll-of (s/coll-of value-spec))) 45 | matching-val-lengths* 46 | (s/conformer conform-vars)) 47 | :values/clojure-format 48 | (s/and (s/map-of any? any? :min-count 1) 49 | (s/map-of ax/variable-spec (s/coll-of value-spec)) 50 | matching-val-lengths 51 | (s/conformer clojure->sparql) 52 | (s/conformer conform-vars))) 53 | (s/conformer second))) 54 | 55 | ;; single-branch `s/or` is used to conform values 56 | (s/def ::values (s/or :values/map values-clause-spec)) 57 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/spec/where.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.where 2 | (:require [clojure.spec.alpha :as s] 3 | [com.yetanalytics.flint.spec.axiom :as ax] 4 | [com.yetanalytics.flint.spec.expr :as es] 5 | [com.yetanalytics.flint.spec.modifier :as ms] 6 | [com.yetanalytics.flint.spec.select :as ss] 7 | [com.yetanalytics.flint.spec.triple :as ts] 8 | [com.yetanalytics.flint.spec.values :as vs]) 9 | #?(:clj (:require 10 | [com.yetanalytics.flint.spec :refer [sparql-keys]]) 11 | :cljs (:require-macros 12 | [com.yetanalytics.flint.spec :refer [sparql-keys]]))) 13 | 14 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 15 | ;; Sub-SELECT query 16 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 17 | 18 | (def key-order-map 19 | {:select 2 20 | :select-distinct 2 21 | :select-reduced 2 22 | :where 5 23 | :group-by 6 24 | :order-by 7 25 | :having 8 26 | :limit 9 27 | :offset 10 28 | :values 11}) 29 | 30 | (defn- key-comp 31 | [k1 k2] 32 | (let [n1 (get key-order-map k1 100) 33 | n2 (get key-order-map k2 100)] 34 | (- n1 n2))) 35 | 36 | (s/def ::select 37 | (sparql-keys :req-un [(or ::ss/select 38 | ::ss/select-distinct 39 | ::ss/select-reduced) 40 | ::where] 41 | :opt-un [::vs/values 42 | ;; s/merge does not result in correct conformation 43 | ::ms/group-by 44 | ::ms/order-by 45 | ::ms/having 46 | ::ms/limit 47 | ::ms/offset] 48 | :key-comp-fn key-comp)) 49 | 50 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 51 | ;; WHERE clause 52 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 53 | 54 | (defmulti where-special-form-mm 55 | "Accepts a special WHERE form/graph pattern in the form `[:keyword ...]` 56 | and returns the appropriate regex spec. The spec applies an additional 57 | conformer in order to allow for identification during formatting." 58 | first) 59 | 60 | (defmethod where-special-form-mm :where [_] ; recursion 61 | (s/& (s/cat :k #{:where} 62 | :v ::where) 63 | (s/conformer (fn [{:keys [v]}] [:where/recurse v])))) 64 | 65 | (defmethod where-special-form-mm :union [_] 66 | (s/& (s/cat :k #{:union} 67 | :v (s/+ ::where)) 68 | (s/conformer (fn [{:keys [v]}] [:where/union v])))) 69 | 70 | (defmethod where-special-form-mm :optional [_] 71 | (s/& (s/cat :k #{:optional} 72 | :v ::where) 73 | (s/conformer (fn [{:keys [v]}] [:where/optional v])))) 74 | 75 | (defmethod where-special-form-mm :minus [_] 76 | (s/& (s/cat :k #{:minus} 77 | :v ::where) 78 | (s/conformer (fn [{:keys [v]}] [:where/minus v])))) 79 | 80 | (defmethod where-special-form-mm :graph [_] 81 | (s/& (s/cat :k #{:graph} 82 | :v1 ax/iri-or-var-spec 83 | :v2 ::where) 84 | (s/conformer (fn [{:keys [v1 v2]}] [:where/graph [v1 v2]])))) 85 | 86 | (defmethod where-special-form-mm :service [_] 87 | (s/& (s/cat :k #{:service} 88 | :v1 ax/iri-or-var-spec 89 | :v2 ::where) 90 | (s/conformer (fn [{:keys [v1 v2]}] [:where/service [v1 v2]])))) 91 | 92 | (defmethod where-special-form-mm :service-silent [_] 93 | (s/& (s/cat :k #{:service-silent} 94 | :v1 ax/iri-or-var-spec 95 | :v2 ::where) 96 | (s/conformer (fn [{:keys [v1 v2]}] [:where/service-silent [v1 v2]])))) 97 | 98 | (defmethod where-special-form-mm :filter [_] 99 | (s/& (s/cat :k #{:filter} 100 | :v ::es/expr) 101 | (s/conformer (fn [{:keys [v]}] [:where/filter v])))) 102 | 103 | (defmethod where-special-form-mm :bind [_] 104 | (s/& (s/cat :k #{:bind} 105 | :v ::es/expr-as-var) 106 | (s/conformer (fn [{:keys [v]}] [:where/bind v])))) 107 | 108 | (defmethod where-special-form-mm :values [_] 109 | (s/& (s/cat :k #{:values} 110 | :v ::vs/values) 111 | (s/conformer (fn [{:keys [v]}] [:where/values v])))) 112 | 113 | (defmethod where-special-form-mm :default [_] 114 | (constantly false)) 115 | 116 | (def where-special-form-spec 117 | "Specs for special WHERE forms/graph patterns, which should be 118 | of the form `[:keyword ...]`." 119 | (s/and vector? 120 | #(keyword? (first %)) 121 | (s/multi-spec where-special-form-mm first))) 122 | 123 | (def where-spec 124 | (s/or :where/special where-special-form-spec 125 | :where/triple ts/triple-spec)) 126 | 127 | (s/def ::where 128 | (s/or :where-sub/select 129 | ::select 130 | :where-sub/where 131 | (s/coll-of where-spec :min-count 1 :kind vector?) 132 | :where-sub/empty 133 | (s/and vector? empty?))) 134 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/util.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.util) 2 | 3 | (defn get-keyword 4 | "If `x` is a `[:keyword value]` pair, return the keyword; otherwise 5 | return `nil`." 6 | [x] 7 | (when (vector? x) 8 | (let [fst (first x)] 9 | (when (keyword? fst) 10 | fst)))) 11 | 12 | (defn get-kv-pair 13 | "Given `coll` of `[:keyword value]` pairs, return the pair 14 | with keyword `k`. In other words, just like `get` for 15 | associative collections." 16 | [coll k] 17 | (some #(when (-> % first (= k)) %) coll)) 18 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/validate/aggregate.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.validate.aggregate 2 | (:require [com.yetanalytics.flint.validate.variable :as vv] 3 | [com.yetanalytics.flint.util :as u] 4 | [com.yetanalytics.flint.validate.util :as vu])) 5 | 6 | ;; In a query level which uses aggregates, only expressions consisting of 7 | ;; aggregates and constants may be projected, with one exception. 8 | ;; When GROUP BY is given with one or more simple expressions consisting of 9 | ;; just a variable, those variables may be projected from the level. 10 | 11 | ;; GOOD: 12 | ;; SELECT (2 AS ?two) WHERE { ?x ?y ?z } 13 | ;; SELECT (SUM(?x) AS ?sum) WHERE { ?x ?y ?z } 14 | ;; SELECT ?x WHERE { ?x ?y ?z } GROUP BY ?x 15 | 16 | ;; BAD: 17 | ;; SELECT (AVG(?x)) AS ?avg) ((?x + ?y) AS ?sum) WHERE { ?x ?y ?z } 18 | ;; SELECT ?y ?z WHERE { ?x ?y ?z } GROUP BY ?x 19 | 20 | (defn- validate-agg-select-clause 21 | "Return a coll of invalid vars in a SELECT clause with aggregates, or 22 | `nil` if valid." 23 | [group-by-vars sel-clause] 24 | (let [[_ bad-vars] 25 | (reduce (fn [[valid-vars bad-vars] [k x]] 26 | (case k 27 | :ax/var 28 | (if-not (valid-vars x) 29 | [valid-vars (conj bad-vars x)] 30 | [valid-vars bad-vars]) 31 | :select/expr-as-var 32 | (let [[_ [expr v-kv]] x 33 | [_ v] v-kv] 34 | (if-some [bad-expr-vars 35 | (->> expr 36 | (vv/invalid-agg-expr-vars valid-vars) 37 | not-empty)] 38 | [valid-vars (concat bad-vars bad-expr-vars)] 39 | ;; Somehow already-projected vars are now valid, 40 | ;; at least according to Apache Jena's query parser 41 | [(conj valid-vars v) bad-vars])))) 42 | [group-by-vars []] 43 | sel-clause)] 44 | (not-empty bad-vars))) 45 | 46 | (defn- validate-agg-select 47 | [[[_select-k select] loc]] 48 | (let [[_ select-cls] (u/get-kv-pair select :select) 49 | [_ ?group-by] (u/get-kv-pair select :group-by) 50 | group-by-vs (if ?group-by 51 | (->> ?group-by 52 | (map vv/group-by-projected-vars) 53 | (filter some?) 54 | set) 55 | #{}) 56 | [sel-k sel-v] select-cls] 57 | (case sel-k 58 | :ax/wildcard 59 | {:kind ::wildcard-group-by 60 | :path (vu/zip-path loc)} 61 | :select/var-or-exprs 62 | (when-some [bad-vars (validate-agg-select-clause group-by-vs sel-v)] 63 | {:kind ::invalid-aggregate-var 64 | :variables bad-vars 65 | :path (vu/zip-path loc)}) 66 | ;; else - perhaps it is a CONSTRUCT, DESCRIBE, or ASK query instead 67 | nil))) 68 | 69 | (defn validate-agg-selects 70 | "Validate, given `node-m` that contains a map from `SELECT` AST nodes to 71 | their zipper locs, that any SELECT that includes aggregates are valid 72 | according to the SPARQL spec. Returns `nil` if valid, a coll of error 73 | maps otherwise." 74 | [node-m] 75 | (->> (:agg/select node-m) 76 | (map validate-agg-select) 77 | (filter some?) 78 | not-empty)) 79 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/validate/bnode.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.validate.bnode 2 | (:require [clojure.set :as cset] 3 | [com.yetanalytics.flint.validate.util :as vu])) 4 | 5 | (defn- invalid-bnode? 6 | "Is the blank node that is associated with `bgp-loc-m` invalid? It is 7 | if the map has more than one entry, indicating that the blank node is 8 | located across multiple BGPs." 9 | [bgp-loc-m] 10 | (< 1 (count bgp-loc-m))) 11 | 12 | (defn- bnode-err-map 13 | [bnode loc] 14 | {:bnode bnode 15 | :path (vu/zip-path loc)}) 16 | 17 | (defn- bnode-locs->err-map 18 | [bnode-locs] 19 | (mapcat (fn [[bnode locs]] (map (partial bnode-err-map bnode) locs)) 20 | bnode-locs)) 21 | 22 | (defn validate-bnodes 23 | "Given the map `node-m` between nodes and zipper locs, validate that 24 | all bnodes satisfy the following conditions: 25 | 26 | - They cannot be duplicated in different Basic Graph Patterns (BGPs). 27 | - They cannot be duplicated across different Updates in a request. 28 | 29 | Returns a pair between the union of `prev-bnodes` and the bnodes in 30 | `node-m`, and a nilable error map." 31 | ([node-m] 32 | (validate-bnodes #{} node-m)) 33 | ([prev-bnodes node-m] 34 | (let [bnode-bgp-m (-> (:ax/bnode node-m) (dissoc '_)) 35 | new-bnodes (set (keys bnode-bgp-m)) 36 | bnode-union (cset/union prev-bnodes new-bnodes)] 37 | (if-some [bad-bnode-locs (->> bnode-bgp-m 38 | (keep (fn [[bnode bgp-loc-m]] 39 | (when (contains? prev-bnodes bnode) 40 | [bnode (apply concat (vals bgp-loc-m))]))) 41 | not-empty)] 42 | [bnode-union 43 | {:kind ::dupe-bnodes-update 44 | :errors (bnode-locs->err-map bad-bnode-locs) 45 | :prev-bnodes prev-bnodes}] 46 | (if-some [bad-bnode-locs (->> bnode-bgp-m 47 | (keep (fn [[bnode bgp-loc-m]] 48 | (when (invalid-bnode? bgp-loc-m) 49 | [bnode (apply concat (vals bgp-loc-m))]))) 50 | not-empty)] 51 | [bnode-union 52 | {:kind ::dupe-bnodes-bgp 53 | :errors (bnode-locs->err-map bad-bnode-locs)}] 54 | [bnode-union nil]))))) 55 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/validate/prefix.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.validate.prefix 2 | (:require [com.yetanalytics.flint.validate.util :as vu])) 3 | 4 | (defn- invalid-prefix? 5 | "Does the prefix of `prefix-iri` exist in the `prefixes` map/set? 6 | (Or for non-namespaced `prefix-iri`, does `:$` exist?)" 7 | [prefixes prefix-iri] 8 | (if-some [pre (namespace prefix-iri)] 9 | (not (contains? prefixes (keyword pre))) 10 | (not (contains? prefixes :$)))) 11 | 12 | (defn- prefix-err-map 13 | [prefixes prefix-iri loc] 14 | {:prefixes prefixes 15 | :iri prefix-iri 16 | :prefix (or (some->> prefix-iri namespace keyword) :$) 17 | :path (conj (vu/zip-path loc) :ax/prefix-iri)}) 18 | 19 | (defn validate-prefixes 20 | "Given `node-m` a map from nodes to zipper locs, check that each prefix 21 | node is included in `prefixes`. If validation fails, return a coll of 22 | error maps; otherwise return `nil`." 23 | [prefixes node-m] 24 | (let [prefix-m (:ax/prefix-iri node-m) 25 | errors (reduce (fn [acc [prefix locs]] 26 | (if (invalid-prefix? prefixes prefix) 27 | (->> locs 28 | (map (partial prefix-err-map prefixes prefix)) 29 | (concat acc)) 30 | acc)) 31 | [] 32 | prefix-m)] 33 | (not-empty errors))) 34 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/validate/scope.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.validate.scope 2 | (:require [clojure.zip :as zip] 3 | [com.yetanalytics.flint.validate.variable :as vv] 4 | [com.yetanalytics.flint.validate.util :as vu] 5 | [com.yetanalytics.flint.util :as u])) 6 | 7 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 8 | ;; Validation on AST zipper 9 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 10 | 11 | (defn- in-scope-err-map 12 | [var scope-vars zip-loc k] 13 | {:kind ::var-in-scope 14 | :variable var 15 | :scope-vars scope-vars 16 | :path (conj (vu/zip-path zip-loc) k)}) 17 | 18 | (defn- not-in-scope-err-map 19 | [vars scope-vars zip-loc k] 20 | {:kind ::var-not-in-scope 21 | :variables vars 22 | :scope-vars scope-vars 23 | :path (conj (vu/zip-path zip-loc) k)}) 24 | 25 | (defn- validate-bind 26 | "Validate `BIND (expr AS var)` in WHERE clauses." 27 | [[_expr-as-var-k [_ v-kv]] loc] 28 | (let [[_ bind-var] v-kv 29 | prev-elems (-> loc 30 | zip/up ; :where/special 31 | zip/lefts) 32 | scope (set (mapcat vv/get-scope-vars prev-elems))] 33 | (when (contains? scope bind-var) 34 | (in-scope-err-map bind-var scope loc :where/bind)))) 35 | 36 | (defn- validate-select 37 | "Validate `SELECT ... (expr AS var) ..." 38 | [[_expr-as-var-k [expr v-kv]] loc] 39 | (let [[_ bind-var] v-kv 40 | expr-vars (vv/get-expr-vars expr) 41 | prev-elems (zip/lefts loc) 42 | sel-query (->> loc 43 | zip/up ; :select/var-or-exprs 44 | zip/up ; :select 45 | zip/up ; :query/select or :where-sub/select 46 | zip/node) 47 | where (-> sel-query second (u/get-kv-pair :where)) 48 | ?group-by (-> sel-query second (u/get-kv-pair :group-by)) 49 | where-vars (-> where second vv/get-scope-vars) 50 | group-vars (some-> ?group-by vv/group-by-projected-vars) 51 | prev-vars (mapcat vv/get-scope-vars prev-elems) 52 | scope (set (concat where-vars group-vars prev-vars))] 53 | (if-some [bad-expr-vars (not-empty (filter #(not (scope %)) expr-vars))] 54 | (not-in-scope-err-map bad-expr-vars scope loc :select/expr-as-var) 55 | (when (contains? scope bind-var) 56 | (in-scope-err-map bind-var scope loc :select/expr-as-var))))) 57 | 58 | (defn- validate-node-locs 59 | [validation-fn node-locs] 60 | (mapcat (fn [[node locs]] 61 | (->> locs 62 | (map (partial validation-fn node)) 63 | (filter some?))) 64 | node-locs)) 65 | 66 | (defn validate-scoped-vars 67 | "Given `node-m` a map from nodes to zipper locs, check that each var in 68 | a `expr AS var` node does not already exist in scope. If invalid, return 69 | a vector of error maps; otherwise return `nil`." 70 | [node-m] 71 | (let [binds (:where/bind node-m) 72 | select-clauses (:select/expr-as-var node-m)] 73 | (some->> (concat (validate-node-locs validate-bind binds) 74 | (validate-node-locs validate-select select-clauses)) 75 | not-empty 76 | vec))) 77 | -------------------------------------------------------------------------------- /src/main/com/yetanalytics/flint/validate/util.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.validate.util 2 | (:require [clojure.zip :as zip])) 3 | 4 | (defn zip-path 5 | "Return the path vector (excluding array indices) that leads up to `loc`." 6 | [loc] 7 | (->> loc zip/path (mapv first))) 8 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/format/axiom_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.axiom-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [com.yetanalytics.flint.axiom.iri :as iri] 4 | [com.yetanalytics.flint.format :as f] 5 | [com.yetanalytics.flint.format.axiom]) 6 | #?(:clj (:import [java.time 7 | Instant 8 | LocalDate LocalTime LocalDateTime 9 | OffsetTime OffsetDateTime 10 | ZonedDateTime]))) 11 | 12 | (deftest axiom-format 13 | (testing "Formatting terminal AST nodes" 14 | (is (= "" 15 | (f/format-ast-node {} [:ax/iri ""]))) 16 | (is (= "foo" 17 | (f/format-ast-node {} [:ax/prefix :foo]))) 18 | (is (= "foo:bar" 19 | (f/format-ast-node {} [:ax/prefix-iri :foo/bar]))) 20 | (is (= ":bar" 21 | (f/format-ast-node {} [:ax/prefix-iri :bar]))) 22 | (is (= "?xyz" 23 | (f/format-ast-node {} [:ax/var '?xyz]))) 24 | (is (= "_:b0" 25 | (f/format-ast-node {} [:ax/bnode '_b0]))) 26 | (is (= "[]" 27 | (f/format-ast-node {} [:ax/bnode '_]))) 28 | (is (= "_:___" 29 | (f/format-ast-node {} [:ax/bnode '____]))) 30 | (is (= "*" 31 | (f/format-ast-node {} [:ax/wildcard '*]))) 32 | (is (= "*" 33 | (f/format-ast-node {} [:ax/wildcard :*]))) 34 | (is (= "a" 35 | (f/format-ast-node {} [:ax/rdf-type 'a]))) 36 | (is (= "a" 37 | (f/format-ast-node {} [:ax/rdf-type :a]))) 38 | (is (= "\"My String\"" 39 | (f/format-ast-node {} [:ax/literal "My String"]))) 40 | (is (= "\"foo\\nbar\\rbaz\"" 41 | (f/format-ast-node {} [:ax/literal "foo\\nbar\\rbaz"]))) 42 | (is (= "\"My String\"@en" 43 | (f/format-ast-node {} [:ax/literal {:en "My String"}]))) 44 | (is (= "123" 45 | (f/format-ast-node {} [:ax/literal 123]))) 46 | (is (= "123.4" 47 | (f/format-ast-node {} [:ax/literal 123.4]))) 48 | (is (= "true" 49 | (f/format-ast-node {} [:ax/literal true]))) 50 | (is (= (str #?(:clj "\"2022-01-20T16:22:19Z\"" 51 | :cljs "\"2022-01-20T16:22:19.000Z\"") 52 | "^^") 53 | (f/format-ast-node {} [:ax/literal #inst "2022-01-20T16:22:19Z"]))) 54 | (is (= (str #?(:clj "\"2022-01-20T16:22:19Z\"" 55 | :cljs "\"2022-01-20T16:22:19.000Z\"") 56 | "^^xsd:dateTime") 57 | (f/format-ast-node {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 58 | [:ax/literal #inst "2022-01-20T16:22:19Z"])))) 59 | (testing "DateTime formatting works on all `inst?` timestamps" 60 | #?(:clj (let [ts-str-1 "\"2022-01-20T16:22:19Z\"^^xsd:dateTime" 61 | ts-str-2 "\"1970-01-01T00:00:00Z\"^^xsd:dateTime" 62 | ts-str-3 "\"1970-01-01\"^^xsd:date" 63 | ts-str-4 "\"00:00:00Z\"^^xsd:time" 64 | literal-1 #inst "2022-01-20T16:22:19Z" 65 | literal-2 #inst "1970-01-01T00:00:00Z" 66 | instant-1 (Instant/parse "2022-01-20T16:22:19Z") 67 | instant-2 (Instant/parse "1970-01-01T00:00:00Z") 68 | date (java.util.Date/from instant-1) 69 | fmt-ts (fn [ts] (f/format-ast-node 70 | {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 71 | [:ax/literal ts]))] 72 | (is (= ts-str-1 (fmt-ts literal-1))) 73 | (is (= ts-str-2 (fmt-ts literal-2))) 74 | (is (= ts-str-1 (fmt-ts instant-1))) 75 | (is (= ts-str-2 (fmt-ts instant-2))) 76 | (is (= ts-str-1 (fmt-ts date))) 77 | (is (= ts-str-2 (fmt-ts (java.sql.Timestamp. 0)))) 78 | (is (= ts-str-3 (fmt-ts (java.sql.Date. 0)))) 79 | (is (= ts-str-4 (fmt-ts (java.sql.Time. 0))))) 80 | :cljs (is (string? 81 | (f/format-ast-node {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 82 | [:ax/literal (js/Date.)]))))) 83 | #?(:clj 84 | (testing "DateTime formatting work on all java.time instances" 85 | ;; Note that here, zeroed-out seconds are preserved 86 | (is (= "\"2022-01-20T16:22:00\"^^xsd:dateTime" 87 | (f/format-ast-node 88 | {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 89 | [:ax/literal (LocalDateTime/parse "2022-01-20T16:22:00")]))) 90 | (is (= "\"2022-01-20T16:22:00-05:00\"^^xsd:dateTime" 91 | (f/format-ast-node 92 | {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 93 | [:ax/literal (ZonedDateTime/parse "2022-01-20T16:22:00-05:00")]))) 94 | (is (= "\"2022-01-20T16:22:00-05:00\"^^xsd:dateTime" 95 | (f/format-ast-node 96 | {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 97 | [:ax/literal (OffsetDateTime/parse "2022-01-20T16:22:00-05:00")]))) 98 | (is (= "\"16:22:00\"^^xsd:time" 99 | (f/format-ast-node 100 | {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 101 | [:ax/literal (LocalTime/parse "16:22:00")]))) 102 | (is (= "\"16:22:00-05:00\"^^xsd:time" 103 | (f/format-ast-node 104 | {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 105 | [:ax/literal (OffsetTime/parse "16:22:00-05:00")]))) 106 | (is (= "\"2022-01-20\"^^xsd:date" 107 | (f/format-ast-node 108 | {:iri-prefix-m {iri/xsd-iri-prefix "xsd"}} 109 | [:ax/literal (LocalDate/parse "2022-01-20")])))))) 110 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/format/modifier_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.modifier-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.modifier])) 5 | 6 | (defn- format-ast [ast] 7 | (f/format-ast ast {})) 8 | 9 | (deftest format-modifier-test 10 | (testing "Formatting ORDER BY clauses" 11 | (is (= "ORDER BY (?foo)" 12 | (->> '[:order-by [[:mod/order-expr 13 | [:expr/terminal [:ax/var ?foo]]]]] 14 | format-ast))) 15 | (is (= "ORDER BY (?foo + ?bar)" 16 | (->> '[:order-by [[:mod/order-expr 17 | [:expr/branch 18 | [[:expr/op +] 19 | [:expr/args [[:expr/terminal [:ax/var ?foo]] 20 | [:expr/terminal [:ax/var ?bar]]]]]]]]] 21 | format-ast))) 22 | (is (= "ORDER BY ASC(?bar)" 23 | (->> '[:order-by [[:mod/asc-desc [[:mod/op asc] 24 | [:mod/asc-desc-expr [:expr/terminal [:ax/var ?bar]]]]]]] 25 | format-ast))) 26 | (is (= "ORDER BY ASC(?x + ?y)" 27 | (->> '[:order-by [[:mod/asc-desc [[:mod/op asc] 28 | [:mod/asc-desc-expr 29 | [:expr/branch [[:expr/op +] 30 | [:expr/args [[:expr/terminal [:ax/var ?x]] 31 | [:expr/terminal [:ax/var ?y]]]]]]]]]]] 32 | format-ast))) 33 | (is (= "ORDER BY DESC(?bar)" 34 | (->> '[:order-by [[:mod/asc-desc [[:mod/op desc] 35 | [:mod/asc-desc-expr [:expr/terminal [:ax/var ?bar]]]]]]] 36 | format-ast))) 37 | (is (= "ORDER BY (?a + ?b) ASC(?bar)" 38 | (->> '[:order-by [[:mod/order-expr 39 | [:expr/branch 40 | [[:expr/op +] 41 | [:expr/args [[:expr/terminal [:ax/var ?a]] 42 | [:expr/terminal [:ax/var ?b]]]]]]] 43 | [:mod/asc-desc [[:mod/op asc] 44 | [:mod/asc-desc-expr [:expr/terminal [:ax/var ?bar]]]]]]] 45 | format-ast))) 46 | (is (= "ORDER BY ?foo ASC(?bar)" 47 | (->> '[:order-by [[:ax/var ?foo] 48 | [:mod/asc-desc [[:mod/op asc] 49 | [:mod/asc-desc-expr [:expr/terminal [:ax/var ?bar]]]]]]] 50 | format-ast)))) 51 | (testing "Formatting GROUP BY clauses" 52 | (is (= "GROUP BY ?foo" 53 | (->> '[:group-by [[:ax/var ?foo]]] 54 | format-ast))) 55 | (is (= "GROUP BY ?foo ?bar" 56 | (->> '[:group-by [[:ax/var ?foo] 57 | [:ax/var ?bar]]] 58 | format-ast))) 59 | ;; This is technically invalid since (?a + ?b) is not a fn call 60 | (is (= "GROUP BY (?a + ?b) (1 AS ?foo) ?bar" 61 | (->> '[:group-by [[:mod/order-expr 62 | [:expr/branch 63 | [[:expr/op +] 64 | [:expr/args [[:expr/terminal [:ax/var ?a]] 65 | [:expr/terminal [:ax/var ?b]]]]]]] 66 | [:mod/expr-as-var [:expr/as-var [[:expr/terminal [:ax/literal 1]] 67 | [:ax/var ?foo]]]] 68 | [:ax/var ?bar]]] 69 | format-ast)))) 70 | (testing "Formatting HAVING clauses" 71 | (is (= "HAVING (1) (!true)" 72 | (->> '[:having [[:expr/terminal [:ax/literal 1]] 73 | [:expr/branch 74 | [[:expr/op not] 75 | [:expr/args [[:expr/terminal [:ax/literal true]]]]]] 76 | ]] 77 | format-ast)))) 78 | (testing "Formatting LIMIT clauses" 79 | (is (= "LIMIT 10" 80 | (->> '[:limit [:ax/literal 10]] 81 | format-ast)))) 82 | (testing "Formatting OFFSET clauses" 83 | (is (= "OFFSET 2" 84 | (->> '[:offset [:ax/literal 2]] 85 | format-ast))))) 86 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/format/path_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.path-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [com.yetanalytics.flint.format :as f] 4 | [com.yetanalytics.flint.format.path])) 5 | 6 | (defn- format-ast [ast] 7 | (f/format-ast ast {})) 8 | 9 | (deftest format-path-test 10 | (testing "Formatting paths" 11 | (is (= "(!foo:bar | (^baz:qux / quu:bee))" 12 | (->> '[:path/branch 13 | [[:path/op alt] 14 | [:path/paths [[:path/branch 15 | [[:path/op not] 16 | [:path/paths [[:path/terminal [:ax/prefix-iri :foo/bar]]]]]] 17 | [:path/branch 18 | [[:path/op cat] 19 | [:path/paths [[:path/branch 20 | [[:path/op inv] 21 | [:path/paths [[:path/terminal 22 | [:ax/prefix-iri :baz/qux]]]]]] 23 | [:path/terminal 24 | [:ax/prefix-iri :quu/bee]]]]]]]]]] 25 | format-ast))) 26 | (is (= "(!foo:bar / (^baz:qux | quu:bee))" 27 | (->> '[:path/branch 28 | [[:path/op cat] 29 | [:path/paths [[:path/branch 30 | [[:path/op not] 31 | [:path/paths [[:path/terminal [:ax/prefix-iri :foo/bar]]]]]] 32 | [:path/branch 33 | [[:path/op alt] 34 | [:path/paths [[:path/branch 35 | [[:path/op inv] 36 | [:path/paths [[:path/terminal 37 | [:ax/prefix-iri :baz/qux]]]]]] 38 | [:path/terminal 39 | [:ax/prefix-iri :quu/bee]]]]]]]]]] 40 | format-ast))) 41 | (is (= "!(foo:bar | baz:qux)" 42 | (->> '[:path/branch 43 | [[:path/op not] 44 | [:path/paths [[:path/branch 45 | [[:path/op alt] 46 | [:path/paths [[:path/terminal [:ax/prefix-iri :foo/bar]] 47 | [:path/terminal [:ax/prefix-iri :baz/qux]]]]]]]]]] 48 | format-ast))) 49 | (is (= "^a" 50 | (->> '[:path/branch 51 | [[:path/op inv] 52 | [:path/paths [[:path/terminal [:ax/rdf-type a]]]]]] 53 | format-ast))) 54 | (is (= "a?" 55 | (->> '[:path/branch 56 | [[:path/op ?] 57 | [:path/paths [[:path/terminal [:ax/rdf-type a]]]]]] 58 | format-ast))) 59 | (is (= "((a?)*)+" 60 | (->> '[:path/branch 61 | [[:path/op +] 62 | [:path/paths [[:path/branch 63 | [[:path/op *] 64 | [:path/paths [[:path/branch 65 | [[:path/op ?] 66 | [:path/paths [[:path/terminal [:ax/rdf-type a]]]]]]]]]]]]]] 67 | format-ast))) 68 | (is (= "(!a)*" 69 | (->> '[:path/branch 70 | [[:path/op *] 71 | [:path/paths [[:path/branch 72 | [[:path/op not] 73 | [:path/paths [[:path/terminal [:ax/rdf-type a]]]]]]]]]] 74 | format-ast))) 75 | (is (= "((!a)*)?" 76 | (->> '[:path/branch 77 | [[:path/op ?] 78 | [:path/paths [[:path/branch 79 | [[:path/op *] 80 | [:path/paths [[:path/branch 81 | [[:path/op not] 82 | [:path/paths [[:path/terminal [:ax/rdf-type a]]]]]]]]]]]]]] 83 | format-ast))) 84 | (is (= "!(!(!a))" 85 | (->> '[:path/branch 86 | [[:path/op not] 87 | [:path/paths [[:path/branch 88 | [[:path/op not] 89 | [:path/paths [[:path/branch 90 | [[:path/op not] 91 | [:path/paths [[:path/terminal [:ax/rdf-type a]]]]]]]]]]]]]] 92 | format-ast))))) 93 | 94 | (deftest format-invalid-test 95 | (testing "Formatting an invalid path" 96 | (is (try (f/format-ast 97 | '[:path/branch 98 | [[:path/op oh-no] 99 | [:path/paths [[:path/terminal [:ax/rdf-type a]]]]]] 100 | {}) 101 | (catch #?(:clj IllegalArgumentException 102 | :cljs js/Error) _ 103 | true))))) 104 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/format/prologue_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.prologue-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.string :as cstr] 4 | [com.yetanalytics.flint.format :as f] 5 | [com.yetanalytics.flint.format.prologue])) 6 | 7 | (defn- format-ast [ast] 8 | (f/format-ast ast {:pretty? true})) 9 | 10 | (deftest format-prologue-test 11 | (testing "Formatting prologues" 12 | (is (= "BASE " 13 | (->> [:base [:ax/iri ""]] 14 | format-ast))) 15 | (is (= (cstr/join "\n" ["PREFIX : " 16 | "PREFIX foo: " 17 | "PREFIX barb: "]) 18 | (->> [:prefixes 19 | [[:prologue/prefix [[:ax/prefix :$] [:ax/iri ""]]] 20 | [:prologue/prefix [[:ax/prefix :foo] [:ax/iri ""]]] 21 | [:prologue/prefix [[:ax/prefix :barb] [:ax/iri ""]]]]] 22 | format-ast))))) 23 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/format/triple_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.triple-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.string :as cstr] 4 | [com.yetanalytics.flint.format :as f] 5 | [com.yetanalytics.flint.format.triple])) 6 | 7 | (deftest format-triples-test 8 | (testing "Formatting triples" 9 | (is (= (cstr/join 10 | "\n" 11 | [" ?p1 ?o1 , ?o2 ;" 12 | " ?p2 ?o1 , ?o2 ." 13 | "?s2 ?p1 ?o1 , ?o2 ;" 14 | " ?p2 ?o1 , ?o2 ."]) 15 | (f/format-ast 16 | '[:triple.nform/spo 17 | [[[:ax/iri ""] 18 | [:triple.nform/po 19 | [[[:ax/var ?p1] 20 | [:triple.nform/o [[:ax/var ?o1] 21 | [:ax/var ?o2]]]] 22 | [[:ax/var ?p2] 23 | [:triple.nform/o [[:ax/var ?o1] 24 | [:ax/var ?o2]]]]]]] 25 | [[:ax/var ?s2] 26 | [:triple.nform/po 27 | [[[:ax/var ?p1] 28 | [:triple.nform/o [[:ax/var ?o1] 29 | [:ax/var ?o2]]]] 30 | [[:ax/var ?p2] 31 | [:triple.nform/o [[:ax/var ?o1] 32 | [:ax/var ?o2]]]]]]]]] 33 | {:pretty? true}))) 34 | (is (= "?s ?p ?o ." 35 | (f/format-ast 36 | '[:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]] 37 | {:pretty? true}))) 38 | (is (= "( 1 2 ) :p \"w\" ." 39 | (f/format-ast 40 | '[:triple.vec/spo 41 | [[:triple/list [[:ax/literal 1] [:ax/literal 2]]] 42 | [:ax/prefix-iri :p] 43 | [:ax/literal "w"]]] 44 | {:pretty? true}))) 45 | (is (= "( ?x ?y ) ." 46 | (f/format-ast 47 | '[:triple.vec/s 48 | [[:triple/list [[:ax/var ?x] [:ax/var ?y]]]]] 49 | {:pretty? true}))) 50 | (is (= "( ?x ?y ) ." 51 | (f/format-ast 52 | '[:triple.nform/spo 53 | [[[:triple/list [[:ax/var ?x] [:ax/var ?y]]] 54 | [:triple.nform/po-empty []]]]] 55 | {:pretty? true}))) 56 | (is (= "\"v\" :p ( 1 2 ( 3 ) ) ." 57 | (f/format-ast 58 | '[:triple.vec/spo 59 | [[:ax/literal "v"] 60 | [:ax/prefix-iri :p] 61 | [:triple/list [[:ax/literal 1] 62 | [:ax/literal 2] 63 | [:triple/list [[:ax/literal 3]]]]]]] 64 | {:pretty? true}))) 65 | (is (= "\"v\" :p () ." 66 | (f/format-ast 67 | '[:triple.vec/spo 68 | [[:ax/literal "v"] 69 | [:ax/prefix-iri :p] 70 | [:triple/list []]]] 71 | {:pretty? true}))) 72 | (is (= "[ foaf:name ?name ;\n foaf:mbox ] :q \"w\" ." 73 | (f/format-ast 74 | '[:triple.vec/spo 75 | [[:triple/bnodes [[[:ax/prefix-iri :foaf/name] 76 | [:ax/var ?name]] 77 | [[:ax/prefix-iri :foaf/mbox] 78 | [:ax/iri ""]]]] 79 | [:ax/prefix-iri :q] 80 | [:ax/literal "w"]]] 81 | {:pretty? true}))) 82 | (is (= "[ foaf:name ?name ; foaf:mbox ] :q \"w\" ." 83 | (f/format-ast 84 | '[:triple.vec/spo 85 | [[:triple/bnodes [[[:ax/prefix-iri :foaf/name] 86 | [:ax/var ?name]] 87 | [[:ax/prefix-iri :foaf/mbox] 88 | [:ax/iri ""]]]] 89 | [:ax/prefix-iri :q] 90 | [:ax/literal "w"]]] 91 | {:pretty? false}))) 92 | (is (= "[ foaf:name ?name ; foaf:mbox ] ." 93 | (f/format-ast 94 | '[:triple.vec/s 95 | [[:triple/bnodes [[[:ax/prefix-iri :foaf/name] 96 | [:ax/var ?name]] 97 | [[:ax/prefix-iri :foaf/mbox] 98 | [:ax/iri ""]]]]]] 99 | {:pretty? false}))) 100 | (is (= "[ foo:bar [ foaf:mbox ] ] ." 101 | (f/format-ast 102 | '[:triple.vec/s 103 | [[:triple/bnodes [[[:ax/prefix-iri :foo/bar] 104 | [:triple/bnodes 105 | [[[:ax/prefix-iri :foaf/mbox] 106 | [:ax/iri ""]]]]]]]]] 107 | {:pretty? true}))) 108 | (is (= "[ foo:bar [ foaf:mbox ] ] ." 109 | (f/format-ast 110 | '[:triple.vec/s 111 | [[:triple/bnodes [[[:ax/prefix-iri :foo/bar] 112 | [:triple/bnodes 113 | [[[:ax/prefix-iri :foaf/mbox] 114 | [:ax/iri ""]]]]]]]]] 115 | {:pretty? false}))) 116 | (is (= "[] ?p ?o ." 117 | (f/format-ast 118 | '[:triple.vec/spo 119 | [[:triple/bnodes []] 120 | [:ax/var ?p] 121 | [:ax/var ?o]]] 122 | {:pretty? false}))))) 123 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/format/values_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.format.values-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.string :as cstr] 4 | [com.yetanalytics.flint.format :as f] 5 | [com.yetanalytics.flint.format.values])) 6 | 7 | (defn- format-ast [ast] 8 | (f/format-ast ast {:pretty? true})) 9 | 10 | (deftest format-values-test 11 | (testing "Formatting VALUES clauses" 12 | (is (= (cstr/join "\n" ["?foo {" 13 | " 1" 14 | " 2" 15 | " 3" 16 | "}"]) 17 | (format-ast [:values/map [[[:ax/var '?foo]] 18 | [[[:ax/literal 1]] 19 | [[:ax/literal 2]] 20 | [[:ax/literal 3]]]]]))) 21 | (is (= (cstr/join "\n" ["(?foo ?bar) {" 22 | " (1 \"a\")" 23 | " (2 \"b\")" 24 | " (3 \"c\")" 25 | "}"]) 26 | (format-ast [:values/map [[[:ax/var '?foo] [:ax/var '?bar]] 27 | [[[:ax/literal 1] [:ax/literal "a"]] 28 | [[:ax/literal 2] [:ax/literal "b"]] 29 | [[:ax/literal 3] [:ax/literal "c"]]]]]))) 30 | (is (= (cstr/join "\n" ["(?foo ?bar) {" 31 | " (UNDEF \"a\")" 32 | " (2 UNDEF)" 33 | "}"]) 34 | (format-ast [:values/map [[[:ax/var '?foo] [:ax/var '?bar]] 35 | [[[:values/undef nil] [:ax/literal "a"]] 36 | [[:ax/literal 2] [:values/undef nil]]]]]))))) 37 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/spec/modifier_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.modifier-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.spec.alpha :as s] 4 | [com.yetanalytics.flint.spec.modifier :as ms])) 5 | 6 | (deftest conform-modifier-test 7 | (testing "Conforming solution modifiers" 8 | (is (= [[:ax/var '?foo]] 9 | (s/conform ::ms/group-by ['?foo]))) 10 | (is (= '[[:mod/expr-as-var 11 | [:expr/as-var 12 | [[:expr/branch [[:expr/op +] 13 | [:expr/args ([:expr/terminal [:ax/literal 2]] 14 | [:expr/terminal [:ax/literal 2]])]]] 15 | [:ax/var ?foo]]]]] 16 | (s/conform ::ms/group-by ['[(+ 2 2) ?foo]]))) 17 | (is (= '[[:ax/var ?foo]] 18 | (s/conform ::ms/order-by '[?foo]))) 19 | (is (= '[[:mod/asc-desc 20 | [[:mod/op asc] 21 | [:mod/asc-desc-expr [:expr/terminal [:ax/var ?bar]]]]]] 22 | (s/conform ::ms/order-by '[(asc ?bar)]))) 23 | (is (= '[[:ax/var ?foo] 24 | [:mod/asc-desc 25 | [[:mod/op asc] 26 | [:mod/asc-desc-expr [:expr/terminal [:ax/var ?bar]]]]]] 27 | (s/conform ::ms/order-by '[?foo (asc ?bar)]))) 28 | (is (= '[[:expr/terminal [:ax/literal 1]] 29 | [:expr/terminal [:ax/literal 2]] 30 | [:expr/terminal [:ax/literal 3]]] 31 | (s/conform ::ms/having '[1 2 3]))) 32 | (is (= [:ax/numeric 10] 33 | (s/conform ::ms/limit 10))) 34 | (is (= [:ax/numeric 0] 35 | (s/conform ::ms/limit 0))) 36 | (is (s/invalid? 37 | (s/conform ::ms/limit -10))) 38 | (is (= [:ax/numeric 2] 39 | (s/conform ::ms/offset 2))) 40 | (is (= [:ax/numeric 0] 41 | (s/conform ::ms/offset 0))) 42 | (is (= [:ax/numeric -2] 43 | (s/conform ::ms/offset -2))))) 44 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/spec/prologue_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.prologue-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.spec.alpha :as s] 4 | [com.yetanalytics.flint.spec.prologue :as ps])) 5 | 6 | (deftest valid-prologue-test 7 | (testing "Valid base IRIs" 8 | (is (s/valid? ::ps/base "")) 9 | (is (not (s/valid? ::ps/base "http://foo.org")))) 10 | (testing "Valid prefixes" 11 | (is (s/valid? ::ps/prefixes {:$ ""})) 12 | (is (s/valid? ::ps/prefixes {:foo ""})) 13 | (is (not (s/valid? ::ps/prefixes {:foo "http://foo.org"}))) 14 | (is (not (s/valid? ::ps/prefixes {:foo/bar ""}))) 15 | (is (not (s/valid? ::ps/prefixes {:& ""}))))) 16 | 17 | (deftest conform-prologue-test 18 | (testing "Conforming prologues" 19 | (is (= [:ax/iri ""] 20 | (s/conform ::ps/base ""))) 21 | (is (= [[:prologue/prefix [[:ax/prefix :$] [:ax/iri ""]]] 22 | [:prologue/prefix [[:ax/prefix :foo] [:ax/iri ""]]] 23 | [:prologue/prefix [[:ax/prefix :bar] [:ax/iri ""]]]] 24 | (s/conform ::ps/prefixes 25 | {:$ "" 26 | :foo "" 27 | :bar ""}))))) 28 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/spec/query_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.query-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.spec.alpha :as s] 4 | [com.yetanalytics.flint.spec.query :as qs])) 5 | 6 | (def select-query 7 | '{:values {[?z] [[1]]} 8 | :order-by [(asc ?y)] 9 | :where [[?x ?y ?z]] 10 | :from [""] 11 | :select [?x] 12 | :prefixes {:foo ""}}) 13 | 14 | (deftest conform-query-test 15 | (testing "Conforming query" 16 | ;; Ensure that re-ordering works 17 | (is (= '[:query/select 18 | [[:prefixes [[:prologue/prefix [[:ax/prefix :foo] [:ax/iri ""]]]]] 19 | [:select [:select/var-or-exprs [[:ax/var ?x]]]] 20 | [:from [:ax/iri ""]] 21 | [:where [:where-sub/where [[:where/triple 22 | [:triple.vec/spo [[:ax/var ?x] 23 | [:ax/var ?y] 24 | [:ax/var ?z]]]]]]] 25 | [:order-by [[:mod/asc-desc 26 | [[:mod/op asc] 27 | [:mod/asc-desc-expr [:expr/terminal [:ax/var ?y]]]]]]] 28 | [:values [:values/map 29 | [[[:ax/var ?z]] 30 | [[[:ax/literal 1]]]]]]]] 31 | (s/conform qs/query-spec select-query))) 32 | (testing "Cannot conform query with extra keys" 33 | (is (s/invalid? 34 | (s/conform qs/query-spec (assoc select-query :foo ['?bar]))))))) 35 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/spec/select_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.select-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.spec.alpha :as s] 4 | [com.yetanalytics.flint.spec.select :as ss])) 5 | 6 | (deftest conform-select-test 7 | (testing "Conform SELECT clauses" 8 | (is (= [:ax/wildcard '*] 9 | (s/conform ss/select-spec '*))) 10 | (is (= '[:select/var-or-exprs 11 | [[:ax/var ?x] 12 | [:ax/var ?y] 13 | [:select/expr-as-var [:expr/as-var 14 | [[:expr/terminal [:ax/literal 2]] 15 | [:ax/var ?z]]]]]] 16 | (s/conform ss/select-spec '[?x ?y [2 ?z]]))))) 17 | 18 | (deftest invalid-select-test 19 | (testing "Invalid SELECT clauses" 20 | (is (not (s/valid? ss/select-spec '[?x ?x]))) 21 | (is (not (s/valid? ss/select-spec '[?x ?y [2 ?y]]))) 22 | (is (not (s/valid? ss/select-spec '[?x [2 ?y] ?y]))) 23 | (is (not (s/valid? ss/select-spec '[[2 ?y] [3 ?y]]))))) 24 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/spec/update_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.update-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.spec.alpha :as s] 4 | [com.yetanalytics.flint.spec.update :as us])) 5 | 6 | (deftest conform-update-test 7 | (testing "Conforming updates" 8 | (is (= '[[:insert-data [[:triple.vec/spo [[:ax/prefix-iri :foo/x] 9 | [:ax/prefix-iri :dc/title] 10 | [:ax/literal "Title"]]] 11 | [:triple.vec/spo [[:ax/prefix-iri :foo/y] 12 | [:ax/rdf-type :a] 13 | [:ax/literal "MyType"]]]]]] 14 | (s/conform us/insert-data-update-spec 15 | '{:insert-data [[:foo/x :dc/title "Title"] 16 | [:foo/y :a "MyType"]]}))) 17 | (is (= '[[:delete-data [[:triple.quad/gspo 18 | [[:ax/iri ""] 19 | [:triple.quad/spo 20 | [[:triple.vec/spo [[:ax/prefix-iri :foo/x] 21 | [:ax/prefix-iri :dc/title] 22 | [:ax/literal "Title"]]] 23 | [:triple.vec/spo [[:ax/prefix-iri :foo/y] 24 | [:ax/rdf-type :a] 25 | [:ax/literal "MyType"]]]]]]]]]] 26 | (s/conform us/delete-data-update-spec 27 | '{:delete-data [[:graph 28 | "" 29 | [[:foo/x :dc/title "Title"] 30 | [:foo/y :a "MyType"]]]]}))) 31 | (testing "- graph management" 32 | (is (= '[[:load [:ax/iri ""]] 33 | [:into [:update/graph [:graph [:ax/iri ""]]]]] 34 | (s/conform us/load-update-spec 35 | '{:load "" 36 | :into [:graph ""]}))) 37 | (is (= '[[:clear [:update/default :default]]] 38 | (s/conform us/clear-update-spec 39 | '{:clear :default}))) 40 | (is (= '[[:clear [:update/named :named]]] 41 | (s/conform us/clear-update-spec 42 | '{:clear :named}))) 43 | (is (= '[[:clear [:update/all :all]]] 44 | (s/conform us/clear-update-spec 45 | '{:clear :all}))) 46 | (is (= '[[:create-silent 47 | [:update/graph [:graph [:ax/iri ""]]]]] 48 | (s/conform us/create-update-spec 49 | '{:create-silent [:graph ""]}))) 50 | (is (= '[[:drop-silent [:update/all :all]]] 51 | (s/conform us/drop-update-spec 52 | '{:drop-silent :all}))) 53 | (is (= '[[:copy [:update/default :default]] 54 | [:to [:update/graph [:graph [:ax/iri ""]]]]] 55 | (s/conform us/copy-update-spec 56 | '{:copy :default 57 | :to [:graph ""]}))) 58 | (is (= '[[:move [:update/default :default]] 59 | [:to [:update/graph-notag [:ax/iri ""]]]] 60 | (s/conform us/move-update-spec 61 | '{:to "" 62 | :move :default}))) 63 | (is (= '[[:add [:update/default :default]] 64 | [:to [:update/default :default]]] 65 | (s/conform us/add-update-spec 66 | '{:to :default 67 | :add :default})))))) 68 | 69 | (deftest invalid-update-test 70 | (testing "Invalid updates" 71 | (is (not (s/valid? us/insert-data-update-spec 72 | '{:insert-data [[?x ?y ?z]]}))) 73 | (is (not (s/valid? us/insert-data-update-spec 74 | '{:insert-data [{:foo/x {:bar/y #{?z}}}]}))) 75 | (is (not (s/valid? us/insert-data-update-spec 76 | '{:delete-data [[:foo/x :bar/y _1]]}))) 77 | (is (not (s/valid? us/insert-data-update-spec 78 | '{:delete-data [{:foo/x {:bar/y #{_1}}}]}))) 79 | (testing "- naked graph IRIs aren't allowed in some updates" 80 | (is (not (s/valid? us/load-update-spec 81 | '{:load "" 82 | :into ""}))) 83 | (is (not (s/valid? us/clear-update-spec 84 | '{:clear ""}))) 85 | (is (not (s/valid? us/create-update-spec 86 | '{:create ""}))) 87 | (is (not (s/valid? us/drop-update-spec 88 | '{:drop ""})))))) 89 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/spec/values_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.values-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.spec.alpha :as s] 4 | [com.yetanalytics.flint.spec.values :as vs])) 5 | 6 | (deftest conform-values-test 7 | (testing "Conforming VALUES clauses" 8 | (is (= '[:values/map [[[:ax/var ?foo] [:ax/var ?bar]] 9 | [[[:ax/literal 1] [:ax/prefix-iri :x]] 10 | [[:ax/literal 2] [:ax/prefix-iri :y]] 11 | [[:ax/literal 3] [:ax/prefix-iri :z]]]]] 12 | (s/conform ::vs/values '{[?foo ?bar] [[1 :x] [2 :y] [3 :z]]}) 13 | (s/conform ::vs/values '{?foo [1 2 3] 14 | ?bar [:x :y :z]}))) 15 | (is (= '[:values/map [[[:ax/var ?foo] [:ax/var ?bar]] 16 | [[[:values/undef nil] [:ax/prefix-iri :x]] 17 | [[:ax/literal 2] [:values/undef nil]]]]] 18 | (s/conform ::vs/values '{[?foo ?bar] [[nil :x] [2 nil]]}) 19 | (s/conform ::vs/values '{?foo [nil 2] 20 | ?bar [:x nil]}))))) 21 | 22 | (deftest invalid-values-test 23 | (testing "Invalid VALUES clauses" 24 | (is (= {::s/problems [{:path [:values/map :values/sparql-format] 25 | :pred `map? 26 | :val 2 27 | :via [::vs/values] 28 | :in []} 29 | {:path [:values/map :values/clojure-format] 30 | :pred `map? 31 | :val 2 32 | :via [::vs/values] 33 | :in []}] 34 | ::s/spec ::vs/values 35 | ::s/value 2} 36 | (s/explain-data ::vs/values 2))) 37 | (is (= {::s/problems [{:path [:values/map :values/sparql-format] 38 | :pred `(<= 1 (count ~'%) 1) 39 | :val '{?foo [1 2] 40 | ?bar [:x :y :z]} 41 | :via [::vs/values] 42 | :in []} 43 | {:path [:values/map :values/clojure-format] 44 | :pred `vs/matching-val-lengths 45 | :val '{?foo [[:ax/literal 1] 46 | [:ax/literal 2]] 47 | ?bar [[:ax/prefix-iri :x] 48 | [:ax/prefix-iri :y] 49 | [:ax/prefix-iri :z]]} 50 | :via [::vs/values] 51 | :in []}] 52 | ::s/spec ::vs/values 53 | ::s/value '{?foo [1 2] 54 | ?bar [:x :y :z]}} 55 | (s/explain-data ::vs/values '{?foo [1 2] 56 | ?bar [:x :y :z]}))))) 57 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/spec/where_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec.where-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.spec.alpha :as s] 4 | [com.yetanalytics.flint.spec.where :as ws])) 5 | 6 | (deftest conform-where-test 7 | (testing "Conforming WHERE clauses" 8 | (is (= '[:where-sub/select 9 | [[:select [:select/var-or-exprs [[:ax/var ?s]]]] 10 | [:where [:where-sub/where [[:where/triple 11 | [:triple.vec/spo [[:ax/var ?s] 12 | [:ax/var ?p] 13 | [:ax/var ?o]]]]]]]]] 14 | (s/conform ::ws/where '{:where [[?s ?p ?o]] 15 | :select [?s]}))) 16 | (is (= '[:where-sub/select 17 | [[:select [:select/var-or-exprs [[:ax/var ?s]]]] 18 | [:where [:where-sub/where [[:where/triple 19 | [:triple.vec/spo [[:ax/var ?s] 20 | [:ax/var ?p] 21 | [:ax/var ?o]]]]]]] 22 | [:group-by [[:mod/expr-as-var 23 | [:expr/as-var 24 | [[:expr/branch 25 | [[:expr/op +] 26 | [:expr/args ([:expr/terminal [:ax/literal 2]] 27 | [:expr/terminal [:ax/literal 2]])]]] 28 | [:ax/var ?foo]]]]]]]] 29 | (s/conform ::ws/where '{:select [?s] 30 | :where [[?s ?p ?o]] 31 | :group-by [[(+ 2 2) ?foo]]}))) 32 | (is (= '[:where-sub/where 33 | [[:where/special 34 | [:where/union 35 | [[:where-sub/where 36 | [[:where/triple [:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]]] 37 | [:where/triple [:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]]]]] 38 | [:where-sub/where 39 | [[:where/triple [:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]]]]]]]]]] 40 | (s/conform ::ws/where '[[:union [[?s ?p ?o] [?s ?p ?o]] [[?s ?p ?o]]]]))) 41 | (is (= '[:where-sub/where 42 | [[:where/special 43 | [:where/optional 44 | [:where-sub/where 45 | [[:where/triple [:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]]]]]]]]] 46 | (s/conform ::ws/where [[:optional '[[?s ?p ?o]]]]))) 47 | (is (= '[:where-sub/where 48 | [[:where/special 49 | [:where/minus 50 | [:where-sub/where 51 | [[:where/triple [:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]]]]]]]]] 52 | (s/conform ::ws/where [[:minus '[[?s ?p ?o]]]]))) 53 | (is (= '[:where-sub/where 54 | [[:where/special 55 | [:where/graph 56 | [[:ax/prefix-iri :foo/my-graph] 57 | [:where-sub/where 58 | [[:where/triple [:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]]]]]]]]]] 59 | (s/conform ::ws/where [[:graph :foo/my-graph '[[?s ?p ?o]]]]))) 60 | (is (= '[:where-sub/where 61 | [[:where/special 62 | [:where/service 63 | [[:ax/prefix-iri :foo/my-uri] 64 | [:where-sub/where 65 | [[:where/triple [:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]]]]]]]]]] 66 | (s/conform ::ws/where [[:service :foo/my-uri '[[?s ?p ?o]]]]))) 67 | (is (= '[:where-sub/where 68 | [[:where/special 69 | [:where/service-silent 70 | [[:ax/prefix-iri :foo/my-uri] 71 | [:where-sub/where 72 | [[:where/triple [:triple.vec/spo [[:ax/var ?s] [:ax/var ?p] [:ax/var ?o]]]]]]]]]]] 73 | (s/conform ::ws/where [[:service-silent :foo/my-uri '[[?s ?p ?o]]]]))) 74 | (is (= '[:where-sub/where 75 | [[:where/special 76 | [:where/bind 77 | [:expr/as-var 78 | [[:expr/branch 79 | [[:expr/op +] 80 | [:expr/args 81 | ([:expr/terminal [:ax/literal 2]] 82 | [:expr/terminal [:ax/literal 2]])]]] 83 | [:ax/var ?foo]]]]]]] 84 | (s/conform ::ws/where [[:bind '[(+ 2 2) ?foo]]]))) 85 | (is (= '[:where-sub/where 86 | [[:where/special 87 | [:where/filter 88 | [:expr/branch 89 | [[:expr/op =] 90 | [:expr/args 91 | ([:expr/terminal [:ax/literal 2]] 92 | [:expr/terminal [:ax/var ?foo]])]]]]]]] 93 | (s/conform ::ws/where [[:filter '(= 2 ?foo)]]))) 94 | (is (= '[:where-sub/where 95 | [[:where/special 96 | [:where/values 97 | [:values/map [[[:ax/var ?bar] [:ax/var ?qux]] 98 | [[[:ax/literal 1] [:ax/literal 2]]]]]]]]] 99 | (s/conform ::ws/where [[:values '{?bar [1] ?qux [2]}]]))) 100 | ;; This is not a special form since it does not conform to the UNION 101 | ;; spec (or any other special form spec really) 102 | (is (= '[:where-sub/where 103 | [[:where/triple [:triple.vec/spo [[:ax/prefix-iri :union] 104 | [:ax/prefix-iri :foo/bar] 105 | [:ax/prefix-iri :baz/qux]]]]]] 106 | (s/conform ::ws/where '[[:union :foo/bar :baz/qux]]))))) 107 | 108 | (deftest invalid-where-test 109 | (testing "invalid WHERE clauses" 110 | (is (not (s/valid? ::ws/where '[[:optional [[?s ?p]]]]))) 111 | (is (not (s/valid? ::ws/where '[[[[?s ?p]]]]))) 112 | (is (not (s/valid? ::ws/where '[[:where [{:select [?s] 113 | :where [[?s ?p ?o]] 114 | :group-by [[(+ 2 2) ?foo]]} 115 | {:select [?s] 116 | :where [[?s ?p ?o]] 117 | :group-by [[(+ 2 2) ?foo]]}]]]))))) 118 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/spec_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.spec-test 2 | #_{:clj-kondo/ignore #?(:clj [] :cljs [:unused-referred-var])} 3 | (:require [clojure.test :refer [deftest testing is]] 4 | [clojure.spec.alpha :as s] 5 | [com.yetanalytics.flint.spec.query :as qs] 6 | [com.yetanalytics.flint.spec.update :as us] 7 | #?@(:clj [[clojure.edn :as edn] 8 | [clojure.java.io :as io]])) 9 | #?(:cljs (:require-macros 10 | [com.yetanalytics.flint.spec-test :refer [make-tests]]))) 11 | 12 | #?(:clj 13 | (defmacro make-tests [spec dir-name] 14 | (let [files# (->> dir-name io/file file-seq (filter #(.isFile %))) 15 | tests# (map (fn [file#] 16 | (let [fname# (.getName file#) 17 | edn# (edn/read-string (slurp file#))] 18 | `(testing ~fname# 19 | (is (s/valid? ~spec (quote ~edn#)))))) 20 | files#)] 21 | `(testing "file:" ~@tests#)))) 22 | 23 | (deftest query-tests 24 | (make-tests qs/query-spec 25 | "dev-resources/test-fixtures/inputs/query/")) 26 | 27 | (deftest update-tests 28 | (make-tests us/update-spec 29 | "dev-resources/test-fixtures/inputs/update/")) 30 | 31 | (deftest update-request-tests 32 | (make-tests (s/coll-of us/update-spec) 33 | "dev-resources/test-fixtures/inputs/update-seq/")) 34 | -------------------------------------------------------------------------------- /src/test/com/yetanalytics/flint/validate/prefix_test.cljc: -------------------------------------------------------------------------------- 1 | (ns com.yetanalytics.flint.validate.prefix-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [clojure.spec.alpha :as s] 4 | [com.yetanalytics.flint.validate :as v] 5 | [com.yetanalytics.flint.validate.prefix :as vp] 6 | [com.yetanalytics.flint.spec.query :as qs] 7 | [com.yetanalytics.flint.spec.update :as us])) 8 | 9 | (def query '{:prefixes {:foo ""} 10 | :select [?x] 11 | :where [[:foo/bar :a ?y] 12 | [:fee/bar :a ?x] 13 | [:union [[:fii/bar :a ?z]] [[:fum/bar :a ?w]]]]}) 14 | 15 | (deftest validate-prefixes-test 16 | (testing "validate-prefixes function" 17 | (is (nil? (->> (assoc query :where '[[:foo/bar :a ?y]]) 18 | (s/conform qs/query-spec) 19 | v/collect-nodes 20 | (vp/validate-prefixes (:prefixes query))))) 21 | (is (nil? (->> (assoc query :where '[[:bar :a ?y]]) 22 | (s/conform qs/query-spec) 23 | v/collect-nodes 24 | (vp/validate-prefixes (assoc (:prefixes query) 25 | :$ ""))))) 26 | (is (= [{:iri :bar 27 | :prefix :$ 28 | :prefixes {:foo ""} 29 | :path [:query/select :where :where-sub/where :where/triple :triple.vec/spo :ax/prefix-iri]}] 30 | (->> (assoc query :where '[[:bar :a ?y]]) 31 | (s/conform qs/query-spec) 32 | v/collect-nodes 33 | (vp/validate-prefixes (:prefixes query))))) 34 | (is (= [{:iri :bar 35 | :prefix :$ 36 | :prefixes {:foo ""} 37 | :path [:query/select :where :where-sub/where :where/triple :triple.vec/spo :triple/bnodes :ax/prefix-iri]} 38 | {:iri :bee 39 | :prefix :$ 40 | :prefixes {:foo ""} 41 | :path [:query/select :where :where-sub/where :where/triple :triple.vec/spo :triple/bnodes :ax/prefix-iri]}] 42 | (->> (assoc query :where '[[[:bar ?x] :a ?y] 43 | [?z :a [:bee ?w]]]) 44 | (s/conform qs/query-spec) 45 | v/collect-nodes 46 | (vp/validate-prefixes (:prefixes query))))) 47 | (is (= [{:iri :fee/bar 48 | :prefix :fee 49 | :prefixes {:foo ""} 50 | :path [:query/select :where :where-sub/where :where/triple :triple.vec/spo :ax/prefix-iri]} 51 | {:iri :fii/bar 52 | :prefix :fii 53 | :prefixes {:foo ""} 54 | :path [:query/select :where :where-sub/where :where/special :where/union :where-sub/where :where/triple :triple.vec/spo :ax/prefix-iri]} 55 | {:iri :fum/bar 56 | :prefix :fum 57 | :prefixes {:foo ""} 58 | :path [:query/select :where :where-sub/where :where/special :where/union :where-sub/where :where/triple :triple.vec/spo :ax/prefix-iri]}] 59 | (->> query 60 | (s/conform qs/query-spec) 61 | v/collect-nodes 62 | (vp/validate-prefixes (:prefixes query))))) 63 | (is (= [{:iri :baz/Qux 64 | :prefix :baz 65 | :prefixes {:rdf ""} 66 | :path [:update/insert-data :insert-data :triple.quad/gspo :triple.quad/spo :triple.nform/spo :triple.nform/po :triple.nform/o :ax/prefix-iri]} 67 | {:iri :baz/Quu 68 | :prefix :baz 69 | :prefixes {:rdf ""} 70 | :path [:update/insert-data :insert-data :triple.quad/gspo :triple.quad/spo :triple.vec/spo :ax/prefix-iri]}] 71 | (->> {:prefixes {:rdf ""} 72 | :insert-data [[:graph "" 73 | [{"" {:rdf/type #{:baz/Qux}}} 74 | ["" :rdf/type :baz/Quu]]]]} 75 | (s/conform us/update-spec) 76 | v/collect-nodes 77 | (vp/validate-prefixes {:rdf ""})))))) 78 | --------------------------------------------------------------------------------