ifc-expressions
Version:
Parsing and evaluation of IFC expressions
191 lines (190 loc) • 9.14 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IfcExpressionValidationListener = void 0;
const IfcExpressionListener_js_1 = __importDefault(require("../gen/parser/IfcExpressionListener.js"));
const IfcExpressionParser_js_1 = require("../gen/parser/IfcExpressionParser.js");
const IfcExpressionFunctions_js_1 = require("../expression/function/IfcExpressionFunctions.js");
const NoSuchFunctionException_js_1 = require("../error/NoSuchFunctionException.js");
const InvalidSyntaxException_js_1 = require("../error/InvalidSyntaxException.js");
const Types_js_1 = require("../type/Types.js");
const TypeManager_js_1 = require("./TypeManager.js");
const ExpressionTypeError_js_1 = require("../error/ExpressionTypeError.js");
const IfcExpressionUtils_js_1 = require("../util/IfcExpressionUtils.js");
const ValidationException_js_1 = require("../error/ValidationException.js");
class IfcExpressionValidationListener extends IfcExpressionListener_js_1.default {
constructor() {
super();
this.methodCallTargetStack = [];
this.enterFunctionCall = (ctx) => {
if (!IfcExpressionFunctions_js_1.IfcExpressionFunctions.isBuiltinFunction(ctx.IDENTIFIER().getText())) {
throw new NoSuchFunctionException_js_1.NoSuchFunctionException(ctx.IDENTIFIER().getText(), ctx);
}
};
this.enterSEUnaryMultipleMinus = (ctx) => {
throw new InvalidSyntaxException_js_1.InvalidSyntaxException("--", ctx);
};
this.exitSEBooleanBinaryOp = (ctx) => {
this.typeManager.requireLogicalOrBoolean(ctx._left);
this.typeManager.requireLogicalOrBoolean(ctx._right);
this.typeManager.setType(ctx, Types_js_1.Types.boolean());
};
this.exitSEComparison = (ctx) => {
this.typeManager.requireTypesOverlap(ctx._left, ctx._right);
this.typeManager.setType(ctx, Types_js_1.Types.boolean());
};
this.exitSEParenthesis = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx._sub);
};
this.exitSELiteral = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx._sub);
};
this.exitLiteral = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx.getChild(0));
};
this.exitNumLiteral = (ctx) => {
this.typeManager.setType(ctx, Types_js_1.Types.numeric());
};
this.exitStringLiteral = (ctx) => {
this.typeManager.setType(ctx, Types_js_1.Types.string());
};
this.exitBooleanLiteral = (ctx) => {
this.typeManager.setType(ctx, Types_js_1.Types.boolean());
};
this.exitLogicalLiteral = (ctx) => {
this.typeManager.setType(ctx, Types_js_1.Types.logical());
};
this.exitExpr = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx.singleExpr());
};
this.exitSEMulDiv = (ctx) => {
this.typeManager.requireNumeric(ctx._left);
this.typeManager.requireNumeric(ctx._right);
this.typeManager.setType(ctx, Types_js_1.Types.numeric());
};
this.exitSEPower = (ctx) => {
this.typeManager.requireNumeric(ctx._left);
this.typeManager.requireNumeric(ctx._right);
this.typeManager.setType(ctx, Types_js_1.Types.numeric());
};
this.exitSEFunctionCall = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx._sub);
};
this.exitSEArrayExpr = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx._sub);
};
this.exitSENot = (ctx) => {
this.typeManager.requireLogicalOrBoolean(ctx._sub);
this.typeManager.setType(ctx, Types_js_1.Types.boolean());
};
this.exitSEVariableRef = (ctx) => {
switch (ctx._sub.IDENTIFIER().getText()) {
case "property":
this.typeManager.setType(ctx, Types_js_1.Type.IFC_PROPERTY_REF);
return;
case "element":
this.typeManager.setType(ctx, Types_js_1.Type.IFC_ELEMENT_REF);
return;
}
throw new ValidationException_js_1.ValidationException(`Encountered Variable ref that was neither $property nor $element`, ctx);
};
this.exitSEUnaryMinus = (ctx) => {
this.typeManager.requireNumeric(ctx._sub);
this.typeManager.setType(ctx, Types_js_1.Types.numeric());
};
this.exitSEAddSub = (ctx) => {
if (this.typeManager.overlapsWithString(ctx._left, ctx._right)) {
this.typeManager.setType(ctx, Types_js_1.Types.string());
}
else if (this.typeManager.overlapsWithNumeric(ctx._left, ctx._right)) {
this.typeManager.setType(ctx, Types_js_1.Types.numeric());
}
else {
throw new ExpressionTypeError_js_1.ExpressionTypeError(`Operator '+' does not allow provided operand types ${this.typeManager
.getType(ctx._left)
.getName()}(left operand) and ${this.typeManager
.getType(ctx._right)
.getName()}(right operand). Operands must be both string or both numeric.`, ctx);
}
};
this.exitSEMethodCall = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx._call);
};
this.enterMethodCallChainInner = (ctx) => {
this.pushMethodCallTarget(ctx);
};
this.exitMethodCallChainInner = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx._call);
};
this.enterMethodCallChainEnd = (ctx) => {
this.pushMethodCallTarget(ctx);
};
this.exitMethodCallChainEnd = (ctx) => {
this.typeManager.copyTypeFrom(ctx, ctx.functionCall());
};
this.exitFunctionCall = (ctx) => {
const func = IfcExpressionFunctions_js_1.IfcExpressionFunctions.getFunction(ctx.IDENTIFIER().getText());
const argumentTypes = this.collectArgumentTypes(ctx.exprList());
const parent = ctx.parentCtx;
if (parent instanceof IfcExpressionParser_js_1.MethodCallChainInnerContext ||
parent instanceof IfcExpressionParser_js_1.MethodCallChainEndContext ||
parent instanceof IfcExpressionParser_js_1.SEMethodCallContext) {
argumentTypes.unshift(this.methodCallTargetStack.pop());
}
const returnType = func.checkArgumentsAndGetReturnType(argumentTypes, ctx);
this.typeManager.setType(ctx, returnType);
};
this.collectArgumentTypes = (ctx, resultSoFar) => {
if ((0, IfcExpressionUtils_js_1.isNullish)(resultSoFar)) {
resultSoFar = [];
}
if (!(0, IfcExpressionUtils_js_1.isNullish)(ctx)) {
resultSoFar.push([
ctx.singleExpr(),
this.typeManager.getType(ctx.singleExpr()),
]);
const rest = ctx.exprList();
if (!(0, IfcExpressionUtils_js_1.isNullish)(rest)) {
return this.collectArgumentTypes(rest, resultSoFar);
}
}
return resultSoFar;
};
this.exitArrayExpr = (ctx) => {
this.typeManager.setType(ctx, Types_js_1.Types.tuple(...this.collectArrayElementTypes(ctx.arrayElementList())));
};
this.collectArrayElementTypes = (ctx, resultSoFar) => {
if ((0, IfcExpressionUtils_js_1.isNullish)(resultSoFar)) {
resultSoFar = [];
}
if (!(0, IfcExpressionUtils_js_1.isNullish)(ctx)) {
resultSoFar.push(this.typeManager.getType(ctx.singleExpr()));
const rest = ctx.arrayElementList();
if (!(0, IfcExpressionUtils_js_1.isNullish)(rest)) {
return this.collectArrayElementTypes(rest, resultSoFar);
}
}
return resultSoFar;
};
this.exitVariableRef = (ctx) => {
this.typeManager.setType(ctx, Types_js_1.Types.or(Types_js_1.Type.IFC_PROPERTY_REF, Types_js_1.Type.IFC_ELEMENT_REF));
};
this.typeManager = new TypeManager_js_1.TypeManager();
}
getTypeManager() {
return this.typeManager;
}
pushMethodCallTarget(ctx) {
if (ctx.parentCtx["_target"]) {
const targetType = this.typeManager.getType(ctx.parentCtx["_target"]);
this.methodCallTargetStack.push([ctx, targetType]);
}
else {
throw new ValidationException_js_1.ValidationException("Did not find expected context attribute 'target' in parent rule context", ctx);
}
}
}
exports.IfcExpressionValidationListener = IfcExpressionValidationListener;
//# sourceMappingURL=IfcExpressionValidationListener.js.map