From 077a0c375504b4c0ea46c1bfa9f05274fb8785f3 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Thu, 26 Feb 2026 12:36:01 +0100 Subject: [PATCH] Run black on express/ --- .../ifcopenshell/express/bootstrap.py | 20 ++- .../ifcopenshell/express/cat.py | 11 +- .../ifcopenshell/express/codegen.py | 2 +- .../ifcopenshell/express/header.py | 1 + .../ifcopenshell/express/implementation.py | 46 ++++--- .../ifcopenshell/express/mapping.py | 1 + .../ifcopenshell/express/nodes.py | 100 ++++++++------ .../ifcopenshell/express/rule_compiler.py | 97 +++++--------- .../ifcopenshell/express/schema.py | 21 ++- .../ifcopenshell/express/schema_class.py | 126 ++++++++++-------- .../ifcopenshell/express/templates.py | 36 ++--- 11 files changed, 239 insertions(+), 222 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/express/bootstrap.py b/src/ifcopenshell-python/ifcopenshell/express/bootstrap.py index 9ff87d5ad0..ef3c3c3ef0 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/bootstrap.py +++ b/src/ifcopenshell-python/ifcopenshell/express/bootstrap.py @@ -163,7 +163,18 @@ statements = [] terminals = reduce(lambda x, y: x | y, (find_bytype(e, Terminal) for id, e in express)) keywords = list(filter(operator.attrgetter("is_keyword"), terminals)) negated_keywords = map(lambda s: "~%s" % s, keywords) -no_action = {"letter", "digit", "digits", "real_literal", "integer_literal", "string_literal", "simple_string_literal", "letter", "not_quote", "not_paren_star_quote_special"} +no_action = { + "letter", + "digit", + "digits", + "real_literal", + "integer_literal", + "string_literal", + "simple_string_literal", + "letter", + "not_quote", + "not_paren_star_quote_special", +} while True: emitted_in_loop = set() @@ -194,7 +205,9 @@ for id in to_emit: if id in to_combine: stmt = "Suppress%s" % stmt if id not in no_action and not isinstance(expr.contents, Keyword): - children = list(map(operator.attrgetter('contents'), reduce(lambda x, y: x | y, (find_bytype(e, Keyword) for e in [expr])))) + children = list( + map(operator.attrgetter("contents"), reduce(lambda x, y: x | y, (find_bytype(e, Keyword) for e in [expr]))) + ) has_duplicates = len(children) > len(set(children)) node_type = "ListNode" if ("ZeroOrMore" in stmt or has_duplicates) else "Node" action = ".setParseAction(%s)" % ( @@ -243,5 +256,4 @@ if __name__ == "__main__": mdl = importlib.import_module(output) mdl.Generator(m).emit() sys.stdout.write(m.schema.name) -""" % ("\n ".join(statements)) - ) +""" % ("\n ".join(statements))) diff --git a/src/ifcopenshell-python/ifcopenshell/express/cat.py b/src/ifcopenshell-python/ifcopenshell/express/cat.py index d6e7b2c880..373afeb1d5 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/cat.py +++ b/src/ifcopenshell-python/ifcopenshell/express/cat.py @@ -1,15 +1,16 @@ import sys, fileinput -if sys.platform == "win32" and not hasattr(sys.stdout, 'buffer'): +if sys.platform == "win32" and not hasattr(sys.stdout, "buffer"): import os, msvcrt + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) files = sys.argv[1:] -if files[0] == '-o': - b = open(files[1], 'wb') +if files[0] == "-o": + b = open(files[1], "wb") files = files[2:] else: - b = getattr(sys.stdout, 'buffer', sys.stdout) + b = getattr(sys.stdout, "buffer", sys.stdout) -for line in fileinput.input(files=files, mode='rb'): +for line in fileinput.input(files=files, mode="rb"): b.write(line) diff --git a/src/ifcopenshell-python/ifcopenshell/express/codegen.py b/src/ifcopenshell-python/ifcopenshell/express/codegen.py index fe997091b1..dfcf5083c8 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/codegen.py +++ b/src/ifcopenshell-python/ifcopenshell/express/codegen.py @@ -26,7 +26,7 @@ def indent(n, s): else: strs = s splitted = itertools.chain.from_iterable(map(functools.partial(str.split, sep="\n"), map(str, strs))) - return "\n".join(" "*n + l for l in splitted) + return "\n".join(" " * n + l for l in splitted) class Base: diff --git a/src/ifcopenshell-python/ifcopenshell/express/header.py b/src/ifcopenshell-python/ifcopenshell/express/header.py index 4cc8c8ea84..6059354925 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/header.py +++ b/src/ifcopenshell-python/ifcopenshell/express/header.py @@ -28,6 +28,7 @@ from collections import defaultdict USE_VIRTUAL_INHERITANCE = True + class Header(codegen.Base): def __init__(self, mapping): declarations = [] diff --git a/src/ifcopenshell-python/ifcopenshell/express/implementation.py b/src/ifcopenshell-python/ifcopenshell/express/implementation.py index 248bbd6e86..bc4b080ad6 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/implementation.py +++ b/src/ifcopenshell-python/ifcopenshell/express/implementation.py @@ -69,7 +69,7 @@ class Implementation(codegen.Base): templates.enum_from_string_stmt % dict(context, **locals()) for value in enum.values ), ) - + if USE_VIRTUAL_INHERITANCE: for name, enum in mapping.schema.selects.items(): write( @@ -118,10 +118,7 @@ class Implementation(codegen.Base): null_check = "" if arg["is_optional"]: - attr_check = ( - "if(get_attribute_value(%d).isNull()) { return %%s; }" - % (arg["index"] - 1,) - ) + attr_check = "if(get_attribute_value(%d).isNull()) { return %%s; }" % (arg["index"] - 1,) if "boost::optional" in arg["full_type"]: null_check = attr_check % "boost::none" else: @@ -157,7 +154,7 @@ class Implementation(codegen.Base): return templates.set_attr_stmt_enum elif arg["is_templated_list"] and not (select or simple or express): return templates.set_attr_stmt_array - elif arg["full_type"].endswith('*'): + elif arg["full_type"].endswith("*"): return templates.set_attr_instance else: return templates.set_attr_stmt @@ -178,7 +175,9 @@ class Implementation(codegen.Base): "non_optional_type": arg["non_optional_type"].replace("::Value", ""), "star_if_optional": "*" if "boost::optional" in arg["full_type"] else "", "check_optional_set_begin": "if (v) {" if "boost::optional" in arg["full_type"] else "", - "check_optional_set_else": "} else {" if "boost::optional" in arg["full_type"] else "if constexpr (false)", + "check_optional_set_else": ( + "} else {" if "boost::optional" in arg["full_type"] else "if constexpr (false)" + ), "check_optional_set_end": "}" if "boost::optional" in arg["full_type"] else "", }, ) @@ -193,11 +192,15 @@ class Implementation(codegen.Base): tmpl = ( templates.constructor_stmt_array if arg["is_templated_list"] - else templates.constructor_stmt_enum - if arg["is_enum"] - else templates.constructor_stmt_instance - if arg["full_type"].endswith('*') - else templates.constructor_stmt + else ( + templates.constructor_stmt_enum + if arg["is_enum"] + else ( + templates.constructor_stmt_instance + if arg["full_type"].endswith("*") + else templates.constructor_stmt + ) + ) ) impl = tmpl % { "name": deref_name, @@ -321,7 +324,7 @@ class Implementation(codegen.Base): else templates.simpletype_impl_is_without_supertype ) - constructor = templates.constructor_single_initlist# if superclass else templates.constructor + constructor = templates.constructor_single_initlist # if superclass else templates.constructor simpletype_impl_cast = ( templates.simpletype_impl_cast_templated @@ -374,8 +377,21 @@ class Implementation(codegen.Base): ("IfcEntityInstanceData&& e",), "", ), - ("", "", constructor, "", ("%s v" % type_str,), ("set_attribute_value(0, v%s);" % ("->generalize()" if mapping.is_templated_list(type) else ""))) if mapping.simple_type_parent(class_name) is None else \ - ("v", "", constructor, "", ("%s v" % type_str,), ""), + ( + ( + "", + "", + constructor, + "", + ("%s v" % type_str,), + ( + "set_attribute_value(0, v%s);" + % ("->generalize()" if mapping.is_templated_list(type) else "") + ), + ) + if mapping.simple_type_parent(class_name) is None + else ("v", "", constructor, "", ("%s v" % type_str,), "") + ), ("", "", templates.cast_function, type_str, (), simpletype_impl_cast), ), ), diff --git a/src/ifcopenshell-python/ifcopenshell/express/mapping.py b/src/ifcopenshell-python/ifcopenshell/express/mapping.py index 7ebac2478b..609f346c2c 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/mapping.py +++ b/src/ifcopenshell-python/ifcopenshell/express/mapping.py @@ -25,6 +25,7 @@ import schema from header import USE_VIRTUAL_INHERITANCE + class Mapping: express_to_cpp_typemapping = { diff --git a/src/ifcopenshell-python/ifcopenshell/express/nodes.py b/src/ifcopenshell-python/ifcopenshell/express/nodes.py index 34d3ef747b..2b8b872c83 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/nodes.py +++ b/src/ifcopenshell-python/ifcopenshell/express/nodes.py @@ -23,6 +23,7 @@ import operator import collections import bootstrap + class Node: def __init__(self, s, loc, tokens, rule=None): self.rule = rule or (type(self).__name__) @@ -58,15 +59,15 @@ class ListNode: rules_as_list = set() for t in self.tokens: - r = getattr(t, 'rule', None) + r = getattr(t, "rule", None) if r: rules_as_list.add(r) self.dict_tokens[r].append(t) - + for r, t in tokens.asDict().items(): if r not in rules_as_list: self.dict_tokens[r].append(t) - + self.flat = sum([getattr(t, "flat", [t]) for t in self.tokens], []) def __repr__(self): @@ -74,7 +75,7 @@ class ListNode: def __iter__(self): return iter(self.tokens) - + # Somehow indexing messes up the pyparsing results, so instead of x[0] use list(x)[0] # def __getitem__(self, i): # return self.tokens[i] @@ -110,7 +111,7 @@ def format_clause(exp): return "".join(whitespace(term) for term in exp.flat) -class TypeDeclaration(Node): +class TypeDeclaration(Node): name = property(lambda self: self.type_id[0]) utype = property(lambda self: self.underlying_type.any().any()) type = property(lambda self: self.utype[0] if isinstance(self.utype, list) else self.utype) @@ -245,7 +246,8 @@ class NamedType(Node): def do_try(fn): try: return fn() - except: pass + except: + pass def get_rule_id(x): @@ -255,8 +257,14 @@ def get_rule_id(x): if matches: return matches[0] + rule_dependencies = { - k: list(map(operator.attrgetter('contents'), bootstrap.reduce(lambda x, y: x | y, (bootstrap.find_bytype(e, bootstrap.Keyword) for e in [v])))) \ + k: list( + map( + operator.attrgetter("contents"), + bootstrap.reduce(lambda x, y: x | y, (bootstrap.find_bytype(e, bootstrap.Keyword) for e in [v])), + ) + ) for k, v in bootstrap.express } @@ -264,16 +272,17 @@ all_rules = [k for k, e in bootstrap.express] rule_definitions = {k: v for k, v in bootstrap.express} + def to_tree(x, key=None): - + def prune(di): # translate class names back to grammar rules if nested actions are encountered di = {get_rule_id(k) or k: v for k, v in di.items()} - + def replace_synonyms(x): for y in x: yield y - if False: # y in di: + if False: # y in di: # production element from grammar is found in parsed data, # return that. @@ -292,19 +301,21 @@ def to_tree(x, key=None): yield S # Do this recursively yield from replace_synonyms([S]) - + # is this a concatenation with zero or more synonyms? then also processs that # @todo catches: # - simple_expression = term { add_like_op term } . # but should probably also work on # - a = b { b } # in which case the second Concat would be eliminated - elif isinstance(rule, bootstrap.Concat) and \ - len(rule.contents) == 2 and \ - is_synonym(rule.contents[0]) and \ - isinstance(rule.contents[1].contents, bootstrap.Repeated) and \ - isinstance(rule.contents[1].contents.contents[0], bootstrap.Concat) and \ - str(rule.contents[1].contents.contents[0].contents[1]) == str(rule.contents[0]): + elif ( + isinstance(rule, bootstrap.Concat) + and len(rule.contents) == 2 + and is_synonym(rule.contents[0]) + and isinstance(rule.contents[1].contents, bootstrap.Repeated) + and isinstance(rule.contents[1].contents.contents[0], bootstrap.Concat) + and str(rule.contents[1].contents.contents[0].contents[1]) == str(rule.contents[0]) + ): S = is_synonym(rule.contents[0]) yield S # Do this recursively @@ -315,13 +326,13 @@ def to_tree(x, key=None): if key == "aggregation_types": # hack hack hack apparently the parser can't distinguish these subrules += list(replace_synonyms(rule_dependencies["general_aggregation_types"])) - + if rule_dependencies[key] and not subrules: # sometimes an intermediate production rule is missing # from the pyparsing output, e.g from parameter to simple_expression # directly. Recover from this. subrules = sum(map(rule_dependencies.__getitem__, rule_dependencies[key]), []) - + if not isinstance(rule_definitions[key], bootstrap.Union): # Filter out terminals when not a union. E.g no # reason to retain TYPE, END_TYPE, but operators @@ -331,7 +342,7 @@ def to_tree(x, key=None): vs = list(di.values()) return {k: v for k, v in di.items() if k in subrules or (k == key and len(vs) == 1 and vs[0] not in all_rules)} - + def simplify(di): if isinstance(di, list): if set(map(type, di)) == {str} and set(map(len, di)) == {1}: @@ -343,11 +354,11 @@ def to_tree(x, key=None): return {k: simplify(v) for k, v in di.items()} else: return di - + if isinstance(x, ListNode): d = to_tree(x.dict_tokens, key=get_rule_id(x) or key) - if key == 'if_stmt': + if key == "if_stmt": # The definition of if statement if (roughy): # 'if' expr 'then' stmt+ 'else' stmt+ # this causes stmt to be joined under the same @@ -355,39 +366,41 @@ def to_tree(x, key=None): # `else_stmt` that collects the second group # of stmts. - statements = x.dict_tokens['stmt'] - + statements = x.dict_tokens["stmt"] + else_index = None if_nesting = 0 - for i, tk in enumerate(x.flat): - if tk == 'if': if_nesting += 1 - if tk == 'end_if': if_nesting -= 1 - if tk == 'else' and if_nesting == 1: + for i, tk in enumerate(x.flat): + if tk == "if": + if_nesting += 1 + if tk == "end_if": + if_nesting -= 1 + if tk == "else" and if_nesting == 1: else_index = i if else_index: indices = [] for s in statements: for i in range(max(indices, default=0), len(x.flat)): - if x.flat[i:i+len(s.flat)] == s.flat: + if x.flat[i : i + len(s.flat)] == s.flat: indices.append(i) break assert len(indices) == len(statements) before_else = [i < else_index for i in indices] - else_stmt = [st for b, st in zip(before_else, d['stmt']) if not b] - d['stmt'] = [st for b, st in zip(before_else, d['stmt']) if b] + else_stmt = [st for b, st in zip(before_else, d["stmt"]) if not b] + d["stmt"] = [st for b, st in zip(before_else, d["stmt"]) if b] if else_stmt: - d['else_stmt'] = else_stmt - - if key == 'formal_parameter': + d["else_stmt"] = else_stmt + + if key == "formal_parameter": # Not so pretty hack to fix the overwriting of simple_id-like # ast nodes. The full solution would probably to register parse # actions. And directly reassign. - pid = d['parameter_id'][0][0] - d['parameter_id'][0] = x.flat[:x.flat.index(pid)+1:2] + pid = d["parameter_id"][0][0] + d["parameter_id"][0] = x.flat[: x.flat.index(pid) + 1 : 2] if key is None: return {get_rule_id(x): d} @@ -400,7 +413,10 @@ def to_tree(x, key=None): elif isinstance(x, dict): # d = {k: to_tree(v, key=k) for k, v in x.items()} # not fully understood, but when finding specific node Types and production rules, prioritize the former - d = {get_rule_id(k) or k: to_tree(v, key=k) for k, v in sorted(x.items(), key=lambda p: get_rule_id(p[0]) is not None)} + d = { + get_rule_id(k) or k: to_tree(v, key=k) + for k, v in sorted(x.items(), key=lambda p: get_rule_id(p[0]) is not None) + } return simplify(prune(d)) elif isinstance(x, list): return [to_tree(v, key=key) for v in x] @@ -459,7 +475,8 @@ class SuperTypeExpression(Node): else: constraint = self.supertype_rule[0] return [ - list(list(s)[0])[0].simple_id for s in list(list(list(constraint.subtype_constraint[0].supertype_expression[0])[0])[0].one_of[0])[2::2] + list(list(s)[0])[0].simple_id + for s in list(list(list(constraint.subtype_constraint[0].supertype_expression[0])[0])[0].one_of[0])[2::2] ] sub_types = property(get_sub_types) @@ -576,10 +593,11 @@ class ProcedureDeclaration(ListNode): @property def name(self): return self.flat[1] - - + + class FunctionDeclaration(ProcedureDeclaration): pass - + + class RuleDeclaration(ProcedureDeclaration): pass diff --git a/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py b/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py index 41fc6764c1..a77936d853 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py @@ -48,11 +48,7 @@ def to_graph(tree): # bootstrap.py that result in an intermediate list index node in to_tree() # Start with the intermediate nodes and filter out root (needs to have predecessors) - intermediate = [ - n - for n in g.nodes - if g.nodes[n].get("label") is None and list(g.predecessors(n)) - ] + intermediate = [n for n in g.nodes if g.nodes[n].get("label") is None and list(g.predecessors(n))] for n in intermediate: pr = list(g.predecessors(n)) @@ -82,8 +78,7 @@ def to_graph(tree): for n in g.nodes: if ( len(list(g.successors(n))) == 0 - and g.nodes[n].get("label") - not in ifcopenshell.express.express_parser.all_rules + and g.nodes[n].get("label") not in ifcopenshell.express.express_parser.all_rules ): g.nodes[n]["is_terminal"] = True @@ -105,8 +100,7 @@ def write_dot(fn, g): def format(di): Q = '"' inner = ",".join( - f"{k}={'' if v.startswith('<') else Q}{v}{'' if v.startswith('<') else Q}" - for k, v in di.items() + f"{k}={'' if v.startswith('<') else Q}{v}{'' if v.startswith('<') else Q}" for k, v in di.items() ) if inner: inner = f"[{inner}]" @@ -179,9 +173,7 @@ class context: def has_inverse(self, a): for r in self.rules: - if a in map( - lambda n: self.graph.nodes[n].get("label"), self.graph.predecessors(r) - ): + if a in map(lambda n: self.graph.nodes[n].get("label"), self.graph.predecessors(r)): return True return False @@ -190,10 +182,7 @@ class context: yield context(self.graph, [r]) def descendants(self): - return [ - b.rules[0][len(self.rules[0]) + 1 :] - for b in self.branches(allow_multiple=True) - ] + return [b.rules[0][len(self.rules[0]) + 1 :] for b in self.branches(allow_multiple=True)] def __repr__(self): try: @@ -206,14 +195,11 @@ class context: assert len(self.rules) == 1 nodes = itertools.chain( self.rules, - itertools.chain.from_iterable( - dict(nx.bfs_successors(self.graph, self.rules[0])).values() - ), + itertools.chain.from_iterable(dict(nx.bfs_successors(self.graph, self.rules[0])).values()), ) terminals_or_values = list( filter( - lambda n: self.graph.nodes[n].get("is_terminal") - or self.graph.nodes[n].get("value"), + lambda n: self.graph.nodes[n].get("is_terminal") or self.graph.nodes[n].get("value"), nodes, ) ) @@ -375,9 +361,7 @@ def calc_{class_name}_{str(derived_attr.attribute_decl.redeclared_attribute.qual """ if context.entity_body.derive_clause: - statements.extend( - map(format_derived, context.entity_body.derive_clause.branches()) - ) + statements.extend(map(format_derived, context.entity_body.derive_clause.branches())) return "\n\n".join(statements) @@ -420,10 +404,12 @@ def process_expression(context): exclude=[context.rel_op_extended], ) else: - if len(context.simple_expression.branches()) == 2 and str(context.rel_op_extended) == 'in': + if len(context.simple_expression.branches()) == 2 and str(context.rel_op_extended) == "in": # IfcBlobTexture try: - is_literal_str_list = set(map(type, ast.literal_eval(str(context.simple_expression.branches()[1])))) == {str} + is_literal_str_list = set( + map(type, ast.literal_eval(str(context.simple_expression.branches()[1]))) + ) == {str} except: is_literal_str_list = False if is_literal_str_list: @@ -567,9 +553,7 @@ def process_function_decl(context): str.lower, map( str, - context.function_head.formal_parameter.parameter_id.branches( - allow_multiple=True - ), + context.function_head.formal_parameter.parameter_id.branches(allow_multiple=True), ), ) return f"def {context.function_head.function_id}({', '.join(arguments)}):\n{indent(4, context.algorithm_head.local_decl)}\n{indent(4, context.stmt.branches())}" @@ -582,9 +566,7 @@ def process_query(context): def process_local_variable(context): if context.expression: expr = str(context.expression) - if ( - context.parameter_type.generalized_types.general_aggregation_types.general_set_type - ): + if context.parameter_type.generalized_types.general_aggregation_types.general_set_type: expr = re.sub(r"(\[[^\]]*\])", "express_set(\\1)", expr) return "%s = %s" % (str(context.variable_id).lower(), expr) @@ -623,9 +605,7 @@ def process_assignment(context): if m := re.match(r"^([^\[]+)\[([^\[]+)\]$", lhs): # @todo ugly regex hack aggr, index = m.groups() - return ( - f"temp = list({aggr})\ntemp[{index}] = {context.expression}\n{aggr} = temp" - ) + return f"temp = list({aggr})\ntemp[{index}] = {context.expression}\n{aggr} = temp" else: return "%s = %s" % (lhs, context.expression) @@ -643,11 +623,7 @@ def process_case_action(context): def process_case_statement(context): branches = context.branches( - exclude=[ - getattr(context, v) - for v in context.descendants() - if not v.startswith("case_action") - ] + exclude=[getattr(context, v) for v in context.descendants() if not v.startswith("case_action")] ) if context.stmt and context.stmt.branches(): branches += [f"else:\n{indent(4, context.stmt)}"] @@ -658,9 +634,7 @@ def process_aggregate_initializer(context): if context.element.repetition: return "([%s] * %s)" % (context.element.expression, context.element.repetition) else: - return "[%s]" % ",".join( - map(str, context.element.branches() if context.element else ()) - ) + return "[%s]" % ",".join(map(str, context.element.branches() if context.element else ())) def process_index(context): @@ -676,9 +650,7 @@ def process_index(context): codegen_rule("function_call", process_function_call) codegen_rule( "actual_parameter_list", - lambda context: ",".join( - map(str, context.expression.branches() if context.expression else []) - ), + lambda context: ",".join(map(str, context.expression.branches() if context.expression else [])), ) codegen_rule("entity_decl", functools.partial(process_type_decl, "entity")) codegen_rule("rule_decl", process_rule_decl) @@ -696,9 +668,7 @@ codegen_rule("simple_factor", simple_concat) codegen_rule("primary", simple_concat) codegen_rule("qualifier", simple_concat) codegen_rule("return_stmt", lambda context: "return %s" % context) -codegen_rule( - "compound_stmt", lambda context: "\n".join(map(str, context.stmt.branches())) -) +codegen_rule("compound_stmt", lambda context: "\n".join(map(str, context.stmt.branches()))) codegen_rule("if_stmt", process_if_stmt) codegen_rule("repeat_stmt", process_repeat_stmt) # codegen_rule("index", lambda context: '**express_index(%s)' % context) @@ -707,19 +677,14 @@ codegen_rule("index_qualifier", process_index) codegen_rule("group_qualifier", lambda context: empty()) codegen_rule("attribute_qualifier", lambda context: ".%s" % context) codegen_rule("rel_op", process_rel_op) -codegen_rule( - "built_in_constant", lambda context: "None" if str(context) == "?" else str(context) -) +codegen_rule("built_in_constant", lambda context: "None" if str(context) == "?" else str(context)) codegen_rule("assignment_stmt", process_assignment) codegen_rule("local_variable", process_local_variable) codegen_rule("local_decl", lambda context: "\n".join(map(str, context.branches()))) codegen_rule("general_ref/parameter_ref", make_lowercase) codegen_rule( "qualifiable_factor/attribute_ref", - make_lowercase_if( - lambda context: str(context) - not in set(map(str, schema.all_declarations.keys())) - ), + make_lowercase_if(lambda context: str(context) not in set(map(str, schema.all_declarations.keys()))), ) codegen_rule("case_action", process_case_action) codegen_rule("case_stmt", process_case_statement) @@ -824,17 +789,17 @@ if __name__ == "__main__": import subprocess schema = ifcopenshell.express.express_parser.parse(sys.argv[1]).schema - + try: ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema.name) except: # @nb note the difference here between: - # + # # - ifcopenshell.express.express_parser.parse # - ifcopenshell.express.parse.parse - # + # # First generates a pyparsing AST - # + # # Second populates a latebound schema # that can be registered in C++. builder = ifcopenshell.express.parse(sys.argv[1]) @@ -1056,13 +1021,13 @@ INDETERMINATE = indeterminate_type() if isinstance(v, str): nl = "\n" es = "\\n" - n[ - "label" - ] = f'<
{n.get("label")}
{v.replace("<", "<").replace(">", ">").replace(nl, "
")}
>' + n["label"] = ( + f'<
{n.get("label")}
{v.replace("<", "<").replace(">", ">").replace(nl, "
")}
>' + ) elif isinstance(v, empty): - n[ - "label" - ] = f'<
{n.get("label")}
---
>' + n["label"] = ( + f'<
{n.get("label")}
---
>' + ) fn = f"{nm}.dot" write_dot(fn, G) diff --git a/src/ifcopenshell-python/ifcopenshell/express/schema.py b/src/ifcopenshell-python/ifcopenshell/express/schema.py index ebb050d1ae..315db817b6 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/schema.py +++ b/src/ifcopenshell-python/ifcopenshell/express/schema.py @@ -27,6 +27,7 @@ if tuple(map(int, platform.python_version_tuple())) < (2, 7): collections.OrderedDict = ordereddict.OrderedDict + # According to ISO 10303-11 7.1.2: Letters: "... The case of # letters is significant only within explicit string literals." class OrderedCaseInsensitiveDict_KeyObject(str): @@ -92,23 +93,21 @@ class Schema: sort = lambda d: OrderedCaseInsensitiveDict(sorted(d)) - declarations = [ - d.any()[0] - for d in schema_declarations - if d.rule == "declaration" - ] + [ - d - for d in schema_declarations - if d.rule == "RuleDeclaration" + declarations = [d.any()[0] for d in schema_declarations if d.rule == "declaration"] + [ + d for d in schema_declarations if d.rule == "RuleDeclaration" ] - + self.types = sort([(t.name, t) for t in declarations if isinstance(t, nodes.TypeDeclaration)]) self.entities = sort([(t.name, t) for t in declarations if isinstance(t, nodes.EntityDeclaration)]) self.rules = sort([(t.name, t) for t in declarations if isinstance(t, nodes.RuleDeclaration)]) self.functions = sort([(t.name, t) for t in declarations if isinstance(t, nodes.FunctionDeclaration)]) - self.keys = list(self.types.keys()) + list(self.entities.keys()) + list(self.rules.keys()) + list(self.functions.keys()) - self.all_declarations = {k: v for d in (self.types, self.entities, self.rules, self.functions) for k, v in d.items()} + self.keys = ( + list(self.types.keys()) + list(self.entities.keys()) + list(self.rules.keys()) + list(self.functions.keys()) + ) + self.all_declarations = { + k: v for d in (self.types, self.entities, self.rules, self.functions) for k, v in d.items() + } of_type = lambda *types: sort( [(a, b.type) for a, b in self.types.items() if any(isinstance(b.type, ty) for ty in types)] diff --git a/src/ifcopenshell-python/ifcopenshell/express/schema_class.py b/src/ifcopenshell-python/ifcopenshell/express/schema_class.py index 50a4e50c5d..d7595ad6cb 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/schema_class.py +++ b/src/ifcopenshell-python/ifcopenshell/express/schema_class.py @@ -123,6 +123,7 @@ class string_pool: def __init__(self, fn): self.di = {} self.fn = fn + def append(self, v): def _(): if i := self.di.get(v): @@ -131,7 +132,9 @@ class string_pool: i = len(self.di) self.di[v] = i return i + return self.fn(_()) + def __iter__(self): return iter(self.di.keys()) @@ -146,9 +149,9 @@ class EarlyBoundCodeWriter: "", '#include "../ifcparse/IfcSchema.h"', '#include "../ifcparse/%(schema_name_title)s.h"' % self.__dict__, - '#include ', + "#include ", "", - 'using namespace std::string_literals;', + "using namespace std::string_literals;", "using namespace IfcParse;", "", ] @@ -180,18 +183,18 @@ class EarlyBoundCodeWriter: self.statements.append("{factory_placeholder}") -# self.statements.append( -# """ -# #if defined(__clang__) -# __attribute__((optnone)) -# #elif defined(__GNUC__) || defined(__GNUG__) -# #pragma GCC push_options -# #pragma GCC optimize ("O0") -# #elif defined(_MSC_VER) -# #pragma optimize("", off) -# #endif -# """ -# ) + # self.statements.append( + # """ + # #if defined(__clang__) + # __attribute__((optnone)) + # #elif defined(__GNUC__) || defined(__GNUG__) + # #pragma GCC push_options + # #pragma GCC optimize ("O0") + # #elif defined(_MSC_VER) + # #pragma optimize("", off) + # #endif + # """ + # ) self.statements.append("IfcParse::schema_definition* %s_populate_schema() {" % self.schema_name.upper()) self.statements.append("{string_pool_placeholder}") @@ -200,7 +203,7 @@ class EarlyBoundCodeWriter: index_in_schema = self.names.index(name) ref = self.strings.append(name) self.statements.append( - ' %(schema_name)s_types[%(index_in_schema)d] = new type_declaration(%(ref)s, %(index_in_schema)d, %(declared_type)s);' + " %(schema_name)s_types[%(index_in_schema)d] = new type_declaration(%(ref)s, %(index_in_schema)d, %(declared_type)s);" % locals() ) @@ -210,7 +213,7 @@ class EarlyBoundCodeWriter: ref = self.strings.append(name) items = ",".join(self.strings.append(v) for v in enum.values) self.statements.append( - ' %(schema_name)s_types[%(index_in_schema)d] = new enumeration_type(%(ref)s, %(index_in_schema)d, {%(items)s});' + " %(schema_name)s_types[%(index_in_schema)d] = new enumeration_type(%(ref)s, %(index_in_schema)d, {%(items)s});" % locals() ) @@ -218,10 +221,14 @@ class EarlyBoundCodeWriter: schema_name = self.schema_name.upper() index_in_schema = self.names.index(name) ref = self.strings.append(name) - supertype = "0" if len(type.supertypes) == 0 else "%s_types[%d]" % (self.schema_name, self.names.index(type.supertypes[0])) + supertype = ( + "0" + if len(type.supertypes) == 0 + else "%s_types[%d]" % (self.schema_name, self.names.index(type.supertypes[0])) + ) is_abstract = "true" if type.abstract else "false" self.statements.append( - ' %(schema_name)s_types[%(index_in_schema)d] = new entity(%(ref)s, %(is_abstract)s, %(index_in_schema)d, (entity*) %(supertype)s);' + " %(schema_name)s_types[%(index_in_schema)d] = new entity(%(ref)s, %(is_abstract)s, %(index_in_schema)d, (entity*) %(supertype)s);" % locals() ) @@ -233,73 +240,89 @@ class EarlyBoundCodeWriter: map(lambda v: "%s_types[%d]" % (self.schema_name, self.names.index(v)), sorted(map(str, type.values))) ) self.statements.append( - ' %(schema_name)s_types[%(index_in_schema)d] = new select_type(%(ref)s, %(index_in_schema)d, {%(items)s});' + " %(schema_name)s_types[%(index_in_schema)d] = new select_type(%(ref)s, %(index_in_schema)d, {%(items)s});" % locals() ) def entity_attributes(self, name, attribute_definitions, is_derived): schema_name = self.schema_name.upper() index_in_schema = self.names.index(name) + def _(): index_in_schema = self.names.index(name) schema_name = self.schema_name for attr_name, decl_type, optional in attribute_definitions: attr_name_ref = self.strings.append(attr_name) optional_cpp = str(optional).lower() - yield 'new attribute(%(attr_name_ref)s, %(decl_type)s, %(optional_cpp)s)' % locals() + yield "new attribute(%(attr_name_ref)s, %(decl_type)s, %(optional_cpp)s)" % locals() + attributes = ",".join(_()) derived = ",".join(map(lambda b: str(b).lower(), is_derived)) - self.statements.append(" ((entity*)%(schema_name)s_types[%(index_in_schema)d])->set_attributes({%(attributes)s}, {%(derived)s});" % locals()) + self.statements.append( + " ((entity*)%(schema_name)s_types[%(index_in_schema)d])->set_attributes({%(attributes)s}, {%(derived)s});" + % locals() + ) def inverse_attributes(self, name, inv_attrs): schema_name = self.schema_name.upper() index_in_schema = self.names.index(name) + def _(): schema_name = self.schema_name index_in_schema = self.names.index(name) for attr_name, aggr_type, bound1, bound2, entity_ref, attribute_entity, attribute_entity_index in inv_attrs: attr_name_ref = self.strings.append(attr_name) opposite_index_in_schema = self.names.index(entity_ref) - opposite1 = '%(schema_name)s_types[%(opposite_index_in_schema)d]' % locals() + opposite1 = "%(schema_name)s_types[%(opposite_index_in_schema)d]" % locals() opposite_index_in_schema = self.names.index(attribute_entity) - opposite2 = '%(schema_name)s_types[%(opposite_index_in_schema)d]' % locals() - yield 'new inverse_attribute(%(attr_name_ref)s, inverse_attribute::%(aggr_type)s_type, %(bound1)d, %(bound2)d, ((entity*) %(opposite1)s), ((entity*) %(opposite2)s)->attributes()[%(attribute_entity_index)d])' % locals() + opposite2 = "%(schema_name)s_types[%(opposite_index_in_schema)d]" % locals() + yield "new inverse_attribute(%(attr_name_ref)s, inverse_attribute::%(aggr_type)s_type, %(bound1)d, %(bound2)d, ((entity*) %(opposite1)s), ((entity*) %(opposite2)s)->attributes()[%(attribute_entity_index)d])" % locals() + attributes = ",".join(_()) - self.statements.append(" ((entity*) %(schema_name)s_types[%(index_in_schema)d])->set_inverse_attributes({%(attributes)s});" % locals()) + self.statements.append( + " ((entity*) %(schema_name)s_types[%(index_in_schema)d])->set_inverse_attributes({%(attributes)s});" + % locals() + ) def entity_subtypes(self, name, tys): schema_name = self.schema_name.upper() index_in_schema = self.names.index(name) - subtypes = ",".join(map(lambda t: ("((entity*) %%(schema_name)s_types[%d])" % self.names.index(t)), tys)) % locals() - self.statements.append(" ((entity*) %(schema_name)s_types[%(index_in_schema)d])->set_subtypes({%(subtypes)s});" % locals()) + subtypes = ( + ",".join(map(lambda t: ("((entity*) %%(schema_name)s_types[%d])" % self.names.index(t)), tys)) % locals() + ) + self.statements.append( + " ((entity*) %(schema_name)s_types[%(index_in_schema)d])->set_subtypes({%(subtypes)s});" % locals() + ) def finalize(self, can_be_instantiated_set): schema_name = self.schema_name.upper() schema_name_title = self.schema_name.capitalize() + def _(): schema_name = self.schema_name.upper() schema_name_title = self.schema_name.capitalize() for type_name in self.names: index_in_schema = self.names.index(type_name) yield "%(schema_name)s_types[%(index_in_schema)d]" % locals() + declarations = ",".join(_()) schema_name_ref = self.strings.append(schema_name) self.statements.append( - ' return new schema_definition(%(schema_name_ref)s, {%(declarations)s}, new %(schema_name)s_instance_factory());' + " return new schema_definition(%(schema_name_ref)s, {%(declarations)s}, new %(schema_name)s_instance_factory());" % locals() ) - self.statements.append("}"); + self.statements.append("}") -# self.statements.append( -# """ -# #if defined(__clang__) -# #elif defined(__GNUC__) || defined(__GNUG__) -# #pragma GCC pop_options -# #elif defined(_MSC_VER) -# #pragma optimize("", on) -# #endif -# """ -# ) + # self.statements.append( + # """ + # #if defined(__clang__) + # #elif defined(__GNUC__) || defined(__GNUG__) + # #pragma GCC pop_options + # #elif defined(_MSC_VER) + # #pragma optimize("", on) + # #endif + # """ + # ) self.statements.extend( ( @@ -340,24 +363,18 @@ class EarlyBoundCodeWriter: ) ) - self.statements[self.statements.index("{factory_placeholder}")] = ( - """ + self.statements[self.statements.index("{factory_placeholder}")] = """ class %(schema_name)s_instance_factory : public IfcParse::instance_factory { virtual IfcUtil::IfcBaseClass* operator()(const IfcParse::declaration* decl, IfcEntityInstanceData&& data) const { %(instance_mapping)s } }; -""" - % locals() - ) +""" % locals() "" - self.statements[self.statements.index("{string_pool_placeholder}")] = ( - """ + self.statements[self.statements.index("{string_pool_placeholder}")] = """ const std::string strings[] = {%s}; -""" - % ",".join(map(lambda s: '"%s"s' % s, self.strings)) - ) +""" % ",".join(map(lambda s: '"%s"s' % s, self.strings)) def __str__(self): return "\n".join(self.statements) @@ -379,16 +396,19 @@ class SchemaClass(codegen.Base): def wrapper(*args, **kwargs): schema_name_upper = mapping.schema.name.upper() declared_type = fn(*args, **kwargs) - if 'simple_type' in declared_type: + if "simple_type" in declared_type: pass else: - match = re.search(r'\((\w+?_[\w+]+?_\w+?)\)', declared_type) + match = re.search(r"\((\w+?_[\w+]+?_\w+?)\)", declared_type) if match: old_decl = match.group(1) - name = old_decl.lower().replace(schema_name.lower() + '_', '').replace('_type', '') + name = old_decl.lower().replace(schema_name.lower() + "_", "").replace("_type", "") idx = [n.lower() for n in x.names].index(name) - declared_type = declared_type.replace(old_decl, '%(schema_name_upper)s_types[%(idx)d]' % locals()) + declared_type = declared_type.replace( + old_decl, "%(schema_name_upper)s_types[%(idx)d]" % locals() + ) return declared_type + return wrapper if code == EarlyBoundCodeWriter else fn @transform_to_indexed diff --git a/src/ifcopenshell-python/ifcopenshell/express/templates.py b/src/ifcopenshell-python/ifcopenshell/express/templates.py index e0398fd58d..564fe881a5 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/templates.py +++ b/src/ifcopenshell-python/ifcopenshell/express/templates.py @@ -217,7 +217,7 @@ const IfcParse::entity& %(schema_name)s::%(name)s::Class() { return *((IfcParse: %(schema_name)s::%(name)s::%(name)s(%(constructor_arguments)s) : %(superclass_num_attrs)s { %(constructor_implementation)s; populate_derived(); } """ -# data_ = e; +# data_ = e; # data_ = new IfcEntityInstanceData(%(schema_name_upper)s_types[%(index_in_schema)d]); optional_attribute_description = "/// Whether the optional attribute %s is defined for this %s" @@ -255,32 +255,16 @@ get_attr_stmt_nested_array = "%(null_check)s aggregate_of_aggregate_of_instance: get_inverse = "if (!file_) { return nullptr; } return file_->getInverse(id_, %(schema_name_upper)s_types[%(type_index)d], %(index)d)->as<%(type)s>();" -set_attr_stmt = ( - "%(check_optional_set_begin)sset_attribute_value(%(index)d, %(star_if_optional)sv);%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s" -) -set_attr_instance = ( - "%(check_optional_set_begin)sset_attribute_value(%(index)d, v->as());%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s" -) -set_attr_stmt_enum = "%(check_optional_set_begin)sset_attribute_value(%(index)d, EnumerationReference(&%(non_optional_type)s::Class(), (size_t) %(star_if_optional)sv));%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s" -set_attr_stmt_array = ( - "%(check_optional_set_begin)sset_attribute_value(%(index)d, (%(star_if_optional)sv)->generalize());%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s" -) +set_attr_stmt = "%(check_optional_set_begin)sset_attribute_value(%(index)d, %(star_if_optional)sv);%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s" +set_attr_instance = "%(check_optional_set_begin)sset_attribute_value(%(index)d, v->as());%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s" +set_attr_stmt_enum = "%(check_optional_set_begin)sset_attribute_value(%(index)d, EnumerationReference(&%(non_optional_type)s::Class(), (size_t) %(star_if_optional)sv));%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s" +set_attr_stmt_array = "%(check_optional_set_begin)sset_attribute_value(%(index)d, (%(star_if_optional)sv)->generalize());%(check_optional_set_else)sunset_attribute_value(%(index)d);%(check_optional_set_end)s" -constructor_stmt = ( - "set_attribute_value(%(index)d, (%(name)s));" -) -constructor_stmt_enum = ( - "set_attribute_value(%(index)d, (EnumerationReference(&%(type)s::Class(),(size_t)%(name)s)));" -) -constructor_stmt_array = ( - "set_attribute_value(%(index)d, (%(name)s)->generalize());" -) -constructor_stmt_derived = ( - "" -) -constructor_stmt_instance = ( - "set_attribute_value(%(index)d, %(name)s ? %(name)s->as() : (IfcUtil::IfcBaseClass*) nullptr);" -) +constructor_stmt = "set_attribute_value(%(index)d, (%(name)s));" +constructor_stmt_enum = "set_attribute_value(%(index)d, (EnumerationReference(&%(type)s::Class(),(size_t)%(name)s)));" +constructor_stmt_array = "set_attribute_value(%(index)d, (%(name)s)->generalize());" +constructor_stmt_derived = "" +constructor_stmt_instance = "set_attribute_value(%(index)d, %(name)s ? %(name)s->as() : (IfcUtil::IfcBaseClass*) nullptr);" constructor_stmt_optional = " if (%(name)s) {%(stmt)s }"