Skip to content

Commit

Permalink
Add generic Stateless SPARQL parser
Browse files Browse the repository at this point in the history
  • Loading branch information
jitsedesmet committed Jan 22, 2025
1 parent 7119cc6 commit 1455821
Show file tree
Hide file tree
Showing 33 changed files with 212 additions and 364 deletions.
58 changes: 6 additions & 52 deletions engines/engine-sparql-1-1-adjust/lib/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {Builder, LexerBuilder, SparqlContext} from '@traqula/core';
import {Builder, LexerBuilder} from '@traqula/core';
import {gram, lex} from '@traqula/rules-sparql-1-1-adjust';
import {Expression, gram as g11, IriTerm, lex as l11, PropertyPath, SparqlQuery} from '@traqula/rules-sparql-1-1';
import {Expression, gram as g11, lex as l11, SparqlParser, SparqlQuery} from '@traqula/rules-sparql-1-1';
import {sparql11ParserBuilder} from '@traqula/engine-sparql-1-1';
import {DataFactory} from "rdf-data-factory";

const builtInPatch: typeof g11.builtInCall = {
name: 'builtInCall',
Expand All @@ -18,56 +17,11 @@ export const adjustBuilder = Builder.createBuilder(sparql11ParserBuilder)
.patchRule(builtInPatch);


export class Parser {
private readonly parser: {
queryOrUpdate: (input: string, context: SparqlContext, arg: undefined) => SparqlQuery;
path: (input: string, context: SparqlContext, arg: undefined) => PropertyPath | IriTerm;
};
private config: SparqlContext;
private readonly initialConfig: SparqlContext;

public constructor(context: Partial<SparqlContext> = {}) {
this.parser =adjustBuilder.consumeToParser({
export class Parser extends SparqlParser<SparqlQuery> {
public constructor() {
const parser = adjustBuilder.consumeToParser({
tokenVocabulary: LexerBuilder.create(l11.sparql11Tokens).addBefore(l11.a, lex.BuiltInAdjust).build(),
});
this.initialConfig = {
dataFactory: context.dataFactory ?? new DataFactory({ blankNodePrefix: 'g_' }),
baseIRI: context.baseIRI,
prefixes: { ...context.prefixes },
parseMode: context.parseMode ? new Set(context.parseMode) : new Set([ g11.canParseVars, g11.canCreateBlankNodes ]),
skipValidation: context.skipValidation ?? false,
}
this.reset();
}

private reset() {
this.config = {
dataFactory: this.initialConfig.dataFactory,
baseIRI: this.initialConfig.baseIRI,
prefixes: { ...this.initialConfig.prefixes },
parseMode: new Set(this.initialConfig.parseMode),
skipValidation: this.initialConfig.skipValidation,
}
}

public _resetBlanks(): void {
this.config.dataFactory.resetBlankNodeCounter();
}

public parse(query: string): SparqlQuery {
this.reset();
return this.parser.queryOrUpdate(query, this.config, undefined);
}

public parsePath(query: string): (PropertyPath & { prefixes: object }) | IriTerm {
this.reset();
const result = this.parser.path(query, this.config, undefined);
if ('type' in result) {
return {
...result,
prefixes: {},
};
}
return result;
super(parser);
}
}
7 changes: 4 additions & 3 deletions engines/engine-sparql-1-1-adjust/spec/parser.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {ErrorSkipped,} from 'rdf-test-suite';
import {Parser} from '../lib'
import {SparqlContext} from "@traqula/core";

import {SparqlContext} from "@traqula/rules-sparql-1-1";

export async function parse(query: string, context: Partial<SparqlContext> = {}) {
const parser = new Parser(context);
parser.parse(query);
const parser = new Parser();
parser.parse(query, context);
}
export function query() {
return Promise.reject(new ErrorSkipped('Querying is not supported'));
Expand Down
14 changes: 6 additions & 8 deletions engines/engine-sparql-1-1-adjust/test/statics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ import {DataFactory} from "rdf-data-factory";
import {BaseQuad} from "@rdfjs/types";

describe('a SPARQL 1.1 + adjust parser', () => {
const parser = new Parser({ prefixes: { ex: 'http://example.org/' }});
beforeEach(() => {
parser._resetBlanks();
});
const parser = new Parser();
const context = { prefixes: { ex: 'http://example.org/' }};

describe('positive paths', () => {
for (const {name, statics} of [...positiveTest('paths')]) {
it(`can parse ${name}`, async ({expect}) => {
const {query, result} = await statics();
const res: unknown = parser.parsePath(query);
const res: unknown = parser.parsePath(query, context);
expect(res).toEqualParsedQuery(result);
});
}
Expand All @@ -24,14 +22,14 @@ describe('a SPARQL 1.1 + adjust parser', () => {
for (const {name, statics} of [...positiveTest('sparql-1-1')]) {
it(`can parse ${name}`, async ({expect}) => {
const {query, result} = await statics();
const res: unknown = parser.parse(query);
const res: unknown = parser.parse(query, context);
expect(res).toEqualParsedQuery(result);
});
}
});

describe('specific sparql 1.1 tests', () => {
importSparql11NoteTests(args => new Parser(args), new DataFactory<BaseQuad>());
importSparql11NoteTests(parser, new DataFactory<BaseQuad>());
});

it('parses ADJUST function', ({expect}) => {
Expand All @@ -40,7 +38,7 @@ SELECT ?s ?p (ADJUST(?o, "-PT10H"^^<http://www.w3.org/2001/XMLSchema#dayTimeDura
?s ?p ?o
}
`;
const res: unknown = new Parser().parse(query);
const res: unknown = parser.parse(query);
expect(res).toMatchObject({});
});
});
76 changes: 11 additions & 65 deletions engines/engine-sparql-1-1/lib/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import { DataFactory } from 'rdf-data-factory';
import {Builder, SparqlContext} from '@traqula/core';
import type { ImplArgs, SparqlRuleDef } from '@traqula/core';
import { gram, lex as l } from '@traqula/rules-sparql-1-1';
import type {
IriTerm,
PropertyPath,
Query,
SparqlQuery,
Update,
} from '@traqula/rules-sparql-1-1';
import { queryUnitParserBuilder } from './queryUnitParser';
import { updateParserBuilder } from './updateUnitParser';
import type * as RDF from '@rdfjs/types';
import {Builder} from '@traqula/core';
import type {Query, SparqlQuery, Update,} from '@traqula/rules-sparql-1-1';
import {gram, lex as l, SparqlParser, SparqlRuleDef} from '@traqula/rules-sparql-1-1';
import {queryUnitParserBuilder} from './queryUnitParser';
import {updateParserBuilder} from './updateUnitParser';

// Create merge of
// ```
Expand Down Expand Up @@ -115,56 +106,11 @@ export const sparql11ParserBuilder = Builder.createBuilder(queryUnitParserBuilde
.deleteRule('updateUnit')
.addRule(queryOrUpdate);

export class Parser {
private readonly parser: {
queryOrUpdate: (input: string, context: SparqlContext, arg: undefined) => SparqlQuery;
path: (input: string, context: SparqlContext, arg: undefined) => PropertyPath | IriTerm;
};
private config: SparqlContext;
private readonly initialConfig: SparqlContext;

public constructor(context: Partial<SparqlContext> = {}) {
this.parser = sparql11ParserBuilder.consumeToParser({
tokenVocabulary: l.sparql11Tokens.build(),
});
this.initialConfig = {
dataFactory: context.dataFactory ?? new DataFactory({ blankNodePrefix: 'g_' }),
baseIRI: context.baseIRI,
prefixes: { ...context.prefixes },
parseMode: context.parseMode ? new Set(context.parseMode) : new Set([ gram.canParseVars, gram.canCreateBlankNodes ]),
skipValidation: context.skipValidation ?? false,
}
this.reset();
}

private reset() {
this.config = {
dataFactory: this.initialConfig.dataFactory,
baseIRI: this.initialConfig.baseIRI,
prefixes: { ...this.initialConfig.prefixes },
parseMode: new Set(this.initialConfig.parseMode),
skipValidation: this.initialConfig.skipValidation,
}
}

public _resetBlanks(): void {
this.config.dataFactory.resetBlankNodeCounter();
}

public parse(query: string): SparqlQuery {
this.reset()
return this.parser.queryOrUpdate(query, this.config, undefined);
}

public parsePath(query: string): (PropertyPath & { prefixes: object }) | IriTerm {
this.reset()
const result = this.parser.path(query, this.config, undefined);
if ('type' in result) {
return {
...result,
prefixes: {},
};
export class Parser extends SparqlParser<SparqlQuery> {
public constructor() {
const parser = sparql11ParserBuilder.consumeToParser({
tokenVocabulary: l.sparql11Tokens.build(),
});
super(parser);
}
return result;
}
}
4 changes: 2 additions & 2 deletions engines/engine-sparql-1-1/lib/expressionParser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Builder, type RuleNamesFromList, SparqlContext} from '@traqula/core';
import { gram }from '@traqula/rules-sparql-1-1';
import {Builder, type RuleNamesFromList} from '@traqula/core';
import {gram, SparqlContext} from '@traqula/rules-sparql-1-1';


const rulesNoBuiltIn = <const> [
Expand Down
10 changes: 5 additions & 5 deletions engines/engine-sparql-1-1/spec/parser.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ErrorSkipped, } from 'rdf-test-suite';
import { Parser } from '../lib'
import {ImplArgs, SparqlContext} from "@traqula/core";
import {ErrorSkipped,} from 'rdf-test-suite';
import {Parser} from '../lib'
import {SparqlContext} from "@traqula/rules-sparql-1-1";

export async function parse(query: string, context: Partial<SparqlContext> = {}) {
const parser = new Parser(context);
parser.parse(query);
const parser = new Parser();
parser.parse(query, context);
}
export function query() {
return Promise.reject(new ErrorSkipped('Querying is not supported'));
Expand Down
12 changes: 5 additions & 7 deletions engines/engine-sparql-1-1/test/statics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ import {DataFactory} from "rdf-data-factory";
import {BaseQuad} from "@rdfjs/types";

describe('a SPARQL 1.1 parser', () => {
const parser = new Parser({ prefixes: { ex: 'http://example.org/' }});
beforeEach(() => {
parser._resetBlanks();
});
const parser = new Parser();
const context = { prefixes: { ex: 'http://example.org/' }};

describe('positive paths', () => {
for (const { name, statics } of [...positiveTest('paths')]) {
it(`can parse ${name}`, async({expect}) => {
const { query, result } = await statics();
const res: unknown = parser.parsePath(query);
const res: unknown = parser.parsePath(query, context);
expect(res).toEqualParsedQuery(result);
});
}
Expand All @@ -24,13 +22,13 @@ describe('a SPARQL 1.1 parser', () => {
for (const { name, statics } of [...positiveTest('sparql-1-1')]) {
it(`can parse ${name}`, async({expect}) => {
const { query, result } = await statics();
const res: unknown = parser.parse(query);
const res: unknown = parser.parse(query,context);
expect(res).toEqualParsedQuery(result);
});
}
});

describe('specific sparql 1.1 tests', () => {
importSparql11NoteTests(args => new Parser(args), new DataFactory<BaseQuad>());
importSparql11NoteTests(parser, new DataFactory<BaseQuad>());
});
});
59 changes: 6 additions & 53 deletions engines/engine-sparql-1-2/lib/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {DataFactory} from 'rdf-data-factory';
import {Builder, SparqlContext} from '@traqula/core';
import {gram as g11, IriTerm, PropertyPath, SparqlQuery,} from '@traqula/rules-sparql-1-1';
import {gram as S11} from '@traqula/rules-sparql-1-1';
import {Builder} from '@traqula/core';
import {gram as g11, SparqlParser, SparqlQuery} from '@traqula/rules-sparql-1-1';
import {gram as S12, lex as l12} from '@traqula/rules-sparql-1-2';
import {sparql11ParserBuilder} from '@traqula/engine-sparql-1-1';

Expand Down Expand Up @@ -49,56 +47,11 @@ export const sparql12ParserBuilder = Builder.createBuilder(sparql11ParserBuilder
.patchRule(S12.builtInCall)
.patchRule(S12.rdfLiteral);

export class Parser {
private readonly parser: {
queryOrUpdate: (input: string, context: SparqlContext, arg: undefined) => SparqlQuery;
path: (input: string, context: SparqlContext, arg: undefined) => PropertyPath | IriTerm;
};
private config: SparqlContext;
private readonly initialConfig: SparqlContext;

public constructor(context: Partial<SparqlContext> = {}) {
this.parser = sparql12ParserBuilder.consumeToParser({
export class Parser extends SparqlParser<SparqlQuery> {
public constructor() {
const parser = sparql12ParserBuilder.consumeToParser({
tokenVocabulary: l12.sparql12Tokens.build(),
});
this.initialConfig = {
dataFactory: context.dataFactory ?? new DataFactory({ blankNodePrefix: 'g_' }),
baseIRI: context.baseIRI,
prefixes: { ...context.prefixes },
parseMode: context.parseMode ? new Set(context.parseMode) : new Set([ g11.canParseVars, g11.canCreateBlankNodes ]),
skipValidation: context.skipValidation ?? false,
}
this.reset();
}

private reset() {
this.config = {
dataFactory: this.initialConfig.dataFactory,
baseIRI: this.initialConfig.baseIRI,
prefixes: { ...this.initialConfig.prefixes },
parseMode: new Set(this.initialConfig.parseMode),
skipValidation: this.initialConfig.skipValidation,
}
}

public _resetBlanks(): void {
this.config.dataFactory.resetBlankNodeCounter();
}

public parse(query: string): SparqlQuery {
this.reset();
return this.parser.queryOrUpdate(query, this.config, undefined);
}

public parsePath(query: string): (PropertyPath & { prefixes: object }) | IriTerm {
this.reset();
const result = this.parser.path(query, this.config, undefined);
if ('type' in result) {
return {
...result,
prefixes: {},
};
}
return result;
super(parser);
}
}
3 changes: 2 additions & 1 deletion engines/engine-sparql-1-2/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './Parser';
export * from './Parser';
export type { SparqlContext } from '@traqula/rules-sparql-1-1';
7 changes: 4 additions & 3 deletions engines/engine-sparql-1-2/spec/parser.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ErrorSkipped, } from 'rdf-test-suite';
import { Parser } from '../lib'
import {ImplArgs, SparqlContext} from "@traqula/core";
import {ImplArgs} from "@traqula/core";
import {SparqlContext} from "@traqula/rules-sparql-1-1";

export async function parse(query: string, context: Partial<SparqlContext> = {}) {
const parser = new Parser(context);
parser.parse(query);
const parser = new Parser();
parser.parse(query, context);
}
export function query() {
return Promise.reject(new ErrorSkipped('Querying is not supported'));
Expand Down
Loading

0 comments on commit 1455821

Please sign in to comment.