ifc-expressions
Version:
Parsing and evaluation of IFC expressions
219 lines (218 loc) • 10.2 kB
JavaScript
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