ifc-expressions
Version:
Parsing and evaluation of IFC expressions
272 lines (271 loc) • 15.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IfcExpressionFunctions = void 0;
const IfcExpressionUtils_js_1 = require("../../util/IfcExpressionUtils.js");
const MAP_js_1 = require("./impl/MAP.js");
const ROUND_js_1 = require("./impl/ROUND.js");
const AttributeAccessorFunction_js_1 = require("./impl/AttributeAccessorFunction.js");
const PROPERTYSET_js_1 = require("./impl/PROPERTYSET.js");
const PROPERTY_js_1 = require("./impl/PROPERTY.js");
const TYPE_js_1 = require("./impl/TYPE.js");
const FuncBooleanBinary_js_1 = require("./impl/FuncBooleanBinary.js");
const NOT_js_1 = require("./impl/NOT.js");
const CHOOSE_js_1 = require("./impl/CHOOSE.js");
const MatchesPattern_js_1 = require("./impl/MatchesPattern.js");
const Types_js_1 = require("../../type/Types.js");
const TOSTRING_js_1 = require("./impl/TOSTRING.js");
const EXISTS_js_1 = require("./impl/EXISTS.js");
const EQUALS_js_1 = require("./impl/EQUALS.js");
const CompareMagnitudes_js_1 = require("./impl/CompareMagnitudes.js");
const ReplacePattern_js_1 = require("./impl/ReplacePattern.js");
const IF_js_1 = require("./impl/IF.js");
const TONUMERIC_js_1 = require("./impl/TONUMERIC.js");
const IfcExpressionFunctionConfigException_js_1 = require("../../error/IfcExpressionFunctionConfigException.js");
const TOBOOLEAN_js_1 = require("./impl/TOBOOLEAN.js");
const TOLOGICAL_js_1 = require("./impl/TOLOGICAL.js");
const TOLOWERCASE_js_1 = require("./impl/TOLOWERCASE.js");
const TOUPPERCASE_js_1 = require("./impl/TOUPPERCASE.js");
const SUBSTRING_js_1 = require("./impl/SUBSTRING.js");
const SPLIT_js_1 = require("./impl/SPLIT.js");
const AT_js_1 = require("./impl/AT.js");
const TOIFCDATE_js_1 = require("./impl/TOIFCDATE.js");
const TOIFCDATETIME_js_1 = require("./impl/TOIFCDATETIME.js");
const TOIFCTIME_js_1 = require("./impl/TOIFCTIME.js");
const TOIFCDURATION_js_1 = require("./impl/TOIFCDURATION.js");
const TOIFCTIMESTAMP_js_1 = require("./impl/TOIFCTIMESTAMP.js");
const ADDDURATION_js_1 = require("./impl/ADDDURATION.js");
const builtinFunctions = new Map();
function registerOrDie(fnKey, func) {
if (builtinFunctions.has(fnKey)) {
throw new IfcExpressionFunctionConfigException_js_1.IfcExpressionFunctionConfigException(`cannot register function with name '${fnKey}': name already in use`);
}
builtinFunctions.set(fnKey, func);
}
function toDocKey(label) {
return label.replace(/[^a-zA-Z0-9]+/g, "_");
}
function withDocumentation(func, summary, args = []) {
func.withDocumentation({
key: `function.${func.getName()}.summary`,
fallback: summary,
});
args.forEach((argument, index) => {
const formalArgument = func.getFormalArguments()[index];
if (!formalArgument) {
return;
}
const docKey = toDocKey(argument.label);
formalArgument.withDocumentation({
key: `function.${func.getName()}.arg.${docKey}.label`,
fallback: argument.label,
}, {
key: `function.${func.getName()}.arg.${docKey}.summary`,
fallback: argument.documentation,
});
});
return func;
}
function registerFunc(func, ...aliases) {
const fnKey = func.getName();
const keys = [...aliases];
keys.unshift(fnKey);
keys.forEach((key) => registerOrDie(IfcExpressionFunctions.normalizeName(key), func));
}
function documentAttributeAccessor(name, summary) {
return withDocumentation(new AttributeAccessorFunction_js_1.AttributeAccessorFunction(name, Types_js_1.Type.STRING), summary, [
{
label: "object",
documentation: "The IFC object to read from",
},
]);
}
class IfcExpressionFunctions {
static normalizeName(name) {
if ((0, IfcExpressionUtils_js_1.isNullish)(name)) {
return undefined;
}
return name.toUpperCase();
}
static isBuiltinFunction(name) {
if ((0, IfcExpressionUtils_js_1.isNullish)(name)) {
return false;
}
return builtinFunctions.has(this.normalizeName(name));
}
static getFunction(name) {
if ((0, IfcExpressionUtils_js_1.isNullish)(name)) {
return undefined;
}
return builtinFunctions.get(this.normalizeName(name));
}
static getBuiltinFunctionNames() {
return [...builtinFunctions.keys()].sort((left, right) => left.localeCompare(right));
}
}
exports.IfcExpressionFunctions = IfcExpressionFunctions;
registerFunc(withDocumentation(new MAP_js_1.MAP(), "map an input value through a list of key/value mappings", [
{ label: "input", documentation: "The value to look up" },
{
label: "mappings",
documentation: "The mapping table as [key, value] pairs",
},
{
label: "defaultValue",
documentation: "The fallback value to return when no mapping matches",
},
]));
registerFunc(withDocumentation(new CHOOSE_js_1.CHOOSE(), "return the value from the first matching boolean case", [
{
label: "cases",
documentation: "The candidate [condition, value] pairs to evaluate in order",
},
{
label: "defaultValue",
documentation: "The fallback value when no case evaluates to true",
},
]));
registerFunc(withDocumentation(new AT_js_1.AT(), "return the item at the specified index from an array or tuple", [
{ label: "input", documentation: "The array or tuple to read from" },
{ label: "index", documentation: "The zero-based index to read" },
]));
registerFunc(withDocumentation(new IF_js_1.IF(), "choose one of two or three values based on a boolean or logical condition", [
{ label: "condition", documentation: "The condition to evaluate" },
{
label: "thenValue",
documentation: "The value returned when the condition is true",
},
{
label: "elseValue",
documentation: "The value returned when the condition is false",
},
{
label: "unknownValue",
documentation: "The value returned when a logical condition is unknown",
},
]));
registerFunc(withDocumentation(new ROUND_js_1.ROUND(), "round a numeric value to the requested number of decimal places", [
{ label: "input", documentation: "The numeric value to round" },
{
label: "decimals",
documentation: "The number of decimal places to keep",
},
]));
registerFunc(documentAttributeAccessor("name", "read the name attribute from an IFC object"));
registerFunc(documentAttributeAccessor("guid", "read the global identifier from an IFC object"));
registerFunc(documentAttributeAccessor("ifcClass", "read the IFC class name from an IFC object"));
registerFunc(documentAttributeAccessor("description", "read the description attribute from an IFC object"));
registerFunc(withDocumentation(new AttributeAccessorFunction_js_1.AttributeAccessorFunction("value", Types_js_1.Types.or(Types_js_1.Type.STRING, Types_js_1.Type.NUMERIC, Types_js_1.Type.BOOLEAN, Types_js_1.Type.ARRAY)), "read the value of an IFC property object", [{ label: "object", documentation: "The IFC property object to read from" }]));
registerFunc(withDocumentation(new PROPERTYSET_js_1.PROPERTYSET(), "resolve a property set on an IFC object", [
{ label: "object", documentation: "The IFC object to inspect" },
{
label: "name",
documentation: "The name of the property set to resolve",
},
]));
registerFunc(withDocumentation(new PROPERTY_js_1.PROPERTY(), "resolve a property on an IFC element, type object, or property set", [
{ label: "object", documentation: "The IFC object to inspect" },
{ label: "name", documentation: "The name of the property to resolve" },
]));
registerFunc(withDocumentation(new TYPE_js_1.TYPE(), "resolve the IFC type object of an element", [
{ label: "object", documentation: "The IFC element to inspect" },
]));
registerFunc(withDocumentation(new NOT_js_1.NOT(), "negate a boolean or logical value", [
{ label: "value", documentation: "The value to negate" },
]));
registerFunc(withDocumentation(new TOSTRING_js_1.TOSTRING(), "convert a value to its string representation", [{ label: "input", documentation: "The value to convert" }]));
registerFunc(withDocumentation(new TONUMERIC_js_1.TONUMERIC(), "convert a value to a numeric representation", [{ label: "input", documentation: "The value to convert" }]), "TONUMBER");
registerFunc(withDocumentation(new TOBOOLEAN_js_1.TOBOOLEAN(), "convert a value to a boolean representation", [{ label: "input", documentation: "The value to convert" }]));
registerFunc(withDocumentation(new TOLOGICAL_js_1.TOLOGICAL(), "convert a value to a logical representation that can be true, false, or unknown", [{ label: "input", documentation: "The value to convert" }]), "NOTFOUNDASUNKNOWN");
registerFunc(withDocumentation(new TOIFCDATE_js_1.TOIFCDATE(), "convert a value to an IFC date", [
{ label: "input", documentation: "The value to convert" },
]));
registerFunc(withDocumentation(new TOIFCTIME_js_1.TOIFCTIME(), "convert a value to an IFC time", [
{ label: "input", documentation: "The value to convert" },
]));
registerFunc(withDocumentation(new TOIFCDATETIME_js_1.TOIFCDATETIME(), "convert a value to an IFC date-time", [{ label: "input", documentation: "The value to convert" }]));
registerFunc(withDocumentation(new TOIFCDURATION_js_1.TOIFCDURATION(), "convert a value to an IFC duration", [
{ label: "input", documentation: "The value to convert" },
]));
registerFunc(withDocumentation(new TOIFCTIMESTAMP_js_1.TOIFCTIMESTAMP(), "convert a value to an IFC timestamp", [{ label: "input", documentation: "The value to convert" }]));
registerFunc(withDocumentation(new ADDDURATION_js_1.ADDDURATION(), "add a duration to an IFC date-time or timestamp", [
{
label: "pointInTime",
documentation: "The IFC date-time or timestamp to shift",
},
{ label: "duration", documentation: "The duration to add" },
]));
registerFunc(withDocumentation(new TOLOWERCASE_js_1.TOLOWERCASE(), "convert a string to lowercase", [
{ label: "input", documentation: "The string to transform" },
]));
registerFunc(withDocumentation(new TOUPPERCASE_js_1.TOUPPERCASE(), "convert a string to uppercase", [
{ label: "input", documentation: "The string to transform" },
]));
registerFunc(withDocumentation(new SUBSTRING_js_1.SUBSTRING(), "extract a substring from a string", [
{ label: "input", documentation: "The source string" },
{ label: "from", documentation: "The inclusive start index" },
{ label: "to", documentation: "The exclusive end index" },
]));
registerFunc(withDocumentation(new SPLIT_js_1.SPLIT(), "split a string into an array of substrings", [
{ label: "input", documentation: "The source string" },
{ label: "separator", documentation: "The separator string to split on" },
{ label: "limit", documentation: "The maximum number of resulting items" },
]));
registerFunc(withDocumentation(new EXISTS_js_1.EXISTS(), "check whether an IFC reference can be resolved", [{ label: "object", documentation: "The IFC reference to test" }]));
registerFunc(withDocumentation(new FuncBooleanBinary_js_1.FuncBooleanBinary("AND", "and"), "return true only when both inputs are true", [
{ label: "left", documentation: "The left logical operand" },
{ label: "right", documentation: "The right logical operand" },
]));
registerFunc(withDocumentation(new FuncBooleanBinary_js_1.FuncBooleanBinary("OR", "or"), "return true when at least one input is true", [
{ label: "left", documentation: "The left logical operand" },
{ label: "right", documentation: "The right logical operand" },
]));
registerFunc(withDocumentation(new FuncBooleanBinary_js_1.FuncBooleanBinary("XOR", "xor"), "return true when exactly one input is true", [
{ label: "left", documentation: "The left logical operand" },
{ label: "right", documentation: "The right logical operand" },
]));
registerFunc(withDocumentation(new FuncBooleanBinary_js_1.FuncBooleanBinary("IMPLIES", "implies"), "evaluate logical implication from left to right", [
{ label: "left", documentation: "The premise" },
{ label: "right", documentation: "The consequence" },
]));
registerFunc(withDocumentation(new EQUALS_js_1.EQUALS(), "compare two values for equality", [
{ label: "left", documentation: "The left value to compare" },
{ label: "right", documentation: "The right value to compare" },
]));
registerFunc(withDocumentation(new CompareMagnitudes_js_1.CompareMagnitudes("GREATERTHAN", (cmp) => cmp > 0), "return true when the left value is greater than the right value", [
{ label: "left", documentation: "The left value to compare" },
{ label: "right", documentation: "The right value to compare" },
]));
registerFunc(withDocumentation(new CompareMagnitudes_js_1.CompareMagnitudes("GREATERTHANOREQUAL", (cmp) => cmp >= 0), "return true when the left value is greater than or equal to the right value", [
{ label: "left", documentation: "The left value to compare" },
{ label: "right", documentation: "The right value to compare" },
]));
registerFunc(withDocumentation(new CompareMagnitudes_js_1.CompareMagnitudes("LESSTHAN", (cmp) => cmp < 0), "return true when the left value is less than the right value", [
{ label: "left", documentation: "The left value to compare" },
{ label: "right", documentation: "The right value to compare" },
]));
registerFunc(withDocumentation(new CompareMagnitudes_js_1.CompareMagnitudes("LESSTHANOREQUAL", (cmp) => cmp <= 0), "return true when the left value is less than or equal to the right value", [
{ label: "left", documentation: "The left value to compare" },
{ label: "right", documentation: "The right value to compare" },
]));
registerFunc(withDocumentation(new MatchesPattern_js_1.MatchesPattern("CONTAINS", true, false), "check whether the input contains a simple wildcard pattern", [
{ label: "input", documentation: "The source string" },
{ label: "pattern", documentation: "The wildcard pattern to search for" },
]));
registerFunc(withDocumentation(new MatchesPattern_js_1.MatchesPattern("MATCHES", true, true), "check whether the input fully matches a simple wildcard pattern", [
{ label: "input", documentation: "The source string" },
{ label: "pattern", documentation: "The wildcard pattern to match" },
]));
registerFunc(withDocumentation(new MatchesPattern_js_1.MatchesPattern("REGEXCONTAINS", false, false), "check whether the input contains a regular-expression match", [
{ label: "input", documentation: "The source string" },
{
label: "pattern",
documentation: "The regular expression to search for",
},
]));
registerFunc(withDocumentation(new MatchesPattern_js_1.MatchesPattern("REGEXMATCHES", false, true), "check whether the input fully matches a regular expression", [
{ label: "input", documentation: "The source string" },
{ label: "pattern", documentation: "The regular expression to match" },
]));
registerFunc(new ReplacePattern_js_1.ReplacePattern("REPLACE", true));
registerFunc(new ReplacePattern_js_1.ReplacePattern("REGEXREPLACE", false));
//# sourceMappingURL=IfcExpressionFunctions.js.map