Skip to content

Commit 0bb8f60

Browse files
committed
my generator no like me
1 parent 21cd49c commit 0bb8f60

10 files changed

Lines changed: 258 additions & 36 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# TRAQULA SPARQL 1.1 generator
2+
3+
TRAQULA Generator Sparql 1.1 is a [SPARQL 1.1](https://www.w3.org/TR/sparql11-query/#grammar) query generator for TypeScript.
4+
It can generate SPARQL given the AST created by [TRAQULA parser SPARQL 1-1](https://github.com/comunica/traqula/tree/main/engines/parser-sparql-1-1).
5+
6+
## Installation
7+
8+
```bash
9+
npm install @traqula/generator-sparql-1-1
10+
```
11+
12+
or
13+
14+
```bash
15+
yarn add @traqula/generator-sparql-1-1
16+
```
17+
18+
## Import
19+
20+
Either through ESM import:
21+
22+
```javascript
23+
import {Parser} from 'engines/generator-sparql-1-1';
24+
```
25+
26+
_or_ CJS require:
27+
28+
```javascript
29+
const Parser = require('engines/generator-sparql-1-1').Parser;
30+
```
31+
32+
## Usage
33+
34+
This package contains a `Generator` that is able to generate SPARQL 1.1 queries:
35+
36+
```typescript
37+
const generator = new Generator();
38+
const abstractSyntaxTree = generator.generate(abstractSyntaxTree);
39+
```
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { GeneratorBuilder } from '@traqula/core';
2+
import { gram } from '@traqula/rules-sparql-1-1';
3+
import type * as T11 from '@traqula/rules-sparql-1-1';
4+
5+
const sparql11GeneratorBuilder = GeneratorBuilder.createBuilder(<const> [
6+
gram.query,
7+
gram.selectQuery,
8+
gram.constructQuery,
9+
gram.describeQuery,
10+
gram.askQuery,
11+
gram.selectClause,
12+
])
13+
.addMany(
14+
gram.update,
15+
gram.update1,
16+
gram.load,
17+
gram.clear,
18+
gram.drop,
19+
gram.create,
20+
gram.copy,
21+
gram.move,
22+
gram.add,
23+
gram.insertData,
24+
gram.deleteData,
25+
gram.deleteWhere,
26+
gram.modify,
27+
gram.graphRef,
28+
gram.graphRefAll,
29+
gram.quads,
30+
gram.quadsNotTriples,
31+
)
32+
.addRule(gram.aggregate)
33+
.addRule(gram.datasetClauseStar)
34+
.addMany(
35+
gram.argList,
36+
gram.expression,
37+
gram.iriOrFunction,
38+
)
39+
.addMany(
40+
gram.prologue,
41+
gram.prefixDecl,
42+
gram.baseDecl,
43+
gram.varOrTerm,
44+
gram.var_,
45+
gram.graphTerm,
46+
)
47+
.addMany(
48+
gram.rdfLiteral,
49+
gram.iri,
50+
gram.iriFull,
51+
gram.prefixedName,
52+
gram.blankNode,
53+
)
54+
.addRule(gram.path)
55+
.addMany(
56+
gram.solutionModifier,
57+
gram.groupClause,
58+
gram.groupCondition,
59+
gram.havingClause,
60+
gram.orderClause,
61+
gram.limitOffsetClauses,
62+
)
63+
.addMany(
64+
gram.triplesBlock,
65+
gram.collectionPath,
66+
gram.propertyListPath,
67+
gram.triplesNodePath,
68+
gram.graphNodePath,
69+
)
70+
.addMany(
71+
gram.whereClause,
72+
gram.groupGraphPattern,
73+
gram.graphPatternNotTriples,
74+
gram.optionalGraphPattern,
75+
gram.graphGraphPattern,
76+
gram.serviceGraphPattern,
77+
gram.bind,
78+
gram.inlineData,
79+
gram.minusGraphPattern,
80+
gram.groupOrUnionGraphPattern,
81+
gram.filter,
82+
);
83+
84+
export class Generator {
85+
private readonly generator = sparql11GeneratorBuilder.build();
86+
87+
public generate(ast: T11.Query): string {
88+
return this.generator.query(ast, undefined, undefined);
89+
}
90+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@traqula/generator-sparql-1-1",
3+
"type": "module",
4+
"version": "0.0.0",
5+
"description": "SPARQL 1.1 generator",
6+
"lsd:module": true,
7+
"license": "MIT",
8+
"repository": {
9+
"type": "git",
10+
"url": "git+https://github.com/comunica/traqula.git",
11+
"directory": "engines/generator-sparql-1-1"
12+
},
13+
"bugs": {
14+
"url": "https://github.com/comunica/traqula/issues"
15+
},
16+
"sideEffects": false,
17+
"exports": {
18+
"import": "./lib/index.js",
19+
"require": "./lib/index.cjs"
20+
},
21+
"main": "lib/index.js",
22+
"publishConfig": {
23+
"access": "public"
24+
},
25+
"files": [
26+
"lib/**/*.cjs",
27+
"lib/**/*.d.ts",
28+
"lib/**/*.js",
29+
"lib/**/*.js.map"
30+
],
31+
"engines": {
32+
"node": ">=18.0"
33+
},
34+
"typings": "lib/index",
35+
"scripts": {
36+
"build": "yarn build:ts && yarn build:transpile",
37+
"build:ts": "node \"../../node_modules/typescript/bin/tsc\"",
38+
"build:transpile": " node \"../../node_modules/esbuild/bin/esbuild\" --format=cjs --bundle --log-level=error --outfile=lib/index.cjs lib/index.ts"
39+
},
40+
"dependencies": {
41+
"@traqula/core": "^0.0.0",
42+
"@traqula/rules-sparql-1-1": "^0.0.0"
43+
},
44+
"devDependencies": {
45+
"@traqula/parser-sparql-1-1": "^0.0.0",
46+
"@traqula/rules-sparql-1-1": "^0.0.0"
47+
}
48+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Parser } from '@traqula/parser-sparql-1-1';
2+
import type * as T11 from '@traqula/rules-sparql-1-1';
3+
import { describe, it } from 'vitest';
4+
import { Generator } from '../lib';
5+
6+
describe('a SPARQL 1.1 generator', () => {
7+
const generator = new Generator();
8+
const parser = new Parser();
9+
10+
it ('should generate a simple query', ({ expect }) => {
11+
const query = 'SELECT * WHERE { ?s ?p ?o }';
12+
const ast = <T11.Query> parser.parse(query);
13+
const result = generator.generate(ast);
14+
expect(result.replaceAll(/\s+/gu, ' ')).toBe(query);
15+
});
16+
});

packages/core/lib/generator-builder/generatorBuilder.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { CoreFactory } from '../CoreFactory';
2-
import type { Node } from '../nodeTypings';
32
import type { CheckOverlap } from '../utils';
43
import type { GeneratorFromRules, GenRuleMap, GenRulesToObject, GenNamesFromList } from './builderTypes';
54
import type { GeneratorRule, RuleDefArg } from './generatorTypes';
@@ -21,7 +20,7 @@ export class GeneratorBuilder<Context, Names extends string, RuleDefs extends Ge
2120
* If a GeneratorBuilder is provided, a new copy will be created.
2221
*/
2322
public static createBuilder<
24-
Rules extends readonly GeneratorRule<any, any, any & Node>[] = readonly GeneratorRule<any, any, any & Node>[],
23+
Rules extends readonly GeneratorRule[] = readonly GeneratorRule[],
2524
Context = Rules[0] extends GeneratorRule<infer context> ? context : never,
2625
Names extends string = GenNamesFromList<Rules>,
2726
RuleDefs extends GenRuleMap<Names> = GenRulesToObject<Rules>,
@@ -36,14 +35,14 @@ export class GeneratorBuilder<Context, Names extends string, RuleDefs extends Ge
3635

3736
private rules: RuleDefs;
3837

39-
protected constructor(startRules: RuleDefs) {
38+
private constructor(startRules: RuleDefs) {
4039
this.rules = startRules;
4140
}
4241

4342
/**
4443
* Change the implementation of an existing generator rule.
4544
*/
46-
public patchRule<U extends Names, RET extends Node, ARGS>(patch: GeneratorRule<Context, U, RET, ARGS>):
45+
public patchRule<U extends Names, RET, ARGS>(patch: GeneratorRule<Context, U, RET, ARGS>):
4746
GeneratorBuilder<Context, Names, {[Key in Names]: Key extends U ?
4847
GeneratorRule<Context, Key, RET, ARGS> :
4948
(RuleDefs[Key] extends GeneratorRule<Context, Key> ? RuleDefs[Key] : never)
@@ -59,7 +58,7 @@ export class GeneratorBuilder<Context, Names extends string, RuleDefs extends Ge
5958
/**
6059
* Add a rule to the grammar. If the rule already exists, but the implementation differs, an error will be thrown.
6160
*/
62-
public addRuleRedundant<U extends string, RET extends Node, ARGS>(rule: GeneratorRule<Context, U, RET, ARGS>):
61+
public addRuleRedundant<U extends string, RET, ARGS>(rule: GeneratorRule<Context, U, RET, ARGS>):
6362
GeneratorBuilder<Context, Names | U, {[K in Names | U]: K extends Names ?
6463
(RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)
6564
: (K extends U ? GeneratorRule<Context, K, RET, ARGS> : never)
@@ -79,7 +78,7 @@ export class GeneratorBuilder<Context, Names extends string, RuleDefs extends Ge
7978
/**
8079
* Add a rule to the grammar. Will raise a typescript error if the rule already exists in the grammar.
8180
*/
82-
public addRule<U extends string, RET extends Node, ARGS>(
81+
public addRule<U extends string, RET, ARGS>(
8382
rule: CheckOverlap<U, Names, GeneratorRule<Context, U, RET, ARGS>>,
8483
): GeneratorBuilder<Context, Names | U, {[K in Names | U]: K extends Names ?
8584
(RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)
@@ -88,7 +87,7 @@ export class GeneratorBuilder<Context, Names extends string, RuleDefs extends Ge
8887
return this.addRuleRedundant(rule);
8988
}
9089

91-
public addMany<U extends readonly GeneratorRule<Context, any, Node>[]>(
90+
public addMany<U extends readonly GeneratorRule<Context>[]>(
9291
...rules: CheckOverlap<GenNamesFromList<U>, Names, U>
9392
): GeneratorBuilder<
9493
Context,

packages/rules-sparql-1-1/lib/RoundTripTypes.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export type Quads = PatternBgp | GraphQuads;
2828
export type GraphQuads = Node & {
2929
type: 'graph';
3030
graph: TermIri | TermVariable;
31-
triples: BasicGraphPattern;
31+
triples: PatternBgp;
3232
};
3333

3434
// https://www.w3.org/TR/sparql11-query/#rUpdate1
@@ -138,8 +138,7 @@ export type SparqlQuery = Query | Update;
138138
// https://www.w3.org/TR/sparql11-query/#rDatasetClause
139139
export type DatasetClauses = Node & {
140140
type: 'datasetClauses';
141-
default: TermIri[];
142-
named: TermIri[];
141+
clauses: { clauseType: 'default' | 'named'; value: TermIri }[];
143142
};
144143

145144
// https://www.w3.org/TR/sparql11-query/#rGraphNode

packages/rules-sparql-1-1/lib/factory.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ import type {
4141
PropertyPathChain,
4242
Quads,
4343
Query,
44+
QueryAsk,
45+
QueryConstruct,
46+
QueryDescribe,
47+
QuerySelect,
4448
SolutionModifierHaving,
4549
SolutionModifierLimitOffset,
4650
SolutionModifierOrder,
@@ -263,6 +267,22 @@ export class TraqulaFactory extends CoreFactory {
263267
return x.type === 'query';
264268
}
265269

270+
public isQuerySelect(query: Query): query is QuerySelect {
271+
return query.queryType === 'select';
272+
}
273+
274+
public isQueryConstruc(query: Query): query is QueryConstruct {
275+
return query.queryType === 'construct';
276+
}
277+
278+
public isQueryDescribe(query: Query): query is QueryDescribe {
279+
return query.queryType === 'describe';
280+
}
281+
282+
public isQueryAsk(query: Query): query is QueryAsk {
283+
return query.queryType === 'ask';
284+
}
285+
266286
public patternBgp(triples: BasicGraphPattern, loc: SourceLocation): PatternBgp {
267287
return { type: 'pattern', patternType: 'bgp', triples, loc };
268288
}

packages/rules-sparql-1-1/lib/grammar/dataSetClause.ts

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,23 @@ import { iri } from './literals';
88
export function datasetClauseUsing<RuleName extends 'usingClause' | 'datasetClause'>(
99
name: RuleName,
1010
token: TokenType,
11-
): SparqlGrammarRule<RuleName, Wrap<{ named: boolean; value: TermIri }>> {
11+
): SparqlGrammarRule<RuleName, Wrap<DatasetClauses['clauses'][0]>> {
1212
return {
1313
name,
1414
impl: ({ ACTION, SUBRULE, CONSUME, OR }) => (C) => {
1515
const start = CONSUME(token);
1616
return OR<RuleDefReturn<typeof datasetClause>>([
1717
{ ALT: () => {
1818
const iri = SUBRULE(defaultGraphClause, undefined);
19-
return ACTION(() => C.factory.wrap({ named: false, value: iri }, C.factory.sourceLocation(start, iri)));
19+
return ACTION(() =>
20+
C.factory.wrap({ clauseType: 'default', value: iri }, C.factory.sourceLocation(start, iri)));
2021
} },
2122
{ ALT: () => {
2223
const namedClause = SUBRULE(namedGraphClause, undefined);
23-
return ACTION(() =>
24-
C.factory.wrap({ named: true, value: namedClause.val }, C.factory.sourceLocation(start, namedClause)));
24+
return ACTION(() => C.factory.wrap({
25+
clauseType: 'named',
26+
value: namedClause.val,
27+
}, C.factory.sourceLocation(start, namedClause)));
2528
} },
2629
]);
2730
},
@@ -52,33 +55,27 @@ export function datasetClauseUsingStar<RuleName extends string>(
5255
return {
5356
name,
5457
impl: ({ ACTION, MANY, SUBRULE }) => (C) => {
55-
const _default: TermIri[] = [];
56-
const named: TermIri[] = [];
57-
let first: RuleDefReturn<typeof datasetClause> | undefined;
58-
let last: RuleDefReturn<typeof datasetClause> | undefined;
58+
const clauses: RuleDefReturn<typeof datasetClause>[] = [];
5959

6060
MANY(() => {
6161
const clause = SUBRULE(subRule, undefined);
62-
if (!first) {
63-
first = clause;
64-
}
65-
last = clause;
66-
ACTION(() => {
67-
if (clause.val.named) {
68-
named.push(clause.val.value);
69-
} else {
70-
_default.push(clause.val.value);
71-
}
72-
});
62+
clauses.push(clause);
7363
});
64+
7465
return ACTION(() => ({
7566
type: 'datasetClauses',
76-
default: _default,
77-
named,
78-
loc: C.factory.sourceLocation(...[ first, last ].filter(x => x !== undefined)),
67+
clauses: clauses.map(clause => clause.val),
68+
loc: C.factory.sourceLocation(...clauses),
7969
}));
8070
},
81-
gImpl: () => () => '',
71+
gImpl: ({ SUBRULE, PRINT_WORD }) => (ast, { factory: F }) => {
72+
for (const clause of ast.clauses) {
73+
if (clause.clauseType === 'named') {
74+
F.printFilter(ast, () => PRINT_WORD('NAMED'));
75+
}
76+
SUBRULE(iri, clause.value, undefined);
77+
}
78+
},
8279
};
8380
}
8481

0 commit comments

Comments
 (0)