ifc-expressions
Version:
Parsing and evaluation of IFC expressions
122 lines (121 loc) • 5.89 kB
JavaScript
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