UNPKG

ifc-expressions

Version:

Parsing and evaluation of IFC expressions

122 lines (121 loc) 5.89 kB
import { IfcExpressionFunctionConfigException } from "../../error/IfcExpressionFunctionConfigException.js"; import { isNullish } from "../../util/IfcExpressionUtils.js"; import { ExprEvalMissingRequiredFunctionArgumentErrorObj, ExprEvalSuccessObj, isExprEvalError, } from "../ExprEvalResult.js"; import { ExprKind } from "../ExprKind.js"; import { Types } from "../../type/Types.js"; import { MissingFunctionArgumentException } from "../../error/MissingFunctionArgumentException.js"; import { WrongFunctionArgumentTypeException } from "../../error/WrongFunctionArgumentTypeException.js"; import { SpuriousFunctionArgumentException } from "../../error/SpuriousFunctionArgumentException.js"; export class Func { constructor(name, args) { this.name = name; if (isNullish(this.name)) { throw new IfcExpressionFunctionConfigException("Function must have a name"); } this.formalArguments = args; this.checkArgs(args); } checkArgumentsAndGetReturnType(argumentTypes, ctx) { this.checkArgumentTypes(argumentTypes, ctx); return this.getReturnType(argumentTypes.map((t) => t[1])); } checkArgumentTypes(providedArgumentTypes, ctx) { const numProvided = providedArgumentTypes.length; if (!isNullish(this.formalArguments)) { if (numProvided > this.formalArguments.length) { throw new SpuriousFunctionArgumentException(this.name, "[unexpected argument]", this.formalArguments.length, ctx, `Function expects (at most) ${this.formalArguments.length} arguments`); } for (let i = 0; i < this.formalArguments.length; i++) { const currentArg = this.formalArguments[i]; if (numProvided > i) { Types.requireWeakIsAssignableFrom(this.formalArguments[i].getType(), providedArgumentTypes[i][1], () => new WrongFunctionArgumentTypeException(this.name, this.formalArguments[i].name, this.formalArguments[i].getType(), providedArgumentTypes[i][1], i, providedArgumentTypes[i][0])); } else { if (currentArg.hasDefaultValue()) { //should be fine. } if (currentArg.required) { throw new MissingFunctionArgumentException(this.name, currentArg.name, i, ctx); } } } } } getName() { return this.name; } evaluate(callingExpr, funcArgs) { const evaluatedArguments = this.getArgumentValues(callingExpr, funcArgs); if (isExprEvalError(evaluatedArguments)) { return evaluatedArguments; } const argumentsReadyForUse = new Map(); for (const [argName, argValue] of evaluatedArguments.entries()) { if (isExprEvalError(argValue)) { return argValue; } argumentsReadyForUse.set(argName, argValue.result); } return this.calculateResult(callingExpr, argumentsReadyForUse); } /** * Override to transform individual arguments if needed. After this step, if any of the ExprEvalResult objects in the * returned array is an ExprEvalError, the function evaluation fails. * @param callingExpr * @param evaluatedArguments * @protected */ transformArguments(callingExpr, evaluatedArguments) { } /** * Gets the argument values from the list of provided arguments in the form of 'name' -> ExprEvalResult (which may contain errors). * Generates an error if a required value is missing. * @param callingExpr * @param provided * @protected */ getArgumentValues(callingExpr, provided) { const result = new Map(); const numProvided = provided.length; if (!isNullish(this.formalArguments)) { for (let i = 0; i < this.formalArguments.length; i++) { const currentArg = this.formalArguments[i]; if (numProvided > i) { result.set(currentArg.name, currentArg.transformValue(callingExpr, provided[i])); } else { if (currentArg.hasDefaultValue()) { result.set(currentArg.name, new ExprEvalSuccessObj(currentArg.defaultValue)); } if (currentArg.required) { return new ExprEvalMissingRequiredFunctionArgumentErrorObj(ExprKind.FUNCTION_ARGUMENTS, `Required argument ${currentArg.name}, expected at position ${i} (starting at 0), is missing`, this.name, currentArg.name, i, callingExpr.getTextSpan()); } } } } this.transformArguments(callingExpr, result); return result; } checkArgs(args) { if (!Array.isArray(this.formalArguments)) { throw new IfcExpressionFunctionConfigException("Formal function arguments is not an array"); } let optionalArgsReached = false; const names = new Set(); for (let i = 0; i < this.formalArguments.length; i++) { if (names.has(this.formalArguments[i].name)) { throw new IfcExpressionFunctionConfigException(`Duplicate argument name '${this.formalArguments[i].name}'.`); } if (optionalArgsReached) { if (this.formalArguments[i].required) { throw new IfcExpressionFunctionConfigException(`Optional arguments must follow required ones. Argument '${this.formalArguments[i].name}' is required but follows an optional one`); } } else { if (!this.formalArguments[i].required) { optionalArgsReached = true; } } } } } //# sourceMappingURL=Func.js.map