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