UNPKG

ifc-expressions

Version:

Parsing and evaluation of IFC expressions

126 lines (125 loc) 6.08 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); } withDocumentation(documentation) { this.documentationRef = documentation; return this; } getDocumentation() { return this.documentationRef; } getFormalArguments() { return this.formalArguments; } getSignatureLabel(displayName = this.name) { const preferredArguments = this.formalArguments.filter((argument) => argument.displayLabel !== undefined); const signatureArguments = preferredArguments.length > 0 ? preferredArguments : this.formalArguments.filter((argument) => argument.required); const labels = signatureArguments.map((argument) => argument.displayLabel?.fallback ?? argument.name); return `${displayName}(${labels.join(", ")})`; } 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); } transformArguments(callingExpr, evaluatedArguments) { } 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