Merge branch 'master' into performance_improvements

# Conflicts:
#	src/ifcexpressparser/implementation.py
#	src/ifcgeom/IfcGeomFunctions.cpp
#	src/ifcparse/Ifc4.cpp
This commit is contained in:
Thomas Krijnen
2016-04-24 15:22:10 +02:00
32 changed files with 1271 additions and 371 deletions
+23 -11
View File
@@ -19,8 +19,14 @@
import sys
import string
import operator
import itertools
from pyparsing import *
try: from functools import reduce
except: pass
class Expression:
def __init__(self, contents):
self.contents = contents[0]
@@ -56,12 +62,12 @@ class Keyword:
class Terminal:
def __init__(self, contents):
self.contents = contents[0]
def __repr__(self):
s = self.contents
is_keyword = len(s) >= 4 and s[0::len(s)-1] == '""' and \
self.is_keyword = len(s) >= 4 and s[0::len(s)-1] == '""' and \
all(c in alphanums+"_" for c in s[1:-1])
ty = "CaselessKeyword" if is_keyword else "CaselessLiteral"
return "%s(%s)" % (ty, s)
def __repr__(self):
ty = "CaselessKeyword" if self.is_keyword else "CaselessLiteral"
return "%s(%s)" % (ty, self.contents)
LPAREN = Suppress("(")
@@ -94,16 +100,16 @@ grammar.ignore(HASH + restOfLine)
express = grammar.parseFile(sys.argv[1])
def find_keywords(expr, li = None):
def find_bytype(expr, ty, li = None):
if li is None: li = []
if isinstance(expr, Term):
expr = expr.contents
if isinstance(expr, Keyword):
li.append(repr(expr))
return li
if isinstance(expr, ty):
li.append(expr)
return set(li)
elif isinstance(expr, Expression):
for term in expr:
find_keywords(term, li)
find_bytype(term, ty, li)
return set(li)
actions = {
@@ -122,6 +128,8 @@ actions = {
'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)",
}
to_emit = set(id for id, expr in express)
@@ -129,18 +137,22 @@ 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)
while True:
emitted_in_loop = set()
for id, expr in express:
kws = find_keywords(expr)
kws = map(repr, find_bytype(expr, Keyword))
found = [k in emitted for k in kws]
if id in to_emit and all(found):
emitted_in_loop.add(id)
emitted.add(id)
stmt = "(%s)" % expr
if id in to_combine:
stmt = "originalTextFor(Combine%s)" % stmt
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))
+4 -2
View File
@@ -31,12 +31,14 @@ import re
import os
import csv
from schema import OrderedCaseInsensitiveDict
try: from html.entities import entitydefs
except: from htmlentitydefs import entitydefs
make_absolute = lambda fn: os.path.join(os.path.dirname(os.path.realpath(__file__)), fn)
name_to_oid = {}
name_to_oid = OrderedCaseInsensitiveDict()
oid_to_desc = {}
oid_to_name = {}
oid_to_pid = {}
@@ -59,7 +61,7 @@ with open(make_absolute('DocAttribute.csv')) as f:
for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'):
pid = oid_to_pid[oid]
pname = oid_to_name[pid]
name_to_oid[(pname, name)] = oid
name_to_oid[".".join((pname, name))] = oid
oid_to_desc[oid] = desc
def description(item):
+2 -4
View File
@@ -1,5 +1,3 @@
# Taken from http://sourceforge.net/p/exp-engine/expresso/ci/master/tree/docs/iso-10303-11--2004.bnf
ABS = "abs" .
ABSTRACT = "abstract" .
ACOS = "acos" .
@@ -202,7 +200,7 @@ constructed_types = enumeration_type | select_type .
declaration = entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl .
derived_attr = attribute_decl ":" parameter_type ":=" expression ";" .
derive_clause = DERIVE derived_attr { derived_attr } .
domain_rule = rule_label_id ":" expression .
domain_rule = [ rule_label_id ":" ] expression .
element = expression [ ":" repetition ] .
entity_body = { explicit_attr } [ derive_clause ] [ inverse_clause ] [ unique_clause ] [ where_clause ] .
entity_constructor = entity_ref "(" [ expression { "," expression } ] ")" .
@@ -334,7 +332,7 @@ type_label_id = simple_id .
unary_op = "+" | "-" | NOT .
underlying_type = constructed_types | concrete_types .
unique_clause = UNIQUE unique_rule ";" { unique_rule ";" } .
unique_rule = rule_label_id ":" referenced_attribute { "," referenced_attribute } .
unique_rule = [ rule_label_id ":" ] referenced_attribute { "," referenced_attribute } .
until_control = UNTIL logical_expression .
use_clause = USE FROM schema_ref [ "(" named_type_or_rename { "," named_type_or_rename } ")" ] ";" .
variable_id = simple_id .
+14 -7
View File
@@ -41,15 +41,18 @@ class Header(codegen.Base):
emitted_simpletypes = set()
while len(emitted_simpletypes) < len(mapping.schema.simpletypes):
for name, type in mapping.schema.simpletypes.items():
if name in emitted_simpletypes: continue
if name.lower() in emitted_simpletypes: continue
type_str = mapping.make_type_string(mapping.flatten_type_string(type))
attr_type = mapping.make_argument_type(type)
superclass = mapping.simple_type_parent(name)
if superclass is None:
superclass = "IfcUtil::IfcBaseType"
elif superclass not in emitted_simpletypes:
elif superclass.lower() not in emitted_simpletypes:
continue
emitted_simpletypes.add(name)
else:
# Case normalize
superclass = [k for k in mapping.schema.simpletypes.keys() if k.lower() == superclass.lower()][0]
emitted_simpletypes.add(name.lower())
write(templates.simpletype, name=name, type=type_str, attr_type=attr_type, superclass=superclass)
class_definitions = []
@@ -60,14 +63,14 @@ class Header(codegen.Base):
emitted_entities = set()
while len(emitted_entities) < len(mapping.schema.entities):
for name, type in mapping.schema.entities.items():
if name in emitted_entities: continue
if len(type.supertypes) == 0 or set(type.supertypes) < emitted_entities:
if name.lower() in emitted_entities: continue
if len(type.supertypes) == 0 or set(map(str.lower, type.supertypes)) <= emitted_entities:
attr_lines = []
def write_method(attr):
if attr.optional:
attr_lines.append(templates.optional_attribute_description % (attr.name, name))
attr_lines.append("bool has%s() const;"%(attr.name))
attr_lines.extend(["/// %s"%d for d in documentation.description((name, attr.name))])
attr_lines.extend(["/// %s"%d for d in documentation.description(".".join((name, attr.name)))])
type_str = mapping.get_parameter_type(attr, allow_optional=False, allow_entities=False)
if mapping.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN":
attr_lines.append("%s %s() const;"%(type_str, attr.name))
@@ -88,7 +91,11 @@ class Header(codegen.Base):
inverse = "\n".join(["%s%s"%(' '*4, a) for a in inv_lines])
if len(inverse): inverse += '\n'
supertypes = type.supertypes if len(type.supertypes) else ['IfcUtil::IfcBaseEntity']
def case_norm(n):
n = n.lower()
return [k for k in mapping.schema.entities.keys() if k.lower() == n][0]
supertypes = map(case_norm, type.supertypes) if len(type.supertypes) else ['IfcUtil::IfcBaseEntity']
superclass = ": %s "%(", ".join(["public %s"%c for c in supertypes]))
argument_count = mapping.argument_count(type)
+6 -4
View File
@@ -20,6 +20,8 @@
import codegen
import templates
from schema import OrderedCaseInsensitiveDict
class Implementation(codegen.Base):
def __init__(self, mapping):
enumeration_functions = []
@@ -132,7 +134,7 @@ class Implementation(codegen.Base):
def get_attribute_index(entity, attr_name):
related_entity = mapping.schema.entities[entity]
return [a['name'] for a in mapping.get_assignable_arguments(related_entity, include_derived=True)].index(attr_name)
return [a['name'].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)].index(attr_name.lower())
inverse = [templates.const_function % {
'class_name' : name,
@@ -155,7 +157,7 @@ class Implementation(codegen.Base):
superclass = superclass
)
selectable_simple_types = sorted(set(sum([b.values for a,b in mapping.schema.selects.items()], [])) & set(mapping.schema.types.keys()))
selectable_simple_types = sorted(set(sum([b.values for a,b in mapping.schema.selects.items()], [])) & set(map(str, mapping.schema.types.keys())))
schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.simpletypes.items()]
schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.entities.items()]
@@ -168,10 +170,10 @@ class Implementation(codegen.Base):
'padding' : ' ' * (max_len - len(name))
} for name in enumerable_types]
enumeration_index_by_str = dict((j,i) for i,j in enumerate(enumerable_types))
enumeration_index_by_str = OrderedCaseInsensitiveDict((j,i) for i,j in enumerate(enumerable_types))
def get_parent_id(s):
e = mapping.schema.entities.get(s)
if e and e.supertypes:
if e and e.supertypes:
return enumeration_index_by_str[e.supertypes[0]]
else: return -1
@@ -41,11 +41,10 @@ class LateBoundImplementation(codegen.Base):
})
emitted_entities = set()
entities_to_emit = mapping.schema.entities.keys()
while len(emitted_entities) < len(mapping.schema.entities):
for name, type in mapping.schema.entities.items():
if name in emitted_entities: continue
if len(type.supertypes) == 0 or set(type.supertypes) < emitted_entities:
if name.lower() in emitted_entities: continue
if len(type.supertypes) == 0 or set(map(str.lower, type.supertypes)) <= emitted_entities:
constructor_arguments = mapping.get_assignable_arguments(type, include_derived = True)
entity_descriptor_attributes = []
for arg in constructor_arguments:
@@ -61,8 +60,9 @@ class LateBoundImplementation(codegen.Base):
})
emitted_entities.add(name)
parent_statement = '0' if len(type.supertypes) != 1 else templates.entity_descriptor_parent % {
'type' : type.supertypes[0]
'type' : [k for k in mapping.schema.entities.keys() if k.lower() == type.supertypes[0].lower()][0]
}
entity_descriptors.append(templates.entity_descriptor % {
'type' : name,
@@ -92,13 +92,13 @@ class LateBoundImplementation(codegen.Base):
if type.inverse:
for attr in type.inverse.elements:
related_entity = mapping.schema.entities[attr.entity]
related_attrs = [a['name'] for a in mapping.get_assignable_arguments(related_entity, include_derived=True)]
related_attrs = [a['name'].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)]
inverse_implementations.append(templates.inverse_implementation % {
'type' : name,
'name' : attr.name,
'related_type' : attr.entity,
'index' : related_attrs.index(attr.attribute)
'index' : related_attrs.index(attr.attribute.lower())
})
self.str = templates.lb_implementation % {
+6 -3
View File
@@ -57,7 +57,7 @@ class Mapping:
return None if str(parent) in self.express_to_cpp_typemapping else parent
def make_type_string(self, type):
if isinstance(type, (str, nodes.BinaryType)):
if isinstance(type, (str, nodes.BinaryType, nodes.StringType)):
return self.express_to_cpp_typemapping.get(str(type), type)
else:
is_list = self.schema.is_entity(type.type)
@@ -89,6 +89,8 @@ class Mapping:
return "ENTITY_INSTANCE"
elif isinstance(type, nodes.BinaryType):
return "BINARY"
elif isinstance(type, nodes.StringType):
return "STRING"
elif isinstance(type, nodes.EnumerationType):
return "ENUMERATION"
elif isinstance(type, nodes.AggregationType):
@@ -128,10 +130,11 @@ class Mapping:
type_str = templates.untyped_list
elif self.schema.is_simpletype(ty) or str(ty) in self.express_to_cpp_typemapping.values():
tmpl = templates.nested_array_type if is_nested_list else templates.array_type
bounds = (attr_type.bounds.lower, attr_type.bounds.upper) if attr_type.bounds else (-1, -1)
type_str = tmpl % {
'instance_type' : ty,
'lower' : attr_type.bounds.lower,
'upper' : attr_type.bounds.upper
'lower' : bounds[0],
'upper' : bounds[1]
}
else:
tmpl = templates.list_list_type if is_nested_list else templates.list_type
+20 -3
View File
@@ -21,8 +21,8 @@ import string
import collections
class Node:
def __init__(self, tokens):
self.tokens = tokens
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)]
@@ -128,7 +128,7 @@ class AttributeList(Node):
class InverseAttribute(Node):
name = property(lambda self: self.tokens[0])
type = property(lambda self: self.tokens[2])
bounds = property(lambda self: None if len(self.tokens) == 6 else self.tokens[3])
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):
@@ -170,6 +170,23 @@ class ExplicitAttribute(Node):
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] == ':'
def __repr__(self):
return "%s : %s%s" % (self.name, self.type, " ?" if self.optional else "")
class WidthSpec(Node):
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
def __repr__(self):
return "string"
+24 -2
View File
@@ -24,6 +24,28 @@ import collections
if tuple(map(int, platform.python_version_tuple())) < (2, 7):
import ordereddict
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(collections.OrderedDict):
class KeyObject(str):
def __eq__(self, other):
return self.lower() == other.lower()
def __hash__(self):
return hash(self.lower())
def __init__(self, *args, **kwargs):
collections.OrderedDict.__init__(self)
for key, value in collections.OrderedDict(*args, **kwargs).items():
self[OrderedCaseInsensitiveDict.KeyObject(key)] = value
def __setitem__(self, key, value):
return collections.OrderedDict.__setitem__(self, OrderedCaseInsensitiveDict.KeyObject(key), value)
def __getitem__(self, key):
return collections.OrderedDict.__getitem__(self, OrderedCaseInsensitiveDict.KeyObject(key))
def get(self, key, *args, **kwargs):
return collections.OrderedDict.get(self, OrderedCaseInsensitiveDict.KeyObject(key), *args, **kwargs)
def __contains__(self, key):
return collections.OrderedDict.__contains__(self, OrderedCaseInsensitiveDict.KeyObject(key))
class Schema:
def is_enumeration(self, v):
@@ -39,7 +61,7 @@ class Schema:
def __init__(self, parsetree):
self.name = parsetree[1]
sort = lambda d: collections.OrderedDict(sorted(d))
sort = lambda d: OrderedCaseInsensitiveDict(sorted(d))
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)])
@@ -48,4 +70,4 @@ class Schema:
self.enumerations = of_type(nodes.EnumerationType)
self.selects = of_type(nodes.SelectType)
self.simpletypes = of_type(str, nodes.AggregationType, nodes.BinaryType)
self.simpletypes = of_type(str, nodes.AggregationType, nodes.BinaryType, nodes.StringType)