Skip to content

Commit

Permalink
transform named & regular functions to classes
Browse files Browse the repository at this point in the history
  • Loading branch information
jitsedesmet committed Oct 23, 2023
1 parent fb036a4 commit c987901
Show file tree
Hide file tree
Showing 8 changed files with 693 additions and 612 deletions.
91 changes: 22 additions & 69 deletions packages/expression-evaluator/lib/functions/Core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,15 @@ import type { ExpressionEvaluator } from '../evaluators/ExpressionEvaluator';
import type * as E from '../expressions';
import type * as C from '../util/Consts';
import * as Err from '../util/Errors';
import type { ISuperTypeProvider } from '../util/TypeHandling';
import type { FunctionArgumentsCache, ImplementationFunction, OverloadTree } from './OverloadTree';
import type { OverloadTree } from './OverloadTree';

// ----------------------------------------------------------------------------
// Overloaded Functions
// ----------------------------------------------------------------------------

// Function and operator arguments are 'flattened' in the SPARQL spec.
// If the argument is a literal, the datatype often also matters.
export type ArgumentType = 'term' | E.TermType | C.TypeURL | C.TypeAlias;

export interface IOverloadedDefinition {
arity: number | number[];
overloads: OverloadTree;
}

export abstract class BaseFunction<Operator> implements IFunctionExpression {
private readonly arity: number | number[];
private readonly overloads: OverloadTree;

protected constructor(public operator: Operator, definition: IOverloadedDefinition) {
this.arity = definition.arity;
this.overloads = definition.overloads;
}
export abstract class SparqlFunction implements IFunctionExpression {
protected abstract readonly arity: number | number[];
public abstract apply(evalContext: IEvalContext): Promise<E.TermExpression>;

public checkArity(args: E.Expression[]): boolean {
if (Array.isArray(this.arity)) {
Expand All @@ -39,47 +24,8 @@ export abstract class BaseFunction<Operator> implements IFunctionExpression {

return args.length === this.arity;
}

/**
* A function application works by monomorphing the function to a specific
* instance depending on the runtime types. We then just apply this function
* to the args.
*/
public applyOnTerms(args: E.TermExpression[], exprEval: ExpressionEvaluator): E.TermExpression {
const concreteFunction =
this.monomorph(args, exprEval.context.superTypeProvider, exprEval.context.functionArgumentsCache) ||
this.handleInvalidTypes(args);
return concreteFunction(exprEval)(args);
}

public async apply({ args, exprEval, mapping }: IEvalContext): Promise<E.TermExpression> {
return this.applyOnTerms(
await Promise.all(args.map(arg => exprEval.evaluator.evaluate(arg, mapping))),
exprEval,
);
}

protected abstract handleInvalidTypes(args: E.TermExpression[]): never;

/**
* We monomorph by checking the map of overloads for keys corresponding
* to the runtime types. We start by checking for an implementation for the
* most concrete types (integer, string, date, IRI), if we find none,
* we consider their term types (literal, blank, IRI), and lastly we consider
* all arguments as generic terms.
*
* Another option would be to populate the overloads with an implementation
* for every concrete type when the function is generic over termtypes or
* terms.
*/
private monomorph(args: E.TermExpression[], superTypeProvider: ISuperTypeProvider,
functionArgumentsCache: FunctionArgumentsCache): ImplementationFunction | undefined {
return this.overloads.search(args, superTypeProvider, functionArgumentsCache);
}
}

// Regular Functions ----------------------------------------------------------

/**
* Varying kinds of functions take arguments of different types on which the
* specific behaviour is dependant. Although their behaviour is often varying,
Expand All @@ -98,23 +44,30 @@ export abstract class BaseFunction<Operator> implements IFunctionExpression {
* See also: https://www.w3.org/TR/sparql11-query/#func-rdfTerms
* and https://www.w3.org/TR/sparql11-query/#OperatorMapping
*/
export class RegularFunction extends BaseFunction<C.RegularOperator> {
public constructor(op: C.RegularOperator, definition: IOverloadedDefinition) {
super(op, definition);
}
export abstract class TermSparqlFunction<O extends C.RegularOperator | C.NamedOperator> extends SparqlFunction {
protected abstract readonly overloads: OverloadTree;
public abstract operator: O;

protected handleInvalidTypes(args: E.TermExpression[]): never {
throw new Err.InvalidArgumentTypes(args, this.operator);
public applyOnTerms(args: E.TermExpression[], exprEval: ExpressionEvaluator): E.TermExpression {
const concreteFunction =
this.overloads.search(args, exprEval.context.superTypeProvider, exprEval.context.functionArgumentsCache) ||
this.handleInvalidTypes(args);
return concreteFunction(exprEval)(args);
}
}

// Named Functions ------------------------------------------------------------
export class NamedFunction extends BaseFunction<C.NamedOperator> {
public constructor(op: C.NamedOperator, definition: IOverloadedDefinition) {
super(op, definition);
public async apply({ args, exprEval, mapping }: IEvalContext): Promise<E.TermExpression> {
return this.applyOnTerms(
await Promise.all(args.map(arg => exprEval.evaluator.evaluate(arg, mapping))),
exprEval,
);
}

protected handleInvalidTypes(args: E.TermExpression[]): never {
throw new Err.InvalidArgumentTypes(args, this.operator);
}
}

export abstract class RegularFunction extends TermSparqlFunction<C.RegularOperator> { }

export abstract class NamedFunction extends TermSparqlFunction<C.NamedOperator> { }

3 changes: 1 addition & 2 deletions packages/expression-evaluator/lib/functions/Helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import * as C from '../util/Consts';
import { TypeURL } from '../util/Consts';
import type { IDateTimeRepresentation } from '../util/DateTimeHelpers';
import * as Err from '../util/Errors';
import type { ArgumentType } from './Core';
import type { ImplementationFunction } from './OverloadTree';
import type { ArgumentType, ImplementationFunction } from './OverloadTree';
import { OverloadTree } from './OverloadTree';

type Term = E.TermExpression;
Expand Down
Loading

0 comments on commit c987901

Please sign in to comment.