mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 18:43:26 +00:00
You can now load and save searches from the search panel
This commit is contained in:
@@ -48,7 +48,7 @@ class LoadGroups(bpy.types.Operator, tool.Ifc.Operator):
|
||||
new = self.props.groups.add()
|
||||
new.ifc_definition_id = group.id()
|
||||
new.name = group.Name or "Unnamed"
|
||||
new.selection_query = group.Description.split("*selector*")[1] if group.Description else ""
|
||||
new.selection_query = ""
|
||||
new.tree_depth = tree_depth
|
||||
new.has_children = False
|
||||
new.is_expanded = group.id() in self.expanded_groups
|
||||
|
||||
@@ -31,10 +31,12 @@ classes = (
|
||||
operator.FilterModelElements,
|
||||
operator.IfcSelector,
|
||||
operator.LoadQuery,
|
||||
operator.LoadSearch,
|
||||
operator.OpenQueryLibrary,
|
||||
operator.RemoveFilter,
|
||||
operator.RemoveFilterGroup,
|
||||
operator.ResetObjectColours,
|
||||
operator.SaveSearch,
|
||||
operator.SaveSelectorQuery,
|
||||
operator.Search,
|
||||
operator.SelectAttribute,
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import json
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def refresh():
|
||||
SearchData.is_loaded = False
|
||||
|
||||
|
||||
class SearchData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {}
|
||||
cls.data["saved_searches"] = cls.saved_searches()
|
||||
|
||||
@classmethod
|
||||
def saved_searches(cls):
|
||||
groups = tool.Ifc.get().by_type("IfcGroup")
|
||||
results = []
|
||||
for group in groups:
|
||||
try:
|
||||
data = json.loads(group.Description)
|
||||
if isinstance(data, dict) and data.get("type", None) == "BBIM_Search":
|
||||
results.append(group)
|
||||
except:
|
||||
pass
|
||||
return [(str(g.id()), g.Name or "Unnamed", "") for g in sorted(results, key=lambda x: x.Name or "Unnamed")]
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import re
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.selector
|
||||
@@ -120,48 +121,9 @@ class Search(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
query = []
|
||||
for filter_group in props.filter_groups:
|
||||
filter_group_query = []
|
||||
has_instance_or_entity_filter = False
|
||||
for ifc_filter in filter_group.filters:
|
||||
if not ifc_filter.value:
|
||||
continue
|
||||
if ifc_filter.type == "instance":
|
||||
has_instance_or_entity_filter = True
|
||||
filter_group_query.append(ifc_filter.value)
|
||||
elif ifc_filter.type == "entity":
|
||||
has_instance_or_entity_filter = True
|
||||
filter_group_query.append(ifc_filter.value)
|
||||
elif ifc_filter.type == "attribute":
|
||||
if not ifc_filter.name:
|
||||
continue
|
||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"{ifc_filter.name}{comparison}{value}")
|
||||
elif ifc_filter.type == "type":
|
||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"type{comparison}{value}")
|
||||
elif ifc_filter.type == "material":
|
||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"material{comparison}{value}")
|
||||
elif ifc_filter.type == "property":
|
||||
if not ifc_filter.pset or not ifc_filter.name:
|
||||
continue
|
||||
pset = self.wrap_value(ifc_filter, ifc_filter.pset)
|
||||
name = self.wrap_value(ifc_filter, ifc_filter.name)
|
||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"{pset}.{name}{comparison}{value}")
|
||||
elif ifc_filter.type == "classification":
|
||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"classification{comparison}{value}")
|
||||
elif ifc_filter.type == "location":
|
||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"location{comparison}{value}")
|
||||
if not has_instance_or_entity_filter:
|
||||
filter_group_query.insert(0, "IfcElement")
|
||||
query.append(", ".join(filter_group_query))
|
||||
query = " + ".join(query)
|
||||
results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query)
|
||||
results = ifcopenshell.util.selector.filter_elements(
|
||||
tool.Ifc.get(), tool.Search.export_filter_query(props.filter_groups)
|
||||
)
|
||||
|
||||
total_selected = 0
|
||||
for element in results:
|
||||
@@ -171,17 +133,47 @@ class Search(Operator):
|
||||
self.report({"INFO"}, f"{len(results)} Results")
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_comparison_and_value(self, ifc_filter):
|
||||
if ifc_filter.value.startswith("!="):
|
||||
return ("!=", self.wrap_value(ifc_filter, ifc_filter.value[2:].strip()))
|
||||
return ("=", self.wrap_value(ifc_filter, ifc_filter.value.strip()))
|
||||
|
||||
def wrap_value(self, ifc_filter, value):
|
||||
if value.startswith("/") and value.endswith("/"):
|
||||
return value
|
||||
elif value in ("NULL", "TRUE", "FALSE"):
|
||||
return value
|
||||
return '"' + value + '"'
|
||||
class SaveSearch(Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.save_search"
|
||||
bl_label = "Save Search"
|
||||
bl_description = "Save search filter to an IFC group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
name: StringProperty(name="Name")
|
||||
|
||||
def _execute(self, context):
|
||||
if not self.name:
|
||||
return
|
||||
|
||||
query = tool.Search.export_filter_query(context.scene.BIMSearchProperties.filter_groups)
|
||||
results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query)
|
||||
|
||||
description = json.dumps({"type": "BBIM_Search", "query": query})
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description)
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=results, group=group)
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class LoadSearch(Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.load_search"
|
||||
bl_label = "Load Search"
|
||||
bl_description = "Load search filter from an IFC group"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
group = tool.Ifc.get().by_id(int(props.saved_searches))
|
||||
query = tool.Search.import_filter_query(group, context.scene.BIMSearchProperties.filter_groups)
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
row = self.layout.row()
|
||||
row.prop(props, "saved_searches", text="")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
|
||||
class SelectGlobalId(Operator):
|
||||
|
||||
@@ -22,6 +22,7 @@ from ifcopenshell.util.selector import Selector
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import ObjProperty, StrProperty
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.search.data import SearchData
|
||||
from bpy.types import PropertyGroup
|
||||
from blenderbim.tool.ifc import Ifc
|
||||
from . import ui, prop, operator
|
||||
@@ -37,6 +38,12 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
def get_saved_searches(self, context):
|
||||
if not SearchData.is_loaded:
|
||||
SearchData.load()
|
||||
return SearchData.data["saved_searches"]
|
||||
|
||||
|
||||
def update_is_class_selected(self, context):
|
||||
if self.is_selected:
|
||||
for obj in self.unselected_objects:
|
||||
@@ -106,6 +113,7 @@ class BIMSearchProperties(PropertyGroup):
|
||||
("instance", "GlobalId", "", "GRIP", 7),
|
||||
],
|
||||
)
|
||||
saved_searches: EnumProperty(items=get_saved_searches, name="Saved Searches")
|
||||
should_use_regex: BoolProperty(name="Search With Regex", default=False)
|
||||
should_ignorecase: BoolProperty(name="Search Ignoring Case", default=True)
|
||||
global_id: StringProperty(name="GlobalId")
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.search.data import SearchData
|
||||
|
||||
|
||||
class BIM_PT_search(Panel):
|
||||
@@ -30,8 +30,18 @@ class BIM_PT_search(Panel):
|
||||
bl_parent_id = "BIM_PT_selection"
|
||||
|
||||
def draw(self, context):
|
||||
if not SearchData.is_loaded:
|
||||
SearchData.load()
|
||||
|
||||
props = context.scene.BIMSearchProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{len(SearchData.data['saved_searches'])} Saved Searches")
|
||||
|
||||
if SearchData.data["saved_searches"]:
|
||||
row.operator("bim.load_search", text="", icon="IMPORT")
|
||||
row.operator("bim.save_search", text="", icon="EXPORT")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD")
|
||||
|
||||
|
||||
@@ -1,11 +1,173 @@
|
||||
import bpy
|
||||
import json
|
||||
import lark
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.selector
|
||||
from ifcopenshell.util.selector import Selector
|
||||
|
||||
|
||||
class Search(blenderbim.core.tool.Search):
|
||||
@classmethod
|
||||
def import_filter_query(cls, group, filter_groups):
|
||||
query = json.loads(group.Description)["query"]
|
||||
filter_groups.clear()
|
||||
l = lark.Lark(ifcopenshell.util.selector.filter_elements_grammar)
|
||||
transformer = ImportFilterQueryTransformer(filter_groups)
|
||||
transformer.transform(l.parse(query))
|
||||
|
||||
@classmethod
|
||||
def export_filter_query(cls, filter_groups):
|
||||
query = []
|
||||
for filter_group in filter_groups:
|
||||
filter_group_query = []
|
||||
has_instance_or_entity_filter = False
|
||||
for ifc_filter in filter_group.filters:
|
||||
if not ifc_filter.value:
|
||||
continue
|
||||
if ifc_filter.type == "instance":
|
||||
has_instance_or_entity_filter = True
|
||||
filter_group_query.append(ifc_filter.value)
|
||||
elif ifc_filter.type == "entity":
|
||||
has_instance_or_entity_filter = True
|
||||
filter_group_query.append(ifc_filter.value)
|
||||
elif ifc_filter.type == "attribute":
|
||||
if not ifc_filter.name:
|
||||
continue
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"{ifc_filter.name}{comparison}{value}")
|
||||
elif ifc_filter.type == "type":
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"type{comparison}{value}")
|
||||
elif ifc_filter.type == "material":
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"material{comparison}{value}")
|
||||
elif ifc_filter.type == "property":
|
||||
if not ifc_filter.pset or not ifc_filter.name:
|
||||
continue
|
||||
pset = cls.wrap_value(ifc_filter, ifc_filter.pset)
|
||||
name = cls.wrap_value(ifc_filter, ifc_filter.name)
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"{pset}.{name}{comparison}{value}")
|
||||
elif ifc_filter.type == "classification":
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"classification{comparison}{value}")
|
||||
elif ifc_filter.type == "location":
|
||||
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||
filter_group_query.append(f"location{comparison}{value}")
|
||||
if not has_instance_or_entity_filter:
|
||||
filter_group_query.insert(0, "IfcElement")
|
||||
query.append(", ".join(filter_group_query))
|
||||
return " + ".join(query)
|
||||
|
||||
@classmethod
|
||||
def get_comparison_and_value(cls, ifc_filter):
|
||||
if ifc_filter.value.startswith("!="):
|
||||
return ("!=", cls.wrap_value(ifc_filter, ifc_filter.value[2:].strip()))
|
||||
return ("=", cls.wrap_value(ifc_filter, ifc_filter.value.strip()))
|
||||
|
||||
@classmethod
|
||||
def wrap_value(cls, ifc_filter, value):
|
||||
if value.startswith("/") and value.endswith("/"):
|
||||
return value
|
||||
elif value in ("NULL", "TRUE", "FALSE"):
|
||||
return value
|
||||
return '"' + value.replace('"', '\\"') + '"'
|
||||
|
||||
@classmethod
|
||||
def from_selector_query(cls, query):
|
||||
"""Returns a list of products from a selector query"""
|
||||
return Selector().parse(tool.Ifc.get(), query)
|
||||
|
||||
|
||||
class ImportFilterQueryTransformer(lark.Transformer):
|
||||
def __init__(self, filter_groups):
|
||||
self.filter_groups = filter_groups
|
||||
|
||||
def get_results(self):
|
||||
results = set()
|
||||
for r in self.results:
|
||||
results |= r
|
||||
return results
|
||||
|
||||
def facet_list(self, args):
|
||||
new = self.filter_groups.add()
|
||||
for arg in args:
|
||||
new2 = new.filters.add()
|
||||
new2.type = arg["type"]
|
||||
new2.value = arg["value"]
|
||||
if "name" in arg:
|
||||
new2.name = arg["name"]
|
||||
if "pset" in arg:
|
||||
new2.pset = arg["pset"]
|
||||
|
||||
def facet(self, args):
|
||||
return args[0]
|
||||
|
||||
def instance(self, args):
|
||||
return {"type": "instance", "value": " ".join([a.children[0].value for a in args])}
|
||||
|
||||
def entity(self, args):
|
||||
return {"type": "entity", "value": " ".join([a.children[0].value for a in args])}
|
||||
|
||||
def attribute(self, args):
|
||||
name, comparison, value = args
|
||||
name = name.children[0].value
|
||||
return {"type": "attribute", "name": name, "value": f"{comparison}{value}"}
|
||||
|
||||
def type(self, args):
|
||||
comparison, value = args
|
||||
return {"type": "type", "value": f"{comparison}{value}"}
|
||||
|
||||
def material(self, args):
|
||||
comparison, value = args
|
||||
return {"type": "material", "value": f"{comparison}{value}"}
|
||||
|
||||
def property(self, args):
|
||||
pset, prop, comparison, value = args
|
||||
return {"type": "property", "pset": pset, "name": prop, "value": f"{comparison}{value}"}
|
||||
|
||||
def classification(self, args):
|
||||
comparison, value = args
|
||||
return {"type": "classification", "value": f"{comparison}{value}"}
|
||||
|
||||
def location(self, args):
|
||||
comparison, value = args
|
||||
return {"type": "location", "value": f"{comparison}{value}"}
|
||||
|
||||
def comparison(self, args):
|
||||
return "" if args[0].data == "equals" else "!="
|
||||
|
||||
def pset(self, args):
|
||||
return self.value(args)
|
||||
|
||||
def prop(self, args):
|
||||
return self.value(args)
|
||||
|
||||
def value(self, args):
|
||||
if args[0].data == "unquoted_string":
|
||||
return args[0].children[0].value
|
||||
elif args[0].data == "quoted_string":
|
||||
return args[0].children[0].value[1:-1].replace('\\"', '"')
|
||||
elif args[0].data == "regex_string":
|
||||
return args[0].children[0].value
|
||||
elif args[0].data == "special":
|
||||
if args[0].children[0].data == "null":
|
||||
return "NULL"
|
||||
elif args[0].children[0].data == "true":
|
||||
return "TRUE"
|
||||
elif args[0].children[0].data == "false":
|
||||
return "FALSE"
|
||||
|
||||
def compare(self, element_value, comparison, value):
|
||||
if isinstance(value, str):
|
||||
if isinstance(element_value, int):
|
||||
value = int(value)
|
||||
elif isinstance(element_value, float):
|
||||
value = float(value)
|
||||
result = element_value == value
|
||||
elif isinstance(value, re.Pattern):
|
||||
result = bool(value.match(element_value))
|
||||
elif value in (None, True, False):
|
||||
result = element_value is value
|
||||
return result if comparison == "=" else not result
|
||||
|
||||
@@ -24,6 +24,71 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.classification
|
||||
|
||||
|
||||
filter_elements_grammar = """start: filter_group
|
||||
filter_group: facet_list ("+" facet_list)*
|
||||
facet_list: facet ("," facet)*
|
||||
|
||||
facet: instance | entity | attribute | type | material | property | classification | location
|
||||
|
||||
instance: not? globalid
|
||||
globalid: /[0-3][a-zA-Z0-9_$]{21}/
|
||||
entity: not? ifc_class
|
||||
attribute: attribute_name comparison value
|
||||
type: "type" comparison value
|
||||
material: "material" comparison value
|
||||
property: pset "." prop comparison value
|
||||
classification: "classification" comparison value
|
||||
location: "location" comparison value
|
||||
|
||||
pset: quoted_string | unquoted_string | regex_string
|
||||
prop: quoted_string | unquoted_string | regex_string
|
||||
|
||||
attribute_name: /[A-Z]\\w+/
|
||||
ifc_class: /Ifc\\w+/
|
||||
|
||||
value: special | quoted_string | unquoted_string | regex_string
|
||||
unquoted_string: /[^.=\\s]+/
|
||||
quoted_string: ESCAPED_STRING
|
||||
regex_string: "/" /[^\\/]+/ "/"
|
||||
|
||||
special: null | true | false
|
||||
|
||||
comparison: not? equals
|
||||
not: "!"
|
||||
equals: "="
|
||||
null: "NULL"
|
||||
true: "TRUE"
|
||||
false: "FALSE"
|
||||
|
||||
// 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 /(?<!\\\\)(\\\\\\\\)*?/
|
||||
ESCAPED_STRING : "\\"" _STRING_ESC_INNER "\\""
|
||||
LCASE_LETTER: "a".."z"
|
||||
UCASE_LETTER: "A".."Z"
|
||||
LETTER: UCASE_LETTER | LCASE_LETTER
|
||||
WORD: LETTER+
|
||||
CNAME: ("_"|LETTER) ("_"|LETTER|DIGIT)*
|
||||
WS_INLINE: (" "|/\\t/)+
|
||||
WS: /[ \\t\\f\\r\\n]/+
|
||||
CR : /\\r/
|
||||
LF : /\\n/
|
||||
NEWLINE: (CR? LF)+
|
||||
|
||||
%ignore WS // Disregard spaces in text
|
||||
"""
|
||||
|
||||
|
||||
def get_element_value(element, query):
|
||||
l = lark.Lark(
|
||||
"""start: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple
|
||||
@@ -50,72 +115,7 @@ def get_element_value(element, query):
|
||||
|
||||
|
||||
def filter_elements(ifc_file, query, elements=None):
|
||||
l = lark.Lark(
|
||||
"""start: filter_group
|
||||
filter_group: facet_list ("+" facet_list)*
|
||||
facet_list: facet ("," facet)*
|
||||
|
||||
facet: instance | entity | attribute | type | material | property | classification | location
|
||||
|
||||
instance: not? globalid
|
||||
globalid: /[0-3][a-zA-Z0-9_$]{21}/
|
||||
entity: not? ifc_class
|
||||
attribute: attribute_name comparison value
|
||||
type: "type" comparison value
|
||||
material: "material" comparison value
|
||||
property: pset "." prop comparison value
|
||||
classification: "classification" comparison value
|
||||
location: "location" comparison value
|
||||
|
||||
pset: quoted_string | unquoted_string | regex_string
|
||||
prop: quoted_string | unquoted_string | regex_string
|
||||
|
||||
attribute_name: /[A-Z]\\w+/
|
||||
ifc_class: /Ifc\\w+/
|
||||
|
||||
value: special | quoted_string | unquoted_string | regex_string
|
||||
unquoted_string: /[^.=\\s]+/
|
||||
quoted_string: ESCAPED_STRING
|
||||
regex_string: "/" /[^\\/]+/ "/"
|
||||
|
||||
special: null | true | false
|
||||
|
||||
comparison: not? equals
|
||||
not: "!"
|
||||
equals: "="
|
||||
null: "NULL"
|
||||
true: "TRUE"
|
||||
false: "FALSE"
|
||||
|
||||
// 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 /(?<!\\\\)(\\\\\\\\)*?/
|
||||
ESCAPED_STRING : "\\"" _STRING_ESC_INNER "\\""
|
||||
LCASE_LETTER: "a".."z"
|
||||
UCASE_LETTER: "A".."Z"
|
||||
LETTER: UCASE_LETTER | LCASE_LETTER
|
||||
WORD: LETTER+
|
||||
CNAME: ("_"|LETTER) ("_"|LETTER|DIGIT)*
|
||||
WS_INLINE: (" "|/\\t/)+
|
||||
WS: /[ \\t\\f\\r\\n]/+
|
||||
CR : /\\r/
|
||||
LF : /\\n/
|
||||
NEWLINE: (CR? LF)+
|
||||
|
||||
%ignore WS // Disregard spaces in text
|
||||
"""
|
||||
)
|
||||
|
||||
l = lark.Lark(filter_elements_grammar)
|
||||
transformer = FacetTransformer(ifc_file, elements)
|
||||
transformer.transform(l.parse(query))
|
||||
return transformer.get_results()
|
||||
|
||||
Reference in New Issue
Block a user