UNPKG

ifc-expressions

Version:

Parsing and evaluation of IFC expressions

265 lines (264 loc) 14.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExprCompiler = void 0; const IfcExpressionVisitor_js_1 = __importDefault(require("../gen/parser/IfcExpressionVisitor.js")); const NumericLiteralExpr_js_1 = require("../expression/numeric/NumericLiteralExpr.js"); const IfcExpressionUtils_js_1 = require("../util/IfcExpressionUtils.js"); const decimal_js_1 = __importDefault(require("decimal.js")); const PlusExpr_js_1 = require("../expression/numeric/PlusExpr.js"); const MinusExpr_js_1 = require("../expression/numeric/MinusExpr.js"); const MultiplyExpr_js_1 = require("../expression/numeric/MultiplyExpr.js"); const DivideExpr_js_1 = require("../expression/numeric/DivideExpr.js"); const PropObjectReferenceExpr_js_1 = require("../expression/reference/PropObjectReferenceExpr.js"); const ElemObjectReferenceExpr_js_1 = require("../expression/reference/ElemObjectReferenceExpr.js"); const StringLiteralExpr_js_1 = require("../expression/string/StringLiteralExpr.js"); const ArrayExpr_js_1 = require("../expression/structure/ArrayExpr.js"); const FunctionExpr_js_1 = require("../expression/function/FunctionExpr.js"); const PowerExpr_js_1 = require("../expression/numeric/PowerExpr.js"); const UnaryMinusExpr_js_1 = require("../expression/numeric/UnaryMinusExpr.js"); const AndExpr_js_1 = require("../expression/boolean/AndExpr.js"); const BooleanLiteralExpr_js_1 = require("../expression/boolean/BooleanLiteralExpr.js"); const OrExpr_js_1 = require("../expression/boolean/OrExpr.js"); const XorExpr_js_1 = require("../expression/boolean/XorExpr.js"); const SyntaxErrorException_js_1 = require("../error/SyntaxErrorException.js"); const NotExpr_js_1 = require("../expression/boolean/NotExpr.js"); const ParenthesisExpr_js_1 = require("../expression/structure/ParenthesisExpr.js"); const EqualsExpr_js_1 = require("../expression/comparison/EqualsExpr.js"); const NotEqualsExpr_js_1 = require("../expression/comparison/NotEqualsExpr.js"); const StringConcatExpr_js_1 = require("../expression/string/StringConcatExpr.js"); const PlusOrConcatExpr_js_1 = require("../expression/poly/PlusOrConcatExpr.js"); const ExpressionTypeError_js_1 = require("../error/ExpressionTypeError.js"); const GreaterThan_js_1 = require("../expression/comparison/GreaterThan.js"); const GreaterThanOrEqual_js_1 = require("../expression/comparison/GreaterThanOrEqual.js"); const LessThan_js_1 = require("../expression/comparison/LessThan.js"); const LessThanOrEqual_js_1 = require("../expression/comparison/LessThanOrEqual.js"); const ExprManager_js_1 = require("./ExprManager.js"); const LogicalValue_js_1 = require("../value/LogicalValue.js"); const LogicalLiteralExpr_js_1 = require("../expression/boolean/LogicalLiteralExpr.js"); class ExprCompiler extends IfcExpressionVisitor_js_1.default { constructor(typeManager) { super(); this.methodCallTargetStack = []; this.visitExpr = (ctx) => { return this.visit(ctx.singleExpr()); }; this.visitVariableRef = (ctx) => { if (ctx.IDENTIFIER().getText().toUpperCase() === "PROPERTY") { return this.associateContextAndReturn(new PropObjectReferenceExpr_js_1.PropObjectReferenceExpr(), ctx); } else if (ctx.IDENTIFIER().getText().toUpperCase() === "ELEMENT") { return this.associateContextAndReturn(new ElemObjectReferenceExpr_js_1.ElemObjectReferenceExpr(), ctx); } throw new Error(`Parsing error: No variable '${ctx.getText()}' found `); }; this.visitSEComparison = (ctx) => { const left = this.visit(ctx.singleExpr(0)); const right = this.visit(ctx.singleExpr(1)); switch (ctx.CMP_OP().getText()) { case "==": return this.associateContextAndReturn(new EqualsExpr_js_1.EqualsExpr(left, right), ctx); case "!=": return this.associateContextAndReturn(new NotEqualsExpr_js_1.NotEqualsExpr(left, right), ctx); case ">": return this.associateContextAndReturn(new GreaterThan_js_1.GreaterThan(left, right), ctx); case ">=": return this.associateContextAndReturn(new GreaterThanOrEqual_js_1.GreaterThanOrEqual(left, right), ctx); case "<": return this.associateContextAndReturn(new LessThan_js_1.LessThan(left, right), ctx); case "<=": return this.associateContextAndReturn(new LessThanOrEqual_js_1.LessThanOrEqual(left, right), ctx); } throw new Error(`Parsing error: comparison operator '${ctx .CMP_OP() .getText()}' not supported`); }; this.visitSEMethodCall = (ctx) => { this.methodCallTargetStack.push(this.visit(ctx.singleExpr())); return this.visit(ctx.methodCallChain()); }; this.visitSELiteral = (ctx) => { return this.visit(ctx.literal()); }; this.visitSEVariableRef = (ctx) => { return this.visit(ctx.variableRef()); }; this.visitSEArrayExpr = (ctx) => { return this.visit(ctx.arrayExpr()); }; this.visitSEFunctionCall = (ctx) => { return this.visit(ctx.functionCall()); }; this.visitSEParenthesis = (ctx) => { return this.associateContextAndReturn(new ParenthesisExpr_js_1.ParenthesisExpr(this.visit(ctx._sub)), ctx); }; /*================================================ * MethodCallChain *==============================================*/ this.visitMethodCallChainInner = (ctx) => { const functionExpr = new FunctionExpr_js_1.FunctionExpr(ctx.functionCall().IDENTIFIER().getText(), this.collectFunctionArguments(ctx.functionCall().exprList(), [ this.methodCallTargetStack.pop(), ])); this.associateContextAndReturn(functionExpr, ctx.functionCall()); this.methodCallTargetStack.push(functionExpr); return this.visit(ctx.methodCallChain()); }; this.visitMethodCallChainEnd = (ctx) => { return this.associateContextAndReturn(new FunctionExpr_js_1.FunctionExpr(ctx.functionCall().IDENTIFIER().getText(), this.collectFunctionArguments(ctx.functionCall().exprList(), [ this.methodCallTargetStack.pop(), ])), ctx.functionCall()); }; this.visitSEAddSub = (ctx) => { this.typeManager.requireTypesOverlap(ctx._left, ctx._right); switch (ctx._op.text) { case "+": return this.makePlusExpr(ctx); case "-": return this.associateContextAndReturn(new MinusExpr_js_1.MinusExpr(this.visit(ctx._left), this.visit(ctx._right)), ctx); default: ExprCompiler.failNode(ctx); } }; this.visitSEMulDiv = (ctx) => { switch (ctx._op.text) { case "*": return this.associateContextAndReturn(new MultiplyExpr_js_1.MultiplyExpr(this.visit(ctx.getChild(0)), this.visit(ctx.getChild(2))), ctx); case "/": return this.associateContextAndReturn(new DivideExpr_js_1.DivideExpr(this.visit(ctx.getChild(0)), this.visit(ctx.getChild(2))), ctx); default: ExprCompiler.failNode(ctx); } }; /*================================================ * StringExpr *===============================================*/ this.visitStringLiteral = (ctx) => { const quotedString = ctx.QUOTED_STRING().getText(); const text = quotedString.substring(1, quotedString.length - 1); return this.associateContextAndReturn(new StringLiteralExpr_js_1.StringLiteralExpr(text), ctx); }; /*================================================ * NumExpr *===============================================*/ this.visitNumLiteral = (ctx) => { let val = ctx.INT(); if ((0, IfcExpressionUtils_js_1.isPresent)(val)) { return this.associateContextAndReturn(new NumericLiteralExpr_js_1.NumericLiteralExpr(new decimal_js_1.default(ctx.getText())), ctx); } val = ctx.FLOAT(); if ((0, IfcExpressionUtils_js_1.isPresent)(val)) { return this.associateContextAndReturn(new NumericLiteralExpr_js_1.NumericLiteralExpr(new decimal_js_1.default(ctx.getText())), ctx); } ExprCompiler.failNode(ctx); }; this.visitSEUnaryMultipleMinus = (ctx) => { return this.visit(ctx.singleExpr()); }; this.visitSEPower = (ctx) => { return this.associateContextAndReturn(new PowerExpr_js_1.PowerExpr(this.visit(ctx._left), this.visit(ctx._right)), ctx); }; this.visitSEUnaryMinus = (ctx) => { return this.associateContextAndReturn(new UnaryMinusExpr_js_1.UnaryMinusExpr(this.visit(ctx.singleExpr())), ctx); }; /*================================================ * BooleanExpr *===============================================*/ this.visitSEBooleanBinaryOp = (ctx) => { switch (ctx._op.text) { case "&&": return this.associateContextAndReturn(new AndExpr_js_1.AndExpr(this.visit(ctx.singleExpr(0)), this.visit(ctx.singleExpr(1))), ctx); case "||": return this.associateContextAndReturn(new OrExpr_js_1.OrExpr(this.visit(ctx.singleExpr(0)), this.visit(ctx.singleExpr(1))), ctx); case "><": return this.associateContextAndReturn(new XorExpr_js_1.XorExpr(this.visit(ctx.singleExpr(0)), this.visit(ctx.singleExpr(1))), ctx); } throw new SyntaxErrorException_js_1.SyntaxErrorException(ctx._op.text, ctx._op.line, ctx._op.column, `Unknown boolean operator ${ctx._op.text}`); }; this.visitBooleanLiteral = (ctx) => { return this.associateContextAndReturn(new BooleanLiteralExpr_js_1.BooleanLiteralExpr(ctx.BOOLEAN().getText().toUpperCase() === "TRUE"), ctx); }; this.visitLogicalLiteral = (ctx) => { // the only literal we recognize is 'UNKNOWN' (true and false are boolean literals) return this.associateContextAndReturn(new LogicalLiteralExpr_js_1.LogicalLiteralExpr(LogicalValue_js_1.LogicalValue.UNKNOWN_VALUE), ctx); }; this.visitSENot = (ctx) => { return this.associateContextAndReturn(new NotExpr_js_1.NotExpr(this.visit(ctx.singleExpr())), ctx); }; /*================================================ * ArrayExpr *===============================================*/ this.visitArrayExpr = (ctx) => { return this.associateContextAndReturn(new ArrayExpr_js_1.ArrayExpr(this.collectArrayElements(ctx.arrayElementList())), ctx); }; this.collectArrayElements = (ctx) => { if (!(0, IfcExpressionUtils_js_1.isNullish)(ctx)) { const first = this.visit(ctx.singleExpr()); const rest = ctx.arrayElementList(); if (!(0, IfcExpressionUtils_js_1.isNullish)(rest)) { const arr = this.collectArrayElements(rest); arr.unshift(first); return arr; } return [first]; } return []; }; /*================================================ * FuncExpr *===============================================*/ this.visitFunctionCall = (ctx) => { const args = (0, IfcExpressionUtils_js_1.isNullish)(ctx.exprList()) ? [] : this.collectFunctionArguments(ctx.exprList()); return this.associateContextAndReturn(new FunctionExpr_js_1.FunctionExpr(ctx.IDENTIFIER().getText(), args), ctx); }; this.collectFunctionArguments = (ctx, resultSoFar) => { if ((0, IfcExpressionUtils_js_1.isNullish)(resultSoFar)) { resultSoFar = []; } if (!(0, IfcExpressionUtils_js_1.isNullish)(ctx)) { resultSoFar.push(this.visit(ctx.singleExpr())); const rest = ctx.exprList(); if (!(0, IfcExpressionUtils_js_1.isNullish)(rest)) { return this.collectFunctionArguments(rest, resultSoFar); } } return resultSoFar; }; this.visitLiteral = (ctx) => { return this.visit(ctx.getChild(0)); }; this.typeManager = typeManager; this.exprManager = new ExprManager_js_1.ExprManager(); } getExprManager() { return this.exprManager; } /*================================================ * VariableRef *==============================================*/ associateContextAndReturn(expr, ctx) { this.exprManager.registerExprWithContext(expr, ctx); return expr; } makePlusExpr(ctx) { if (this.typeManager.isNumeric(ctx._left, ctx._right)) { return this.associateContextAndReturn(new PlusExpr_js_1.PlusExpr(this.visit(ctx._left), this.visit(ctx._right)), ctx); } if (this.typeManager.isString(ctx._left, ctx._right)) { return this.associateContextAndReturn(new StringConcatExpr_js_1.StringConcatExpr(this.visit(ctx._left), this.visit(ctx._right)), ctx); } if (this.typeManager.overlapsWithString(ctx._left, ctx._right) || this.typeManager.overlapsWithNumeric(ctx._left, ctx._right)) { return this.associateContextAndReturn(new PlusOrConcatExpr_js_1.PlusOrConcatExpr(this.visit(ctx._left), this.visit(ctx._right)), ctx); } throw new ExpressionTypeError_js_1.ExpressionTypeError("Operator '+' requires the operands to be either both of type numeric or both of type string, but they are not.", ctx); } static failNode(ctx) { throw new Error(`Cannot parse (sub)expression ${ctx.getText()}`); } } exports.ExprCompiler = ExprCompiler; //# sourceMappingURL=ExprCompiler.js.map