Skip to content

Commit

Permalink
semi stateless parsing using context
Browse files Browse the repository at this point in the history
  • Loading branch information
jitsedesmet committed Jan 21, 2025
1 parent 6d49000 commit 7119cc6
Show file tree
Hide file tree
Showing 29 changed files with 1,076 additions and 983 deletions.
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
yarn run build
yarn run test
yarn run spec:all
65 changes: 36 additions & 29 deletions engines/engine-sparql-1-1-adjust/lib/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
import {Builder, type ImplArgs, LexerBuilder} from '@traqula/core';
import {Builder, LexerBuilder, SparqlContext} from '@traqula/core';
import {gram, lex} from '@traqula/rules-sparql-1-1-adjust';
import {
Expression,
gram as g11,
lex as l11,
IriTerm,
PropertyPath,
SparqlParser as ISparqlParser,
SparqlQuery
} from '@traqula/rules-sparql-1-1';
import {Expression, gram as g11, IriTerm, lex as l11, PropertyPath, SparqlQuery} from '@traqula/rules-sparql-1-1';
import {sparql11ParserBuilder} from '@traqula/engine-sparql-1-1';
import {DataFactory} from "rdf-data-factory";
import type * as RDF from "@rdfjs/types";

const builtInPatch: typeof g11.builtInCall = {
name: 'builtInCall',
impl: ({ SUBRULE, OR }) => () => OR<Expression>([
{ ALT: () => SUBRULE(gram.builtInAdjust) },
{ ALT: () => SUBRULE(gram.existingBuildInCall) },
{ ALT: () => SUBRULE(gram.builtInAdjust, undefined) },
{ ALT: () => SUBRULE(gram.existingBuildInCall, undefined) },
]),
};

Expand All @@ -26,35 +17,51 @@ export const adjustBuilder = Builder.createBuilder(sparql11ParserBuilder)
.addRule(gram.existingBuildInCall)
.patchRule(builtInPatch);

export class Parser implements ISparqlParser {

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

private readonly dataFactory: DataFactory<RDF.BaseQuad>;

public constructor(context: Partial<ImplArgs['context']> = {}) {
this.dataFactory = context.dataFactory ?? new DataFactory({ blankNodePrefix: 'g_' });
this.parser = adjustBuilder.consumeToParser({
public constructor(context: Partial<SparqlContext> = {}) {
this.parser =adjustBuilder.consumeToParser({
tokenVocabulary: LexerBuilder.create(l11.sparql11Tokens).addBefore(l11.a, lex.BuiltInAdjust).build(),
}, {
parseMode: new Set([ g11.canParseVars, g11.canCreateBlankNodes ]),
...context,
dataFactory: this.dataFactory,
});
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.dataFactory.resetBlankNodeCounter();
this.config.dataFactory.resetBlankNodeCounter();
}

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

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

export async function parse(query: string, context: Partial<ImplArgs['context']> = {}) {
export async function parse(query: string, context: Partial<SparqlContext> = {}) {
const parser = new Parser(context);
parser.parse(query);
}
Expand Down
68 changes: 41 additions & 27 deletions engines/engine-sparql-1-1/lib/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { DataFactory } from 'rdf-data-factory';
import { Builder } from '@traqula/core';
import type { ImplArgs, RuleDef } from '@traqula/core';
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,
SparqlParser as ISparqlParser,
SparqlQuery,
Update,
} from '@traqula/rules-sparql-1-1';
Expand All @@ -24,19 +23,19 @@ import type * as RDF from '@rdfjs/types';
// ```
// Prologue ( Update1 ( ';' Update )? )?
// ```
const queryOrUpdate: RuleDef<'queryOrUpdate', Query | Update | Pick<Update, 'base' | 'prefixes'>> = {
const queryOrUpdate: SparqlRuleDef<'queryOrUpdate', Query | Update | Pick<Update, 'base' | 'prefixes'>> = {
name: 'queryOrUpdate',
impl: ({ ACTION, SUBRULE, SUBRULE2, OR1, OR2, CONSUME, OPTION1, OPTION2, MANY, context }) => () => {
const prologueValues = SUBRULE(gram.prologue);
impl: ({ ACTION, SUBRULE, SUBRULE2, OR1, OR2, CONSUME, OPTION1, MANY }) => () => {
const prologueValues = SUBRULE(gram.prologue, undefined);
return OR1<Query | Update | Pick<Update, 'base' | 'prefixes'>>([
{ ALT: () => {
const queryType = OR2<Omit<Query, gram.HandledByBase>>([
{ ALT: () => SUBRULE(gram.selectQuery) },
{ ALT: () => SUBRULE(gram.constructQuery) },
{ ALT: () => SUBRULE(gram.describeQuery) },
{ ALT: () => SUBRULE(gram.askQuery) },
{ ALT: () => SUBRULE(gram.selectQuery, undefined) },
{ ALT: () => SUBRULE(gram.constructQuery, undefined) },
{ ALT: () => SUBRULE(gram.describeQuery, undefined) },
{ ALT: () => SUBRULE(gram.askQuery, undefined) },
]);
const values = SUBRULE(gram.valuesClause);
const values = SUBRULE(gram.valuesClause, undefined);
return ACTION(() => (<Query>{
...prologueValues,
...queryType,
Expand All @@ -58,13 +57,13 @@ const queryOrUpdate: RuleDef<'queryOrUpdate', Query | Update | Pick<Update, 'bas
GATE: () => parsedPrologue,
DEF: () => {
parsedPrologue = false;
const updateOperation = SUBRULE(gram.update1);
const updateOperation = SUBRULE(gram.update1, undefined);

updateResult.updates.push(updateOperation);

OPTION1(() => {
CONSUME(l.symbols.semi);
const prologueValues = SUBRULE2(gram.prologue);
const prologueValues = SUBRULE2(gram.prologue, undefined);

ACTION(() => {
updateResult.base = prologueValues.base ?? updateResult.base;
Expand Down Expand Up @@ -116,35 +115,50 @@ export const sparql11ParserBuilder = Builder.createBuilder(queryUnitParserBuilde
.deleteRule('updateUnit')
.addRule(queryOrUpdate);

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

private readonly dataFactory: DataFactory<RDF.BaseQuad>;

public constructor(context: Partial<ImplArgs['context']> = {}) {
this.dataFactory = context.dataFactory ?? new DataFactory({ blankNodePrefix: 'g_' });
public constructor(context: Partial<SparqlContext> = {}) {
this.parser = sparql11ParserBuilder.consumeToParser({
tokenVocabulary: l.sparql11Tokens.build(),
}, {
parseMode: new Set([ gram.canParseVars, gram.canCreateBlankNodes ]),
...context,
dataFactory: this.dataFactory,
});
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.dataFactory.resetBlankNodeCounter();
this.config.dataFactory.resetBlankNodeCounter();
}

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

public parsePath(query: string): (PropertyPath & { prefixes: object }) | IriTerm {
const result = this.parser.path(query);
this.reset()
const result = this.parser.path(query, this.config, undefined);
if ('type' in result) {
return {
...result,
Expand Down
2 changes: 1 addition & 1 deletion engines/engine-sparql-1-1/lib/expressionParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Builder } from '@traqula/core';
import {Builder, type RuleNamesFromList, SparqlContext} from '@traqula/core';
import { gram }from '@traqula/rules-sparql-1-1';


Expand Down
20 changes: 10 additions & 10 deletions engines/engine-sparql-1-1/lib/updateNoModifyParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import { triplesTemplateParserBuilder } from './triplesTemplateParserBuilder';
const update1Patch: typeof gram.update1 = {
name: 'update1',
impl: ({ SUBRULE, OR }) => () => OR([
{ ALT: () => SUBRULE(gram.load) },
{ ALT: () => SUBRULE(gram.clear) },
{ ALT: () => SUBRULE(gram.drop) },
{ ALT: () => SUBRULE(gram.add) },
{ ALT: () => SUBRULE(gram.move) },
{ ALT: () => SUBRULE(gram.copy) },
{ ALT: () => SUBRULE(gram.create) },
{ ALT: () => SUBRULE(gram.insertData) },
{ ALT: () => SUBRULE(gram.deleteData) },
{ ALT: () => SUBRULE(gram.deleteWhere) },
{ ALT: () => SUBRULE(gram.load, undefined) },
{ ALT: () => SUBRULE(gram.clear, undefined) },
{ ALT: () => SUBRULE(gram.drop, undefined) },
{ ALT: () => SUBRULE(gram.add, undefined) },
{ ALT: () => SUBRULE(gram.move, undefined) },
{ ALT: () => SUBRULE(gram.copy, undefined) },
{ ALT: () => SUBRULE(gram.create, undefined) },
{ ALT: () => SUBRULE(gram.insertData, undefined) },
{ ALT: () => SUBRULE(gram.deleteData, undefined) },
{ ALT: () => SUBRULE(gram.deleteWhere, undefined) },
]),
};

Expand Down
4 changes: 2 additions & 2 deletions engines/engine-sparql-1-1/spec/parser.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ErrorSkipped, } from 'rdf-test-suite';
import { Parser } from '../lib'
import {ImplArgs} from "@traqula/core";
import {ImplArgs, SparqlContext} from "@traqula/core";

export async function parse(query: string, context: Partial<ImplArgs['context']> = {}) {
export async function parse(query: string, context: Partial<SparqlContext> = {}) {
const parser = new Parser(context);
parser.parse(query);
}
Expand Down
63 changes: 35 additions & 28 deletions engines/engine-sparql-1-2/lib/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { DataFactory } from 'rdf-data-factory';
import {Builder, ImplArgs} from '@traqula/core';
import { gram as S11 } from '@traqula/rules-sparql-1-1';
import type {
IriTerm,
PropertyPath,
SparqlParser as ISparqlParser,
SparqlQuery,
} from '@traqula/rules-sparql-1-1';
import { gram as S12 } from '@traqula/rules-sparql-1-2';
import type { BaseQuadTerm } from '@traqula/rules-sparql-1-2';
import { lex as l12 } from '@traqula/rules-sparql-1-2';
import { sparql11ParserBuilder } from '@traqula/engine-sparql-1-1';
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 {gram as S12, lex as l12} from '@traqula/rules-sparql-1-2';
import {sparql11ParserBuilder} from '@traqula/engine-sparql-1-1';

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


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

private readonly dataFactory: DataFactory<BaseQuadTerm>;

public constructor(context: Partial<ImplArgs['context']> = {}) {
this.dataFactory = context.dataFactory ?? new DataFactory({ blankNodePrefix: 'g_' });
public constructor(context: Partial<SparqlContext> = {}) {
this.parser = sparql12ParserBuilder.consumeToParser({
tokenVocabulary: l12.sparql12Tokens.build(),
}, {
parseMode: new Set([ S11.canParseVars, S11.canCreateBlankNodes ]),
...context,
dataFactory: this.dataFactory,
});
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.dataFactory.resetBlankNodeCounter();
this.config.dataFactory.resetBlankNodeCounter();
}

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

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

export async function parse(query: string, context: Partial<ImplArgs['context']> = {}) {
export async function parse(query: string, context: Partial<SparqlContext> = {}) {
const parser = new Parser(context);
parser.parse(query);
}
Expand Down
Loading

0 comments on commit 7119cc6

Please sign in to comment.