@matdata/sparql-formatter 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/dist/ast.d.ts +447 -0
- package/dist/browser.d.ts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2621 -0
- package/dist/errors.d.ts +12 -0
- package/dist/index.cjs +2573 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +2540 -0
- package/dist/lexer.d.ts +47 -0
- package/dist/parser.d.ts +12 -0
- package/dist/printer.d.ts +89 -0
- package/dist/spfmt.min.js +8 -0
- package/dist/writer.d.ts +73 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matdata
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# SPARQL Formatter
|
|
2
|
+
|
|
3
|
+
### A SPARQL 1.2 formatter that keeps your comments where you put them
|
|
4
|
+
|
|
5
|
+
[](https://github.com/Matdata-eu/sparql-formatter/actions/workflows/test.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/@matdata/sparql-formatter)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
`@matdata/sparql-formatter` pretty-prints SPARQL queries and updates. It is the formatter of
|
|
10
|
+
[MatGUI](https://github.com/Matdata-eu/MatGUI) and a drop-in replacement for
|
|
11
|
+
[sparql-formatter](https://github.com/sparqling/sparql-formatter) (`spfmt`), with:
|
|
12
|
+
|
|
13
|
+
- **SPARQL 1.2** — `VERSION`, triple terms `<<( s p o )>>`, reified triples `<< s p o ~ r >>`, reifiers and
|
|
14
|
+
annotations `{| ... |}`, base direction `"x"@ar--rtl`, and the new functions (`LANGDIR`, `STRLANGDIR`,
|
|
15
|
+
`hasLANG`, `hasLANGDIR`, `isTRIPLE`, `TRIPLE`, `SUBJECT`, `PREDICATE`, `OBJECT`). SPARQL 1.0 and 1.1 are
|
|
16
|
+
fully supported too.
|
|
17
|
+
- **Comments that stay in place** — every comment is kept next to the code it belongs to, including comments
|
|
18
|
+
inside empty groups, between predicates, in expressions and at the end of the query
|
|
19
|
+
([sparqling/sparql-formatter#30](https://github.com/sparqling/sparql-formatter/issues/30)).
|
|
20
|
+
- **Safe output** — formatting never changes the meaning of a query and running it twice gives the same
|
|
21
|
+
result. This is tested on the complete W3C SPARQL 1.0, 1.1 and 1.2 test suites, also with comments inserted
|
|
22
|
+
between every pair of tokens.
|
|
23
|
+
- **Zero dependencies**, ESM + CommonJS + a browser bundle, TypeScript types and a CLI.
|
|
24
|
+
|
|
25
|
+
🌐 **Try it**: [playground](https://matdata-eu.github.io/sparql-formatter/)
|
|
26
|
+
|
|
27
|
+
## Example
|
|
28
|
+
|
|
29
|
+
```sparql
|
|
30
|
+
#comment 1
|
|
31
|
+
PREFIX : <http://example.org/>
|
|
32
|
+
select * where { :alice :knows :bob ~:claim1 {| :source ?doc ; :certainty 0.9 |} .
|
|
33
|
+
graph ?g {
|
|
34
|
+
#comment 2
|
|
35
|
+
}
|
|
36
|
+
?city :type :City; # a city
|
|
37
|
+
:country :Belgium ; :population ?pop
|
|
38
|
+
optional { ?city :name ?name } filter(?pop > 10000) } order by desc(?pop) limit 10
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
becomes
|
|
42
|
+
|
|
43
|
+
```sparql
|
|
44
|
+
#comment 1
|
|
45
|
+
PREFIX : <http://example.org/>
|
|
46
|
+
|
|
47
|
+
SELECT *
|
|
48
|
+
WHERE {
|
|
49
|
+
:alice :knows :bob ~ :claim1 {| :source ?doc ; :certainty 0.9 |} .
|
|
50
|
+
GRAPH ?g {
|
|
51
|
+
#comment 2
|
|
52
|
+
}
|
|
53
|
+
?city :type :City ; # a city
|
|
54
|
+
:country :Belgium ;
|
|
55
|
+
:population ?pop .
|
|
56
|
+
OPTIONAL {
|
|
57
|
+
?city :name ?name .
|
|
58
|
+
}
|
|
59
|
+
FILTER (?pop > 10000)
|
|
60
|
+
}
|
|
61
|
+
ORDER BY DESC(?pop)
|
|
62
|
+
LIMIT 10
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install @matdata/sparql-formatter
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
### JavaScript / TypeScript
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { format } from "@matdata/sparql-formatter";
|
|
77
|
+
|
|
78
|
+
const pretty = format("select * where {?s ?p ?o}");
|
|
79
|
+
// SELECT *
|
|
80
|
+
// WHERE {
|
|
81
|
+
// ?s ?p ?o .
|
|
82
|
+
// }
|
|
83
|
+
|
|
84
|
+
format(query, { indent: 4, compact: true });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
CommonJS works too: `const { format } = require("@matdata/sparql-formatter");`
|
|
88
|
+
|
|
89
|
+
`format` throws a `SparqlSyntaxError` (with `line`, `column` and `offset`) when the input is not valid SPARQL:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
import { format, isValid, SparqlSyntaxError } from "@matdata/sparql-formatter";
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
format("SELECT * WHERE { ?s ?p }");
|
|
96
|
+
} catch (e) {
|
|
97
|
+
if (e instanceof SparqlSyntaxError) console.log(e.line, e.column, e.message);
|
|
98
|
+
// 1 24 "Expected an RDF term or a variable but found '}' (line 1, column 24)"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
isValid("SELECT * {}"); // true
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Drop-in replacement for `sparql-formatter`
|
|
105
|
+
|
|
106
|
+
The package exports an `spfmt` object with the same `format(query, formattingMode, indentDepth)` signature as
|
|
107
|
+
[sparql-formatter](https://github.com/sparqling/sparql-formatter), so switching is a one-line change:
|
|
108
|
+
|
|
109
|
+
```diff
|
|
110
|
+
- import { spfmt } from "sparql-formatter";
|
|
111
|
+
+ import { spfmt } from "@matdata/sparql-formatter";
|
|
112
|
+
|
|
113
|
+
spfmt.format(query); // formattingMode "default"
|
|
114
|
+
spfmt.format(query, "compact");
|
|
115
|
+
spfmt.format(query, "default", 4);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The `turtle` and `jsonld` modes of sparql-formatter are not supported; use `parse()` to get the syntax tree.
|
|
119
|
+
|
|
120
|
+
### In the browser
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<script src="https://cdn.jsdelivr.net/npm/@matdata/sparql-formatter/dist/spfmt.min.js"></script>
|
|
124
|
+
<script>
|
|
125
|
+
spfmt.format("select * where {?s ?p ?o}");
|
|
126
|
+
sparqlFormatter.format("select * where {?s ?p ?o}", { indent: 4 }); // full API
|
|
127
|
+
</script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Or as an ES module: `import { format } from "https://cdn.jsdelivr.net/npm/@matdata/sparql-formatter/+esm";`
|
|
131
|
+
|
|
132
|
+
### Command line
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx @matdata/sparql-formatter query.rq # print the formatted query
|
|
136
|
+
cat query.rq | npx @matdata/sparql-formatter # read from stdin
|
|
137
|
+
npx @matdata/sparql-formatter --write *.rq *.ru # format files in place
|
|
138
|
+
npx @matdata/sparql-formatter --check *.rq # exit code 1 if a file isn't formatted (for CI)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Run `sparql-formatter --help` for all options (`--indent`, `--keyword-case`, `--function-case`, `--no-align`,
|
|
142
|
+
`--no-blank-lines`, `--compact`, `--line-width`).
|
|
143
|
+
|
|
144
|
+
## Options
|
|
145
|
+
|
|
146
|
+
| Option | Default | Description |
|
|
147
|
+
| -------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
|
|
148
|
+
| `indent` | `2` | Number of spaces, or the indent string itself (e.g. `"\t"`). |
|
|
149
|
+
| `keywordCase` | `"upper"` | Case of keywords (`SELECT`, `WHERE`, `OPTIONAL`, ...): `"upper"`, `"lower"` or `"preserve"`. |
|
|
150
|
+
| `functionCase` | `"preserve"` | Case of built-in function names (`regex`, `STR`, `COUNT`, ...): `"upper"`, `"lower"`, `"preserve"`. |
|
|
151
|
+
| `alignPredicates` | `true` | Align the predicates of a subject under its first predicate. When `false`, indent them instead. |
|
|
152
|
+
| `preserveBlankLines` | `true` | Keep (at most one) blank line where the query has blank lines between statements. |
|
|
153
|
+
| `compact` | `false` | Put group patterns with a single triple on one line: `OPTIONAL { ?s :p ?o }`. |
|
|
154
|
+
| `lineWidth` | `100` | Line width used by `compact` and to wrap long object and `VALUES` lists. |
|
|
155
|
+
| `insertWhere` | `true` | Write the optional `WHERE` keyword of `SELECT`, `CONSTRUCT` and `DESCRIBE` queries. |
|
|
156
|
+
|
|
157
|
+
## Formatting style
|
|
158
|
+
|
|
159
|
+
- Keywords are upper case; prefixed names, IRIs, variables and literals are written exactly as in the input.
|
|
160
|
+
- The prologue (`BASE`, `PREFIX`, `VERSION`) is followed by a blank line.
|
|
161
|
+
- Each clause (`SELECT`, `FROM`, `WHERE`, `GROUP BY`, `HAVING`, `ORDER BY`, `LIMIT`, `OFFSET`, `VALUES`) starts
|
|
162
|
+
on a new line; every pattern in a group gets its own line.
|
|
163
|
+
- Triple patterns end with ` .`, predicate lists with ` ;` are aligned, and objects are separated by `, `.
|
|
164
|
+
- The optional `.` after `OPTIONAL { }`, `FILTER`, ... and superfluous `;` are removed; their comments are kept.
|
|
165
|
+
- Long object lists and `VALUES` lists are wrapped, one item per line.
|
|
166
|
+
|
|
167
|
+
### Comments
|
|
168
|
+
|
|
169
|
+
Comments are attached to the code around them:
|
|
170
|
+
|
|
171
|
+
- a comment at the end of a line stays at the end of that line;
|
|
172
|
+
- a comment on its own line stays on its own line, before the code that follows it, at that code's indentation;
|
|
173
|
+
- comments before a closing `}` stay inside the block;
|
|
174
|
+
- blank lines before comments are kept.
|
|
175
|
+
|
|
176
|
+
If a comment ends up in the middle of an expression, the formatter continues the expression on the next line
|
|
177
|
+
rather than moving the comment. The formatter never drops a comment: if it can't place one, it throws an error
|
|
178
|
+
instead of returning a query without it.
|
|
179
|
+
|
|
180
|
+
## What is checked
|
|
181
|
+
|
|
182
|
+
The parser implements the complete [SPARQL 1.2 grammar](https://www.w3.org/TR/sparql12-query/#sparqlGrammar)
|
|
183
|
+
for queries and updates, including the grammar notes (no variables in `INSERT DATA`, no blank nodes in
|
|
184
|
+
`DELETE`, the number of values in `VALUES` rows, no reifiers after property paths, ...). It does not check
|
|
185
|
+
rules that go beyond the grammar, such as variable scoping in `SELECT` and `BIND`, grouping rules of
|
|
186
|
+
aggregates, or blank node label reuse across basic graph patterns: formatting such a query works.
|
|
187
|
+
|
|
188
|
+
## API
|
|
189
|
+
|
|
190
|
+
| Export | Description |
|
|
191
|
+
| ---------------------------- | ---------------------------------------------------------------------- |
|
|
192
|
+
| `format(query, options?)` | Format a query or update. Also the default export. |
|
|
193
|
+
| `isValid(query)` | `true` when the query is syntactically valid SPARQL 1.2. |
|
|
194
|
+
| `parse(query)` | Parse into a concrete syntax tree (`Query` or `Update`). |
|
|
195
|
+
| `formatAst(ast, options?)` | Format a tree returned by `parse`. |
|
|
196
|
+
| `tokenize(query)` | The tokens of a query, with the comments attached to them. |
|
|
197
|
+
| `spfmt` | `{ format, parse, tokenize }`, compatible with `sparql-formatter`. |
|
|
198
|
+
| `SparqlSyntaxError` | Error thrown for invalid input (`line`, `column`, `offset`, `expected`). |
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
npm install
|
|
204
|
+
npm test # unit tests + W3C test suites (Node 22+, runs the TypeScript sources directly)
|
|
205
|
+
npm run typecheck
|
|
206
|
+
npm run lint # prettier --check
|
|
207
|
+
npm run build # dist/: ESM, CommonJS, browser bundle, CLI and type declarations
|
|
208
|
+
npm run test:dist # smoke test of the built package
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The source is in `src/`: `lexer.ts` (tokens and comments), `parser.ts` (recursive-descent parser),
|
|
212
|
+
`printer.ts` (layout) and `writer.ts` (output buffer that places the comments).
|
|
213
|
+
|
|
214
|
+
The W3C test queries in `test/w3c` are imported from [w3c/rdf-tests](https://github.com/w3c/rdf-tests) with:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
git clone --depth 1 https://github.com/w3c/rdf-tests
|
|
218
|
+
node scripts/import-w3c-tests.mjs rdf-tests
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Releasing
|
|
222
|
+
|
|
223
|
+
Releases are published to npm by the [publish workflow](.github/workflows/publish.yml) when a GitHub release is
|
|
224
|
+
published. The version is taken from the release tag (`v1.2.3` → `1.2.3`; a pre-release such as `v1.3.0-beta.1`
|
|
225
|
+
is published with the `next` dist-tag).
|
|
226
|
+
|
|
227
|
+
The workflow uses [npm trusted publishing](https://docs.npmjs.com/trusted-publishers): there is no npm token in
|
|
228
|
+
the repository. One-time setup on [npmjs.com](https://www.npmjs.com/package/@matdata/sparql-formatter) →
|
|
229
|
+
**Settings** → **Trusted Publisher** → **GitHub Actions**:
|
|
230
|
+
|
|
231
|
+
- Organization or user: `Matdata-eu`
|
|
232
|
+
- Repository: `sparql-formatter`
|
|
233
|
+
- Workflow filename: `publish.yml`
|
|
234
|
+
|
|
235
|
+
npm only lets you add a trusted publisher to a package that already exists, so the very first version has to be
|
|
236
|
+
published once by hand (`npm run build && npm publish --access public`) by a member of the `@matdata` npm
|
|
237
|
+
organization.
|
|
238
|
+
|
|
239
|
+
The [playground](https://matdata-eu.github.io/sparql-formatter/) is deployed to GitHub Pages on every push to
|
|
240
|
+
`main` (repository **Settings** → **Pages** → Source: **GitHub Actions**).
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
[MIT](LICENSE). The test queries in `test/w3c` come from the W3C test suites and are distributed under the
|
|
245
|
+
[W3C test suite licenses](https://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html).
|
package/dist/ast.d.ts
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concrete syntax tree for SPARQL 1.2 queries and updates.
|
|
3
|
+
*
|
|
4
|
+
* The tree keeps a reference to every source token (keywords, punctuation,
|
|
5
|
+
* terms) so the printer can re-emit the comments attached to them and keep
|
|
6
|
+
* the original spelling of terms.
|
|
7
|
+
*/
|
|
8
|
+
import type { Token } from "./lexer.js";
|
|
9
|
+
/**
|
|
10
|
+
* A term printed as the concatenation of its tokens: an IRI, a variable, a
|
|
11
|
+
* literal (`"x"@en`, `"1"^^xsd:int`), a signed number (`-1`), a blank node,
|
|
12
|
+
* `a`, `true`, `NIL` (`()`), `ANON` (`[]`), `UNDEF`.
|
|
13
|
+
*/
|
|
14
|
+
export interface Term {
|
|
15
|
+
type: "Term";
|
|
16
|
+
tokens: Token[];
|
|
17
|
+
}
|
|
18
|
+
/** `<<( s p o )>>` */
|
|
19
|
+
export interface TripleTerm {
|
|
20
|
+
type: "TripleTerm";
|
|
21
|
+
open: Token;
|
|
22
|
+
subject: Node;
|
|
23
|
+
predicate: Term;
|
|
24
|
+
object: Node;
|
|
25
|
+
close: Token;
|
|
26
|
+
}
|
|
27
|
+
/** `<< s p o ~ r >>` */
|
|
28
|
+
export interface ReifiedTriple {
|
|
29
|
+
type: "ReifiedTriple";
|
|
30
|
+
open: Token;
|
|
31
|
+
subject: Node;
|
|
32
|
+
predicate: Term;
|
|
33
|
+
object: Node;
|
|
34
|
+
reifier: Reifier | null;
|
|
35
|
+
close: Token;
|
|
36
|
+
}
|
|
37
|
+
/** `~ id` */
|
|
38
|
+
export interface Reifier {
|
|
39
|
+
type: "Reifier";
|
|
40
|
+
tilde: Token;
|
|
41
|
+
id: Term | null;
|
|
42
|
+
}
|
|
43
|
+
/** `{| p o ; ... |}` */
|
|
44
|
+
export interface AnnotationBlock {
|
|
45
|
+
type: "AnnotationBlock";
|
|
46
|
+
open: Token;
|
|
47
|
+
properties: PropertyList;
|
|
48
|
+
close: Token;
|
|
49
|
+
}
|
|
50
|
+
/** `( a b c )` */
|
|
51
|
+
export interface Collection {
|
|
52
|
+
type: "Collection";
|
|
53
|
+
open: Token;
|
|
54
|
+
items: Node[];
|
|
55
|
+
close: Token;
|
|
56
|
+
}
|
|
57
|
+
/** `[ p o ; ... ]` */
|
|
58
|
+
export interface BlankNodePropertyList {
|
|
59
|
+
type: "BlankNodePropertyList";
|
|
60
|
+
open: Token;
|
|
61
|
+
properties: PropertyList;
|
|
62
|
+
close: Token;
|
|
63
|
+
}
|
|
64
|
+
export interface ObjectEntry {
|
|
65
|
+
node: Node;
|
|
66
|
+
annotations: (Reifier | AnnotationBlock)[];
|
|
67
|
+
/** The `,` that follows this object, if any. */
|
|
68
|
+
comma: Token | null;
|
|
69
|
+
}
|
|
70
|
+
export interface PropertyEntry {
|
|
71
|
+
verb: Node;
|
|
72
|
+
objects: ObjectEntry[];
|
|
73
|
+
/** The `;` tokens that follow this entry (`; ;` is legal). */
|
|
74
|
+
semicolons: Token[];
|
|
75
|
+
}
|
|
76
|
+
export interface PropertyList {
|
|
77
|
+
type: "PropertyList";
|
|
78
|
+
/** `;` tokens that appear before the first entry are not legal; entries only. */
|
|
79
|
+
entries: PropertyEntry[];
|
|
80
|
+
}
|
|
81
|
+
/** A subject with its property list: `?s :p ?o ; :q ?r` */
|
|
82
|
+
export interface Triples {
|
|
83
|
+
type: "Triples";
|
|
84
|
+
subject: Node;
|
|
85
|
+
properties: PropertyList | null;
|
|
86
|
+
}
|
|
87
|
+
export interface PathAlternative {
|
|
88
|
+
type: "PathAlternative";
|
|
89
|
+
items: Node[];
|
|
90
|
+
separators: Token[];
|
|
91
|
+
}
|
|
92
|
+
export interface PathSequence {
|
|
93
|
+
type: "PathSequence";
|
|
94
|
+
items: Node[];
|
|
95
|
+
separators: Token[];
|
|
96
|
+
}
|
|
97
|
+
export interface PathInverse {
|
|
98
|
+
type: "PathInverse";
|
|
99
|
+
caret: Token;
|
|
100
|
+
item: Node;
|
|
101
|
+
}
|
|
102
|
+
export interface PathModified {
|
|
103
|
+
type: "PathModified";
|
|
104
|
+
item: Node;
|
|
105
|
+
modifier: Token;
|
|
106
|
+
}
|
|
107
|
+
export interface PathNegated {
|
|
108
|
+
type: "PathNegated";
|
|
109
|
+
bang: Token;
|
|
110
|
+
item: Node;
|
|
111
|
+
}
|
|
112
|
+
/** `( path )` in a property path, also `!( a | ^b )` */
|
|
113
|
+
export interface PathGroup {
|
|
114
|
+
type: "PathGroup";
|
|
115
|
+
open: Token;
|
|
116
|
+
path: Node | null;
|
|
117
|
+
close: Token;
|
|
118
|
+
}
|
|
119
|
+
export interface Binary {
|
|
120
|
+
type: "Binary";
|
|
121
|
+
left: Node;
|
|
122
|
+
operator: Token;
|
|
123
|
+
right: Node;
|
|
124
|
+
}
|
|
125
|
+
/** `x IN (...)` / `x NOT IN (...)` */
|
|
126
|
+
export interface InExpression {
|
|
127
|
+
type: "In";
|
|
128
|
+
expression: Node;
|
|
129
|
+
not: Token | null;
|
|
130
|
+
in: Token;
|
|
131
|
+
list: ExpressionList;
|
|
132
|
+
}
|
|
133
|
+
export interface Unary {
|
|
134
|
+
type: "Unary";
|
|
135
|
+
operator: Token;
|
|
136
|
+
expression: Node;
|
|
137
|
+
}
|
|
138
|
+
export interface Bracketed {
|
|
139
|
+
type: "Bracketed";
|
|
140
|
+
open: Token;
|
|
141
|
+
expression: Node;
|
|
142
|
+
close: Token;
|
|
143
|
+
}
|
|
144
|
+
/** `( e1, e2 )` or `()` */
|
|
145
|
+
export interface ExpressionList {
|
|
146
|
+
type: "ExpressionList";
|
|
147
|
+
open: Token;
|
|
148
|
+
items: Node[];
|
|
149
|
+
commas: Token[];
|
|
150
|
+
close: Token;
|
|
151
|
+
}
|
|
152
|
+
/** A built-in call, aggregate or function call. */
|
|
153
|
+
export interface Call {
|
|
154
|
+
type: "Call";
|
|
155
|
+
/** A NAME token for built-ins, a Term for IRI function calls. */
|
|
156
|
+
name: Token | Term;
|
|
157
|
+
builtin: boolean;
|
|
158
|
+
/** The opening parenthesis; a call written as `NOW()` has no args. */
|
|
159
|
+
open: Token;
|
|
160
|
+
distinct: Token | null;
|
|
161
|
+
args: Node[];
|
|
162
|
+
commas: Token[];
|
|
163
|
+
/** GROUP_CONCAT separator: `; SEPARATOR = "x"` */
|
|
164
|
+
separator: {
|
|
165
|
+
semicolon: Token;
|
|
166
|
+
keyword: Token;
|
|
167
|
+
equals: Token;
|
|
168
|
+
value: Term;
|
|
169
|
+
} | null;
|
|
170
|
+
close: Token;
|
|
171
|
+
}
|
|
172
|
+
/** `EXISTS { }` / `NOT EXISTS { }` */
|
|
173
|
+
export interface Exists {
|
|
174
|
+
type: "Exists";
|
|
175
|
+
not: Token | null;
|
|
176
|
+
keyword: Token;
|
|
177
|
+
group: GroupGraphPattern;
|
|
178
|
+
}
|
|
179
|
+
export interface GroupGraphPattern {
|
|
180
|
+
type: "GroupGraphPattern";
|
|
181
|
+
open: Token;
|
|
182
|
+
subSelect: SubSelect | null;
|
|
183
|
+
items: PatternItem[];
|
|
184
|
+
close: Token;
|
|
185
|
+
}
|
|
186
|
+
export interface PatternItem {
|
|
187
|
+
node: Node;
|
|
188
|
+
/** The `.` that follows the item, if any. */
|
|
189
|
+
dot: Token | null;
|
|
190
|
+
}
|
|
191
|
+
export interface Union {
|
|
192
|
+
type: "Union";
|
|
193
|
+
groups: GroupGraphPattern[];
|
|
194
|
+
keywords: Token[];
|
|
195
|
+
}
|
|
196
|
+
export interface Optional {
|
|
197
|
+
type: "Optional";
|
|
198
|
+
keyword: Token;
|
|
199
|
+
group: GroupGraphPattern;
|
|
200
|
+
}
|
|
201
|
+
export interface Minus {
|
|
202
|
+
type: "Minus";
|
|
203
|
+
keyword: Token;
|
|
204
|
+
group: GroupGraphPattern;
|
|
205
|
+
}
|
|
206
|
+
export interface GraphPattern {
|
|
207
|
+
type: "Graph";
|
|
208
|
+
keyword: Token;
|
|
209
|
+
name: Term;
|
|
210
|
+
group: GroupGraphPattern;
|
|
211
|
+
}
|
|
212
|
+
export interface Service {
|
|
213
|
+
type: "Service";
|
|
214
|
+
keyword: Token;
|
|
215
|
+
silent: Token | null;
|
|
216
|
+
name: Term;
|
|
217
|
+
group: GroupGraphPattern;
|
|
218
|
+
}
|
|
219
|
+
export interface Filter {
|
|
220
|
+
type: "Filter";
|
|
221
|
+
keyword: Token;
|
|
222
|
+
constraint: Node;
|
|
223
|
+
}
|
|
224
|
+
export interface Bind {
|
|
225
|
+
type: "Bind";
|
|
226
|
+
keyword: Token;
|
|
227
|
+
open: Token;
|
|
228
|
+
expression: Node;
|
|
229
|
+
as: Token;
|
|
230
|
+
variable: Term;
|
|
231
|
+
close: Token;
|
|
232
|
+
}
|
|
233
|
+
export interface Values {
|
|
234
|
+
type: "Values";
|
|
235
|
+
keyword: Token;
|
|
236
|
+
block: DataBlock;
|
|
237
|
+
}
|
|
238
|
+
export interface DataBlockRow {
|
|
239
|
+
/** `( v1 v2 )`; for NIL rows `open`/`close` are the `(` `)` tokens and values is empty. */
|
|
240
|
+
open: Token;
|
|
241
|
+
values: Node[];
|
|
242
|
+
close: Token;
|
|
243
|
+
}
|
|
244
|
+
export interface DataBlock {
|
|
245
|
+
type: "DataBlock";
|
|
246
|
+
/** Single variable form: `?x { ... }` */
|
|
247
|
+
variable: Term | null;
|
|
248
|
+
/** Multi-variable form: `( ?a ?b ) { ... }` */
|
|
249
|
+
variables: {
|
|
250
|
+
open: Token;
|
|
251
|
+
vars: Term[];
|
|
252
|
+
close: Token;
|
|
253
|
+
} | null;
|
|
254
|
+
open: Token;
|
|
255
|
+
/** Values of the single-variable form. */
|
|
256
|
+
values: Node[];
|
|
257
|
+
/** Rows of the multi-variable form. */
|
|
258
|
+
rows: DataBlockRow[];
|
|
259
|
+
close: Token;
|
|
260
|
+
}
|
|
261
|
+
export interface PrologueDecl {
|
|
262
|
+
type: "Base" | "Prefix" | "Version";
|
|
263
|
+
keyword: Token;
|
|
264
|
+
/** PREFIX only */
|
|
265
|
+
prefix: Token | null;
|
|
266
|
+
value: Token;
|
|
267
|
+
}
|
|
268
|
+
export interface Projection {
|
|
269
|
+
type: "Projection";
|
|
270
|
+
open: Token;
|
|
271
|
+
expression: Node;
|
|
272
|
+
as: Token | null;
|
|
273
|
+
variable: Term | null;
|
|
274
|
+
close: Token;
|
|
275
|
+
}
|
|
276
|
+
export interface SelectClause {
|
|
277
|
+
type: "SelectClause";
|
|
278
|
+
keyword: Token;
|
|
279
|
+
modifier: Token | null;
|
|
280
|
+
star: Token | null;
|
|
281
|
+
items: (Term | Projection)[];
|
|
282
|
+
}
|
|
283
|
+
export interface DatasetClause {
|
|
284
|
+
type: "DatasetClause";
|
|
285
|
+
from: Token;
|
|
286
|
+
named: Token | null;
|
|
287
|
+
iri: Term;
|
|
288
|
+
}
|
|
289
|
+
export interface WhereClause {
|
|
290
|
+
keyword: Token | null;
|
|
291
|
+
group: GroupGraphPattern;
|
|
292
|
+
}
|
|
293
|
+
export interface OrderCondition {
|
|
294
|
+
type: "OrderCondition";
|
|
295
|
+
direction: Token | null;
|
|
296
|
+
expression: Node;
|
|
297
|
+
}
|
|
298
|
+
export interface SolutionModifier {
|
|
299
|
+
groupBy: {
|
|
300
|
+
group: Token;
|
|
301
|
+
by: Token;
|
|
302
|
+
conditions: Node[];
|
|
303
|
+
} | null;
|
|
304
|
+
having: {
|
|
305
|
+
keyword: Token;
|
|
306
|
+
conditions: Node[];
|
|
307
|
+
} | null;
|
|
308
|
+
orderBy: {
|
|
309
|
+
order: Token;
|
|
310
|
+
by: Token;
|
|
311
|
+
conditions: Node[];
|
|
312
|
+
} | null;
|
|
313
|
+
/** LIMIT and OFFSET in source order */
|
|
314
|
+
limitOffset: {
|
|
315
|
+
keyword: Token;
|
|
316
|
+
value: Token;
|
|
317
|
+
}[];
|
|
318
|
+
}
|
|
319
|
+
export interface SelectQuery {
|
|
320
|
+
type: "Select";
|
|
321
|
+
select: SelectClause;
|
|
322
|
+
datasets: DatasetClause[];
|
|
323
|
+
where: WhereClause;
|
|
324
|
+
modifiers: SolutionModifier;
|
|
325
|
+
}
|
|
326
|
+
export interface SubSelect extends Omit<SelectQuery, "type"> {
|
|
327
|
+
type: "SubSelect";
|
|
328
|
+
values: Values | null;
|
|
329
|
+
}
|
|
330
|
+
/** Triples inside `{ }` of a CONSTRUCT template or quad data */
|
|
331
|
+
export interface TriplesTemplate {
|
|
332
|
+
type: "TriplesTemplate";
|
|
333
|
+
open: Token;
|
|
334
|
+
items: PatternItem[];
|
|
335
|
+
close: Token;
|
|
336
|
+
}
|
|
337
|
+
export interface ConstructQuery {
|
|
338
|
+
type: "Construct";
|
|
339
|
+
keyword: Token;
|
|
340
|
+
template: TriplesTemplate | null;
|
|
341
|
+
datasets: DatasetClause[];
|
|
342
|
+
/** Full form: WHERE clause. Short form (`CONSTRUCT WHERE { triples }`): `where.keyword` + `shortTemplate` */
|
|
343
|
+
where: WhereClause | null;
|
|
344
|
+
shortForm: {
|
|
345
|
+
keyword: Token;
|
|
346
|
+
template: TriplesTemplate;
|
|
347
|
+
} | null;
|
|
348
|
+
modifiers: SolutionModifier;
|
|
349
|
+
}
|
|
350
|
+
export interface DescribeQuery {
|
|
351
|
+
type: "Describe";
|
|
352
|
+
keyword: Token;
|
|
353
|
+
star: Token | null;
|
|
354
|
+
items: Term[];
|
|
355
|
+
datasets: DatasetClause[];
|
|
356
|
+
where: WhereClause | null;
|
|
357
|
+
modifiers: SolutionModifier;
|
|
358
|
+
}
|
|
359
|
+
export interface AskQuery {
|
|
360
|
+
type: "Ask";
|
|
361
|
+
keyword: Token;
|
|
362
|
+
datasets: DatasetClause[];
|
|
363
|
+
where: WhereClause;
|
|
364
|
+
modifiers: SolutionModifier;
|
|
365
|
+
}
|
|
366
|
+
export type QueryForm = SelectQuery | ConstructQuery | DescribeQuery | AskQuery;
|
|
367
|
+
export interface Query {
|
|
368
|
+
type: "Query";
|
|
369
|
+
prologue: PrologueDecl[];
|
|
370
|
+
form: QueryForm;
|
|
371
|
+
values: Values | null;
|
|
372
|
+
eof: Token;
|
|
373
|
+
}
|
|
374
|
+
/** Keyword/IRI sequences such as `GRAPH <g>`, `DEFAULT`, `SILENT` */
|
|
375
|
+
export interface Words {
|
|
376
|
+
type: "Words";
|
|
377
|
+
tokens: (Token | Term)[];
|
|
378
|
+
}
|
|
379
|
+
export interface QuadsGraph {
|
|
380
|
+
type: "QuadsGraph";
|
|
381
|
+
keyword: Token;
|
|
382
|
+
name: Term;
|
|
383
|
+
template: TriplesTemplate;
|
|
384
|
+
}
|
|
385
|
+
export interface Load {
|
|
386
|
+
type: "Load";
|
|
387
|
+
keyword: Token;
|
|
388
|
+
silent: Token | null;
|
|
389
|
+
iri: Term;
|
|
390
|
+
into: Words | null;
|
|
391
|
+
}
|
|
392
|
+
/** CLEAR, DROP, CREATE */
|
|
393
|
+
export interface GraphManagement {
|
|
394
|
+
type: "GraphManagement";
|
|
395
|
+
keyword: Token;
|
|
396
|
+
silent: Token | null;
|
|
397
|
+
target: Words;
|
|
398
|
+
}
|
|
399
|
+
/** ADD, MOVE, COPY */
|
|
400
|
+
export interface GraphTransfer {
|
|
401
|
+
type: "GraphTransfer";
|
|
402
|
+
keyword: Token;
|
|
403
|
+
silent: Token | null;
|
|
404
|
+
from: Words;
|
|
405
|
+
to: Token;
|
|
406
|
+
target: Words;
|
|
407
|
+
}
|
|
408
|
+
/** INSERT DATA, DELETE DATA, DELETE WHERE */
|
|
409
|
+
export interface QuadDataOperation {
|
|
410
|
+
type: "QuadData";
|
|
411
|
+
keywords: [Token, Token];
|
|
412
|
+
quads: TriplesTemplate;
|
|
413
|
+
}
|
|
414
|
+
export interface Modify {
|
|
415
|
+
type: "Modify";
|
|
416
|
+
with: {
|
|
417
|
+
keyword: Token;
|
|
418
|
+
iri: Term;
|
|
419
|
+
} | null;
|
|
420
|
+
delete: {
|
|
421
|
+
keyword: Token;
|
|
422
|
+
quads: TriplesTemplate;
|
|
423
|
+
} | null;
|
|
424
|
+
insert: {
|
|
425
|
+
keyword: Token;
|
|
426
|
+
quads: TriplesTemplate;
|
|
427
|
+
} | null;
|
|
428
|
+
using: {
|
|
429
|
+
keyword: Token;
|
|
430
|
+
named: Token | null;
|
|
431
|
+
iri: Term;
|
|
432
|
+
}[];
|
|
433
|
+
where: WhereClause;
|
|
434
|
+
}
|
|
435
|
+
export type UpdateOperation = Load | GraphManagement | GraphTransfer | QuadDataOperation | Modify;
|
|
436
|
+
export interface UpdatePart {
|
|
437
|
+
prologue: PrologueDecl[];
|
|
438
|
+
operation: UpdateOperation | null;
|
|
439
|
+
semicolon: Token | null;
|
|
440
|
+
}
|
|
441
|
+
export interface Update {
|
|
442
|
+
type: "Update";
|
|
443
|
+
parts: UpdatePart[];
|
|
444
|
+
eof: Token;
|
|
445
|
+
}
|
|
446
|
+
export type Node = Term | TripleTerm | ReifiedTriple | Reifier | AnnotationBlock | Collection | BlankNodePropertyList | PropertyList | Triples | PathAlternative | PathSequence | PathInverse | PathModified | PathNegated | PathGroup | Binary | InExpression | Unary | Bracketed | ExpressionList | Call | Exists | GroupGraphPattern | Union | Optional | Minus | GraphPattern | Service | Filter | Bind | Values | DataBlock | Projection | OrderCondition | SubSelect | TriplesTemplate | QuadsGraph;
|
|
447
|
+
export type Ast = Query | Update;
|