Skip to content

Commit 89f6859

Browse files
committed
initial startings on rounttripping completed
1 parent 0bb8f60 commit 89f6859

16 files changed

Lines changed: 173 additions & 73 deletions

File tree

engines/generator-sparql-1-1/lib/index.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GeneratorBuilder } from '@traqula/core';
2-
import { gram } from '@traqula/rules-sparql-1-1';
2+
import { gram, TraqulaFactory } from '@traqula/rules-sparql-1-1';
33
import type * as T11 from '@traqula/rules-sparql-1-1';
44

55
const sparql11GeneratorBuilder = GeneratorBuilder.createBuilder(<const> [
@@ -55,20 +55,20 @@ const sparql11GeneratorBuilder = GeneratorBuilder.createBuilder(<const> [
5555
.addMany(
5656
gram.solutionModifier,
5757
gram.groupClause,
58-
gram.groupCondition,
5958
gram.havingClause,
6059
gram.orderClause,
6160
gram.limitOffsetClauses,
6261
)
6362
.addMany(
6463
gram.triplesBlock,
6564
gram.collectionPath,
66-
gram.propertyListPath,
65+
gram.blankNodePropertyListPath,
6766
gram.triplesNodePath,
6867
gram.graphNodePath,
6968
)
7069
.addMany(
7170
gram.whereClause,
71+
gram.generatePattern,
7272
gram.groupGraphPattern,
7373
gram.graphPatternNotTriples,
7474
gram.optionalGraphPattern,
@@ -83,8 +83,9 @@ const sparql11GeneratorBuilder = GeneratorBuilder.createBuilder(<const> [
8383

8484
export class Generator {
8585
private readonly generator = sparql11GeneratorBuilder.build();
86+
private readonly factory = new TraqulaFactory();
8687

87-
public generate(ast: T11.Query): string {
88-
return this.generator.query(ast, undefined, undefined);
88+
public generate(ast: T11.Query, origSource?: string): string {
89+
return this.generator.query(ast, { factory: this.factory, offset: 0, origSource: origSource ?? '' }, undefined);
8990
}
9091
}

engines/generator-sparql-1-1/test/integration.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,68 @@
11
import { Parser } from '@traqula/parser-sparql-1-1';
22
import type * as T11 from '@traqula/rules-sparql-1-1';
3+
import type { Query } from '@traqula/rules-sparql-1-1';
4+
import { TraqulaFactory } from '@traqula/rules-sparql-1-1';
35
import { describe, it } from 'vitest';
46
import { Generator } from '../lib';
57

68
describe('a SPARQL 1.1 generator', () => {
79
const generator = new Generator();
810
const parser = new Parser();
11+
const F = new TraqulaFactory();
912

1013
it ('should generate a simple query', ({ expect }) => {
1114
const query = 'SELECT * WHERE { ?s ?p ?o }';
1215
const ast = <T11.Query> parser.parse(query);
16+
console.log(JSON.stringify(ast, null, 2));
17+
const result = generator.generate(ast, query);
18+
expect(result.replaceAll(/\s+/gu, ' ')).toBe(query);
19+
});
20+
21+
it ('should generate a simple query 5', ({ expect }) => {
22+
/* eslint-disable ts/ban-ts-comment */
23+
function alterType(curObject: object, searchType: string, patch: (current: object) => object): object {
24+
for (const [ key, value ] of Object.entries(curObject)) {
25+
if (value && typeof value === 'object') {
26+
(<Record<string, unknown>> curObject)[key] = alterType(value, searchType, patch);
27+
}
28+
}
29+
if ((<{ type?: unknown }> curObject).type === searchType) {
30+
return patch(curObject);
31+
}
32+
return curObject;
33+
}
34+
const query = 'SELECT * WHERE { ?s ?p ?o }';
35+
const ast = <T11.Query> parser.parse(query);
36+
37+
const altered = alterType(ast, 'term', (current) => {
38+
// @ts-expect-error
39+
if (current.termType === 'Variable' && current.value === 's') {
40+
// @ts-expect-error
41+
return F.variable('subject', F.sourceLocationNodeReplace(current.loc));
42+
}
43+
return current;
44+
});
45+
/* eslint-enable ts/ban-ts-comment */
46+
47+
console.error(JSON.stringify(altered, null, 2));
48+
const result = generator.generate(<Query>altered, query);
49+
expect(result.replaceAll(/\s+/gu, ' ')).toBe(query);
50+
});
51+
52+
it ('can auto gen query', ({ expect }) => {
53+
const query = 'SELECT * WHERE { ?s ?p ?o . } ';
54+
const ast = F.querySelect({
55+
variables: [ F.wildcard(F.gen()) ],
56+
datasets: F.datasetClauses([], F.sourceLocation()),
57+
context: [],
58+
where: F.patternGroup([
59+
F.patternBgp([
60+
F.triple(F.variable('s', F.gen()), F.variable('p', F.gen()), F.variable('o', F.gen())),
61+
], F.gen()),
62+
], F.gen()),
63+
solutionModifiers: {},
64+
}, F.gen());
65+
console.log(JSON.stringify(ast, null, 2));
1366
const result = generator.generate(ast);
1467
expect(result.replaceAll(/\s+/gu, ' ')).toBe(query);
1568
});

engines/parser-sparql-1-1/test/triplesTemplateParser.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { BasicGraphPattern, SparqlContext, Wrap } from '@traqula/rules-sparql-1-1';
1+
import type { PatternBgp, SparqlContext } from '@traqula/rules-sparql-1-1';
22
import { TraqulaFactory, completeParseContext, lex as l } from '@traqula/rules-sparql-1-1';
33
import { describe, it } from 'vitest';
44
import { triplesTemplateParserBuilder } from '../lib';
55

66
describe('a SPARQL 1.1 objectlist parser', () => {
77
const F = new TraqulaFactory();
8-
function parse(query: string, context: Partial<SparqlContext>): Wrap<BasicGraphPattern> {
8+
function parse(query: string, context: Partial<SparqlContext>): PatternBgp {
99
const parser = triplesTemplateParserBuilder.consumeToParser({
1010
tokenVocabulary: l.sparql11Tokens.build(),
1111
});

packages/core/lib/CoreFactory.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,30 @@ export class CoreFactory {
1919

2020
public isLocalized(obj: unknown): obj is Localized {
2121
return typeof obj === 'object' && obj !== null && 'loc' in obj &&
22-
typeof obj.loc === 'object' && obj.loc !== null && 'sourceLocationType' in obj;
22+
typeof obj.loc === 'object' && obj.loc !== null && 'sourceLocationType' in obj.loc;
2323
}
2424

2525
public sourceLocation(...elements: (undefined | IToken | Localized)[]): SourceLocation {
26-
const filtered = <(Localized | IToken)[]> elements.filter(element =>
27-
element && (!this.isLocalized(element) || this.isSourceLocationSource(element.loc)));
28-
if (filtered.length === 0) {
26+
const pureElements = elements.filter(x => x !== undefined);
27+
if (pureElements.length === 0) {
2928
return this.sourceLocationNoMaterialize();
3029
}
30+
const filtered = pureElements.filter(element =>
31+
!this.isLocalized(element) || this.isSourceLocationSource(element.loc) ||
32+
this.isSourceLocationStringReplace(element.loc) || this.isSourceLocationNodeReplace(element.loc));
33+
if (filtered.length === 0) {
34+
return this.gen();
35+
}
3136
const first = filtered[0];
3237
const last = filtered.at(-1)!;
3338
return {
3439
sourceLocationType: 'source',
35-
start: this.isLocalized(first) ? (<SourceLocationSource> first.loc).start : first.startOffset,
36-
end: this.isLocalized(last) ? (<SourceLocationSource> last.loc).end : (last.endOffset! + 1),
40+
start: this.isLocalized(first) ?
41+
(<SourceLocationSource | SourceLocationStringReplace> first.loc).start :
42+
first.startOffset,
43+
end: this.isLocalized(last) ?
44+
(<SourceLocationSource | SourceLocationStringReplace> last.loc).end :
45+
(last.endOffset! + 1),
3746
};
3847
}
3948

@@ -60,6 +69,10 @@ export class CoreFactory {
6069
};
6170
}
6271

72+
public gen(): SourceLocationNodeAutoGenerate {
73+
return { sourceLocationType: 'autoGenerate' };
74+
}
75+
6376
public isSourceLocationSource(loc: object): loc is SourceLocationSource {
6477
return this.isSourceLocation(loc) && loc.sourceLocationType === 'source';
6578
}
@@ -68,11 +81,22 @@ export class CoreFactory {
6881
return this.isSourceLocation(loc) && loc.sourceLocationType === 'stringReplace';
6982
}
7083

71-
public sourceLocationNodeReplace(start: number, end: number): SourceLocationNodeReplace {
84+
public sourceLocationNodeReplace(location: SourceLocationSource): SourceLocationNodeReplace;
85+
public sourceLocationNodeReplace(start: number, end: number): SourceLocationNodeReplace;
86+
public sourceLocationNodeReplace(startOrLoc: number | SourceLocationSource, end?: number): SourceLocationNodeReplace {
87+
let starting;
88+
let ending;
89+
if (typeof startOrLoc === 'number') {
90+
starting = startOrLoc;
91+
ending = end!;
92+
} else {
93+
starting = startOrLoc.start;
94+
ending = startOrLoc.end;
95+
}
7296
return {
7397
sourceLocationType: 'nodeReplace',
74-
start,
75-
end,
98+
start: starting,
99+
end: ending,
76100
};
77101
}
78102

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

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -216,18 +216,25 @@ export class Generator<Context, Names extends string, RuleDefs extends GenRuleMa
216216
if (!def) {
217217
throw new Error(`Rule ${cstDef.name} not found`);
218218
}
219-
if (this.factory.isSourceLocationNoMaterialize(ast.loc)) {
220-
return;
221-
}
222-
if (this.factory.isSourceLocationStringReplace(ast.loc)) {
223-
this.catchup(ast.loc.start);
224-
this.print(ast.loc.newSource);
225-
this.generatedUntil = ast.loc.end;
226-
return;
227-
}
228-
if (this.factory.isSourceLocationNodeReplace(ast.loc) || this.factory.isSourceLocationSource(ast.loc)) {
229-
this.catchup(ast.loc.start);
219+
if (this.factory.isLocalized(ast)) {
220+
if (this.factory.isSourceLocationNoMaterialize(ast.loc)) {
221+
return;
222+
}
223+
if (this.factory.isSourceLocationStringReplace(ast.loc)) {
224+
this.catchup(ast.loc.start);
225+
this.print(ast.loc.newSource);
226+
this.generatedUntil = ast.loc.end;
227+
return;
228+
}
229+
if (this.factory.isSourceLocationNodeReplace(ast.loc)) {
230+
this.catchup(ast.loc.start);
231+
this.generatedUntil = ast.loc.end;
232+
}
233+
if (this.factory.isSourceLocationSource(ast.loc)) {
234+
this.catchup(ast.loc.start);
235+
}
230236
}
237+
231238
// If autoGenerate - do nothing
232239

233240
// Do call generation
@@ -238,9 +245,7 @@ export class Generator<Context, Names extends string, RuleDefs extends GenRuleMa
238245
CATCHUP: this.catchup,
239246
})(ast, this.getSafeContext(), arg);
240247

241-
if (this.factory.isSourceLocationNodeReplace(ast.loc)) {
242-
this.generatedUntil = ast.loc.end;
243-
} else if (this.factory.isSourceLocationSource(ast.loc)) {
248+
if (this.factory.isLocalized(ast) && this.factory.isSourceLocationSource(ast.loc)) {
244249
this.catchup(ast.loc.end);
245250
}
246251
};

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type GeneratorRule<
1616
* Generation happens on a per AST node basis.
1717
* The core will implement the generation as such. If this ever changes, we will cross that bridge when we get there.
1818
*/
19-
ReturnType = unknown,
19+
ReturnType = any,
2020
/**
2121
* Function arguments that can be given to convey the state of the current parse operation.
2222
*/
@@ -28,7 +28,7 @@ export type GeneratorRule<
2828
};
2929

3030
export interface RuleDefArg {
31-
SUBRULE: <T, U>(cstDef: GeneratorRule<any, string, T, U>, input: T, arg: U) => void;
31+
SUBRULE: <T, U>(cstDef: GeneratorRule<any, any, T, U>, input: T, arg: U) => void;
3232
PRINT: (...args: string[]) => void;
3333
PRINT_WORD: (...args: string[]) => void;
3434
CATCHUP: (until: number) => void;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ export type PatternValues = PatternBase & {
231231
patternType: 'values';
232232
values: ValuePatternRow[];
233233
};
234-
export type SubSelect = Omit<QuerySelect, 'context' | 'datasets'>;
234+
export type SubSelect = QuerySelect;
235235

236236
export type Pattern =
237237
| PatternBgp
@@ -314,7 +314,7 @@ export type ExpressionPatternOperation = ExpressionBase & {
314314
expressionType: 'patternOperation';
315315
operator: string;
316316
// Can be a pattern in case of exists and not exists
317-
args: Pattern[];
317+
args: PatternGroup;
318318
};
319319

320320
export type ExpressionFunctionCall = ExpressionBase & {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ SparqlGrammarRule<Uncapitalize<T>, ExpressionPatternOperation> {
212212
const group = SUBRULE(groupGraphPattern, undefined);
213213
return ACTION(() => C.factory.expressionPatternOperation(
214214
operator.image,
215-
[ C.factory.deGroupSingle(group) ],
216-
C.factory.sourceLocation(operator, group.loc),
215+
group,
216+
C.factory.sourceLocation(operator, group),
217217
));
218218
},
219219
};

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,11 @@ export class TraqulaFactory extends CoreFactory {
222222
};
223223
}
224224

225-
public expressionPatternOperation<Args extends Pattern[]>(
225+
public expressionPatternOperation(
226226
operator: string,
227-
args: Args,
227+
args: PatternGroup,
228228
loc: SourceLocation,
229-
): ExpressionPatternOperation & { args: Args } {
229+
): ExpressionPatternOperation {
230230
return {
231231
type: 'expression',
232232
expressionType: 'patternOperation',
@@ -283,6 +283,23 @@ export class TraqulaFactory extends CoreFactory {
283283
return query.queryType === 'ask';
284284
}
285285

286+
public querySelect(arg: Omit<QuerySelect, 'type' | 'queryType' | 'loc'>, loc: SourceLocation): QuerySelect {
287+
return {
288+
type: 'query',
289+
queryType: 'select',
290+
...arg,
291+
loc,
292+
};
293+
}
294+
295+
public datasetClauses(clauses: DatasetClauses['clauses'], loc: SourceLocation): DatasetClauses {
296+
return {
297+
type: 'datasetClauses',
298+
clauses,
299+
loc,
300+
};
301+
}
302+
286303
public patternBgp(triples: BasicGraphPattern, loc: SourceLocation): PatternBgp {
287304
return { type: 'pattern', patternType: 'bgp', triples, loc };
288305
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,10 @@ export function datasetClauseUsingStar<RuleName extends string>(
6262
clauses.push(clause);
6363
});
6464

65-
return ACTION(() => ({
66-
type: 'datasetClauses',
67-
clauses: clauses.map(clause => clause.val),
68-
loc: C.factory.sourceLocation(...clauses),
69-
}));
65+
return ACTION(() => C.factory.datasetClauses(
66+
clauses.map(clause => clause.val),
67+
C.factory.sourceLocation(...clauses),
68+
));
7069
},
7170
gImpl: ({ SUBRULE, PRINT_WORD }) => (ast, { factory: F }) => {
7271
for (const clause of ast.clauses) {

0 commit comments

Comments
 (0)