ifc-expressions
Version:
Parsing and evaluation of IFC expressions
118 lines (117 loc) • 4.57 kB
JavaScript
import { ContextObjectType } from "../type/ContextObjectType.js";
import { ArrayType } from "../type/ArrayType.js";
import { TupleType } from "../type/TupleType.js";
import { Type } from "../type/Types.js";
import { TypeDisjunction } from "../type/TypeDisjunction.js";
import { NumericValue } from "../value/NumericValue.js";
import { StringValue } from "../value/StringValue.js";
import { BooleanValue } from "../value/BooleanValue.js";
import { LogicalValue } from "../value/LogicalValue.js";
import { ArrayValue } from "../value/ArrayValue.js";
import { ContextObjectValue } from "../value/ContextObjectValue.js";
function isValue(candidate) {
return (typeof candidate === "object" &&
candidate !== null &&
typeof candidate["getValue"] === "function" &&
typeof candidate["getType"] === "function");
}
function normalizeName(name) {
return name?.replace(/^\$/, "").toUpperCase();
}
export function toExpressionValue(value, expectedType) {
if (isValue(value)) {
return value;
}
if (expectedType instanceof TypeDisjunction) {
for (const option of expectedType.getTypes()) {
try {
return toExpressionValue(value, option);
}
catch {
// try next
}
}
}
if (expectedType instanceof ContextObjectType) {
if (typeof value !== "object" || value === null) {
throw new Error(`Expected context object value for type ${expectedType.getName()}`);
}
return new ContextObjectValue(value, expectedType);
}
if (expectedType instanceof TupleType) {
if (!Array.isArray(value)) {
throw new Error(`Expected tuple value for type ${expectedType.getName()}`);
}
const itemTypes = expectedType.getTypes();
return ArrayValue.of(value.map((item, index) => toExpressionValue(item, itemTypes[index] ?? Type.ANY)));
}
if (expectedType instanceof ArrayType) {
if (!Array.isArray(value)) {
throw new Error(`Expected array value for type ${expectedType.getName()}`);
}
return ArrayValue.of(value.map((item) => toExpressionValue(item, expectedType.getElementType())));
}
if (expectedType.isSameTypeAs(Type.NUMERIC)) {
return NumericValue.of(value);
}
if (expectedType.isSameTypeAs(Type.STRING)) {
return StringValue.of(value);
}
if (expectedType.isSameTypeAs(Type.BOOLEAN)) {
return BooleanValue.of(value);
}
if (expectedType.isSameTypeAs(Type.LOGICAL)) {
return LogicalValue.of(value);
}
if (expectedType.isSameTypeAs(Type.ARRAY)) {
if (!Array.isArray(value)) {
throw new Error(`Expected array value for type ${expectedType.getName()}`);
}
return ArrayValue.of(value.map((item) => inferExpressionValue(item)));
}
return inferExpressionValue(value, expectedType);
}
export function inferExpressionValue(value, fallbackType = Type.ANY) {
if (isValue(value)) {
return value;
}
if (typeof value === "number" || typeof value === "string") {
if (typeof value === "number") {
return NumericValue.of(value);
}
return StringValue.of(value);
}
if (typeof value === "boolean") {
return BooleanValue.of(value);
}
if (value === LogicalValue.UNKNOWN_VALUE) {
return LogicalValue.unknown();
}
if (Array.isArray(value)) {
return ArrayValue.of(value.map((item) => inferExpressionValue(item)));
}
if (typeof value === "object" && value !== null) {
return new ContextObjectValue(value, fallbackType);
}
throw new Error(`Cannot convert runtime context value '${value}' to expression value`);
}
export function unwrapExpressionValue(value) {
const raw = value.getValue();
if (value instanceof ArrayValue) {
const arrayValue = raw;
return arrayValue.map((item) => unwrapExpressionValue(item));
}
return raw;
}
export function getBuiltinMemberValue(value, memberName) {
const objectValue = value.getValue();
if (Object.prototype.hasOwnProperty.call(objectValue, memberName)) {
return objectValue[memberName];
}
const normalizedMemberName = normalizeName(memberName);
const matchingKey = Object.keys(objectValue).find((key) => normalizeName(key) === normalizedMemberName);
return typeof matchingKey === "undefined"
? undefined
: objectValue[matchingKey];
}
//# sourceMappingURL=BuiltinRuntimeValueConverter.js.map