ifc-expressions
Version:
Parsing and evaluation of IFC expressions
268 lines (267 loc) • 15.1 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 = 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 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");
const BuiltinVariableRegistry_js_1 = require("../builtin/BuiltinVariableRegistry.js");
const ContextObjectType_js_1 = require("../type/ContextObjectType.js");
const BuiltinPropertyAccessExpr_js_1 = require("../expression/reference/BuiltinPropertyAccessExpr.js");
const BuiltinFunctionCallExpr_js_1 = require("../expression/reference/BuiltinFunctionCallExpr.js");
class ExprCompiler extends IfcExpressionVisitor_js_1.IfcExpressionVisitor {
defaultResult() {
return null;
}
constructor(typeManager, builtinVariableRegistry = BuiltinVariableRegistry_js_1.BuiltinVariableRegistry.getDefaultRegistry()) {
super();
this.methodCallTargetStack = [];
this.visitExpr = (ctx) => {
return this.visit(ctx.singleExpr());
};
this.visitVariableRef = (ctx) => {
const builtin = this.builtinVariableRegistry.getDefinition(ctx.IDENTIFIER().getText());
if (!(0, IfcExpressionUtils_js_1.isNullish)(builtin)) {
return this.associateContextAndReturn(builtin.createReferenceExpr(), 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);
};
this.visitMethodCallChainInner = (ctx) => {
const accessExpr = this.visit(ctx._target);
this.methodCallTargetStack.push(accessExpr);
return this.visit(ctx._call);
};
this.visitMethodCallChainEnd = (ctx) => {
return this.visit(ctx._call);
};
this.visitMethodFunctionCall = (ctx) => {
const targetExpr = this.methodCallTargetStack.pop();
const functionName = ctx.functionCall().IDENTIFIER().getText();
const args = (0, IfcExpressionUtils_js_1.isNullish)(ctx.functionCall().exprList())
? []
: this.collectFunctionArguments(ctx.functionCall().exprList());
const targetType = targetExpr.getType();
if (targetType instanceof ContextObjectType_js_1.ContextObjectType) {
const member = targetType.getMemberDefinition(functionName);
if ((0, BuiltinVariableRegistry_js_1.isBuiltinFunctionDefinition)(member)) {
return this.associateContextAndReturn(new BuiltinFunctionCallExpr_js_1.BuiltinFunctionCallExpr(targetExpr, member.name, args, member.returnType, targetType.getName()), ctx);
}
}
return this.associateContextAndReturn(new FunctionExpr_js_1.FunctionExpr(functionName, [targetExpr, ...args]), ctx.functionCall());
};
this.visitMethodPropertyAccess = (ctx) => {
const targetExpr = this.methodCallTargetStack.pop();
const targetType = targetExpr.getType();
if (targetType instanceof ContextObjectType_js_1.ContextObjectType) {
const member = targetType.getMemberDefinition(ctx.IDENTIFIER().getText());
if ((0, BuiltinVariableRegistry_js_1.isBuiltinPropertyDefinition)(member)) {
return this.associateContextAndReturn(new BuiltinPropertyAccessExpr_js_1.BuiltinPropertyAccessExpr(targetExpr, member.name, member.valueType, targetType.getName()), ctx);
}
}
throw new Error(`Parsing error: No property '${ctx.getText()}' found for type '${targetType.getName()}'`);
};
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);
}
};
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);
};
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);
};
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) => {
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);
};
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 [];
};
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();
this.builtinVariableRegistry = builtinVariableRegistry;
}
getExprManager() {
return this.exprManager;
}
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