diff --git a/src/ifcexpressparser/bootstrap.py b/src/ifcexpressparser/bootstrap.py index 0cbf839d69..53f87d75ac 100644 --- a/src/ifcexpressparser/bootstrap.py +++ b/src/ifcexpressparser/bootstrap.py @@ -113,35 +113,35 @@ def find_bytype(expr, ty, li = None): return set(li) actions = { - 'type_decl' : "lambda t: TypeDeclaration(t)", - 'entity_decl' : "lambda t: EntityDeclaration(t)", - 'underlying_type' : "lambda t: UnderlyingType(t)", - 'enumeration_type' : "lambda t: EnumerationType(t)", - 'aggregation_types' : "lambda t: AggregationType(t)", - 'general_aggregation_types' : "lambda t: AggregationType(t)", - 'select_type' : "lambda t: SelectType(t)", - 'binary_type' : "lambda t: BinaryType(t)", - 'subtype_declaration' : "lambda t: SubTypeExpression(t)", - 'supertype_constraint' : "lambda t: SuperTypeExpression(t)", - 'derive_clause' : "lambda t: AttributeList('derive', t)", - 'derived_attr' : "lambda t: DerivedAttribute(t)", - 'inverse_clause' : "lambda t: AttributeList('inverse', t)", - 'inverse_attr' : "lambda t: InverseAttribute(t)", - 'bound_spec' : "lambda t: BoundSpecification(t)", - 'explicit_attr' : "lambda t: ExplicitAttribute(t)", - 'width_spec' : "lambda t: WidthSpec(t)", - 'string_type' : "lambda t: StringType(t)", + 'type_decl' : "TypeDeclaration", + 'entity_decl' : "EntityDeclaration", + 'enumeration_type' : "EnumerationType", + 'aggregation_types' : "AggregationType", + 'general_aggregation_types' : "AggregationType", + 'select_type' : "SelectType", + 'binary_type' : "BinaryType", + 'subtype_declaration' : "SubTypeExpression", + 'supertype_constraint' : "SuperTypeExpression", + 'derive_clause' : "AttributeList", + 'inverse_clause' : "AttributeList", + 'inverse_attr' : "InverseAttribute", + 'bound_spec' : "BoundSpecification", + 'explicit_attr' : "ExplicitAttribute", + 'width_spec' : "WidthSpec", + 'string_type' : "StringType", + 'named_types' : "NamedType", + 'simple_types' : "SimpleType", } to_emit = set(id for id, expr in express) emitted = set() to_combine = set(["simple_id"]) -to_ignore = set(["where_clause", "supertype_constraint", "unique_clause"]) 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"} while True: emitted_in_loop = set() @@ -154,54 +154,65 @@ while True: stmt = "(%s)" % expr if id in to_combine: stmt = " + ".join(itertools.chain(negated_keywords, ("originalTextFor(Combine%s)" % stmt,))) - if id in actions: - stmt = "%s.setParseAction(%s)" % (stmt, actions[id]) - statements.append("%s = %s" % (id, stmt)) + if id not in no_action and not isinstance(expr.contents, Keyword) and not id in to_combine: + node_type = "ListNode" if "ZeroOrMore" in stmt else "Node" + action = actions.get(id, "lambda s, loc, t: %s(s, loc, t, rule=\"%s\")" % (node_type, id)) + stmt = "%s.setParseAction(%s)" % (stmt, action) + statements.append("%s = %s(\"%s\")" % (id, stmt, id)) to_emit -= emitted_in_loop if not emitted_in_loop: break for id in to_emit: - action = ".setParseAction(%s)" % actions[id] if id in actions else "" - statements.append("%s = Forward()%s" % (id, action)) + statements.append("%s = Forward()(\"%s\")" % (id, id)) for id in to_emit: expr = [e for k, e in express if k == id][0] stmt = "(%s)" % expr if id in to_combine: stmt = "Suppress%s" % stmt + if id not in no_action and not isinstance(expr.contents, Keyword): + node_type = "ListNode" if "ZeroOrMore" in stmt else "Node" + action = ".setParseAction(%s)" % (actions[id] if id in actions else "lambda s, loc, t: %s(s, loc, t, rule=\"%s\")" % (node_type, id)) + stmt = "(%s)%s" % (stmt, action) statements.append("%s << %s" % (id, stmt)) -print ("""import os +print (""" +# This file is generated by IfcOpenShell ifcexpressparser bootstrap.py + +import os +import os import sys import pickle -cache_file = sys.argv[1] + ".cache.dat" -if os.path.exists(cache_file): - with open(cache_file, "rb") as f: - mapping = pickle.load(f) - schema = mapping.schema -else: - from pyparsing import * - from nodes import * - - import schema - import mapping - - %s +import schema +import mapping - syntax.ignore("--" + restOfLine) - syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))")) - ast = syntax.parseFile(sys.argv[1]) - schema = schema.Schema(ast) - mapping = mapping.Mapping(schema) +from pyparsing import * +from nodes import * - with open(cache_file, "wb") as f: - pickle.dump(mapping, f, protocol=0) +def parse(fn): + cache_file = fn + ".cache.dat" + if os.path.exists(cache_file): + with open(cache_file, "rb") as f: + m = pickle.load(f) + else: + %s -import importlib -for output in sys.argv[2:]: - mdl = importlib.import_module(output) - mdl.Generator(mapping).emit() + syntax.ignore("--" + restOfLine) + syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))")) + ast = syntax.parseFile(fn) + s = schema.Schema(ast) + m = mapping.Mapping(s) -sys.stdout.write(schema.name) -"""%('\n '.join(statements))) + with open(cache_file, "wb") as f: + pickle.dump(m, f, protocol=0) + return m + +if __name__ == "__main__": + m = parse(sys.argv[1]) + import importlib + for output in sys.argv[2:]: + mdl = importlib.import_module(output) + mdl.Generator(m).emit() + sys.stdout.write(m.schema.name) +"""%('\n '.join(statements))) diff --git a/src/ifcexpressparser/definitions.py b/src/ifcexpressparser/definitions.py index 9683c4ffb5..d1563c096c 100644 --- a/src/ifcexpressparser/definitions.py +++ b/src/ifcexpressparser/definitions.py @@ -39,7 +39,7 @@ class Definitions(codegen.Base): statements.append("#define SCHEMA_%(name)s_HAS_%(attr)s" % locals()) if is_optional: statements.append("#define SCHEMA_%(name)s_%(attr)s_IS_OPTIONAL" % locals()) - inverse_attribute_names = list(map(operator.attrgetter('name'), type.inverse.elements)) if type.inverse else [] + inverse_attribute_names = list(map(operator.attrgetter('name'), type.inverse)) for attr in inverse_attribute_names: statements.append("#define SCHEMA_%(name)s_HAS_%(attr)s" % locals()) diff --git a/src/ifcexpressparser/header.py b/src/ifcexpressparser/header.py index 7b6ae41a59..35bb0e03e2 100644 --- a/src/ifcexpressparser/header.py +++ b/src/ifcexpressparser/header.py @@ -85,7 +85,7 @@ class Header(codegen.Base): inv_lines.append(templates.inverse_attr%{'name':attr.name, 'entity':attr.entity, 'attribute':attr.attribute}) if type.inverse: - [write_inverse(attr) for attr in type.inverse.elements] + [write_inverse(attr) for attr in type.inverse] attributes = "\n".join(["%s%s"%(' '*4, a) for a in attr_lines]) if len(attributes): attributes += '\n' diff --git a/src/ifcexpressparser/implementation.py b/src/ifcexpressparser/implementation.py index e9dab2ae86..9f7275aa38 100644 --- a/src/ifcexpressparser/implementation.py +++ b/src/ifcexpressparser/implementation.py @@ -55,7 +55,7 @@ class Implementation(codegen.Base): write = lambda str, **kwargs: entity_implementations.append(str%kwargs) - for name, type in mapping.schema.entities.items(): + for name, type in mapping.schema.entities.items(): parent_type_test = "" if not type.supertypes or len(type.supertypes) != 1 \ else templates.parent_type_test%(type.supertypes[0]) @@ -154,7 +154,7 @@ class Implementation(codegen.Base): 'arguments' : '', 'return_type' : '::%s::%s::list::ptr' % (schema_name, i.entity), 'body' : templates.get_inverse % {'type': i.entity, 'index':get_attribute_index(i.entity, i.attribute), 'schema_name' : schema_name, 'schema_name_upper': schema_name_upper} - } for i in (type.inverse.elements if type.inverse else [])] + } for i in type.inverse] superclass = "%s((IfcEntityInstanceData*)0)" % type.supertypes[0] if len(type.supertypes) == 1 else 'IfcUtil::IfcBaseEntity()' diff --git a/src/ifcexpressparser/mapping.py b/src/ifcexpressparser/mapping.py index ef80b97fd4..2ea3379c22 100644 --- a/src/ifcexpressparser/mapping.py +++ b/src/ifcexpressparser/mapping.py @@ -45,21 +45,28 @@ class Mapping: self.schema = schema def flatten_type_string(self, type): - return self.flatten_type_string(self.schema.types[type].type.type) if self.schema.is_simpletype(type) else type + return self.flatten_type_string(self.schema.types[type].type) if self.schema.is_simpletype(type) else type def flatten_type(self, type): - res = self.flatten_type(self.schema.types[type].type.type) if self.schema.is_simpletype(type) else type + res = self.flatten_type(self.schema.types[type].type) if self.schema.is_simpletype(type) else type return res def simple_type_parent(self, type): - parent = self.schema.types[type].type.type - if isinstance(parent, nodes.AggregationType): parent = None - return None if str(parent) in self.express_to_cpp_typemapping else parent + parent = self.schema.types[type].type + if isinstance(parent, (nodes.AggregationType, nodes.StringType)) or (isinstance(parent, nodes.SimpleType) and isinstance(parent.type, nodes.StringType)): + return None + if str(parent) in self.express_to_cpp_typemapping: + return None + return str(parent) def make_type_string(self, type): - if isinstance(type, (str, nodes.BinaryType, nodes.StringType)): + if isinstance(type, nodes.StringType) or (isinstance(type, nodes.SimpleType) and isinstance(type.type, nodes.StringType)): + type = "string" + if isinstance(type, (str, nodes.BinaryType, nodes.SimpleType, nodes.NamedType)): return self.express_to_cpp_typemapping.get(str(type), "::%s::%s" % (self.schema.name.capitalize(), type)) else: + if type.bounds is None: + import pdb; pdb.set_trace() is_list = self.schema.is_entity(type.type) is_nested_list = isinstance(type.type, nodes.AggregationType) tmpl = templates.list_list_type if is_nested_list else templates.list_type if is_list else templates.array_type @@ -73,7 +80,7 @@ class Mapping: if isinstance(type, nodes.AggregationType): return True elif isinstance(type, str) and self.schema.is_type(type): - return self.is_array(self.schema.types[type].type.type) + return self.is_array(self.schema.types[type].type) else: return False @@ -85,6 +92,8 @@ class Mapping: def make_argument_type(self, attr): def _make_argument_type(type): + if isinstance(type, nodes.SimpleType): + type = type.type if self.schema.is_entity(type) or isinstance(type, nodes.SelectType): return "ENTITY_INSTANCE" elif isinstance(type, nodes.BinaryType): @@ -100,7 +109,7 @@ class Mapping: elif str(type) in self.express_to_cpp_typemapping: return self.express_to_cpp_typemapping.get(str(type), type).split('::')[-1].upper() elif self.schema.is_type(type): - return _make_argument_type(self.schema.types[type].type.type) + return _make_argument_type(self.schema.types[type].type) else: raise ValueError("Unable to map type %r for attribute %r" % (type, attr)) ty = _make_argument_type(attr.type if hasattr(attr, 'type') else attr) @@ -117,7 +126,11 @@ class Mapping: def get_parameter_type(self, attr, allow_optional, allow_entities, allow_pointer = True): attr_type = self.flatten_type(attr.type) - type_str = self.express_to_cpp_typemapping.get(str(attr_type), attr_type) + + if (isinstance(attr_type, nodes.SimpleType) and isinstance(attr_type.type, nodes.StringType)) or isinstance(attr_type, nodes.StringType): + type_str = self.express_to_cpp_typemapping["string"] + else: + type_str = self.express_to_cpp_typemapping.get(str(attr_type), attr_type) is_ptr = False @@ -163,13 +176,16 @@ class Mapping: def derived_in_supertype(self, t): c = sum([self.derived_in_supertype(self.schema.entities[s]) for s in t.supertypes], []) - return c + ([str(s) for s in t.derive.elements] if t.derive else []) + derived = c + t.derive + return [d[0][1] for d in derived if isinstance(d[0], tuple)] def list_instance_type(self, attr): attr_type = attr.type if isinstance(attr, nodes.ExplicitAttribute) else attr if isinstance(attr_type, str): return None def f(v): v = self.flatten_type(v) + if isinstance(v, (nodes.AggregationType, nodes.StringType)) or (isinstance(v, nodes.SimpleType) and isinstance(v.type, nodes.StringType)): + return "string" if self.schema.is_select(v): return 'IfcUtil::IfcBaseClass' elif str(v) in self.schema.types or str(v) in self.schema.entities: diff --git a/src/ifcexpressparser/nodes.py b/src/ifcexpressparser/nodes.py index 0650e66950..abd77a96e4 100644 --- a/src/ifcexpressparser/nodes.py +++ b/src/ifcexpressparser/nodes.py @@ -24,45 +24,149 @@ import string import collections class Node: - def __init__(self, tokens = None): - self.tokens = tokens or [] - self.init() - def tokens_of_type(self, cls): - return [t for t in self.tokens if isinstance(t, cls)] - def single_token_of_type(self, cls, k = None, v = None): - ts = [t for t in self.tokens if isinstance(t, cls) and (k is None or getattr(t, k) == v)] - return ts[0] if len(ts) == 1 else None + def __init__(self, s, loc, tokens, rule=None): + self.rule = rule or (type(self).__name__) + self.tokens = tokens.asDict() + self.flat = sum([getattr(t, 'flat', [t]) for t in tokens.asList()], []) + if rule is None: + self.init() + def __repr__(self): + return "%s(%s)" % (self.rule, ",".join("%s:%s" % i for i in self.tokens.items())) + def __getattr__(self, k): + return self.tokens.get(k) + def __getstate__(self): return self.__dict__ + def __setstate__(self, d): self.__dict__.update(d) + def init(self): pass + def any(self): + return next(iter(self.tokens.values())) + +class ListNode: + def __init__(self, s, loc, tokens, rule=None): + self.rule = rule or (type(self).__name__) + self.tokens = tokens.asList() + self.flat = sum([getattr(t, 'flat', [t]) for t in self.tokens], []) + def __repr__(self): + return "%s[%s]" % (self.rule, ",".join("%s" % i for i in self.tokens)) + def __iter__(self): + return iter(self.tokens) + def __getitem__(self, i): + return self.tokens[i] + def init(self): pass + + +class SimpleType(Node): + + def get_type(self): + t = self.any() + if (type(t) == Node): + return t.any() + else: + t = t[0] + if (type(t) == Node): + return t.any().any() + else: + return t + + type = property(get_type) + + def __repr__(self): + return str(self.type) + + +def format_clause(exp): + def whitespace(t): + if t in {'=', '|', '<*', 'or', 'in', '<>', 'and'}: + return ' %s ' % t + return t + return "".join(whitespace(term) for term in exp.flat) class TypeDeclaration(Node): - name = property(lambda self: self.tokens[1]) - type = property(lambda self: self.tokens[3]) - def init(self): - assert self.tokens[0] == 'type' - assert isinstance(self.type, UnderlyingType) + name = property(lambda self: self.type_id[0]) + type = property(lambda self: self.underlying_type.any().any()) + def init(self): + + assert hasattr(self, "TYPE") + + self.where = [] + clause = self.where_clause + if clause: + clause = clause[0] + + self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]] + def __repr__(self): - return "%s = TypeDeclaration(%s)" % (self.name, self.type) + s = "TYPE %s = %s;\n" % (self.name, self.type) + if self.where: + s += " WHERE\n" + for nm_exp in self.where: + s += " %s : %s;\n" % nm_exp + s += "END_TYPE;" + return s class EntityDeclaration(Node): - name = property(lambda self: self.tokens[1]) - attributes = property(lambda self: self.tokens_of_type(ExplicitAttribute)) - abstract = property(lambda self: self.single_token_of_type(SuperTypeExpression) is not None and \ - self.single_token_of_type(SuperTypeExpression).abstract) + name = property(lambda self: self.entity_head[0].entity_id[0]) + supertype = property(lambda self: self.entity_head[0].subsuper[0].supertype_constraint) + subtype = property(lambda self: self.entity_head[0].subsuper[0].subtype_declaration) + supertypes = property(lambda self: [self.subtype.super_type] if self.subtype else []) + + def get_abstract(self): + if self.entity_head[0].subsuper[0].supertype_constraint: + return self.entity_head[0].subsuper[0].supertype_constraint.abstract + else: + return False + + abstract = property(get_abstract) + def init(self): - assert self.tokens[0] == 'entity' - s = self.single_token_of_type(SubTypeExpression) - self.inverse = self.single_token_of_type(AttributeList, 'type', 'inverse') - self.derive = self.single_token_of_type(AttributeList, 'type', 'derive') - self.supertypes = s.types if s else [] + + def redeclared_attribute(a): + try: + return ( + a.attribute_decl.redeclared_attribute.qualified_attribute.group_qualifier.simple_id, + a.attribute_decl.redeclared_attribute.qualified_attribute.attribute_qualifier.simple_id + ) + except: + return a.attribute_decl.simple_id + + assert self.flat[0] == 'entity' + + self.attributes = [a for a in self.entity_body[0] if isinstance(a, ExplicitAttribute)] + self.inverse = [] + alist = [x for x in self.entity_body[0] if isinstance(x, AttributeList) and x.type == 'inverse'] + if alist: + self.inverse = alist[0] + + self.derive = [] + alist = [x for x in self.entity_body[0] if isinstance(x, AttributeList) and x.type == 'derive'] + if alist: + alist = alist[0] + self.derive = [(redeclared_attribute(a), format_clause(a.expression[0])) for a in alist] + + self.where = [] + clause = [r for r in self.entity_body[0] if r.rule == "where_clause"] + if clause: + clause = clause[0] + + self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]] + + self.unique = [] + clause = [r for r in self.entity_body[0] if r.rule == "unique_clause"] + if clause: + clause = clause[0] + self.unique = [(r[0], r[2].simple_id) for r in clause[1::2]] + def __repr__(self): strm = io.StringIO() print("ENTITY %s" % self.name, file=strm) - for x in (SuperTypeExpression, SubTypeExpression): - tk = self.single_token_of_type(x) - if tk is not None: - print("", tk, file=strm) + + if self.supertype: + print("", self.supertype, file=strm) + if self.subtype: + print("", self.subtype, file=strm) + strm.seek(strm.tell() - 1) print(";", file=strm) @@ -71,171 +175,184 @@ class EntityDeclaration(Node): if self.derive: print(" DERIVE", file=strm) - print(self.derive, file=strm) + for nm, exp in self.derive: + if isinstance(nm, tuple): + nm = "SELF\\%s.%s" % nm + print(" %s : %s;" % (nm, exp), file=strm) if self.inverse: print(" INVERSE", file=strm) print(self.inverse, file=strm) - tks = self.tokens.asList() - try: - whr = tks.index('where') + if self.where: print(" WHERE", file=strm) - tks = tks[whr:-2] - tk_pairs = zip(tks[1:], tks) - def fmt(ss): - # import pdb; pdb.set_trace() - is_narrow = lambda s: s != ':' and len(s) == 1 - narrow = any(map(is_narrow, ss)) - lbreak = ss[0] == ';' - indent = ss[1] == 'where' or ss[1] == ';' - return "".join(((' ', '')[narrow or indent], ('', ' ')[indent], ss[0], ('', '\n')[lbreak])) - print(*map(fmt, tk_pairs), sep='', file=strm) - strm.seek(strm.tell() - 1) - except ValueError as e: - pass + for nm_exp in self.where: + print(" %s : %s;" % nm_exp, file=strm) + + if self.unique: + print(" UNIQUE", file=strm) + for nm_exp in self.unique: + print(" %s : %s;" % nm_exp, file=strm) print("END_ENTITY;", file=strm) return strm.getvalue() -class UnderlyingType(Node): - type = property(lambda self: self.tokens[0]) - def init(self): - pass - def __repr__(self): - return repr(self.type) - - class EnumerationType(Node): - type = property(lambda self: self.tokens[0]) - values = property(lambda self: self.tokens[3::2]) - def init(self): - assert self.type == 'enumeration' + values = property(lambda self: self.enumeration_type[2][1::2]) def __repr__(self): - return "ENUMERATION OF (" + ",".join(self.values) + ")" + return "ENUMERATION OF (" + ",".join(self.values) + ")" + + +class NamedType(Node): + type = property(lambda self: self.simple_id) + def __repr__(self): + return self.type class AggregationType(Node): - aggregate_type = property(lambda self: self.tokens[0]) - bounds = property(lambda self: None if self.tokens[1] == 'of' else self.tokens[1]) - type = property(lambda self: self.tokens[-1]) + aggregate_type = property(lambda self: self.flat[0]) + bounds = property(lambda self: (list(self.tokens.values())[0][0].bound_spec or [None])[0]) + unique = property(lambda self: list(self.tokens.values())[0][0].UNIQUE is not None) + + def get_type(self): + v = list(self.tokens.values())[0][0] + if v.instantiable_type: + try: + return v.instantiable_type.concrete_types.simple_id or v.instantiable_type.concrete_types.simple_types + except: + return v.instantiable_type + elif v.parameter_type.simple_types: + return v.parameter_type.simple_types + elif v.parameter_type.named_types: + return v.parameter_type.named_types + elif v.parameter_type.generalized_types.general_aggregation_types: + return v.parameter_type.generalized_types.general_aggregation_types + else: + import pdb; pdb.set_trace() + raise ValueError() + + type = property(get_type) + def init(self): assert self.bounds is None or isinstance(self.bounds, BoundSpecification) + def __repr__(self): - return "%s%s of %s"%(self.aggregate_type, self.bounds, self.type) + return "%s%s of %s%s"%(self.aggregate_type, self.bounds, "unique " if self.unique else "", self.type) class SelectType(Node): - type = property(lambda self: self.tokens[0]) - values = property(lambda self: self.tokens[2::2]) - def init(self): - assert self.type == 'select' + values = property(lambda self: self.select_type[1][1::2]) def __repr__(self): - return "SELECT (" + ",".join(self.values) + ")" + return "SELECT (" + ",".join(map(str, self.values)) + ")" -class SubSuperTypeExpression(Node): - type = property(lambda self: self.tokens[0]) - types = property(lambda self: self.tokens[3::2]) - abstract = False - def init(self): - if self.tokens[0] == 'abstract': - self.tokens = self.tokens[1:] - self.abstract = True - assert self.type == self.type_relationship - - def __repr__(self): - terminals = {"abstract","subtype", "supertype", "of", "oneof"} - tks = [s.upper() if s in terminals else s for s in self.tokens] +class SuperTypeExpression(Node): + abstract = property(lambda self: self.abstract_supertype_declaration is not None) + def get_sub_types(self): if self.abstract: - tks.insert(0, "ABSTRACT") - return " ".join(tks) + constraint = self.abstract_supertype_declaration[0] + else: + constraint = self.supertype_rule[0] + return [s[0][0].simple_id for s in constraint.subtype_constraint[0].supertype_expression[0][0][0].one_of[0][2::2]] - -class SubTypeExpression(SubSuperTypeExpression): - type_relationship = 'subtype' - - -class SuperTypeExpression(SubSuperTypeExpression): - type_relationship = 'supertype' - - -class AttributeList(Node): - elements = property(lambda self: self.tokens[1:]) - def __init__(self, ty, toks): - self.type = ty - Node.__init__(self, toks) - def init(self): - assert self.type == self.tokens[0] + sub_types = property(get_sub_types) + def __repr__(self): - return "\n".join([" %s;"%s for s in self.elements]) + return "%sSUPERTYPE OF(ONEOF(%s))" % ("ABSTRACT " if self.abstract else "",",".join(self.sub_types)) + + +class SubTypeExpression(Node): + super_type = property(lambda self: self.entity_ref[0]) + + def __repr__(self): + return "SUBTYPE OF(%s)" % self.super_type + +class AttributeList(ListNode): + type = property(lambda self: self.flat[0] if self.flat[0] in {'inverse', 'derive'} else 'explicit') + def __repr__(self): + return "\n".join([" %s;"%s for s in self.tokens[1:]]) def __iter__(self): - return iter(self.elements) + return iter(self.tokens[1:]) + def __len__(self): + return len(self.tokens[1:]) class InverseAttribute(Node): - name = property(lambda self: self.tokens[0]) - type = property(lambda self: self.tokens[2] if self.tokens[2] != self.tokens[-4] else None) - bounds = property(lambda self: None if len(self.tokens) != 9 else self.tokens[3]) - entity = property(lambda self: self.tokens[-4]) - attribute = property(lambda self: self.tokens[-2]) - def init(self): - assert self.bounds is None or isinstance(self.bounds, BoundSpecification) + name = property(lambda self: self.attribute_decl.simple_id) + type = property(lambda self: self.flat[2] if self.flat[2] != self.flat[-4] else None) + bounds = property(lambda self: self.bound_spec[0] if self.bound_spec else None) + entity = property(lambda self: self.entity_ref[0]) + attribute = property(lambda self: self.attribute_ref[0]) def __repr__(self): - return "%s : %s %s OF %s FOR %s" % (self.name, (self.type or "").upper(), self.bounds or "", self.entity, self.attribute) - + def _(): + yield self.name + yield ":" + if self.type: + yield self.type.upper() + yield "OF" + if self.bounds: + yield self.bounds + yield self.entity + yield "FOR" + yield self.attribute + return " ".join(map(str, _())) +""" class DerivedAttribute(Node): def init(self): + return name_index = list(self.tokens).index(':') - 1 self.name = self.tokens[name_index] def __repr__(self): return str(self.name) - +""" class BinaryType(Node): - def init(self): - pass def __repr__(self): return "binary" class BoundSpecification(Node): - lower = property(lambda self: self.tokens[1]) - upper = property(lambda self: self.tokens[3]) - def init(self): - # assert self.lower in string.digits or self.lower == '?' - # assert self.upper in string.digits or self.upper == '?' - pass + lower = property(lambda self: self.flat[1]) + upper = property(lambda self: self.flat[3]) + def __repr__(self): return "[%s:%s]"%(self.lower, self.upper) class ExplicitAttribute(Node): - name = property(lambda self: self.tokens[0]) - type = property(lambda self: self.tokens[-2]) - optional = property(lambda self: len(self.tokens) == 5 and self.tokens[-3] == 'optional') - def init(self): - # NB: This assumes a single name per attribute - # definition, which is not necessarily the case. - if self.tokens[0] == "self": - i = list(self.tokens).index(":") - self.tokens = self.tokens[i-1:] - assert self.tokens[1] == ':' + name = property(lambda self: self.attribute_decl.simple_id) + optional = property(lambda self: self.OPTIONAL is not None) + + def get_type(self): + v = next(iter(self.parameter_type.tokens.values())) + if v.general_aggregation_types: + return v.general_aggregation_types + else: + return v + + type = property(get_type) + def __repr__(self): - return "%s : %s%s" % (self.name, "OPTIONAL " if self.optional else "", self.type) + return "%s : %s%s" % (self.name, "optional " if self.optional else "", self.type) class WidthSpec(Node): + fixed = property(lambda self: self.FIXED is not None) + def init(self): - if self.tokens[-1] == "fixed": - self.tokens[-1:] = [] - assert (self.tokens[0], self.tokens[-1]) == ("(", ")") - self.width = int("".join(self.tokens[1:-1])) - -class StringType(Node): - def init(self): - pass + self.width = int(''.join(self.width[0].flat)) + def __repr__(self): - return "string" + return "(%d)%s" % (self.width, " fixed" if self.fixed else "") + + +class StringType(Node): + width = property(lambda self: self.width_spec[0] if self.width_spec else None) + + def __repr__(self): + s = "string" + if self.width: + s += " " + repr(self.width) + return s diff --git a/src/ifcexpressparser/schema.py b/src/ifcexpressparser/schema.py index 40935b09a1..d7614b685b 100644 --- a/src/ifcexpressparser/schema.py +++ b/src/ifcexpressparser/schema.py @@ -70,18 +70,22 @@ class Schema: def __getitem__(self, key): return self.types_entities[key] def __init__(self, parsetree): - self.name = parsetree[1] - + self.name = parsetree.syntax[0][0].simple_id + sort = lambda d: OrderedCaseInsensitiveDict(sorted(d)) + + declarations = [d.any()[0] for d in parsetree.syntax[0][0].schema_body[0] if d.rule == 'declaration' and d.any()[0].rule != 'function_decl'] - self.types = sort([(t.name,t) for t in parsetree if isinstance(t, nodes.TypeDeclaration)]) - self.entities = sort([(t.name,t) for t in parsetree if isinstance(t, nodes.EntityDeclaration)]) + 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.keys = list(self.types.keys()) + list(self.entities.keys()) self.types_entities = {k: v for d in (self.types, self.entities) for k, v in d.items()} - of_type = lambda *types: sort([(a, b.type.type) for a,b in self.types.items() if any(isinstance(b.type.type, ty) for ty in types)]) + 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)]) self.enumerations = of_type(nodes.EnumerationType) self.selects = of_type(nodes.SelectType) - self.simpletypes = of_type(str, nodes.AggregationType, nodes.BinaryType, nodes.StringType) + self.simpletypes = of_type(str, nodes.AggregationType, nodes.BinaryType, nodes.StringType, nodes.SimpleType, nodes.NamedType) + + assert len(self.enumerations) + len(self.selects) + len(self.simpletypes) == len(self.types) diff --git a/src/ifcexpressparser/schema_class.py b/src/ifcexpressparser/schema_class.py index 9d2e2201de..0e682702e3 100644 --- a/src/ifcexpressparser/schema_class.py +++ b/src/ifcexpressparser/schema_class.py @@ -36,6 +36,11 @@ class SchemaClass(codegen.Base): declared_types = [] def get_declared_type(type, emitted_names=None): + if isinstance(type, nodes.SimpleType): + type = type.type + if isinstance(type, nodes.NamedType): + type = str(type) + if isinstance(type, nodes.AggregationType): aggr_type = type.aggregate_type make_bound = lambda b: -1 if b == '?' else int(b) @@ -54,6 +59,8 @@ class SchemaClass(codegen.Base): raise UnmetDependenciesException(type) else: return "new simple_type(simple_type::%s_type)" % type + else: + raise ValueError("No mapping for '%s'" % type) def find_inverse_name_and_index(entity_name, attribute_name): attributes_per_subtype = [] @@ -132,10 +139,10 @@ __attribute__((optnone)) else: return False def write_select(schema_name, name, type): - if set(map(lambda s: s.lower(),type.values)) < emitted: + if set(map(lambda s: str(s).lower(), type.values)) < emitted: statements.append(' {') statements.append(' std::vector items; items.reserve(%d);' % len(type.values)) - statements.extend(map(lambda v: ' items.push_back(%s_%s_type);' % (schema_name, v), sorted(type.values))) + statements.extend(map(lambda v: ' items.push_back(%s_%s_type);' % (schema_name, v), sorted(map(str, type.values)))) statements.append(' %(schema_name)s_%(name)s_type = new select_type("%(name)s", %%(index_in_schema_%(name)s)d, items);' % locals()) statements.append(' }') else: return False @@ -152,7 +159,7 @@ __attribute__((optnone)) decl = mapping.schema[name] if isinstance(decl, nodes.TypeDeclaration): - decl = decl.type.type + decl = decl.type return fn(schema_name, name, decl) is not False while len(emitted) < len_to_emit: @@ -183,8 +190,8 @@ __attribute__((optnone)) for name, type in mapping.schema.entities.items(): if type.inverse: statements.append(' {') - statements.append(' std::vector attributes; attributes.reserve(%d);' % len(type.inverse.elements)) - for attr in type.inverse.elements: + statements.append(' std::vector attributes; attributes.reserve(%d);' % len(type.inverse)) + for attr in type.inverse: if attr.bounds: make_bound = lambda b: -1 if b == '?' else int(b) bound1, bound2 = map(make_bound, (attr.bounds.lower, attr.bounds.upper))