UNPKG

ifc-expressions

Version:

Parsing and evaluation of IFC expressions

169 lines (102 loc) 6.86 kB
# IFC Expressions This project defines an expression language for IFC models. An expression is evaluated in the context of an element of the IFC model and a specific property of that element. ## Interface Two ways to interact with 'ifc-expression': - evaluate an expression string directly - receive `ExprEvalSuccessResult | ExprEvalError` or - parse an expression - receive a `IfcExpressionParseResult` - evaluate a `IfcExpressionParseResult` - receive `ExprEvalSuccessResult | ExprEvalError` ## Usage To connect `ifc-expressions` with your IFC model, you have to provide an implementation of `src/context/IfcExpressionContext`. Without such a context, the expressions that contain references to the model cannot be evaluated. ```ts import {IfcExpression} from "ifc-expression"; import {IfcExpressionContext} from "./IfcExpressionContext"; const result = IfcExpression.evaluate("1 + 1"); // evaluation without context console.log(JSON.stringify(result)); // ExprEvalSuccessObj { // status: 1000, // result: NumericValue { // value: 2 // } //} const ctx: IfcExpressionContext = ... ; // set to your context here const result2 = IfcExpression.evaluate("$element.property('width').value() * 2 ", ctx); ``` ## Quick Reference Get the current property value: `$property.value()` Get the name of the property set the property is in: `$property.propertySet.name()` Get the name of the current element: `$element.name()` Get the name of the type of the current element: `$element.type().name()` Get value of property `myProp` from property set `myPset` in the current element: `$element.propertySet('myPset').property('myProp').value()` The last expression can also be written as `VALUE(PROPERTY(PROPERTY_SET($element, 'myPset'),'myProp'))` Check if property `myProp` exists in property set `myPset` in the current element: `$element.propertySet('myPset').property('myProp').exists()` Hints: - Function/Method names are case-insensitive - you have `+ - * / ^` for numerics - you have `&& || >< !` for booleans (`><`is xor) - you have `== != >= <= > <` for strings, booleans and numerics - you have a `.toString()` method on anything (or, equivalently, a function `toString(x)` for any `x` ). - you have `REPLACE`, which only knows the wildcard character `*`. (same for `MATCHES, CONTAINS`) - you have `REGEXREPLACE` with full js Regular Expressions (same for `REGEXMATCHES, REGEXCONTAINS`) ## IFC Expression Language Syntax The project uses ANTLR4 for parsing. The grammar is in src/grammar/ifcExpression.g4. The result of expression evaluation is a value of type `string, numeric, boolean, ifcObjectRef`, where `ifcObjectRef` is a reference to some object in the IFC model, or a temporal type (`IfcDateTime`, `IfcDate`, `IfcTime`, `IfcDuration`, and `IfcTimeStamp`) The language allows for specifying _a single expression_. There are no control statements and there is no way to define custom functions or custom types. An expression can be - a literal, such as `'hello world'`, or `17` - a variable reference, such as `$property` or `$element`,(for accessing the IFC model). ... or a combination of multiple expressions: - a function call, such as `REPLACE("hello world", "world", "friends")` - a function call in 'method-call style', such as `"hello world".replace("world", "friends")` - a combination using operators, such as `+`, `&&`, `==` ### Types - `string`, e.g. `'abc'` or `"abc"`: text enclosed in single or double quotes - `boolean`, e.g. `TRUE` or `false`: true or false, either spelled all-uppercase or all-lowercase - `numeric`, e.g. `1` or `3.141`: a decimal number optionally containing one period to separate integer part from fractional part - `array`, e.g. `[1,2,"hi there"]: an ordered list of expressions ### Operators - numeric operators - '+', '-', '\*', '/': plus, minus, multiplication, division - with the ususal precedence rules and associativity - '^': raise to the power or, e.g. `2^3` (= 8) - boolean operators - `&&', `||`, `><`: boolean and, or, xor - `!`: boolean not - string operators - '+': string concatenation ### Functions #### conversion functions `toString(a)`, `toNumeric(a)`, `toIfcDateTime(a)`, etc. #### temporal functions `addDuration(IfcTimeStamp|IfcDateTime, IfcDuration)` #### comparison functions `equals(a,b)`, `greaterThan(a,b)`, `greaterThanOrEquals(a,b)`, `lessThan(a,b)`, `lessThanOrEqual(a,b)`", #### boolean operator functions `not(a)`, `and(a,b)`, `or(a,b)`, `xor(a,b)`, `implies(a,b)` #### string matching `contains(string, pattern)`, e.g. `contains('hello world', 'he*o')` returns true if the string contains the pattern. `*` matches any number of characters. `regexContains(string, regex)`, e.g. `regexContains('hello, world', 'h[aeiou]ll[aeiou]+\\\s')` returns true if the string contains the regular expression. `matches(string, pattern)`, e.g. `matches('hello world', 'he*o')` returns true if the whole string matches the pattern. `*` matches any number of characters. `regexMatches(string, regex)`, e.g. `regexMatches('hello, world', 'h[aeiou]ll[aeiou]+\\\s')` returns true if the whole string matches the regular expression. #### string replacement `replace(string, pattern, replacement)`, e.g. `replace('hello world', 'he*o', 'bye')` returns the `string`, with all occurrences of the `pattern` replaced with `replacement`. `*` matches any number of characters. `regexReplace(string, regex, replacement)`, e.g. `regexReplace('hello, world', 'h([aeiou]ll[aeiou]+) ', 'm$1w ')` returns the `string`, with all occurrences of the `regex` replaced with `replacement`. #### ifc object accessor functions `property(object: ifcPropertySetRef|ifcTypeObjectRef|ifcElementRef, name: string)`: returns an `ifcPropertyRef` or an error or an error if object has no property with that name `propertySet(object: ifcProperty)`: returns an `ifcPropertySetRef` `propertySet(object: ifcTypeObjectRef|ifcElementRef, name: string)`: returns an `ifcPropertySetRef` or an error if the element or type has no property set with that name. `type(object: ifcElementRef)`: returns an `ifcTypeObjectRef` or an error. `exists(object: ifcObjectRef): boolean` check whether an object reference obtained by the above methods actually exists (suppresses the error they generate) #### Translation and condition functions `map(input, mapping: [[in, out], [in, out], ... ], default)`: finds the first `[in,out]` pair in the specified mapping where `in == input` and returns that pair's `out` value. If none is found, `default` is returned. `if(condition, thenValue, elseValue)`: returns `thenValue` if `condition` is `true`, `elseValue` otherwise. `choose([[condition, out], [condition, out], ... ], default)`: finds the first `[condition, out]` pair where `condition == true` and returns its `out` value. In none is found, `default` is returned.