UNPKG

ifc-expressions

Version:

Parsing and evaluation of IFC expressions

147 lines (146 loc) 7.32 kB
import { CharStream, CommonTokenStream, ParseTreeWalker, } from "antlr4"; import { ExprCompiler } from "./compiler/ExprCompiler.js"; import { IfcExpressionErrorListener } from "./IfcExpressionErrorListener.js"; import { IfcExpressionValidationListener } from "./compiler/IfcExpressionValidationListener.js"; import { isNullish, isPresent } from "./util/IfcExpressionUtils.js"; import { IfcElementAccessor } from "./context/IfcElementAccessor.js"; import { StringValue } from "./value/StringValue.js"; import { NumericValue } from "./value/NumericValue.js"; import { BooleanValue } from "./value/BooleanValue.js"; import { LogicalValue } from "./value/LogicalValue.js"; import { ReferenceValue } from "./value/ReferenceValue.js"; import { IfcPropertySetAccessor } from "./context/IfcPropertySetAccessor.js"; import { IfcPropertyAccessor } from "./context/IfcPropertyAccessor.js"; import { IfcRootObjectAccessor } from "./context/IfcRootObjectAccessor.js"; import { IfcTypeObjectAccessor } from "./context/IfcTypeObjectAccessor.js"; import { NamedObjectAccessor } from "./context/NamedObjectAccessor.js"; import IfcExpressionParser from "./gen/parser/IfcExpressionParser.js"; import IfcExpressionVisitor from "./gen/parser/IfcExpressionVisitor.js"; import IfcExpressionLexer from "./gen/parser/IfcExpressionLexer.js"; import { IfcExpressionEvaluationException } from "./expression/IfcExpressionEvaluationException.js"; import { isExprEvalError, isExprEvalSuccess, } from "./expression/ExprEvalResult.js"; import { ExprKind } from "./expression/ExprKind.js"; import { ValidationException } from "./error/ValidationException.js"; import { mapException } from "./error/ExceptionToExprEvalErrorMapper.js"; import { NopContext } from "./context/NopContext.js"; import { ExprToTextInputLinker } from "./compiler/ExprToTextInputLinker.js"; import { ExprFacade } from "./expression/ExprFacade.js"; import { IfcDurationValue } from "./value/IfcDurationValue.js"; import { IfcDateValue } from "./value/IfcDateValue.js"; import { IfcDateTimeValue } from "./value/IfcDateTimeValue.js"; import { IfcTimeValue } from "./value/IfcTimeValue.js"; import { IfcTimeStampValue } from "./value/IfcTimeStampValue.js"; import { ArrayType } from "./type/ArrayType.js"; import { SimpleType } from "./type/SimpleType.js"; import { TupleType } from "./type/TupleType.js"; import { TypeDisjunction } from "./type/TypeDisjunction.js"; import { Types } from "./type/Types.js"; export { IfcElementAccessor, StringValue, BooleanValue, LogicalValue, NumericValue, ReferenceValue, IfcDateValue, IfcDateTimeValue, IfcTimeValue, IfcDurationValue, IfcTimeStampValue, IfcPropertySetAccessor, IfcPropertyAccessor, IfcRootObjectAccessor, IfcTypeObjectAccessor, NamedObjectAccessor, ExprCompiler, ExprKind, IfcExpressionEvaluationException, IfcExpressionErrorListener, IfcExpressionVisitor, isPresent, isNullish, isExprEvalError, isExprEvalSuccess, ExprToTextInputLinker, ExprFacade, ArrayType, SimpleType, TupleType, TypeDisjunction, Types, }; export class IfcExpressionParseResult { constructor(input, typeManager, exprContext) { this._typeManager = typeManager; this._parseTree = exprContext; this._input = input; } get typeManager() { return this._typeManager; } get parseTree() { return this._parseTree; } get input() { return this._input; } } export class IfcExpression { /** * Parses the input and returns a parse result, which contains the parse tree, the type information per parse tree node, and the input. * @param input * @param errorListener * @return the parse result, which can subequently be compiled into an Expr using compile(). */ static parse(input, errorListener) { const chars = new CharStream(input); // replace this with a FileStream as required const lexer = new IfcExpressionLexer(chars); const tokens = new CommonTokenStream(lexer); const parser = new IfcExpressionParser(tokens); lexer.removeErrorListeners(); parser.removeErrorListeners(); const myErrorListener = new IfcExpressionErrorListener(); if (isPresent(errorListener)) { lexer.addErrorListener(errorListener); parser.addErrorListener(errorListener); } lexer.addErrorListener(myErrorListener); parser.addErrorListener(myErrorListener); let expr; expr = parser.expr(); const validationListener = new IfcExpressionValidationListener(); if (!myErrorListener.isErrorOccurred()) { const walker = new ParseTreeWalker(); try { walker.walk(validationListener, expr); } catch (e) { if (e instanceof ValidationException) { if (errorListener instanceof IfcExpressionErrorListener) { errorListener.validationException(e); } myErrorListener.validationException(e); } } } return new IfcExpressionParseResult(input, validationListener.getTypeManager(), expr); } /** * Compiles the specified parseResult into an Expr. * @param parseResult * @return the Expression (Expr), which can be evaluated to obtain its result. */ static compile(parseResult) { const compiler = new ExprCompiler(parseResult.typeManager); const expr = compiler.visit(parseResult.parseTree); ExprToTextInputLinker.linkTextToExpressions(parseResult.input, expr, compiler.getExprManager()); return new ExprFacade(expr); } /** * Evaluates the specified input expression and returns the evaluation result. The parse * and compile steps are done internally. * * @param expression: the input expression * @param context: the context required for accessing the IFC model * @return the result (or an error object). */ static evaluate(expression, context = new NopContext()) { const errorListener = new IfcExpressionErrorListener(); const parseResult = IfcExpression.parse(expression, errorListener); if (errorListener.isErrorOccurred()) { return mapException(errorListener.getException()); } const compiledExpression = this.compile(parseResult); return compiledExpression.evaluate(context); } static formatError(input, error) { const lines = ["** Error **"]; if (!isNullish(error["textSpan"])) { const span = error["textSpan"]; const underlined = span.underline(input, "^"); const startString = "Input: "; const indented = this.indent(underlined, startString.length); const replaced = startString + indented.substr(startString.length); replaced.split("\n").forEach((line) => lines.push(line)); } lines.push("Problem: " + error.message); return lines.join("\n"); } static indent(s, by) { return s .split("\n") .map((line) => " ".repeat(by) + line) .join("\n"); } static evaluateExpression(expr, context = new NopContext()) { return expr.evaluate(context); } } //# sourceMappingURL=IfcExpression.js.map