ifc-expressions
Version:
Parsing and evaluation of IFC expressions
265 lines (264 loc) • 14.6 kB
JavaScript
;
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