UNPKG

ifc-expressions

Version:

Parsing and evaluation of IFC expressions

219 lines (218 loc) 10.2 kB
import { CharStream, CommonTokenStream, ParseTreeWalker, } from "antlr4ng"; import Decimal from "decimal.js"; 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 { ArrayValue } from "./value/ArrayValue.js"; import { ContextObjectValue } from "./value/ContextObjectValue.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"; import { ContextObjectType } from "./type/ContextObjectType.js"; import { BuiltinVariableRegistry } from "./builtin/BuiltinVariableRegistry.js"; import { IfcExpressionAutocomplete } from "./autocomplete/IfcExpressionAutocomplete.js"; import { ObjectAccessorValue } from "./value/ObjectAccessorValue.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, ContextObjectType, BuiltinVariableRegistry, IfcExpressionAutocomplete, }; export class IfcExpressionParseResult { constructor(input, typeManager, exprContext, builtinVariableRegistry) { this._typeManager = typeManager; this._parseTree = exprContext; this._input = input; this._builtinVariableRegistry = builtinVariableRegistry; } get typeManager() { return this._typeManager; } get parseTree() { return this._parseTree; } get input() { return this._input; } get builtinVariableRegistry() { return this._builtinVariableRegistry; } } export class IfcExpression { static parse(input, errorListener, options = {}) { const builtinVariableRegistry = options.builtinVariableRegistry ?? BuiltinVariableRegistry.getDefaultRegistry(); const chars = CharStream.fromString(input); 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(builtinVariableRegistry); 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, builtinVariableRegistry); } static compile(parseResult) { const compiler = new ExprCompiler(parseResult.typeManager, parseResult.builtinVariableRegistry); const expr = compiler.visit(parseResult.parseTree); ExprToTextInputLinker.linkTextToExpressions(parseResult.input, expr, compiler.getExprManager()); return new ExprFacade(expr); } static evaluate(expression, context = new NopContext(), options = {}) { const errorListener = new IfcExpressionErrorListener(); const parseResult = IfcExpression.parse(expression, errorListener, options); 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); } static unwrapValue(value) { return this.unwrapUnknownValue(value); } static unwrapUnknownValue(value) { if (value instanceof StringValue || value instanceof BooleanValue) { return value.getValue(); } if (value instanceof NumericValue) { return value.getValue(); } if (value instanceof LogicalValue) { return value.isUnknown() ? undefined : value.getValue(); } if (value instanceof ReferenceValue) { return value.getValue(); } if (value instanceof IfcTimeStampValue) { return value.getTimeStampSeconds(); } if (value instanceof IfcDateValue || value instanceof IfcDateTimeValue || value instanceof IfcTimeValue || value instanceof IfcDurationValue) { return value.toString(); } if (value instanceof ArrayValue) { return value.getValue().map((entry) => this.unwrapUnknownValue(entry)); } if (value instanceof ObjectAccessorValue) { return this.unwrapObjectAccessor(value.getValue()); } if (value instanceof ContextObjectValue) { return this.unwrapPlainObject(value.getValue()); } if (Decimal.isDecimal(value)) { return value; } if (typeof value === "number") { return NumericValue.of(value).getValue(); } if (typeof value === "string" || typeof value === "boolean" || typeof value === "undefined") { return value; } if (Array.isArray(value)) { return value.map((entry) => this.unwrapUnknownValue(entry)); } if (value instanceof Date) { return value.toISOString(); } if (this.isPlainObject(value)) { return this.unwrapPlainObject(value); } return undefined; } static unwrapObjectAccessor(objectAccessor) { const result = {}; for (const attributeName of objectAccessor.listAttributes()) { const attributeValue = objectAccessor.getAttribute(attributeName); if (typeof attributeValue !== "undefined") { result[attributeName] = this.unwrapUnknownValue(attributeValue); } } return result; } static unwrapPlainObject(value) { const result = {}; for (const [key, nestedValue] of Object.entries(value)) { result[key] = this.unwrapUnknownValue(nestedValue); } return result; } static isPlainObject(value) { return (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype); } } //# sourceMappingURL=IfcExpression.js.map