diff --git a/packages/core/lib/grammar-builder/parserBuilder.ts b/packages/core/lib/grammar-builder/parserBuilder.ts index 3e247c1..f5f1b43 100644 --- a/packages/core/lib/grammar-builder/parserBuilder.ts +++ b/packages/core/lib/grammar-builder/parserBuilder.ts @@ -56,7 +56,7 @@ export class Builder> { /** * Add a rule to the grammar. If the rule already exists, but the implementation differs, an error will be thrown. */ - public addRuleRedundant(rule: RuleDef): + public addRuleRedundant(rule: RuleDef): Builder : ( K extends Names ? (RuleDefs[K] extends RuleDef ? RuleDefs[K] : never ) : never) }> { const self = : ( K extends Names ? (RuleDefs[K] extends RuleDef ? RuleDefs[K] : never ) : never) }>> this; @@ -156,7 +156,7 @@ export class Builder> { const selfSufficientParser: Partial> = {}; // eslint-disable-next-line ts/no-unnecessary-type-assertion for (const rule of []> Object.values(this.rules)) { - selfSufficientParser[rule.name] = ((input: string, ...args: unknown[]) => { + selfSufficientParser[rule.name] = ((input: string, arg: unknown) => { // Transform input in accordance to 19.2 input = input.replaceAll( /\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{8})/gu, @@ -183,7 +183,7 @@ export class Builder> { parser.reset(); parser.input = lexResult.tokens; - const result = parser[rule.name](...args); + const result = parser[rule.name](arg); if (parser.errors.length > 0) { // Console.log(lexResult.tokens); throw new Error(`Parse error on line ${parser.errors.map(x => x.token.startLine).join(', ')} @@ -251,6 +251,11 @@ ${parser.errors.map(x => `${x.token.startLine}: ${x.message}`).join('\n')}`); } private getSelfRef(): CstDef { + const subRuleImpl = (subrule: typeof this.SUBRULE): CstDef['SUBRULE'] => { + return ((cstDef, ...args) => { + return subrule( this[ cstDef.name], { ARGS: args }); + }) satisfies CstDef['SUBRULE']; + } return { CONSUME: (tokenType, option) => this.CONSUME(tokenType, option), CONSUME1: (tokenType, option) => this.CONSUME1(tokenType, option), @@ -330,76 +335,16 @@ ${parser.errors.map(x => `${x.token.startLine}: ${x.message}`).join('\n')}`); throw error; } }, - SUBRULE: (cstDef, ...args) => { - try { - return this.SUBRULE( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE1: (cstDef, ...args) => { - try { - return this.SUBRULE1( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE2: (cstDef, ...args) => { - try { - return this.SUBRULE2( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE3: (cstDef, ...args) => { - try { - return this.SUBRULE3( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE4: (cstDef, ...args) => { - try { - return this.SUBRULE4( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE5: (cstDef, ...args) => { - try { - return this.SUBRULE5( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE6: (cstDef, ...args) => { - try { - return this.SUBRULE6( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE7: (cstDef, ...args) => { - try { - return this.SUBRULE7( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE8: (cstDef, ...args) => { - try { - return this.SUBRULE8( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, - SUBRULE9: (cstDef, ...args) => { - try { - return this.SUBRULE9( this[ cstDef.name], { ARGS: args }); - } catch (error: unknown) { - throw error; - } - }, + SUBRULE: subRuleImpl((rule, args) => this.SUBRULE(rule, args)), + SUBRULE1: subRuleImpl((rule, args) => this.SUBRULE1(rule, args)), + SUBRULE2: subRuleImpl((rule, args) => this.SUBRULE2(rule, args)), + SUBRULE3: subRuleImpl((rule, args) => this.SUBRULE3(rule, args)), + SUBRULE4: subRuleImpl((rule, args) => this.SUBRULE4(rule, args)), + SUBRULE5: subRuleImpl((rule, args) => this.SUBRULE5(rule, args)), + SUBRULE6: subRuleImpl((rule, args) => this.SUBRULE6(rule, args)), + SUBRULE7: subRuleImpl((rule, args) => this.SUBRULE7(rule, args)), + SUBRULE8: subRuleImpl((rule, args) => this.SUBRULE8(rule, args)), + SUBRULE9: subRuleImpl((rule, args) => this.SUBRULE9(rule, args)), }; } } diff --git a/packages/core/lib/grammar-builder/ruleDefTypes.ts b/packages/core/lib/grammar-builder/ruleDefTypes.ts index 3438940..0088087 100644 --- a/packages/core/lib/grammar-builder/ruleDefTypes.ts +++ b/packages/core/lib/grammar-builder/ruleDefTypes.ts @@ -17,13 +17,13 @@ export type RuleDef< ParamType extends unknown[] = unknown[], > = { name: NameType; - impl: (def: ImplArgs) => (...args: ArrayElementsUndefinable) => ReturnType; + impl: (def: ImplArgs) => (...args: ArrayMagicWork) => ReturnType; }; export type RuleDefReturn = T extends RuleDef ? Ret : never; -type ArrayElementsUndefinable = - ArrayType extends [infer First, ...infer Rest] ? [First | undefined, ...ArrayElementsUndefinable] : []; +type ArrayMagicWork = + ArrayType extends [infer First, ...infer Rest] ? [First, ...ArrayMagicWork] : []; export interface ImplArgs extends CstDef { cache: WeakMap; diff --git a/packages/rules-sparql-1-1/lib/Sparql11types.ts b/packages/rules-sparql-1-1/lib/Sparql11types.ts index 79555ef..fb78576 100644 --- a/packages/rules-sparql-1-1/lib/Sparql11types.ts +++ b/packages/rules-sparql-1-1/lib/Sparql11types.ts @@ -18,9 +18,6 @@ export type Triple = { object: Term; }; -export type TripleCreatorS = (part: Pick) => Triple; -export type TripleCreatorSP = (part: Pick) => Triple; - export interface IGraphNode { node: ITriplesNode['node'] | Term; triples: Triple[]; diff --git a/packages/rules-sparql-1-1/lib/grammar/tripleBlock.ts b/packages/rules-sparql-1-1/lib/grammar/tripleBlock.ts index b7b9e71..8294f5a 100644 --- a/packages/rules-sparql-1-1/lib/grammar/tripleBlock.ts +++ b/packages/rules-sparql-1-1/lib/grammar/tripleBlock.ts @@ -5,14 +5,12 @@ import { var_, varOrTerm, verb } from './general'; import { canCreateBlankNodes } from './literals'; import { path } from './propertyPaths'; import type { - BgpPattern, + BgpPattern, BlankTerm, IGraphNode, IriTerm, ITriplesNode, PropertyPath, Triple, - TripleCreatorS, - TripleCreatorSP, VariableTerm, } from '../Sparql11types'; @@ -45,18 +43,15 @@ function triplesSameSubjectImpl(name: T, allowPaths: boolean): { ALT: () => { const subject = SUBRULE(varOrTerm); - const propNotEmpty = SUBRULE(allowPaths ? propertyListPathNotEmpty : propertyListNotEmpty); - - return ACTION(() => - propNotEmpty.map(partial => partial({ subject }))); + return SUBRULE(allowPaths ? propertyListPathNotEmpty : propertyListNotEmpty, subject); }, }, { ALT: () => { const subjectNode = SUBRULE(allowPaths ? triplesNodePath : triplesNode); - const restNode = SUBRULE(allowPaths ? propertyListPath : propertyList); + const restNode = SUBRULE(allowPaths ? propertyListPath : propertyList, subjectNode.node); return ACTION(() => [ - ...restNode.map(partial => partial({ subject: subjectNode.node })), + ...restNode, ...subjectNode.triples, ]); }, @@ -71,11 +66,11 @@ export const triplesSameSubjectPath = triplesSameSubjectImpl('triplesSameSubject * [[76]](https://www.w3.org/TR/sparql11-query/#rPropertyList) * [[82]](https://www.w3.org/TR/sparql11-query/#rPropertyListPath) */ -function propertyListImpl(name: T, allowPaths: boolean): RuleDef { +function propertyListImpl(name: T, allowPaths: boolean): RuleDef { return { name, - impl: ({ SUBRULE, OPTION }) => () => - OPTION(() => SUBRULE(allowPaths ? propertyListPathNotEmpty : propertyListNotEmpty)) ?? [], + impl: ({ SUBRULE, OPTION }) => (subject) => + OPTION(() => SUBRULE(allowPaths ? propertyListPathNotEmpty : propertyListNotEmpty, subject)) ?? [], }; } export const propertyList = propertyListImpl('propertyList', false); @@ -90,11 +85,11 @@ export const propertyListPath = propertyListImpl('propertyListPath', true); function propertyListNotEmptyImplementation( name: T, allowPaths: boolean, -): RuleDef { +): RuleDef { return { name, - impl: ({ ACTION, CONSUME, MANY, SUBRULE1, SUBRULE2, OPTION, OR1, OR2, context }) => () => { - const result: TripleCreatorS[] = []; + impl: ({ ACTION, CONSUME, MANY, SUBRULE1, SUBRULE2, OPTION, OR1, OR2, context }) => (subject) => { + const result: Triple[] = []; const resultAppendage: typeof result = []; // Generates predicate and objectList @@ -104,19 +99,22 @@ function propertyListNotEmptyImplementation( { ALT: () => SUBRULE1(verbSimple) }, ]) : SUBRULE1(verb); - const firstObjects = SUBRULE1(allowPaths ? objectListPath : objectList); + let filterSubject: IriTerm; + const firstObjects = SUBRULE1( + allowPaths ? objectListPath : objectList, + ACTION(() => filterSubject = context.dataFactory.namedNode('internal:filterSubject')), + firstProperty + ); ACTION(() => { - // TODO: this filter is only here to be compliant with sparqlJS and is quite arbitrary. - // For the first predicate, - // additionally generated triples (like from collections) are shoved to the back of the result. - const filterSubject = context.dataFactory.namedNode('internal:filterSubject'); - for (const cObject of firstObjects) { - const triple = cObject({ subject: filterSubject, predicate: firstProperty }); - const generator: TripleCreatorS = rest => cObject({ ...rest, predicate: firstProperty }); + for (const triple of firstObjects) { if (triple.subject === filterSubject) { - result.push(generator); + result.push({ + subject: subject, + predicate: triple.predicate, + object: triple.object, + }); } else { - resultAppendage.push(generator); + resultAppendage.push(triple); } } }); @@ -131,13 +129,10 @@ function propertyListNotEmptyImplementation( ]) : SUBRULE2(verb); // https://www.w3.org/2013/sparql-errata#errata-query-3 - const objects = SUBRULE2(allowPaths ? objectListPath : objectList); + const objects = SUBRULE2(allowPaths ? objectListPath : objectList, subject, predicate); ACTION(() => { - result.push( - ...objects - .map(object => (partS: Pick) => object({ ...partS, predicate })), - ); + result.push(...objects); }); }); }); @@ -168,14 +163,14 @@ export const verbSimple: RuleDef<'verbSimple', VariableTerm> = { * [[79]](https://www.w3.org/TR/sparql11-query/#rObjectList) * [[86]](https://www.w3.org/TR/sparql11-query/#rObjectListPath) */ -function objectListImpl(name: T, allowPaths: boolean): RuleDef { +function objectListImpl(name: T, allowPaths: boolean): RuleDef { return { name, - impl: ({ ACTION, SUBRULE, AT_LEAST_ONE_SEP }) => () => { - const objects: TripleCreatorSP[] = []; + impl: ({ ACTION, SUBRULE, AT_LEAST_ONE_SEP }) => (subject, predicate) => { + const objects: Triple[] = []; AT_LEAST_ONE_SEP({ DEF: () => { - const objectTriples = SUBRULE(allowPaths ? objectPath : object); + const objectTriples = SUBRULE(allowPaths ? objectPath : object, subject, predicate); ACTION(() => objects.push(...objectTriples)); }, SEP: l.symbols.comma, @@ -187,15 +182,15 @@ function objectListImpl(name: T, allowPaths: boolean): RuleDef export const objectList = objectListImpl('objectList', false); export const objectListPath = objectListImpl('objectListPath', true); -function objectImpl(name: T, allowPaths: boolean): RuleDef { +function objectImpl(name: T, allowPaths: boolean): RuleDef { return { name, - impl: ({ ACTION, SUBRULE }) => () => { + impl: ({ ACTION, SUBRULE }) => (subject, predicate) => { const node = SUBRULE(allowPaths ? graphNodePath : graphNode); return ACTION(() => [ - part => ({ ...part, object: node.node }), - ...node.triples.map(val => () => val), - ] satisfies TripleCreatorSP[]); + { subject, predicate, object: node.node }, + ...node.triples, + ]); }, }; } @@ -235,14 +230,17 @@ function blankNodePropertyListImpl(name: T, allowPaths: boolea name, impl: ({ ACTION, SUBRULE, CONSUME, context }) => () => { CONSUME(l.symbols.LSquare); - const propList = SUBRULE(allowPaths ? propertyListPathNotEmpty : propertyListNotEmpty); + let blankNode: BlankTerm; + const propList = SUBRULE( + allowPaths ? propertyListPathNotEmpty : propertyListNotEmpty, + ACTION(() => blankNode = context.dataFactory.blankNode()), + ); CONSUME(l.symbols.RSquare); return ACTION(() => { - const subject = context.dataFactory.blankNode(); return { - node: subject, - triples: propList.map(part => part({ subject })), + node: blankNode, + triples: propList, }; }); }, diff --git a/packages/rules-sparql-1-1/lib/utils.ts b/packages/rules-sparql-1-1/lib/utils.ts index ca5a806..e175898 100644 --- a/packages/rules-sparql-1-1/lib/utils.ts +++ b/packages/rules-sparql-1-1/lib/utils.ts @@ -1,4 +1,10 @@ -import type {GroupPattern, Pattern, PropertyPath, Term, VariableTerm} from "./Sparql11types"; +import type { + GroupPattern, + Pattern, + PropertyPath, + Term, + VariableTerm +} from "./Sparql11types"; export function deGroupSingle(group: GroupPattern): Pattern { return group.patterns.length === 1 ? group.patterns[0] : group; @@ -6,4 +12,4 @@ export function deGroupSingle(group: GroupPattern): Pattern { export function isVariable(term: Term | PropertyPath): term is VariableTerm { return 'termType' in term && term.termType === 'Variable'; -} \ No newline at end of file +} diff --git a/packages/rules-sparql-1-2/lib/grammar.ts b/packages/rules-sparql-1-2/lib/grammar.ts index c214b16..b9d4c48 100644 --- a/packages/rules-sparql-1-2/lib/grammar.ts +++ b/packages/rules-sparql-1-2/lib/grammar.ts @@ -9,7 +9,7 @@ import type { DirectionalLanguage } from '@rdfjs/types'; import type { NamedNode } from 'rdf-data-factory'; import * as l12 from './lexer'; import type { RuleDefReturn, RuleDef } from '@traqula/core'; -import { funcExpr1, funcExpr3 } from '@traqula/rules-sparql-1-1'; +import {funcExpr1, funcExpr3} from '@traqula/rules-sparql-1-1'; import { gram as S11, lex as l11 } from '@traqula/rules-sparql-1-1'; import type * as T11 from '@traqula/rules-sparql-1-1'; import { CommonIRIs } from '@traqula/core'; @@ -19,8 +19,6 @@ import type { IGraphNode, Term, Triple, - TripleCreatorS, - TripleCreatorSP, } from './sparql12Types'; function reifiedTripleBlockImpl(name: T, allowPath: boolean): RuleDef { @@ -28,11 +26,11 @@ function reifiedTripleBlockImpl(name: T, allowPath: boolean): name, impl: ({ ACTION, SUBRULE }) => () => { const triple = SUBRULE(reifiedTriple); - const properties = SUBRULE(allowPath ? S11.propertyListPath : S11.propertyList); + const properties = SUBRULE(allowPath ? S11.propertyListPath : S11.propertyList, triple.node); return ACTION(() => [ ...triple.triples, - ...properties.map(partial => partial({ subject: triple.node })), + ...properties, ]); }, }; @@ -108,36 +106,38 @@ export const triplesSameSubject = triplesSameSubjectImpl('triplesSameSubject', f */ export const triplesSameSubjectPath = triplesSameSubjectImpl('triplesSameSubjectPath', true); -function objectImpl(name: T, allowPaths: boolean): RuleDef { +function objectImpl(name: T, allowPaths: boolean): RuleDef { return { name, - impl: ({ ACTION, SUBRULE, context }) => () => { + impl: ({ ACTION, SUBRULE, context }) => (subject, predicate) => { const objectVal = SUBRULE(allowPaths ? graphNodePath : graphNode); const annotationVal = SUBRULE(allowPaths ? annotationPath : annotation); + // This rule knows the annotation. And for each annotation node, we need to make a triple: + // + return ACTION(() => { - const result: TripleCreatorSP[] = [ + if ('type' in predicate && predicate.type === 'path' && annotationVal.length > 0) { + throw new Error('Note 17 violation'); + } + + const result: Triple[] = [ // You parse the object - ({ subject, predicate }) => ({ subject, predicate, object: objectVal.node }), + { subject, predicate, object: objectVal.node }, // You might get some additional triples from parsing the object (like when it's a collection) - ...objectVal.triples.map(triple => () => triple), + ...objectVal.triples, ]; for (const annotation of annotationVal) { - result.push(({ subject, predicate }) => { - if ('type' in predicate && predicate.type === 'path') { - throw new Error('Note 17 violation'); - } - return context.dataFactory.quad( - annotation.node, - context.dataFactory.namedNode(CommonIRIs.REIFIES), - context.dataFactory.quad( - subject, - >predicate, - objectVal.node, - ), - ) - }); - result.push(...annotation.triples.map(triple => () => triple)); + result.push( context.dataFactory.quad( + annotation.node, + context.dataFactory.namedNode(CommonIRIs.REIFIES), + context.dataFactory.quad( + subject, + >predicate, + objectVal.node, + ), + )); + result.push(...annotation.triples); } return result; }); @@ -177,18 +177,23 @@ function annotationImpl(name: T, allowPaths: boolean): RuleDef currentReifier = node; } }, { ALT: () => { - const block = SUBRULE(allowPaths ? annotationBlockPath : annotationBlock); - + let node: Triple['subject']; + const block = SUBRULE( + allowPaths ? annotationBlockPath : annotationBlock, + ACTION(() => { + if (currentReifier === undefined && !context.parseMode.has(S11.canCreateBlankNodes)) { + throw new Error('Cannot create blanknodes in current parse mode'); + } + node = currentReifier ?? context.dataFactory.blankNode(); + return node; + }), + ); ACTION(() => { - if (currentReifier === undefined && !context.parseMode.has(S11.canCreateBlankNodes)) { - throw new Error('Cannot create blanknodes in current parse mode'); - } - const node = currentReifier ?? context.dataFactory.blankNode(); annotations.push({ node, - triples: block.map(partial => partial({ subject: node })), + triples: block, }); - currentReifier = undefined; + currentReifier = undefined }); } }, ]); @@ -209,12 +214,12 @@ export const annotationPath = annotationImpl('annotationPath', true); */ export const annotation = annotationImpl('annotation', false); -function annotationBlockImpl(name: T, allowPaths: boolean): RuleDef { +function annotationBlockImpl(name: T, allowPaths: boolean): RuleDef { return { name, - impl: ({ SUBRULE, CONSUME }) => () => { + impl: ({ SUBRULE, CONSUME }) => (subject) => { CONSUME(l12.annotationOpen); - const res = SUBRULE(allowPaths ? S11.propertyListPathNotEmpty : S11.propertyListNotEmpty); + const res = SUBRULE(allowPaths ? S11.propertyListPathNotEmpty : S11.propertyListNotEmpty, subject); CONSUME(l12.annotationClose); return res; diff --git a/packages/rules-sparql-1-2/lib/sparql12Types.ts b/packages/rules-sparql-1-2/lib/sparql12Types.ts index 03a65dc..699c888 100644 --- a/packages/rules-sparql-1-2/lib/sparql12Types.ts +++ b/packages/rules-sparql-1-2/lib/sparql12Types.ts @@ -12,15 +12,6 @@ export type Triple = { object: Term; }; -/** - * OVERRIDES: {@link T11.TripleCreatorS} - */ -export type TripleCreatorS = (part: Pick) => Triple; -/** - * OVERRIDES: {@link T11.TripleCreatorSP} - */ -export type TripleCreatorSP = (part: Pick) => Triple; - /** * OVERRIDES: {@link T11.ITriplesNode} */ diff --git a/packages/test-utils/README.md b/packages/test-utils/README.md index c6a4f95..dd3a610 100644 --- a/packages/test-utils/README.md +++ b/packages/test-utils/README.md @@ -5,11 +5,11 @@ TRAQULA test utils contains some developer dependencies for testing packages and ## Installation ```bash -npm install -D @traqula/test-utils +npm install -D @traqula/test-utils.ts ``` or ```bash -yarn add -D @traqula/test-utils +yarn add -D @traqula/test-utils.ts ``` \ No newline at end of file