├── .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 :