import ifcopenshell.util import ifcopenshell.util.element import lark class Selector(): def parse(self, ifc_file, query): self.file = ifc_file self.cobie_type_assets = [ 'IfcDoorStyle', 'IfcBuildingElementProxyType', 'IfcChimneyType', 'IfcCoveringType', 'IfcDoorType', 'IfcFootingType', 'IfcPileType', 'IfcRoofType', 'IfcShadingDeviceType', 'IfcWindowType', 'IfcDistributionControlElementType', 'IfcDistributionChamberElementType', 'IfcEnergyConversionDeviceType', 'IfcFlowControllerType', 'IfcFlowMovingDeviceType', 'IfcFlowStorageDeviceType', 'IfcFlowTerminalType', 'IfcFlowTreatmentDeviceType', 'IfcElementAssemblyType', 'IfcBuildingElementPartType', 'IfcDiscreteAccessoryType', 'IfcMechanicalFastenerType', 'IfcReinforcingElementType', 'IfcVibrationIsolatorType', 'IfcFurnishingElementType', 'IfcGeographicElementType', 'IfcTransportElementType', 'IfcSpatialZoneType', 'IfcWindowStyle', ] self.cobie_component_assets = [ 'IfcBuildingElementProxy', 'IfcChimney', 'IfcCovering', 'IfcDoor', 'IfcShadingDevice', 'IfcWindow', 'IfcDistributionControlElement', 'IfcDistributionChamberElement', 'IfcEnergyConversionDevice', 'IfcFlowController', 'IfcFlowMovingDevice', 'IfcFlowStorageDevice', 'IfcFlowTerminal', 'IfcFlowTreatmentDevice', 'IfcDiscreteAccessory', 'IfcTendon', 'IfcTendonAnchor', 'IfcVibrationIsolator', 'IfcFurnishingElement', 'IfcGeographicElement', 'IfcTransportElement', ] l = lark.Lark('''start: query (lfunction query)* query: selector | group group: "(" query (lfunction query)* ")" selector: (inverse_relationship)? guid_selector | (inverse_relationship)? class_selector guid_selector: "#" /[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$]{22}/ class_selector: "." WORD filter ? filter: "[" filter_key (comparison filter_value)? "]" filter_key: WORD | pset_or_qto filter_value: ESCAPED_STRING pset_or_qto: /[A-Za-z0-9_]+/ "." /[A-Za-z0-9_]+/ lfunction: and | or inverse_relationship: types | contains_elements types: "*" contains_elements: "@" and: "&" or: "|" comparison: contains | morethanequalto | lessthanequalto | equal | morethan | lessthan contains: "*=" morethanequalto: ">=" lessthanequalto: "<" equal: "=" morethan: ">" lessthan: "<" // 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 /(? 1 \ and class_selector.children[1].data == 'filter': return self.filter_elements(elements, class_selector.children[1]) return elements def filter_elements(self, elements, filter_rule): results = [] key = filter_rule.children[0].children[0] if not isinstance(key, str): key = key.children[0] + '.' + key.children[1] comparison = value = None if len(filter_rule.children) > 1: comparison = filter_rule.children[1].children[0].data value = filter_rule.children[2].children[0][1:-1] for element in elements: element_value = self.get_element_value(element, key) if not element_value: continue if not comparison \ or self.filter_element(element, element_value, comparison, value): results.append(element) return results def get_element_value(self, element, key): info = element.get_info() if key in info: return info[key] elif '.' in key: pset_name, prop = key.split('.') psets = ifcopenshell.util.element.get_psets(element) if pset_name in psets and prop in psets[pset_name]: return psets[pset_name][prop] def filter_element(self, element, element_value, comparison, value): if comparison == 'equal': return str(element_value) == value elif comparison == 'contains': return value in str(element_value) elif comparison == 'morethan': return element_value > float(value) elif comparison == 'lessthan': return element_value < float(value) elif comparison == 'morethanequalto': return element_value >= float(value) elif comparison == 'lessthanequalto': return element_value <= float(value) return False def get_guid_selector(self, guid_selector): return [self.file.by_id(guid_selector.children[0])]