# IfcOpenShell - IFC toolkit and geometry engine # Copyright (C) 2021 Dion Moult # # This file is part of IfcOpenShell. # # IfcOpenShell is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # IfcOpenShell is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . import lark arithmetic_operator_symbols = {"ADD": "+", "DIVIDE": "/", "MULTIPLY": "*", "SUBTRACT": "-"} symbol_arithmetic_operators = {"+": "ADD", "/": "DIVIDE", "*": "MULTIPLY", "-": "SUBTRACT"} def serialise_cost_value(cost_value): result = _serialise_cost_value(cost_value) if result and result[0] == "(" and result[-1] == ")": return result[1:-1] return result def _serialise_cost_value(cost_value): value = "" if cost_value.ArithmeticOperator and cost_value.Components: operator = arithmetic_operator_symbols[cost_value.ArithmeticOperator] serialised_components = [] for component in cost_value.Components: serialised_components.append(_serialise_cost_value(component)) value = operator.join(serialised_components) elif cost_value.AppliedValue is not None: value = serialise_applied_value(cost_value.AppliedValue) category = "" if cost_value.Category == "*": category = "SUM" elif cost_value.Category: category = cost_value.Category if not category and not value: value = "0" if category: return f"{category}({value})" elif cost_value.Components: return f"({value})" return value def serialise_applied_value(applied_value): if applied_value.is_a("IfcMonetaryMeasure"): return str(applied_value.wrappedValue) return "?" def unserialise_cost_value(formula, cost_value): unserialiser = CostValueUnserialiser() result = unserialiser.parse(formula) def map_element_to_result(element, result): result["ifc"] = element for i, component in enumerate(result.get("Components", [])): if element.Components and i < len(element.Components): map_element_to_result(element.Components[i], result["Components"][i]) map_element_to_result(cost_value, result) return result class CostValueUnserialiser: def parse(self, formula): l = lark.Lark( """start: formula formula: operand (operator operand)* operand: value | category "(" formula ")" value: NUMBER? category: WORD? operator: add | divide | multiply | subtract add: "+" divide: "/" multiply: "*" subtract: "-" // Embed common.lark for packaging DIGIT: "0".."9" HEXDIGIT: "a".."f"|"A".."F"|DIGIT INT: DIGIT+ SIGNED_INT: ["+"|"-"] INT DECIMAL: INT "." INT? | "." INT _EXP: ("e"|"E") SIGNED_INT FLOAT: INT _EXP | DECIMAL _EXP? SIGNED_FLOAT: ["+"|"-"] FLOAT NUMBER: FLOAT | INT SIGNED_NUMBER: ["+"|"-"] NUMBER _STRING_INNER: /.*?/ _STRING_ESC_INNER: _STRING_INNER /(?