First Commit

This commit is contained in:
2026-05-31 10:17:09 +07:00
commit 17a9c69379
4547 changed files with 1170384 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,40 @@
# IfcOpenShell express schema parser and code generator
This folder contains Python code to generate C++ type information based on an
Express schema. In particular is has only been tested using recent version of
the IFC schema and will most likely fail on any other Express schema.
The code can be invoked in the following way and results in several code outputs
named according to the schema name in the Express file. A python 3 interpreter
with the `pyparsing` (`pip install pyparsing`) library is required.
note:
**Keep in mind that Express is not very suitable for this kind of usage. In the end, (as far as I can tell) no tool is able to read your model, because it doesn't understand the schema as it has no notion of the core part vs the addition.**
## Command line usage for code generation
The usage for C++ project.
~~~
# bootstrap.py express.bnf in ifcopenshell-python.ifcopenshell.express.
>>> python3 bootstrap.py express.bnf > express_parser.py
>>> python3 express_parser.py IFC2X3_TC1.exp header implementation schema_class definitions
~~~
The usage for python project.
~~~
# code rule generation
# generating 'ifcopenshell.express.rules.IFC2X3_TC1' in ifcopenshell-python.ifcopenshell.express.rules.
# rule_compiler in ifcopenshell-python.ifcopenshell.express.
>>> python -m rule_compiler IFC2X3_TC1.exp
~~~
## Programmatic usage
~~~py
import ifcopenshell
import ifcopenshell.express
schema = ifcopenshell.express.parse('IFC.exp')
ifcopenshell.register_schema(schema)
f = ifcopenshell.file(schema=schema.schema.name())
f.createIfcProject(ifcopenshell.guid.new())
~~~
@@ -0,0 +1,43 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import os
import sys
import subprocess
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import schema_class
d = os.path.abspath(os.path.dirname(__file__))
sys.path.append(d)
exp_parser_fn = os.path.join(d, "express_parser.py")
if not os.path.exists(exp_parser_fn):
with open(exp_parser_fn, "w") as f:
subprocess.call([sys.executable, "bootstrap.py"], cwd=d, stdout=f)
def parse(fn: str) -> schema_class.SchemaClass:
import express_parser
import schema_class
mapping = express_parser.parse(fn)
return schema_class.SchemaClass(mapping, schema_class.LateBoundSchemaInstantiator).code
@@ -0,0 +1,259 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import os
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]
def __repr__(self):
if self.op is None:
return repr(self.contents)
c = [isinstance(c, str) and c or str(c) for c in self.contents]
if "%s" in self.op:
return self.op % (" ".join(c))
else:
return "(%s)" % (" %s " % self.op).join(c)
def __iter__(self):
return self.contents.__iter__()
class Union(Expression):
op = "|"
class Concat(Expression):
op = "+"
class Optional(Expression):
op = "Optional(%s)"
class Repeated(Expression):
op = "ZeroOrMore(%s)"
class Term(Expression):
op = None
class Keyword:
def __init__(self, contents):
self.contents = contents[0]
def __repr__(self):
return self.contents
class Terminal:
def __init__(self, contents):
self.contents = contents[0]
s = self.contents
self.is_keyword = len(s) >= 4 and s[0 :: len(s) - 1] == '""' and all(c in alphanums + "_" for c in s[1:-1])
def __repr__(self):
ty = "CaselessKeyword" if self.is_keyword else "CaselessLiteral"
return "%s(%s)" % (ty, self.contents)
LPAREN = Suppress("(")
RPAREN = Suppress(")")
LBRACK = Suppress("[")
RBRACK = Suppress("]")
LBRACE = Suppress("{")
RBRACE = Suppress("}")
EQUALS = Suppress("=")
VBAR = Suppress("|")
PERIOD = Suppress(".")
HASH = Suppress("#")
identifier = Word(alphanums + "_")
keyword = Word(alphanums + "_").setParseAction(Keyword)
expression = Forward()
optional = Group(LBRACK + expression + RBRACK).setParseAction(Optional)
repeated = Group(LBRACE + expression + RBRACE).setParseAction(Repeated)
terminal = quotedString.setParseAction(Terminal)
term = (keyword | terminal | optional | repeated | (LPAREN + expression + RPAREN)).setParseAction(Term)
concat = Group(term + OneOrMore(term)).setParseAction(Concat)
factor = concat | term
union = Group(factor + OneOrMore(VBAR + factor)).setParseAction(Union)
rule = identifier + EQUALS + expression + PERIOD
expression << (union | factor)
grammar = OneOrMore(Group(rule))
grammar.ignore(HASH + restOfLine)
express = grammar.parseFile(os.path.join(os.path.dirname(__file__), "express.bnf"))
def find_bytype(expr, ty, li=None):
if li is None:
li = []
if isinstance(expr, Term):
expr = expr.contents
if isinstance(expr, ty):
li.append(expr)
return set(li)
elif isinstance(expr, Expression):
for term in expr:
find_bytype(term, ty, li)
return set(li)
actions = {
"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",
"function_decl": "FunctionDeclaration",
"rule_decl": "RuleDeclaration",
}
to_emit = set(id for id, expr in express)
emitted = set()
to_combine = set(["simple_id"])
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",
}
while True:
emitted_in_loop = set()
for id, expr in express:
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 = " + ".join(itertools.chain(negated_keywords, ("originalTextFor(Combine%s)" % 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:
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):
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)" % (
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))
if __name__ == "__main__":
print(r"""
# This file is generated by IfcOpenShell ifcexpressparser bootstrap.py
from __future__ import annotations
import os
import sys
import pickle
import schema
import mapping
from pyparsing import *
from nodes import *
def parse(fn: str) -> mapping.Mapping:
cache_file = fn + ".cache.dat"
if os.path.exists(cache_file) and os.path.getmtime(cache_file) >= os.path.getmtime(fn):
with open(cache_file, "rb") as f:
m = pickle.load(f)
else:
%s
syntax.ignore("--" + restOfLine)
syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))"))
ast = syntax.parseFile(fn)
s = schema.Schema(ast)
m = mapping.Mapping(s)
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)))
@@ -0,0 +1,16 @@
import sys, fileinput
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")
files = files[2:]
else:
b = getattr(sys.stdout, "buffer", sys.stdout)
for line in fileinput.input(files=files, mode="rb"):
b.write(line)
@@ -0,0 +1,50 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import itertools
import functools
def indent(n, s):
if isinstance(s, str):
strs = [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)
class Base:
"""
A base class for all code generation classes. Currently only working around
some python 2/3 incompatibilities in terms of unicode file handling.
"""
def emit(self):
import platform
if tuple(map(int, platform.python_version_tuple())) < (2, 8):
from io import open as unicode_open
unicode_type = unicode
else:
unicode_open = open
unicode_type = lambda x, *args, **kwargs: x
f = unicode_open(self.file_name, "w", encoding="utf-8")
f.write(unicode_type(repr(self), encoding="utf-8", errors="ignore"))
f.close()
@@ -0,0 +1,70 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import operator
import nodes
import codegen
from collections import defaultdict
class Definitions(codegen.Base):
def __init__(self, mapping):
schema_name = mapping.schema.name
self.schema_name = schema_name_title = schema_name.capitalize()
statements = [""]
def write_entity(schema_name, name, type):
attribute_names = list(map(lambda t: (t.name, t.optional), type.attributes))
for attr, is_optional in attribute_names:
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))
for attr in inverse_attribute_names:
statements.append("#define SCHEMA_%(name)s_HAS_%(attr)s" % locals())
def write(name):
statements.append("#define SCHEMA_HAS_%(name)s" % locals())
fn = None
if mapping.schema.is_entity(name):
fn = write_entity
if fn is not None:
decl = mapping.schema[name]
if isinstance(decl, nodes.TypeDeclaration):
decl = decl.type.type
fn(schema_name, name, decl) is not False
for name in mapping.schema:
write(name)
self.str = "\n".join(statements) + "\n"
self.file_name = "%s-definitions.h" % self.schema_name
def __repr__(self):
return self.str
Generator = Definitions
@@ -0,0 +1,88 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
###############################################################################
# #
# This files uses the documentation files from buildingSMART to generate #
# descriptions from EXPRESS names that are suitable for comments in the C++ #
# code. The .csv files used by this file are generated from the MS Office #
# Access database, which in turn has been generated from the IFC baseline #
# documentation by the IFCDOC utility provided by buildingSMART. #
# #
###############################################################################
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 = OrderedCaseInsensitiveDict()
oid_to_desc = {}
oid_to_name = {}
oid_to_pid = {}
regices = list(
zip(
[re.compile(s, re.M) for s in [r'<[\w\n=" \-/\.;_\t:%#,\?\(\)]+>', r"(\n[\t ]*){2,}", r"^[\t ]+"]],
["", "\n\n", " "],
)
)
definition_files = ["DocEntity.csv", "DocEnumeration.csv", "DocDefined.csv", "DocSelect.csv"]
definition_files = map(make_absolute, definition_files)
for fn in definition_files:
with open(fn, encoding="utf8", errors="ignore") as f:
for oid, name, desc in csv.reader(f, delimiter=";", quotechar='"'):
name_to_oid[name] = oid
oid_to_name[oid] = name
oid_to_desc[oid] = desc
with open(make_absolute("DocEntityAttributes.csv")) as f:
for pid, x, oid in csv.reader(f, delimiter=";", quotechar='"'):
oid_to_pid[oid] = pid
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[".".join((pname, name))] = oid
oid_to_desc[oid] = desc
def description(item):
global name_to_oid, oid_to_desc, oid_to_name, oid_to_pid
oid = name_to_oid.get(item, 0)
desc = oid_to_desc.get(oid, None)
if desc:
for a, b in entitydefs.items():
desc = desc.replace("&%s;" % a, b)
desc = desc.replace("\r", "")
for r, s in regices:
desc = r.sub(s, desc)
desc = desc.strip()
return desc.split("\n")
else:
return []
@@ -0,0 +1,342 @@
ABS = "abs" .
ABSTRACT = "abstract" .
ACOS = "acos" .
AGGREGATE = "aggregate" .
ALIAS = "alias" .
AND = "and" .
ANDOR = "andor" .
ARRAY = "array" .
AS = "as" .
ASIN = "asin" .
ATAN = "atan" .
BAG = "bag" .
BASED_ON = "based_on" .
BEGIN = "begin" .
BINARY = "binary" .
BLENGTH = "blength" .
BOOLEAN = "boolean" .
BY = "by" .
CASE = "case" .
CONSTANT = "constant" .
CONST_E = "const_e" .
COS = "cos" .
DERIVE = "derive" .
DIV = "div" .
ELSE = "else" .
END = "end" .
END_ALIAS = "end_alias" .
END_CASE = "end_case" .
END_CONSTANT = "end_constant" .
END_ENTITY = "end_entity" .
END_FUNCTION = "end_function" .
END_IF = "end_if" .
END_LOCAL = "end_local" .
END_PROCEDURE = "end_procedure" .
END_REPEAT = "end_repeat" .
END_RULE = "end_rule" .
END_SCHEMA = "end_schema" .
END_SUBTYPE_CONSTRAINT = "end_subtype_constraint" .
END_TYPE = "end_type" .
ENTITY = "entity" .
ENUMERATION = "enumeration" .
ESCAPE = "escape" .
EXISTS = "exists" .
EXTENSIBLE = "extensible" .
EXP = "exp" .
FALSE = "false" .
FIXED = "fixed" .
FOR = "for" .
FORMAT = "format" .
FROM = "from" .
FUNCTION = "function" .
GENERIC = "generic" .
GENERIC_ENTITY = "generic_entity" .
HIBOUND = "hibound" .
HIINDEX = "hiindex" .
IF = "if" .
IN = "in" .
INSERT = "insert" .
INTEGER = "integer" .
INVERSE = "inverse" .
LENGTH = "length" .
LIKE = "like" .
LIST = "list" .
LOBOUND = "lobound" .
LOCAL = "local" .
LOG = "log" .
LOG10 = "log10" .
LOG2 = "log2" .
LOGICAL = "logical" .
LOINDEX = "loindex" .
MOD = "mod" .
NOT = "not" .
NUMBER = "number" .
NVL = "nvl" .
ODD = "odd" .
OF = "of" .
ONEOF = "oneof" .
OPTIONAL = "optional" .
OR = "or" .
OTHERWISE = "otherwise" .
PI = "pi" .
PROCEDURE = "procedure" .
QUERY = "query" .
REAL = "real" .
REFERENCE = "reference" .
REMOVE = "remove" .
RENAMED = "renamed" .
REPEAT = "repeat" .
RETURN = "return" .
ROLESOF = "rolesof" .
RULE = "rule" .
SCHEMA = "schema" .
SELECT = "select" .
SELF = "self" .
SET = "set" .
SIN = "sin" .
SIZEOF = "sizeof" .
SKIP = "skip" .
SQRT = "sqrt" .
STRING = "string" .
SUBTYPE = "subtype" .
SUBTYPE_CONSTRAINT = "subtype_constraint" .
SUPERTYPE = "supertype" .
TAN = "tan" .
THEN = "then" .
TO = "to" .
TOTAL_OVER = "total_over" .
TRUE = "true" .
TYPE = "type" .
TYPEOF = "typeof" .
UNIQUE = "unique" .
UNKNOWN = "unknown" .
UNTIL = "until" .
USE = "use" .
USEDIN = "usedin" .
VALUE = "value" .
VALUE_IN = "value_in" .
VALUE_UNIQUE = "value_unique" .
VAR = "var" .
WHERE = "where" .
WHILE = "while" .
WITH = "with" .
XOR = "xor" .
bit = "0" | "1" .
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" .
digits = digit { digit } .
encoded_character = octet octet octet octet .
hex_digit = digit | "a" | "b" | "c" | "d" | "e" | "f" .
letter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" .
lparen_then_not_lparen_star = "(" { "(" } not_lparen_star { not_lparen_star } .
not_lparen_star = not_paren_star | ")" .
not_paren_star = letter | digit | not_paren_star_special .
not_paren_star_quote_special = "!" | "#" | "$" | "%" | "&" | "+" | "," | "-" | "." | "/" | ":" | ";" | "<" | "=" | ">" | "?" | "@" | "[" | "\\" | "]" | "^" | "_" | "{" | "|" | "}" | "~" .
not_paren_star_special = not_paren_star_quote_special | "\"\"" .
not_quote = not_paren_star_quote_special | letter | digit | "(" | ")" | "*" .
not_rparen_star = not_paren_star | "(" .
octet = hex_digit hex_digit .
special = not_paren_star_quote_special | "(" | ")" | "*" | "\"\"" .
not_rparen_star_then_rparen = not_rparen_star { not_rparen_star } ")" { ")" } .
binary_literal = "%" bit { bit } .
encoded_string_literal = "\"" encoded_character { encoded_character } "\"" .
integer_literal = digits .
real_literal = ( digits "." [ digits ] [ "e" [ sign ] digits ] ) | integer_literal .
simple_id = letter { letter | digit | "_" } .
simple_string_literal = "'" { ( "'" "'" ) | not_quote } "'" .
embedded_remark = "(*" [ remark_tag ] { ( not_paren_star { not_paren_star } ) | lparen_then_not_lparen_star | ( "*" { "*" } ) | not_rparen_star_then_rparen | embedded_remark } "*)" .
remark = embedded_remark | tail_remark .
remark_tag = "\"" remark_ref { "." remark_ref } "\"" .
remark_ref = attribute_ref | constant_ref | entity_ref | enumeration_ref | function_ref | parameter_ref | procedure_ref | rule_label_ref | rule_ref | schema_ref | subtype_constraint_ref | type_label_ref | type_ref | variable_ref .
tail_remark = "--" [ remark_tag ] .
attribute_ref = attribute_id .
constant_ref = constant_id .
entity_ref = entity_id .
enumeration_ref = enumeration_id .
function_ref = function_id .
parameter_ref = parameter_id .
procedure_ref = procedure_id .
rule_label_ref = rule_label_id .
rule_ref = rule_id .
schema_ref = schema_id .
subtype_constraint_ref = subtype_constraint_id .
type_label_ref = type_label_id .
type_ref = type_id .
variable_ref = variable_id .
abstract_entity_declaration = ABSTRACT .
abstract_supertype = ABSTRACT SUPERTYPE ";" .
abstract_supertype_declaration = ABSTRACT SUPERTYPE [ subtype_constraint ] .
actual_parameter_list = "(" [ parameter ] { "," parameter } ")" .
add_like_op = "+" | "-" | OR | XOR .
aggregate_initializer = "[" [ element { "," element } ] "]" .
aggregate_source = simple_expression .
aggregate_type = AGGREGATE [ ":" type_label ] OF parameter_type .
aggregation_types = array_type | bag_type | list_type | set_type .
algorithm_head = { declaration } [ constant_decl ] [ local_decl ] .
alias_stmt = ALIAS variable_id FOR general_ref { qualifier } ";" stmt { stmt } END_ALIAS ";" .
array_type = ARRAY bound_spec OF [ OPTIONAL ] [ UNIQUE ] instantiable_type .
assignment_stmt = general_ref { qualifier } ":=" expression ";" .
attribute_decl = redeclared_attribute | attribute_id .
attribute_id = simple_id .
attribute_qualifier = "." attribute_ref .
bag_type = BAG [ bound_spec ] OF instantiable_type .
binary_type = BINARY [ width_spec ] .
boolean_type = BOOLEAN .
bound_1 = numeric_expression .
bound_2 = numeric_expression .
bound_spec = "[" bound_1 ":" bound_2 "]" .
built_in_constant = CONST_E | PI | SELF | "?" .
built_in_function = ABS | ACOS | ASIN | ATAN | BLENGTH | COS | EXISTS | EXP | FORMAT | HIBOUND | HIINDEX | LENGTH | LOBOUND | LOINDEX | LOG | LOG2 | LOG10 | NVL | ODD | ROLESOF | SIN | SIZEOF | SQRT | TAN | TYPEOF | USEDIN | VALUE | VALUE_IN | VALUE_UNIQUE .
built_in_procedure = INSERT | REMOVE .
case_action = case_label { "," case_label } ":" stmt .
case_label = expression .
case_stmt = CASE selector OF { case_action } [ OTHERWISE ":" stmt ] END_CASE ";" .
compound_stmt = BEGIN stmt { stmt } END ";" .
concrete_types = aggregation_types | simple_types | type_ref .
constant_body = constant_id ":" instantiable_type ":=" expression ";" .
constant_decl = CONSTANT constant_body { constant_body } END_CONSTANT ";" .
constant_factor = built_in_constant | constant_ref .
constant_id = simple_id .
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 .
element = expression [ ":" repetition ] .
entity_body = { explicit_attr } [ derive_clause ] [ inverse_clause ] [ unique_clause ] [ where_clause ] .
entity_constructor = entity_ref "(" [ expression { "," expression } ] ")" .
entity_decl = entity_head entity_body END_ENTITY ";" .
entity_head = ENTITY entity_id subsuper ";" .
entity_id = simple_id .
enumeration_extension = BASED_ON type_ref [ WITH enumeration_items ] .
enumeration_id = simple_id .
enumeration_items = "(" enumeration_id { "," enumeration_id } ")" .
enumeration_reference = [ type_ref "." ] enumeration_ref .
enumeration_type = [ EXTENSIBLE ] ENUMERATION [ ( OF enumeration_items ) | enumeration_extension ] .
escape_stmt = ESCAPE ";" .
explicit_attr = attribute_decl { "," attribute_decl } ":" [ OPTIONAL ] parameter_type ";" .
expression = simple_expression [ rel_op_extended simple_expression ] .
factor = simple_factor [ "**" simple_factor ] .
formal_parameter = parameter_id { "," parameter_id } ":" parameter_type .
function_call = ( built_in_function | function_ref ) actual_parameter_list .
function_decl = function_head algorithm_head stmt { stmt } END_FUNCTION ";" .
function_head = FUNCTION function_id [ "(" formal_parameter { ";" formal_parameter } ")" ] ":" parameter_type ";" .
function_id = simple_id .
generalized_types = aggregate_type | general_aggregation_types | generic_entity_type | generic_type .
general_aggregation_types = general_array_type | general_bag_type | general_list_type | general_set_type .
general_array_type = ARRAY [ bound_spec ] OF [ OPTIONAL ] [ UNIQUE ] parameter_type .
general_bag_type = BAG [ bound_spec ] OF parameter_type .
general_list_type = LIST [ bound_spec ] OF [ UNIQUE ] parameter_type .
general_ref = parameter_ref | variable_ref .
general_set_type = SET [ bound_spec ] OF parameter_type .
generic_entity_type = GENERIC_ENTITY [ ":" type_label ] .
generic_type = GENERIC [ ":" type_label ] .
group_qualifier = "\\" entity_ref .
if_stmt = IF logical_expression THEN stmt { stmt } [ ELSE stmt { stmt } ] END_IF ";" .
increment = numeric_expression .
increment_control = variable_id ":=" bound_1 TO bound_2 [ BY increment ] .
index = numeric_expression .
index_1 = index .
index_2 = index .
index_qualifier = "[" index_1 [ ":" index_2 ] "]" .
instantiable_type = concrete_types | entity_ref .
integer_type = INTEGER .
interface_specification = reference_clause | use_clause .
interval = "{" interval_low interval_op interval_item interval_op interval_high "}" .
interval_high = simple_expression .
interval_item = simple_expression .
interval_low = simple_expression .
interval_op = "<=" | "<" .
inverse_attr = attribute_decl ":" [ ( SET | BAG ) [ bound_spec ] OF ] entity_ref FOR [ entity_ref "." ] attribute_ref ";" .
inverse_clause = INVERSE inverse_attr { inverse_attr } .
list_type = LIST [ bound_spec ] OF [ UNIQUE ] instantiable_type .
literal = binary_literal | logical_literal | real_literal | string_literal .
local_decl = LOCAL local_variable { local_variable } END_LOCAL ";" .
local_variable = variable_id { "," variable_id } ":" parameter_type [ ":=" expression ] ";" .
logical_expression = expression .
logical_literal = FALSE | TRUE | UNKNOWN .
logical_type = LOGICAL .
multiplication_like_op = "*" | "/" | DIV | MOD | AND | "||" .
named_types = entity_ref | type_ref .
named_type_or_rename = named_types [ AS ( entity_id | type_id ) ] .
null_stmt = ";" .
number_type = NUMBER .
numeric_expression = simple_expression .
one_of = ONEOF "(" supertype_expression { "," supertype_expression } ")" .
parameter = expression .
parameter_id = simple_id .
parameter_type = generalized_types | simple_types | named_types .
population = entity_ref .
precision_spec = numeric_expression .
primary = literal | ( qualifiable_factor { qualifier } ) .
procedure_call_stmt = ( built_in_procedure | procedure_ref ) actual_parameter_list ";" .
procedure_decl = procedure_head algorithm_head { stmt } END_PROCEDURE ";" .
procedure_head = PROCEDURE procedure_id [ "(" [ VAR ] formal_parameter { ";" [ VAR ] formal_parameter } ")" ] ";" .
procedure_id = simple_id .
qualifiable_factor = function_call | attribute_ref | constant_factor | general_ref | population .
qualified_attribute = SELF group_qualifier attribute_qualifier .
qualifier = attribute_qualifier | group_qualifier | index_qualifier .
query_expression = QUERY "(" variable_id "<*" aggregate_source "|" logical_expression ")" .
real_type = REAL [ "(" precision_spec ")" ] .
redeclared_attribute = qualified_attribute [ RENAMED attribute_id ] .
referenced_attribute = attribute_ref | qualified_attribute .
reference_clause = REFERENCE FROM schema_ref [ "(" resource_or_rename { "," resource_or_rename } ")" ] ";" .
rel_op = "<=" | ">=" | "<>" | "=" | ":<>:" | ":=:" | "<" | ">" .
rel_op_extended = rel_op | IN | LIKE .
rename_id = constant_id | entity_id | function_id | procedure_id | type_id .
repeat_control = [ increment_control ] [ while_control ] [ until_control ] .
repeat_stmt = REPEAT repeat_control ";" stmt { stmt } END_REPEAT ";" .
repetition = numeric_expression .
resource_or_rename = resource_ref [ AS rename_id ] .
resource_ref = constant_ref | entity_ref | function_ref | procedure_ref | type_ref .
return_stmt = RETURN [ "(" expression ")" ] ";" .
rule_decl = rule_head algorithm_head { stmt } where_clause END_RULE ";" .
rule_head = RULE rule_id FOR "(" entity_ref { "," entity_ref } ")" ";" .
rule_id = simple_id .
rule_label_id = simple_id .
schema_body = { interface_specification } [ constant_decl ] { declaration | rule_decl } .
schema_decl = SCHEMA schema_id [ schema_version_id ] ";" schema_body END_SCHEMA ";" .
schema_id = simple_id .
schema_version_id = string_literal .
selector = expression .
select_extension = BASED_ON type_ref [ WITH select_list ] .
select_list = "(" named_types { "," named_types } ")" .
select_type = [ EXTENSIBLE [ GENERIC_ENTITY ] ] SELECT [ select_list | select_extension ] .
set_type = SET [ bound_spec ] OF instantiable_type .
sign = "+" | "-" .
simple_expression = term { add_like_op term } .
simple_factor = aggregate_initializer | interval | query_expression | ( [ unary_op ] ( "(" expression ")" | primary ) ) | entity_constructor | enumeration_reference .
simple_types = binary_type | boolean_type | integer_type | logical_type | number_type | real_type | string_type .
skip_stmt = SKIP ";" .
stmt = alias_stmt | assignment_stmt | case_stmt | compound_stmt | escape_stmt | if_stmt | null_stmt | procedure_call_stmt | repeat_stmt | return_stmt | skip_stmt .
string_literal = simple_string_literal | encoded_string_literal .
string_type = STRING [ width_spec ] .
subsuper = [ supertype_constraint ] [ subtype_declaration ] .
subtype_constraint = OF "(" supertype_expression ")" .
subtype_constraint_body = [ abstract_supertype ] [ total_over ] [ supertype_expression ";" ] .
subtype_constraint_decl = subtype_constraint_head subtype_constraint_body END_SUBTYPE_CONSTRAINT ";" .
subtype_constraint_head = SUBTYPE_CONSTRAINT subtype_constraint_id FOR entity_ref ";" .
subtype_constraint_id = simple_id .
subtype_declaration = SUBTYPE OF "(" entity_ref { "," entity_ref } ")" .
supertype_constraint = abstract_supertype_declaration | abstract_entity_declaration | supertype_rule .
supertype_expression = supertype_factor { ANDOR supertype_factor } .
supertype_factor = supertype_term { AND supertype_term } .
supertype_rule = SUPERTYPE subtype_constraint .
supertype_term = one_of | "(" supertype_expression ")" | entity_ref .
syntax = schema_decl { schema_decl } .
term = factor { multiplication_like_op factor } .
total_over = TOTAL_OVER "(" entity_ref { "," entity_ref } ")" ";" .
type_decl = TYPE type_id "=" underlying_type ";" [ where_clause ] END_TYPE ";" .
type_id = simple_id .
type_label = type_label_id | type_label_ref .
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 } .
until_control = UNTIL logical_expression .
use_clause = USE FROM schema_ref [ "(" named_type_or_rename { "," named_type_or_rename } ")" ] ";" .
variable_id = simple_id .
where_clause = WHERE domain_rule ";" { domain_rule ";" } .
while_control = WHILE logical_expression .
width = numeric_expression .
width_spec = "(" width ")" [ FIXED ] .
@@ -0,0 +1,490 @@
# This file is generated by IfcOpenShell ifcexpressparser bootstrap.py
from __future__ import annotations
import os
import sys
import pickle
import schema
import mapping
from pyparsing import *
from nodes import *
def parse(fn: str) -> mapping.Mapping:
cache_file = fn + ".cache.dat"
if os.path.exists(cache_file) and os.path.getmtime(cache_file) >= os.path.getmtime(fn):
with open(cache_file, "rb") as f:
m = pickle.load(f)
else:
ABS = (CaselessKeyword("abs")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ABS"))("ABS")
ABSTRACT = (CaselessKeyword("abstract")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ABSTRACT"))("ABSTRACT")
ACOS = (CaselessKeyword("acos")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ACOS"))("ACOS")
AGGREGATE = (CaselessKeyword("aggregate")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="AGGREGATE"))("AGGREGATE")
ALIAS = (CaselessKeyword("alias")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ALIAS"))("ALIAS")
AND = (CaselessKeyword("and")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="AND"))("AND")
ANDOR = (CaselessKeyword("andor")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ANDOR"))("ANDOR")
ARRAY = (CaselessKeyword("array")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ARRAY"))("ARRAY")
AS = (CaselessKeyword("as")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="AS"))("AS")
ASIN = (CaselessKeyword("asin")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ASIN"))("ASIN")
ATAN = (CaselessKeyword("atan")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ATAN"))("ATAN")
BAG = (CaselessKeyword("bag")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="BAG"))("BAG")
BASED_ON = (CaselessKeyword("based_on")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="BASED_ON"))("BASED_ON")
BEGIN = (CaselessKeyword("begin")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="BEGIN"))("BEGIN")
BINARY = (CaselessKeyword("binary")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="BINARY"))("BINARY")
BLENGTH = (CaselessKeyword("blength")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="BLENGTH"))("BLENGTH")
BOOLEAN = (CaselessKeyword("boolean")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="BOOLEAN"))("BOOLEAN")
BY = (CaselessKeyword("by")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="BY"))("BY")
CASE = (CaselessKeyword("case")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="CASE"))("CASE")
CONSTANT = (CaselessKeyword("constant")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="CONSTANT"))("CONSTANT")
CONST_E = (CaselessKeyword("const_e")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="CONST_E"))("CONST_E")
COS = (CaselessKeyword("cos")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="COS"))("COS")
DERIVE = (CaselessKeyword("derive")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="DERIVE"))("DERIVE")
DIV = (CaselessKeyword("div")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="DIV"))("DIV")
ELSE = (CaselessKeyword("else")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ELSE"))("ELSE")
END = (CaselessKeyword("end")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END"))("END")
END_ALIAS = (CaselessKeyword("end_alias")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_ALIAS"))("END_ALIAS")
END_CASE = (CaselessKeyword("end_case")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_CASE"))("END_CASE")
END_CONSTANT = (CaselessKeyword("end_constant")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_CONSTANT"))("END_CONSTANT")
END_ENTITY = (CaselessKeyword("end_entity")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_ENTITY"))("END_ENTITY")
END_FUNCTION = (CaselessKeyword("end_function")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_FUNCTION"))("END_FUNCTION")
END_IF = (CaselessKeyword("end_if")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_IF"))("END_IF")
END_LOCAL = (CaselessKeyword("end_local")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_LOCAL"))("END_LOCAL")
END_PROCEDURE = (CaselessKeyword("end_procedure")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_PROCEDURE"))("END_PROCEDURE")
END_REPEAT = (CaselessKeyword("end_repeat")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_REPEAT"))("END_REPEAT")
END_RULE = (CaselessKeyword("end_rule")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_RULE"))("END_RULE")
END_SCHEMA = (CaselessKeyword("end_schema")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_SCHEMA"))("END_SCHEMA")
END_SUBTYPE_CONSTRAINT = (CaselessKeyword("end_subtype_constraint")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_SUBTYPE_CONSTRAINT"))("END_SUBTYPE_CONSTRAINT")
END_TYPE = (CaselessKeyword("end_type")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="END_TYPE"))("END_TYPE")
ENTITY = (CaselessKeyword("entity")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ENTITY"))("ENTITY")
ENUMERATION = (CaselessKeyword("enumeration")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ENUMERATION"))("ENUMERATION")
ESCAPE = (CaselessKeyword("escape")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ESCAPE"))("ESCAPE")
EXISTS = (CaselessKeyword("exists")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="EXISTS"))("EXISTS")
EXTENSIBLE = (CaselessKeyword("extensible")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="EXTENSIBLE"))("EXTENSIBLE")
EXP = (CaselessKeyword("exp")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="EXP"))("EXP")
FALSE = (CaselessKeyword("false")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="FALSE"))("FALSE")
FIXED = (CaselessKeyword("fixed")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="FIXED"))("FIXED")
FOR = (CaselessKeyword("for")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="FOR"))("FOR")
FORMAT = (CaselessKeyword("format")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="FORMAT"))("FORMAT")
FROM = (CaselessKeyword("from")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="FROM"))("FROM")
FUNCTION = (CaselessKeyword("function")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="FUNCTION"))("FUNCTION")
GENERIC = (CaselessKeyword("generic")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="GENERIC"))("GENERIC")
GENERIC_ENTITY = (CaselessKeyword("generic_entity")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="GENERIC_ENTITY"))("GENERIC_ENTITY")
HIBOUND = (CaselessKeyword("hibound")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="HIBOUND"))("HIBOUND")
HIINDEX = (CaselessKeyword("hiindex")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="HIINDEX"))("HIINDEX")
IF = (CaselessKeyword("if")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="IF"))("IF")
IN = (CaselessKeyword("in")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="IN"))("IN")
INSERT = (CaselessKeyword("insert")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="INSERT"))("INSERT")
INTEGER = (CaselessKeyword("integer")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="INTEGER"))("INTEGER")
INVERSE = (CaselessKeyword("inverse")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="INVERSE"))("INVERSE")
LENGTH = (CaselessKeyword("length")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LENGTH"))("LENGTH")
LIKE = (CaselessKeyword("like")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LIKE"))("LIKE")
LIST = (CaselessKeyword("list")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LIST"))("LIST")
LOBOUND = (CaselessKeyword("lobound")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LOBOUND"))("LOBOUND")
LOCAL = (CaselessKeyword("local")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LOCAL"))("LOCAL")
LOG = (CaselessKeyword("log")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LOG"))("LOG")
LOG10 = (CaselessKeyword("log10")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LOG10"))("LOG10")
LOG2 = (CaselessKeyword("log2")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LOG2"))("LOG2")
LOGICAL = (CaselessKeyword("logical")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LOGICAL"))("LOGICAL")
LOINDEX = (CaselessKeyword("loindex")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="LOINDEX"))("LOINDEX")
MOD = (CaselessKeyword("mod")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="MOD"))("MOD")
NOT = (CaselessKeyword("not")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="NOT"))("NOT")
NUMBER = (CaselessKeyword("number")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="NUMBER"))("NUMBER")
NVL = (CaselessKeyword("nvl")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="NVL"))("NVL")
ODD = (CaselessKeyword("odd")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ODD"))("ODD")
OF = (CaselessKeyword("of")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="OF"))("OF")
ONEOF = (CaselessKeyword("oneof")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ONEOF"))("ONEOF")
OPTIONAL = (CaselessKeyword("optional")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="OPTIONAL"))("OPTIONAL")
OR = (CaselessKeyword("or")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="OR"))("OR")
OTHERWISE = (CaselessKeyword("otherwise")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="OTHERWISE"))("OTHERWISE")
PI = (CaselessKeyword("pi")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="PI"))("PI")
PROCEDURE = (CaselessKeyword("procedure")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="PROCEDURE"))("PROCEDURE")
QUERY = (CaselessKeyword("query")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="QUERY"))("QUERY")
REAL = (CaselessKeyword("real")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="REAL"))("REAL")
REFERENCE = (CaselessKeyword("reference")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="REFERENCE"))("REFERENCE")
REMOVE = (CaselessKeyword("remove")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="REMOVE"))("REMOVE")
RENAMED = (CaselessKeyword("renamed")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="RENAMED"))("RENAMED")
REPEAT = (CaselessKeyword("repeat")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="REPEAT"))("REPEAT")
RETURN = (CaselessKeyword("return")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="RETURN"))("RETURN")
ROLESOF = (CaselessKeyword("rolesof")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="ROLESOF"))("ROLESOF")
RULE = (CaselessKeyword("rule")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="RULE"))("RULE")
SCHEMA = (CaselessKeyword("schema")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SCHEMA"))("SCHEMA")
SELECT = (CaselessKeyword("select")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SELECT"))("SELECT")
SELF = (CaselessKeyword("self")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SELF"))("SELF")
SET = (CaselessKeyword("set")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SET"))("SET")
SIN = (CaselessKeyword("sin")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SIN"))("SIN")
SIZEOF = (CaselessKeyword("sizeof")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SIZEOF"))("SIZEOF")
SKIP = (CaselessKeyword("skip")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SKIP"))("SKIP")
SQRT = (CaselessKeyword("sqrt")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SQRT"))("SQRT")
STRING = (CaselessKeyword("string")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="STRING"))("STRING")
SUBTYPE = (CaselessKeyword("subtype")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SUBTYPE"))("SUBTYPE")
SUBTYPE_CONSTRAINT = (CaselessKeyword("subtype_constraint")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SUBTYPE_CONSTRAINT"))("SUBTYPE_CONSTRAINT")
SUPERTYPE = (CaselessKeyword("supertype")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="SUPERTYPE"))("SUPERTYPE")
TAN = (CaselessKeyword("tan")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="TAN"))("TAN")
THEN = (CaselessKeyword("then")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="THEN"))("THEN")
TO = (CaselessKeyword("to")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="TO"))("TO")
TOTAL_OVER = (CaselessKeyword("total_over")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="TOTAL_OVER"))("TOTAL_OVER")
TRUE = (CaselessKeyword("true")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="TRUE"))("TRUE")
TYPE = (CaselessKeyword("type")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="TYPE"))("TYPE")
TYPEOF = (CaselessKeyword("typeof")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="TYPEOF"))("TYPEOF")
UNIQUE = (CaselessKeyword("unique")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="UNIQUE"))("UNIQUE")
UNKNOWN = (CaselessKeyword("unknown")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="UNKNOWN"))("UNKNOWN")
UNTIL = (CaselessKeyword("until")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="UNTIL"))("UNTIL")
USE = (CaselessKeyword("use")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="USE"))("USE")
USEDIN = (CaselessKeyword("usedin")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="USEDIN"))("USEDIN")
VALUE = (CaselessKeyword("value")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="VALUE"))("VALUE")
VALUE_IN = (CaselessKeyword("value_in")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="VALUE_IN"))("VALUE_IN")
VALUE_UNIQUE = (CaselessKeyword("value_unique")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="VALUE_UNIQUE"))("VALUE_UNIQUE")
VAR = (CaselessKeyword("var")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="VAR"))("VAR")
WHERE = (CaselessKeyword("where")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="WHERE"))("WHERE")
WHILE = (CaselessKeyword("while")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="WHILE"))("WHILE")
WITH = (CaselessKeyword("with")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="WITH"))("WITH")
XOR = (CaselessKeyword("xor")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="XOR"))("XOR")
bit = ((CaselessLiteral("0") | CaselessLiteral("1"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="bit"))("bit")
digit = ((CaselessLiteral("0") | CaselessLiteral("1") | CaselessLiteral("2") | CaselessLiteral("3") | CaselessLiteral("4") | CaselessLiteral("5") | CaselessLiteral("6") | CaselessLiteral("7") | CaselessLiteral("8") | CaselessLiteral("9")))("digit")
digits = ((digit + ZeroOrMore(digit)))("digits")
hex_digit = ((digit | CaselessLiteral("a") | CaselessLiteral("b") | CaselessLiteral("c") | CaselessLiteral("d") | CaselessLiteral("e") | CaselessLiteral("f"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="hex_digit"))("hex_digit")
letter = ((CaselessLiteral("a") | CaselessLiteral("b") | CaselessLiteral("c") | CaselessLiteral("d") | CaselessLiteral("e") | CaselessLiteral("f") | CaselessLiteral("g") | CaselessLiteral("h") | CaselessLiteral("i") | CaselessLiteral("j") | CaselessLiteral("k") | CaselessLiteral("l") | CaselessLiteral("m") | CaselessLiteral("n") | CaselessLiteral("o") | CaselessLiteral("p") | CaselessLiteral("q") | CaselessLiteral("r") | CaselessLiteral("s") | CaselessLiteral("t") | CaselessLiteral("u") | CaselessLiteral("v") | CaselessLiteral("w") | CaselessLiteral("x") | CaselessLiteral("y") | CaselessLiteral("z")))("letter")
not_paren_star_quote_special = ((CaselessLiteral("!") | CaselessLiteral("#") | CaselessLiteral("$") | CaselessLiteral("%") | CaselessLiteral("&") | CaselessLiteral("+") | CaselessLiteral(",") | CaselessLiteral("-") | CaselessLiteral(".") | CaselessLiteral("/") | CaselessLiteral(":") | CaselessLiteral(";") | CaselessLiteral("<") | CaselessLiteral("=") | CaselessLiteral(">") | CaselessLiteral("?") | CaselessLiteral("@") | CaselessLiteral("[") | CaselessLiteral("\\") | CaselessLiteral("]") | CaselessLiteral("^") | CaselessLiteral("_") | CaselessLiteral("{") | CaselessLiteral("|") | CaselessLiteral("}") | CaselessLiteral("~")))("not_paren_star_quote_special")
not_paren_star_special = ((not_paren_star_quote_special | CaselessLiteral("\"\""))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="not_paren_star_special"))("not_paren_star_special")
not_quote = ((not_paren_star_quote_special | letter | digit | CaselessLiteral("(") | CaselessLiteral(")") | CaselessLiteral("*")))("not_quote")
octet = ((hex_digit + hex_digit)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="octet"))("octet")
special = ((not_paren_star_quote_special | CaselessLiteral("(") | CaselessLiteral(")") | CaselessLiteral("*") | CaselessLiteral("\"\""))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="special"))("special")
binary_literal = ((CaselessLiteral("%") + bit + ZeroOrMore(bit))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="binary_literal"))("binary_literal")
integer_literal = (digits)("integer_literal")
simple_id = ~CaselessKeyword("generic") + ~CaselessKeyword("value") + ~CaselessKeyword("case") + ~CaselessKeyword("for") + ~CaselessKeyword("sin") + ~CaselessKeyword("value_unique") + ~CaselessKeyword("extensible") + ~CaselessKeyword("tan") + ~CaselessKeyword("local") + ~CaselessKeyword("string") + ~CaselessKeyword("procedure") + ~CaselessKeyword("derive") + ~CaselessKeyword("end_if") + ~CaselessKeyword("supertype") + ~CaselessKeyword("entity") + ~CaselessKeyword("oneof") + ~CaselessKeyword("constant") + ~CaselessKeyword("end_case") + ~CaselessKeyword("end_alias") + ~CaselessKeyword("unknown") + ~CaselessKeyword("total_over") + ~CaselessKeyword("div") + ~CaselessKeyword("type") + ~CaselessKeyword("true") + ~CaselessKeyword("end_repeat") + ~CaselessKeyword("unique") + ~CaselessKeyword("end_rule") + ~CaselessKeyword("number") + ~CaselessKeyword("end_function") + ~CaselessKeyword("where") + ~CaselessKeyword("self") + ~CaselessKeyword("usedin") + ~CaselessKeyword("end_type") + ~CaselessKeyword("logical") + ~CaselessKeyword("generic_entity") + ~CaselessKeyword("end_schema") + ~CaselessKeyword("xor") + ~CaselessKeyword("until") + ~CaselessKeyword("to") + ~CaselessKeyword("in") + ~CaselessKeyword("inverse") + ~CaselessKeyword("enumeration") + ~CaselessKeyword("var") + ~CaselessKeyword("value_in") + ~CaselessKeyword("const_e") + ~CaselessKeyword("use") + ~CaselessKeyword("exists") + ~CaselessKeyword("exp") + ~CaselessKeyword("while") + ~CaselessKeyword("if") + ~CaselessKeyword("fixed") + ~CaselessKeyword("subtype") + ~CaselessKeyword("format") + ~CaselessKeyword("as") + ~CaselessKeyword("and") + ~CaselessKeyword("rule") + ~CaselessKeyword("function") + ~CaselessKeyword("lobound") + ~CaselessKeyword("length") + ~CaselessKeyword("hiindex") + ~CaselessKeyword("log2") + ~CaselessKeyword("reference") + ~CaselessKeyword("skip") + ~CaselessKeyword("with") + ~CaselessKeyword("integer") + ~CaselessKeyword("sqrt") + ~CaselessKeyword("insert") + ~CaselessKeyword("nvl") + ~CaselessKeyword("log") + ~CaselessKeyword("boolean") + ~CaselessKeyword("from") + ~CaselessKeyword("rolesof") + ~CaselessKeyword("hibound") + ~CaselessKeyword("abs") + ~CaselessKeyword("like") + ~CaselessKeyword("pi") + ~CaselessKeyword("alias") + ~CaselessKeyword("not") + ~CaselessKeyword("repeat") + ~CaselessKeyword("based_on") + ~CaselessKeyword("subtype_constraint") + ~CaselessKeyword("asin") + ~CaselessKeyword("optional") + ~CaselessKeyword("list") + ~CaselessKeyword("abstract") + ~CaselessKeyword("mod") + ~CaselessKeyword("false") + ~CaselessKeyword("log10") + ~CaselessKeyword("loindex") + ~CaselessKeyword("aggregate") + ~CaselessKeyword("end_constant") + ~CaselessKeyword("end") + ~CaselessKeyword("sizeof") + ~CaselessKeyword("remove") + ~CaselessKeyword("acos") + ~CaselessKeyword("set") + ~CaselessKeyword("renamed") + ~CaselessKeyword("end_local") + ~CaselessKeyword("of") + ~CaselessKeyword("escape") + ~CaselessKeyword("begin") + ~CaselessKeyword("select") + ~CaselessKeyword("end_procedure") + ~CaselessKeyword("else") + ~CaselessKeyword("end_subtype_constraint") + ~CaselessKeyword("cos") + ~CaselessKeyword("real") + ~CaselessKeyword("query") + ~CaselessKeyword("odd") + ~CaselessKeyword("andor") + ~CaselessKeyword("return") + ~CaselessKeyword("then") + ~CaselessKeyword("end_entity") + ~CaselessKeyword("array") + ~CaselessKeyword("blength") + ~CaselessKeyword("or") + ~CaselessKeyword("typeof") + ~CaselessKeyword("binary") + ~CaselessKeyword("atan") + ~CaselessKeyword("by") + ~CaselessKeyword("otherwise") + ~CaselessKeyword("bag") + ~CaselessKeyword("schema") + originalTextFor(Combine((letter + ZeroOrMore((letter | digit | CaselessLiteral("_"))))))("simple_id")
simple_string_literal = ((CaselessLiteral("'") + ZeroOrMore(((CaselessLiteral("'") + CaselessLiteral("'")) | not_quote)) + CaselessLiteral("'")))("simple_string_literal")
abstract_entity_declaration = (ABSTRACT)("abstract_entity_declaration")
abstract_supertype = ((ABSTRACT + SUPERTYPE + CaselessLiteral(";"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="abstract_supertype"))("abstract_supertype")
add_like_op = ((CaselessLiteral("+") | CaselessLiteral("-") | OR | XOR)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="add_like_op"))("add_like_op")
attribute_id = (simple_id)("attribute_id")
boolean_type = (BOOLEAN)("boolean_type")
built_in_constant = ((CONST_E | PI | SELF | CaselessLiteral("?"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="built_in_constant"))("built_in_constant")
built_in_function = ((ABS | ACOS | ASIN | ATAN | BLENGTH | COS | EXISTS | EXP | FORMAT | HIBOUND | HIINDEX | LENGTH | LOBOUND | LOINDEX | LOG | LOG2 | LOG10 | NVL | ODD | ROLESOF | SIN | SIZEOF | SQRT | TAN | TYPEOF | USEDIN | VALUE | VALUE_IN | VALUE_UNIQUE)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="built_in_function"))("built_in_function")
built_in_procedure = ((INSERT | REMOVE)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="built_in_procedure"))("built_in_procedure")
constant_id = (simple_id)("constant_id")
entity_id = (simple_id)("entity_id")
enumeration_id = (simple_id)("enumeration_id")
enumeration_items = ((CaselessLiteral("(") + enumeration_id + ZeroOrMore((CaselessLiteral(",") + enumeration_id)) + CaselessLiteral(")"))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="enumeration_items"))("enumeration_items")
escape_stmt = ((ESCAPE + CaselessLiteral(";"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="escape_stmt"))("escape_stmt")
function_id = (simple_id)("function_id")
integer_type = (INTEGER)("integer_type")
interval_op = ((CaselessLiteral("<=") | CaselessLiteral("<"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="interval_op"))("interval_op")
logical_literal = ((FALSE | TRUE | UNKNOWN)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="logical_literal"))("logical_literal")
logical_type = (LOGICAL)("logical_type")
multiplication_like_op = ((CaselessLiteral("*") | CaselessLiteral("/") | DIV | MOD | AND | CaselessLiteral("||"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="multiplication_like_op"))("multiplication_like_op")
null_stmt = (CaselessLiteral(";")).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="null_stmt"))("null_stmt")
number_type = (NUMBER)("number_type")
parameter_id = (simple_id)("parameter_id")
procedure_id = (simple_id)("procedure_id")
rel_op = ((CaselessLiteral("<=") | CaselessLiteral(">=") | CaselessLiteral("<>") | CaselessLiteral("=") | CaselessLiteral(":<>:") | CaselessLiteral(":=:") | CaselessLiteral("<") | CaselessLiteral(">"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="rel_op"))("rel_op")
rel_op_extended = ((rel_op | IN | LIKE)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="rel_op_extended"))("rel_op_extended")
rule_id = (simple_id)("rule_id")
rule_label_id = (simple_id)("rule_label_id")
schema_id = (simple_id)("schema_id")
sign = ((CaselessLiteral("+") | CaselessLiteral("-"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="sign"))("sign")
skip_stmt = ((SKIP + CaselessLiteral(";"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="skip_stmt"))("skip_stmt")
subtype_constraint_id = (simple_id)("subtype_constraint_id")
type_id = (simple_id)("type_id")
type_label_id = (simple_id)("type_label_id")
unary_op = ((CaselessLiteral("+") | CaselessLiteral("-") | NOT)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="unary_op"))("unary_op")
variable_id = (simple_id)("variable_id")
encoded_character = ((octet + octet + octet + octet)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="encoded_character"))("encoded_character")
not_paren_star = ((letter | digit | not_paren_star_special)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="not_paren_star"))("not_paren_star")
not_rparen_star = ((not_paren_star | CaselessLiteral("("))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="not_rparen_star"))("not_rparen_star")
not_rparen_star_then_rparen = ((not_rparen_star + ZeroOrMore(not_rparen_star) + CaselessLiteral(")") + ZeroOrMore(CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="not_rparen_star_then_rparen"))("not_rparen_star_then_rparen")
encoded_string_literal = ((CaselessLiteral("\"") + encoded_character + ZeroOrMore(encoded_character) + CaselessLiteral("\""))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="encoded_string_literal"))("encoded_string_literal")
real_literal = (((digits + CaselessLiteral(".") + Optional(digits) + Optional((CaselessLiteral("e") + Optional(sign) + digits))) | integer_literal))("real_literal")
attribute_ref = (attribute_id)("attribute_ref")
constant_ref = (constant_id)("constant_ref")
entity_ref = (entity_id)("entity_ref")
enumeration_ref = (enumeration_id)("enumeration_ref")
function_ref = (function_id)("function_ref")
parameter_ref = (parameter_id)("parameter_ref")
procedure_ref = (procedure_id)("procedure_ref")
rule_label_ref = (rule_label_id)("rule_label_ref")
rule_ref = (rule_id)("rule_ref")
schema_ref = (schema_id)("schema_ref")
subtype_constraint_ref = (subtype_constraint_id)("subtype_constraint_ref")
type_label_ref = (type_label_id)("type_label_ref")
type_ref = (type_id)("type_ref")
variable_ref = (variable_id)("variable_ref")
attribute_qualifier = ((CaselessLiteral(".") + attribute_ref)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="attribute_qualifier"))("attribute_qualifier")
constant_factor = ((built_in_constant | constant_ref)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="constant_factor"))("constant_factor")
enumeration_extension = ((BASED_ON + type_ref + Optional((WITH + enumeration_items)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="enumeration_extension"))("enumeration_extension")
enumeration_reference = ((Optional((type_ref + CaselessLiteral("."))) + enumeration_ref)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="enumeration_reference"))("enumeration_reference")
enumeration_type = ((Optional(EXTENSIBLE) + ENUMERATION + Optional(((OF + enumeration_items) | enumeration_extension)))).setParseAction(EnumerationType)("enumeration_type")
general_ref = ((parameter_ref | variable_ref)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_ref"))("general_ref")
group_qualifier = ((CaselessLiteral("\\") + entity_ref)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="group_qualifier"))("group_qualifier")
named_types = ((entity_ref | type_ref)).setParseAction(NamedType)("named_types")
named_type_or_rename = ((named_types + Optional((AS + (entity_id | type_id))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="named_type_or_rename"))("named_type_or_rename")
population = (entity_ref)("population")
qualified_attribute = ((SELF + group_qualifier + attribute_qualifier)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="qualified_attribute"))("qualified_attribute")
redeclared_attribute = ((qualified_attribute + Optional((RENAMED + attribute_id)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="redeclared_attribute"))("redeclared_attribute")
referenced_attribute = ((attribute_ref | qualified_attribute)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="referenced_attribute"))("referenced_attribute")
rename_id = ((constant_id | entity_id | function_id | procedure_id | type_id)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="rename_id"))("rename_id")
resource_ref = ((constant_ref | entity_ref | function_ref | procedure_ref | type_ref)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="resource_ref"))("resource_ref")
rule_head = ((RULE + rule_id + FOR + CaselessLiteral("(") + entity_ref + ZeroOrMore((CaselessLiteral(",") + entity_ref)) + CaselessLiteral(")") + CaselessLiteral(";"))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="rule_head"))("rule_head")
select_list = ((CaselessLiteral("(") + named_types + ZeroOrMore((CaselessLiteral(",") + named_types)) + CaselessLiteral(")"))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="select_list"))("select_list")
string_literal = ((simple_string_literal | encoded_string_literal))("string_literal")
subtype_constraint_head = ((SUBTYPE_CONSTRAINT + subtype_constraint_id + FOR + entity_ref + CaselessLiteral(";"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subtype_constraint_head"))("subtype_constraint_head")
subtype_declaration = ((SUBTYPE + OF + CaselessLiteral("(") + entity_ref + ZeroOrMore((CaselessLiteral(",") + entity_ref)) + CaselessLiteral(")"))).setParseAction(SubTypeExpression)("subtype_declaration")
total_over = ((TOTAL_OVER + CaselessLiteral("(") + entity_ref + ZeroOrMore((CaselessLiteral(",") + entity_ref)) + CaselessLiteral(")") + CaselessLiteral(";"))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="total_over"))("total_over")
type_label = ((type_label_id | type_label_ref)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="type_label"))("type_label")
unique_rule = ((Optional((rule_label_id + CaselessLiteral(":"))) + referenced_attribute + ZeroOrMore((CaselessLiteral(",") + referenced_attribute)))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="unique_rule"))("unique_rule")
use_clause = ((USE + FROM + schema_ref + Optional((CaselessLiteral("(") + named_type_or_rename + ZeroOrMore((CaselessLiteral(",") + named_type_or_rename)) + CaselessLiteral(")"))) + CaselessLiteral(";"))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="use_clause"))("use_clause")
not_lparen_star = ((not_paren_star | CaselessLiteral(")"))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="not_lparen_star"))("not_lparen_star")
remark_ref = ((attribute_ref | constant_ref | entity_ref | enumeration_ref | function_ref | parameter_ref | procedure_ref | rule_label_ref | rule_ref | schema_ref | subtype_constraint_ref | type_label_ref | type_ref | variable_ref)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="remark_ref"))("remark_ref")
attribute_decl = ((redeclared_attribute | attribute_id)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="attribute_decl"))("attribute_decl")
generic_entity_type = ((GENERIC_ENTITY + Optional((CaselessLiteral(":") + type_label)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="generic_entity_type"))("generic_entity_type")
generic_type = ((GENERIC + Optional((CaselessLiteral(":") + type_label)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="generic_type"))("generic_type")
literal = ((binary_literal | logical_literal | real_literal | string_literal)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="literal"))("literal")
resource_or_rename = ((resource_ref + Optional((AS + rename_id)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="resource_or_rename"))("resource_or_rename")
schema_version_id = (string_literal)("schema_version_id")
select_extension = ((BASED_ON + type_ref + Optional((WITH + select_list)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="select_extension"))("select_extension")
select_type = ((Optional((EXTENSIBLE + Optional(GENERIC_ENTITY))) + SELECT + Optional((select_list | select_extension)))).setParseAction(SelectType)("select_type")
unique_clause = ((UNIQUE + unique_rule + CaselessLiteral(";") + ZeroOrMore((unique_rule + CaselessLiteral(";"))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="unique_clause"))("unique_clause")
lparen_then_not_lparen_star = ((CaselessLiteral("(") + ZeroOrMore(CaselessLiteral("(")) + not_lparen_star + ZeroOrMore(not_lparen_star))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="lparen_then_not_lparen_star"))("lparen_then_not_lparen_star")
remark_tag = ((CaselessLiteral("\"") + remark_ref + ZeroOrMore((CaselessLiteral(".") + remark_ref)) + CaselessLiteral("\""))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="remark_tag"))("remark_tag")
tail_remark = ((CaselessLiteral("--") + Optional(remark_tag))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="tail_remark"))("tail_remark")
constructed_types = ((enumeration_type | select_type)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="constructed_types"))("constructed_types")
reference_clause = ((REFERENCE + FROM + schema_ref + Optional((CaselessLiteral("(") + resource_or_rename + ZeroOrMore((CaselessLiteral(",") + resource_or_rename)) + CaselessLiteral(")"))) + CaselessLiteral(";"))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="reference_clause"))("reference_clause")
interface_specification = ((reference_clause | use_clause)).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="interface_specification"))("interface_specification")
supertype_factor = Forward()("supertype_factor")
interval_item = Forward()("interval_item")
subtype_constraint = Forward()("subtype_constraint")
repeat_stmt = Forward()("repeat_stmt")
subsuper = Forward()("subsuper")
increment = Forward()("increment")
remark = Forward()("remark")
increment_control = Forward()("increment_control")
local_variable = Forward()("local_variable")
until_control = Forward()("until_control")
while_control = Forward()("while_control")
parameter = Forward()("parameter")
width = Forward()("width")
string_type = Forward()("string_type")
array_type = Forward()("array_type")
if_stmt = Forward()("if_stmt")
index = Forward()("index")
repetition = Forward()("repetition")
index_qualifier = Forward()("index_qualifier")
bound_1 = Forward()("bound_1")
procedure_decl = Forward()("procedure_decl")
entity_constructor = Forward()("entity_constructor")
inverse_clause = Forward()("inverse_clause")
function_head = Forward()("function_head")
formal_parameter = Forward()("formal_parameter")
interval_high = Forward()("interval_high")
entity_decl = Forward()("entity_decl")
abstract_supertype_declaration = Forward()("abstract_supertype_declaration")
index_1 = Forward()("index_1")
general_aggregation_types = Forward()("general_aggregation_types")
real_type = Forward()("real_type")
type_decl = Forward()("type_decl")
stmt = Forward()("stmt")
declaration = Forward()("declaration")
explicit_attr = Forward()("explicit_attr")
compound_stmt = Forward()("compound_stmt")
aggregation_types = Forward()("aggregation_types")
simple_factor = Forward()("simple_factor")
where_clause = Forward()("where_clause")
entity_head = Forward()("entity_head")
underlying_type = Forward()("underlying_type")
subtype_constraint_decl = Forward()("subtype_constraint_decl")
logical_expression = Forward()("logical_expression")
case_label = Forward()("case_label")
expression = Forward()("expression")
general_list_type = Forward()("general_list_type")
actual_parameter_list = Forward()("actual_parameter_list")
width_spec = Forward()("width_spec")
selector = Forward()("selector")
syntax = Forward()("syntax")
aggregate_source = Forward()("aggregate_source")
return_stmt = Forward()("return_stmt")
embedded_remark = Forward()("embedded_remark")
parameter_type = Forward()("parameter_type")
term = Forward()("term")
derived_attr = Forward()("derived_attr")
repeat_control = Forward()("repeat_control")
assignment_stmt = Forward()("assignment_stmt")
bag_type = Forward()("bag_type")
inverse_attr = Forward()("inverse_attr")
constant_body = Forward()("constant_body")
precision_spec = Forward()("precision_spec")
general_bag_type = Forward()("general_bag_type")
qualifiable_factor = Forward()("qualifiable_factor")
bound_2 = Forward()("bound_2")
instantiable_type = Forward()("instantiable_type")
general_set_type = Forward()("general_set_type")
supertype_rule = Forward()("supertype_rule")
factor = Forward()("factor")
list_type = Forward()("list_type")
one_of = Forward()("one_of")
aggregate_type = Forward()("aggregate_type")
entity_body = Forward()("entity_body")
generalized_types = Forward()("generalized_types")
case_stmt = Forward()("case_stmt")
binary_type = Forward()("binary_type")
local_decl = Forward()("local_decl")
alias_stmt = Forward()("alias_stmt")
simple_expression = Forward()("simple_expression")
general_array_type = Forward()("general_array_type")
interval = Forward()("interval")
procedure_head = Forward()("procedure_head")
function_decl = Forward()("function_decl")
supertype_expression = Forward()("supertype_expression")
set_type = Forward()("set_type")
primary = Forward()("primary")
procedure_call_stmt = Forward()("procedure_call_stmt")
simple_types = Forward()("simple_types")
query_expression = Forward()("query_expression")
index_2 = Forward()("index_2")
constant_decl = Forward()("constant_decl")
case_action = Forward()("case_action")
schema_body = Forward()("schema_body")
element = Forward()("element")
numeric_expression = Forward()("numeric_expression")
aggregate_initializer = Forward()("aggregate_initializer")
schema_decl = Forward()("schema_decl")
supertype_term = Forward()("supertype_term")
algorithm_head = Forward()("algorithm_head")
supertype_constraint = Forward()("supertype_constraint")
interval_low = Forward()("interval_low")
domain_rule = Forward()("domain_rule")
rule_decl = Forward()("rule_decl")
concrete_types = Forward()("concrete_types")
qualifier = Forward()("qualifier")
subtype_constraint_body = Forward()("subtype_constraint_body")
function_call = Forward()("function_call")
bound_spec = Forward()("bound_spec")
derive_clause = Forward()("derive_clause")
supertype_factor << (((supertype_term + ZeroOrMore((AND + supertype_term))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="supertype_factor"))
interval_item << (simple_expression)
subtype_constraint << (((OF + CaselessLiteral("(") + supertype_expression + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subtype_constraint"))
repeat_stmt << (((REPEAT + repeat_control + CaselessLiteral(";") + stmt + ZeroOrMore(stmt) + END_REPEAT + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="repeat_stmt"))
subsuper << (((Optional(supertype_constraint) + Optional(subtype_declaration)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subsuper"))
increment << (numeric_expression)
remark << (((embedded_remark | tail_remark))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="remark"))
increment_control << (((variable_id + CaselessLiteral(":=") + bound_1 + TO + bound_2 + Optional((BY + increment))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="increment_control"))
local_variable << (((variable_id + ZeroOrMore((CaselessLiteral(",") + variable_id)) + CaselessLiteral(":") + parameter_type + Optional((CaselessLiteral(":=") + expression)) + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="local_variable"))
until_control << (((UNTIL + logical_expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="until_control"))
while_control << (((WHILE + logical_expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="while_control"))
parameter << (expression)
width << (numeric_expression)
string_type << (((STRING + Optional(width_spec)))).setParseAction(StringType)
array_type << (((ARRAY + bound_spec + OF + Optional(OPTIONAL) + Optional(UNIQUE) + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="array_type"))
if_stmt << (((IF + logical_expression + THEN + stmt + ZeroOrMore(stmt) + Optional((ELSE + stmt + ZeroOrMore(stmt))) + END_IF + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="if_stmt"))
index << (numeric_expression)
repetition << (numeric_expression)
index_qualifier << (((CaselessLiteral("[") + index_1 + Optional((CaselessLiteral(":") + index_2)) + CaselessLiteral("]")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="index_qualifier"))
bound_1 << (numeric_expression)
procedure_decl << (((procedure_head + algorithm_head + ZeroOrMore(stmt) + END_PROCEDURE + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="procedure_decl"))
entity_constructor << (((entity_ref + CaselessLiteral("(") + Optional((expression + ZeroOrMore((CaselessLiteral(",") + expression)))) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="entity_constructor"))
inverse_clause << (((INVERSE + inverse_attr + ZeroOrMore(inverse_attr)))).setParseAction(AttributeList)
function_head << (((FUNCTION + function_id + Optional((CaselessLiteral("(") + formal_parameter + ZeroOrMore((CaselessLiteral(";") + formal_parameter)) + CaselessLiteral(")"))) + CaselessLiteral(":") + parameter_type + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="function_head"))
formal_parameter << (((parameter_id + ZeroOrMore((CaselessLiteral(",") + parameter_id)) + CaselessLiteral(":") + parameter_type))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="formal_parameter"))
interval_high << (simple_expression)
entity_decl << (((entity_head + entity_body + END_ENTITY + CaselessLiteral(";")))).setParseAction(EntityDeclaration)
abstract_supertype_declaration << (((ABSTRACT + SUPERTYPE + Optional(subtype_constraint)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="abstract_supertype_declaration"))
index_1 << (index)
general_aggregation_types << (((general_array_type | general_bag_type | general_list_type | general_set_type))).setParseAction(AggregationType)
real_type << (((REAL + Optional((CaselessLiteral("(") + precision_spec + CaselessLiteral(")")))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="real_type"))
type_decl << (((TYPE + type_id + CaselessLiteral("=") + underlying_type + CaselessLiteral(";") + Optional(where_clause) + END_TYPE + CaselessLiteral(";")))).setParseAction(TypeDeclaration)
stmt << (((alias_stmt | assignment_stmt | case_stmt | compound_stmt | escape_stmt | if_stmt | null_stmt | procedure_call_stmt | repeat_stmt | return_stmt | skip_stmt))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="stmt"))
declaration << (((entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="declaration"))
explicit_attr << (((attribute_decl + ZeroOrMore((CaselessLiteral(",") + attribute_decl)) + CaselessLiteral(":") + Optional(OPTIONAL) + parameter_type + CaselessLiteral(";")))).setParseAction(ExplicitAttribute)
compound_stmt << (((BEGIN + stmt + ZeroOrMore(stmt) + END + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="compound_stmt"))
aggregation_types << (((array_type | bag_type | list_type | set_type))).setParseAction(AggregationType)
simple_factor << (((aggregate_initializer | interval | query_expression | (Optional(unary_op) + ((CaselessLiteral("(") + expression + CaselessLiteral(")")) | primary)) | entity_constructor | enumeration_reference))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="simple_factor"))
where_clause << (((WHERE + domain_rule + CaselessLiteral(";") + ZeroOrMore((domain_rule + CaselessLiteral(";")))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="where_clause"))
entity_head << (((ENTITY + entity_id + subsuper + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="entity_head"))
underlying_type << (((constructed_types | concrete_types))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="underlying_type"))
subtype_constraint_decl << (((subtype_constraint_head + subtype_constraint_body + END_SUBTYPE_CONSTRAINT + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subtype_constraint_decl"))
logical_expression << (expression)
case_label << (expression)
expression << (((simple_expression + Optional((rel_op_extended + simple_expression))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="expression"))
general_list_type << (((LIST + Optional(bound_spec) + OF + Optional(UNIQUE) + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_list_type"))
actual_parameter_list << (((CaselessLiteral("(") + Optional(parameter) + ZeroOrMore((CaselessLiteral(",") + parameter)) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="actual_parameter_list"))
width_spec << (((CaselessLiteral("(") + width + CaselessLiteral(")") + Optional(FIXED)))).setParseAction(WidthSpec)
selector << (expression)
syntax << (((schema_decl + ZeroOrMore(schema_decl)))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="syntax"))
aggregate_source << (simple_expression)
return_stmt << (((RETURN + Optional((CaselessLiteral("(") + expression + CaselessLiteral(")"))) + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="return_stmt"))
embedded_remark << (((CaselessLiteral("(*") + Optional(remark_tag) + ZeroOrMore(((not_paren_star + ZeroOrMore(not_paren_star)) | lparen_then_not_lparen_star | (CaselessLiteral("*") + ZeroOrMore(CaselessLiteral("*"))) | not_rparen_star_then_rparen | embedded_remark)) + CaselessLiteral("*)")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="embedded_remark"))
parameter_type << (((generalized_types | simple_types | named_types))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="parameter_type"))
term << (((factor + ZeroOrMore((multiplication_like_op + factor))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="term"))
derived_attr << (((attribute_decl + CaselessLiteral(":") + parameter_type + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="derived_attr"))
repeat_control << (((Optional(increment_control) + Optional(while_control) + Optional(until_control)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="repeat_control"))
assignment_stmt << (((general_ref + ZeroOrMore(qualifier) + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="assignment_stmt"))
bag_type << (((BAG + Optional(bound_spec) + OF + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="bag_type"))
inverse_attr << (((attribute_decl + CaselessLiteral(":") + Optional(((SET | BAG) + Optional(bound_spec) + OF)) + entity_ref + FOR + Optional((entity_ref + CaselessLiteral("."))) + attribute_ref + CaselessLiteral(";")))).setParseAction(InverseAttribute)
constant_body << (((constant_id + CaselessLiteral(":") + instantiable_type + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="constant_body"))
precision_spec << (numeric_expression)
general_bag_type << (((BAG + Optional(bound_spec) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_bag_type"))
qualifiable_factor << (((function_call | attribute_ref | constant_factor | general_ref | population))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="qualifiable_factor"))
bound_2 << (numeric_expression)
instantiable_type << (((concrete_types | entity_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="instantiable_type"))
general_set_type << (((SET + Optional(bound_spec) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_set_type"))
supertype_rule << (((SUPERTYPE + subtype_constraint))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="supertype_rule"))
factor << (((simple_factor + Optional((CaselessLiteral("**") + simple_factor))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="factor"))
list_type << (((LIST + Optional(bound_spec) + OF + Optional(UNIQUE) + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="list_type"))
one_of << (((ONEOF + CaselessLiteral("(") + supertype_expression + ZeroOrMore((CaselessLiteral(",") + supertype_expression)) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="one_of"))
aggregate_type << (((AGGREGATE + Optional((CaselessLiteral(":") + type_label)) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="aggregate_type"))
entity_body << (((ZeroOrMore(explicit_attr) + Optional(derive_clause) + Optional(inverse_clause) + Optional(unique_clause) + Optional(where_clause)))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="entity_body"))
generalized_types << (((aggregate_type | general_aggregation_types | generic_entity_type | generic_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="generalized_types"))
case_stmt << (((CASE + selector + OF + ZeroOrMore(case_action) + Optional((OTHERWISE + CaselessLiteral(":") + stmt)) + END_CASE + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="case_stmt"))
binary_type << (((BINARY + Optional(width_spec)))).setParseAction(BinaryType)
local_decl << (((LOCAL + local_variable + ZeroOrMore(local_variable) + END_LOCAL + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="local_decl"))
alias_stmt << (((ALIAS + variable_id + FOR + general_ref + ZeroOrMore(qualifier) + CaselessLiteral(";") + stmt + ZeroOrMore(stmt) + END_ALIAS + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="alias_stmt"))
simple_expression << (((term + ZeroOrMore((add_like_op + term))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="simple_expression"))
general_array_type << (((ARRAY + Optional(bound_spec) + OF + Optional(OPTIONAL) + Optional(UNIQUE) + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_array_type"))
interval << (((CaselessLiteral("{") + interval_low + interval_op + interval_item + interval_op + interval_high + CaselessLiteral("}")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="interval"))
procedure_head << (((PROCEDURE + procedure_id + Optional((CaselessLiteral("(") + Optional(VAR) + formal_parameter + ZeroOrMore((CaselessLiteral(";") + Optional(VAR) + formal_parameter)) + CaselessLiteral(")"))) + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="procedure_head"))
function_decl << (((function_head + algorithm_head + stmt + ZeroOrMore(stmt) + END_FUNCTION + CaselessLiteral(";")))).setParseAction(FunctionDeclaration)
supertype_expression << (((supertype_factor + ZeroOrMore((ANDOR + supertype_factor))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="supertype_expression"))
set_type << (((SET + Optional(bound_spec) + OF + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="set_type"))
primary << (((literal | (qualifiable_factor + ZeroOrMore(qualifier))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="primary"))
procedure_call_stmt << ((((built_in_procedure | procedure_ref) + actual_parameter_list + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="procedure_call_stmt"))
simple_types << (((binary_type | boolean_type | integer_type | logical_type | number_type | real_type | string_type))).setParseAction(SimpleType)
query_expression << (((QUERY + CaselessLiteral("(") + variable_id + CaselessLiteral("<*") + aggregate_source + CaselessLiteral("|") + logical_expression + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="query_expression"))
index_2 << (index)
constant_decl << (((CONSTANT + constant_body + ZeroOrMore(constant_body) + END_CONSTANT + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="constant_decl"))
case_action << (((case_label + ZeroOrMore((CaselessLiteral(",") + case_label)) + CaselessLiteral(":") + stmt))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="case_action"))
schema_body << (((ZeroOrMore(interface_specification) + Optional(constant_decl) + ZeroOrMore((declaration | rule_decl))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="schema_body"))
element << (((expression + Optional((CaselessLiteral(":") + repetition))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="element"))
numeric_expression << (simple_expression)
aggregate_initializer << (((CaselessLiteral("[") + Optional((element + ZeroOrMore((CaselessLiteral(",") + element)))) + CaselessLiteral("]")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="aggregate_initializer"))
schema_decl << (((SCHEMA + schema_id + Optional(schema_version_id) + CaselessLiteral(";") + schema_body + END_SCHEMA + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="schema_decl"))
supertype_term << (((one_of | (CaselessLiteral("(") + supertype_expression + CaselessLiteral(")")) | entity_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="supertype_term"))
algorithm_head << (((ZeroOrMore(declaration) + Optional(constant_decl) + Optional(local_decl)))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="algorithm_head"))
supertype_constraint << (((abstract_supertype_declaration | abstract_entity_declaration | supertype_rule))).setParseAction(SuperTypeExpression)
interval_low << (simple_expression)
domain_rule << (((Optional((rule_label_id + CaselessLiteral(":"))) + expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="domain_rule"))
rule_decl << (((rule_head + algorithm_head + ZeroOrMore(stmt) + where_clause + END_RULE + CaselessLiteral(";")))).setParseAction(RuleDeclaration)
concrete_types << (((aggregation_types | simple_types | type_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="concrete_types"))
qualifier << (((attribute_qualifier | group_qualifier | index_qualifier))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="qualifier"))
subtype_constraint_body << (((Optional(abstract_supertype) + Optional(total_over) + Optional((supertype_expression + CaselessLiteral(";")))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subtype_constraint_body"))
function_call << ((((built_in_function | function_ref) + actual_parameter_list))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="function_call"))
bound_spec << (((CaselessLiteral("[") + bound_1 + CaselessLiteral(":") + bound_2 + CaselessLiteral("]")))).setParseAction(BoundSpecification)
derive_clause << (((DERIVE + derived_attr + ZeroOrMore(derived_attr)))).setParseAction(AttributeList)
syntax.ignore("--" + restOfLine)
syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))"))
ast = syntax.parseFile(fn)
s = schema.Schema(ast)
m = mapping.Mapping(s)
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)
@@ -0,0 +1,264 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import operator
import itertools
import codegen
import templates
import documentation
from collections import defaultdict
USE_VIRTUAL_INHERITANCE = True
class Header(codegen.Base):
def __init__(self, mapping):
declarations = []
case_lookup = lambda nm: [k for k in mapping.schema.keys if k.lower() == nm.lower()][0]
case_normalize = lambda nm: nm if nm.startswith("IfcUtil::") else case_lookup(nm)
create_supertype_statement = lambda nms: ", ".join(
"public %s %s" % ("" if c.startswith("IfcUtil::") else "", c) for c in nms
)
write = lambda str, **kwargs: declarations.append(
str
% dict({"documentation": templates.multi_line_comment(documentation.description(kwargs["name"]))}, **kwargs)
)
forward_names = list(mapping.schema.entities.keys()) + list(mapping.schema.simpletypes.keys())
forward_definitions = "".join(["class %s; " % n for n in forward_names])
select_super_types = defaultdict(list)
for name, type in mapping.schema.selects.items():
for nm in type.values:
select_super_types[str(nm).lower()].append(name)
write(templates.select_virtual if USE_VIRTUAL_INHERITANCE else templates.select_plain, name=name)
def get_select_super_types(nm, bases=[]):
x = list(select_super_types[nm.lower()])
for y in x:
x.extend(get_select_super_types(y))
return sorted(set(x) - set(itertools.chain.from_iterable(map(get_select_super_types, bases))))
for name, type in mapping.schema.enumerations.items():
short_name = name[:-4] if name.endswith("Enum") else name
write(templates.enumeration, name=name, values=", ".join(["%s_%s" % (short_name, v) for v in type.values]))
emitted_simpletypes = set()
while len(emitted_simpletypes) < len(mapping.schema.simpletypes):
for name, type in mapping.schema.simpletypes.items():
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)
superclasses = []
all_superclasses = []
if superclass is not None:
superclasses.append(superclass)
while superclass:
all_superclasses.append(superclass)
superclass = mapping.simple_type_parent(superclass)
else:
superclasses.append("IfcUtil::IfcBaseType")
if USE_VIRTUAL_INHERITANCE:
superclasses.extend(get_select_super_types(name, bases=all_superclasses))
is_emitted = (
lambda nm: nm == "IfcUtil::IfcBaseType"
or nm in mapping.schema.selects
or nm.lower() in emitted_simpletypes
)
if not all(map(is_emitted, superclasses)):
continue
superclasses = list(map(case_normalize, superclasses))
emitted_simpletypes.add(name.lower())
superclass_statement = create_supertype_statement(superclasses)
write(
templates.simpletype, name=name, type=type_str, attr_type=attr_type, superclass=superclass_statement
)
class_definitions = []
write = lambda str, **kwargs: class_definitions.append(
str
% dict({"documentation": templates.multi_line_comment(documentation.description(kwargs["name"]))}, **kwargs)
)
emitted_entities = set()
while len(emitted_entities) < len(mapping.schema.entities):
for name, type in mapping.schema.entities.items():
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):
attr_lines.extend(
["/// %s" % d for d in documentation.description(".".join((name, attr.name)))]
)
type_str = mapping.get_parameter_type(attr)
if mapping.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN":
attr_lines.append("%s %s() const;" % (type_str, attr.name))
attr_lines.append("void set%s(%s v);" % (attr.name, type_str))
[write_method(attr) for attr in type.attributes]
inv_lines = []
def write_inverse(attr):
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]
attributes = "\n".join(["%s%s" % (" " * 4, a) for a in attr_lines])
if len(attributes):
attributes += "\n"
inverse = "\n".join(["%s%s" % (" " * 4, a) for a in inv_lines])
if len(inverse):
inverse += "\n"
all_supertypes = []
tt = type
while len(tt.supertypes):
all_supertypes.append(tt.supertypes[0])
tt = mapping.schema.entities[tt.supertypes[0]]
supertypes = list(type.supertypes) if len(type.supertypes) else ["IfcUtil::IfcBaseEntity"]
if USE_VIRTUAL_INHERITANCE:
supertypes.extend(get_select_super_types(name, bases=all_supertypes))
supertypes = list(map(case_normalize, supertypes))
superclass = create_supertype_statement(supertypes)
argument_count = mapping.argument_count(type)
argument_start = argument_count - len(type.attributes)
argument_name_function_body_switch_stmt = (
" switch (i) {%s}"
% (
"".join(
[
'case %d: return "%s"; ' % (i + argument_start, attr.name)
for i, attr in enumerate(type.attributes)
]
)
)
if len(type.attributes)
else ""
)
argument_name_function_body_tail = (
(" return %s::getArgumentName(i); " % type.supertypes[0])
if len(type.supertypes) == 1
else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
)
argument_name_function_body = (
argument_name_function_body_switch_stmt + argument_name_function_body_tail
)
derived = mapping.derived_in_supertype(type)
attribute_names = list(map(operator.attrgetter("name"), mapping.arguments(type)))
derived_in_supertype = set(derived) & set(attribute_names)
derived_in_supertype_indices = sorted(attribute_names.index(nm) for nm in derived_in_supertype)
attribute_type_cases = [
"case %d: return IfcUtil::Argument_DERIVED; " % idx for idx in derived_in_supertype_indices
]
attribute_type_cases += [
"case %d: return %s; " % (i + argument_start, mapping.make_argument_type(attr))
for i, attr in enumerate(type.attributes)
]
argument_type_function_body_switch_stmt = (
" switch (i) {%s}" % ("".join(attribute_type_cases)) if len(type.attributes) else ""
)
argument_type_function_body_tail = (
(" return %s::getArgumentType(i); " % type.supertypes[0])
if len(type.supertypes) == 1
else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
)
argument_type_function_body = (
argument_type_function_body_switch_stmt + argument_type_function_body_tail
)
argument_entity_function_body_switch_stmt = (
" switch (i) {%s}"
% (
"".join(
[
"case %d: return %s; " % (i + argument_start, mapping.make_argument_entity(attr))
for i, attr in enumerate(type.attributes)
]
)
)
if len(type.attributes)
else ""
)
argument_entity_function_body_tail = (
(" return %s::getArgumentEntity(i); " % type.supertypes[0])
if len(type.supertypes) == 1
else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
)
argument_entity_function_body = (
argument_entity_function_body_switch_stmt + argument_entity_function_body_tail
)
constructor_arguments = ", ".join(
"%(full_type)s v%(index)d_%(name)s" % a for a in mapping.get_assignable_arguments(type)
)
write(templates.entity, **locals())
emitted_entities.add(name)
self.str = templates.header % {
"schema_name_upper": mapping.schema.name.upper(),
"schema_name": mapping.schema.name.capitalize(),
"declarations": "".join(declarations),
"forward_definitions": forward_definitions,
"class_definitions": "".join(class_definitions),
}
self.schema_name = mapping.schema.name.capitalize()
self.file_name = "%s.h" % self.schema_name
def __repr__(self):
return self.str
Generator = Header
@@ -0,0 +1,28 @@
SCHEMA header_section_schema;
TYPE time_stamp_text = STRING(256);
END_TYPE;
TYPE schema_name = STRING(1024);
END_TYPE;
ENTITY file_name;
name : STRING (256);
time_stamp : time_stamp_text;
author : LIST [1:?] OF STRING (256);
organization : LIST [1:?] OF STRING (256);
preprocessor_version : STRING (256);
originating_system : STRING (256);
authorisation : STRING (256);
END_ENTITY;
ENTITY file_description;
description : LIST [1:?] OF STRING (256);
implementation_level : STRING (256);
END_ENTITY;
ENTITY file_schema;
schema_identifiers : LIST [1:?] OF UNIQUE schema_name;
END_ENTITY;
END_SCHEMA;
@@ -0,0 +1,444 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import codegen
import templates
from schema import OrderedCaseInsensitiveDict
from header import USE_VIRTUAL_INHERITANCE
class Implementation(codegen.Base):
def __init__(self, mapping):
enumeration_functions = []
entity_implementations = []
schema_entity_statements = []
schema_name = mapping.schema.name.capitalize()
schema_name_upper = mapping.schema.name.upper()
stringify = lambda s: '"%s"' % s
cat = lambda vs: "".join(vs)
catc = lambda vs: ", ".join(vs)
catnl = lambda vs: "\n".join(vs)
cator = lambda vs: " || ".join(vs)
nl = lambda s: "%s\n" % s if len(s) else s
write = lambda str, **kwargs: enumeration_functions.append(str % kwargs)
collections_by_type = (
("entity", mapping.schema.entities),
("type_declaration", mapping.schema.simpletypes),
("select_type", mapping.schema.selects),
("enumeration_type", mapping.schema.enumerations),
)
self.names = []
for _, collection in collections_by_type:
self.names.extend(collection.keys())
self.names.sort(key=str.lower)
for name, enum in mapping.schema.enumerations.items():
short_name = name[:-4] if name.endswith("Enum") else name
context = locals()
write(
templates.enumeration_function,
max_id=len(enum.values),
name=name,
schema_name=schema_name,
schema_name_upper=schema_name_upper,
index_in_schema=self.names.index(str(name)),
values=catc(map(stringify, enum.values)),
from_string_statements=catnl(
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(
templates.select_function,
name=name,
schema_name=schema_name,
schema_name_upper=schema_name_upper,
index_in_schema=self.names.index(str(name)),
)
write = lambda str, **kwargs: entity_implementations.append(str % kwargs)
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])
)
constructor_arguments = mapping.get_assignable_arguments(type, include_derived=True)
constructor_arguments_str = catc(
"%(full_type)s v%(index)d_%(name)s" % a for a in constructor_arguments if not a["is_derived"]
)
attributes = []
constructor_implementations = []
write_attr = lambda str, **kwargs: attributes.append(str % kwargs)
for arg in constructor_arguments:
if not arg["is_inherited"] and not arg["is_derived"]:
def find_template(arg):
simple = mapping.schema.is_simpletype(arg["list_instance_type"])
select = arg["list_instance_type"] == "IfcUtil::IfcBaseClass"
express = (
mapping.flatten_type_string(arg["list_instance_type"]) in mapping.express_to_cpp_typemapping
)
if arg["is_enum"]:
return templates.get_attr_stmt_enum
elif arg["is_nested"] and arg["is_templated_list"]:
return templates.get_attr_stmt_nested_array
elif arg["is_templated_list"] and not (select or simple or express):
return templates.get_attr_stmt_array
elif arg["non_optional_type"].endswith("*"):
return templates.get_attr_stmt_entity
else:
return templates.get_attr_stmt
null_check = ""
if arg["is_optional"]:
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:
null_check = attr_check % "nullptr"
tmpl = find_template(arg)
write_attr(
templates.const_function,
class_name=name,
name=arg["name"],
arguments="",
schema_name=schema_name,
schema_name_upper=schema_name_upper,
return_type=arg["full_type"],
body=tmpl
% {
"index": arg["index"] - 1,
"type": arg["full_type"].replace("::Value", ""),
"non_optional_type": arg["non_optional_type"].replace("::Value", ""),
"non_optional_type_no_pointer": arg["non_optional_type"]
.replace("::Value", "")
.replace("*", ""),
"list_instance_type": arg["list_instance_type"],
"null_check": null_check,
},
)
def find_template(arg):
simple = mapping.schema.is_simpletype(arg["list_instance_type"])
select = arg["list_instance_type"] == "IfcUtil::IfcBaseClass"
express = arg["list_instance_type"] in mapping.express_to_cpp_typemapping
if arg["is_enum"]:
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("*"):
return templates.set_attr_instance
else:
return templates.set_attr_stmt
tmpl = find_template(arg)
write_attr(
templates.function,
class_name=name,
name="set%s" % arg["name"],
arguments="%s v" % arg["full_type"],
return_type="void",
schema_name=schema_name,
schema_name_upper=schema_name_upper,
body=tmpl
% {
"index": arg["index"] - 1,
"type": arg["full_type"].replace("::Value", ""),
"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_end": "}" if "boost::optional" in arg["full_type"] else "",
},
)
if arg["is_derived"]:
constructor_implementations.append(templates.constructor_stmt_derived % {"index": arg["index"] - 1})
else:
is_optional_non_naked_ptr = arg["is_optional"] and not arg["non_optional_type"].endswith("*")
arg_name = "v%(index)d_%(name)s" % arg
deref_name = ("*%s" % arg_name) if is_optional_non_naked_ptr else arg_name
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
)
)
)
impl = tmpl % {
"name": deref_name,
"index": arg["index"] - 1,
"type": arg["non_optional_type"].replace("::Value", ""),
}
if is_optional_non_naked_ptr:
impl = templates.constructor_stmt_optional % {
"name": arg_name,
"index": arg["index"] - 1,
"stmt": impl,
}
constructor_implementations.append(impl)
def get_attribute_index(entity, attr_name):
related_entity = mapping.schema.entities[entity]
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,
"schema_name": schema_name,
"schema_name_upper": schema_name_upper,
"name": i.name,
"arguments": "",
"return_type": "::%s::%s::list::ptr" % (schema_name, i.entity),
"body": templates.get_inverse
% {
"type": i.entity,
"type_index": self.names.index(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
]
superclass = (
"%s(std::move(e))" % type.supertypes[0]
if len(type.supertypes) == 1
else "IfcUtil::IfcBaseEntity(std::move(e))"
)
superclass_num_attrs = (
"%s(IfcEntityInstanceData(in_memory_attribute_storage(%%d)))" % type.supertypes[0]
if len(type.supertypes) == 1
else "IfcUtil::IfcBaseEntity(IfcEntityInstanceData(in_memory_attribute_storage(%d)))"
) % len(constructor_arguments)
write(
templates.entity_implementation,
name=name,
parent_type_test=parent_type_test,
constructor_arguments=constructor_arguments_str,
constructor_implementation=cat(constructor_implementations),
attributes=nl(catnl(attributes)),
inverse=nl(catnl(inverse)),
superclass=superclass,
superclass_num_attrs=superclass_num_attrs,
schema_name=schema_name,
schema_name_upper=schema_name_upper,
index_in_schema=self.names.index(str(name)),
)
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.enumerations.items()
]
schema_entity_statements += [
templates.schema_entity_stmt % locals() for name, type in mapping.schema.entities.items()
]
enumerable_types = sorted(
set(
[name for name, type in mapping.schema.types.items()]
+ [name for name, type in mapping.schema.entities.items()]
)
)
max_len = max(map(len, enumerable_types))
type_name_strings = catc(map(stringify, enumerable_types))
string_map_statements = [
templates.string_map_statement
% {"uppercase_name": name.upper(), "name": name, "padding": " " * (max_len - len(name))}
for name in 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:
return enumeration_index_by_str[e.supertypes[0]]
else:
return -1
parent_type_statements = ",".join(map(str, map(get_parent_id, enumerable_types)))
max_id = len(enumerable_types)
simple_type_statements = cator("v == Type::%s" % name for name in selectable_simple_types)
simple_type_impl = []
for class_name, type in mapping.schema.simpletypes.items():
type_str = mapping.make_type_string(mapping.flatten_type_string(type))
attr_type = mapping.make_argument_type(type)
superclass = mapping.simple_type_parent(class_name) or "IfcUtil::IfcBaseType"
simpletype_impl_is = (
templates.simpletype_impl_is_with_supertype
if superclass
else templates.simpletype_impl_is_without_supertype
)
constructor = templates.constructor_single_initlist # if superclass else templates.constructor
simpletype_impl_cast = (
templates.simpletype_impl_cast_templated
if mapping.is_templated_list(type)
else templates.simpletype_impl_cast
)
simpletype_impl_constructor = (
templates.simpletype_impl_constructor_templated
if mapping.is_templated_list(type)
else templates.simpletype_impl_constructor
)
def compose(params, schema_name=schema_name, schema_name_upper=schema_name_upper):
class_name, attr_type, superclass, superclass_init, name, tmpl, return_type, args, body = params
index_in_schema = self.names.index(str(class_name))
underlying_type = mapping.list_instance_type(type)
arguments = ",".join(args)
body = body % locals()
return tmpl % locals()
simple_type_impl.append(templates.simpletype_impl_comment % {"name": class_name})
simple_type_impl.extend(
map(
compose,
map(
lambda x: (class_name, attr_type, superclass) + x,
(
(
"",
"Class",
templates.function,
"const IfcParse::type_declaration&",
(),
templates.simpletype_impl_class,
),
(
"",
"declaration",
templates.const_function,
"const IfcParse::type_declaration&",
(),
templates.simpletype_impl_declaration,
),
(
"std::move(e)",
"",
constructor,
"",
("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,), "")
),
("", "", templates.cast_function, type_str, (), simpletype_impl_cast),
),
),
)
)
simple_type_impl.append("")
"""
external_definitions = (
[("extern entity* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.entities.keys()]
+ [
("extern type_declaration* %s_%%s_type;" % schema_name_upper) % n
for n in mapping.schema.simpletypes.keys()
]
+ [
("extern enumeration_type* %s_%%s_type;" % schema_name_upper) % n
for n in mapping.schema.enumerations.keys()
]
+ [
("extern select_type* %s_%%s_type;" % schema_name_upper) % n
for n in mapping.schema.selects.keys()
]
)
"""
external_definitions = ["extern declaration* %s_types[%d];" % (schema_name_upper, len(self.names))]
self.str = templates.implementation % {
"schema_name_upper": schema_name_upper,
"schema_name": schema_name,
"max_id": max_id,
"enumeration_functions": cat(enumeration_functions),
"schema_entity_statements": catnl(schema_entity_statements),
"type_name_strings": type_name_strings,
"string_map_statements": catnl(string_map_statements),
"simple_type_statement": simple_type_statements,
"parent_type_statements": parent_type_statements,
"entity_implementations": catnl(entity_implementations),
"simple_type_impl": catnl(simple_type_impl),
"external_definitions": catnl(external_definitions),
}
self.schema_name = mapping.schema.name.capitalize()
self.file_name = "%s.cpp" % self.schema_name
def __repr__(self):
return self.str
Generator = Implementation
@@ -0,0 +1,295 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import sys
import nodes
import templates
import schema
from header import USE_VIRTUAL_INHERITANCE
class Mapping:
express_to_cpp_typemapping = {
"boolean": "bool",
"logical": "boost::logic::tribool",
"integer": "int",
"real": "double",
"number": "double",
"string": "std::string",
"binary": "boost::dynamic_bitset<>",
}
supported_argument_types = set(
[
"INT",
"BOOL",
"LOGICAL",
"DOUBLE",
"STRING",
"BINARY",
"ENUMERATION",
"ENTITY_INSTANCE",
"AGGREGATE_OF_INT",
"AGGREGATE_OF_DOUBLE",
"AGGREGATE_OF_STRING",
"AGGREGATE_OF_BINARY",
"AGGREGATE_OF_ENTITY_INSTANCE",
"AGGREGATE_OF_AGGREGATE_OF_INT",
"AGGREGATE_OF_AGGREGATE_OF_DOUBLE",
"AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE",
]
)
def __init__(self, schema: schema.Schema):
self.schema = schema
def flatten_type_string(self, 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) if self.schema.is_simpletype(type) else type
return res
def simple_type_parent(self, type):
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, 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
)
return tmpl % {
"instance_type": self.make_type_string(self.flatten_type_string(type.type)),
"lower": type.bounds.lower,
"upper": type.bounds.upper,
}
def is_array(self, type):
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)
else:
return False
def make_argument_entity(self, attr):
type = attr.type if hasattr(attr, "type") else attr
while isinstance(type, nodes.AggregationType):
type = type.type
if str(type) in self.express_to_cpp_typemapping:
return "Type::UNDEFINED"
else:
return "Type::%s" % type
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):
return "BINARY"
elif isinstance(type, nodes.StringType):
return "STRING"
elif isinstance(type, nodes.EnumerationType):
return "ENUMERATION"
elif isinstance(type, nodes.AggregationType):
ty = _make_argument_type(type.type)
if ty == "UNKNOWN":
return "UNKNOWN"
return "AGGREGATE_OF_" + ty
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)
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)
if ty == "TRIBOOL":
ty = "LOGICAL"
if ty not in self.supported_argument_types:
import pdb
pdb.set_trace()
print("Attribute %r mapped as 'unknown'" % (attr), file=sys.stderr)
ty = "UNKNOWN"
return "IfcUtil::Argument_%s" % ty
def get_type_dep(self, type):
if isinstance(type, str):
return self.express_to_cpp_typemapping.get(str(type), type)
else:
return self.get_type_dep(type.type)
def get_parameter_type(self, attr, allow_optional=True):
attr_type = self.flatten_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
if self.schema.is_enumeration(attr_type):
type_str = "::%s::%s::Value" % (self.schema.name.capitalize(), attr_type)
elif isinstance(type_str, nodes.AggregationType):
is_nested_list = isinstance(attr_type.type, nodes.AggregationType)
ty = self.get_parameter_type(attr_type.type if is_nested_list else attr_type)
# We do not use pointers in aggregate_of<T>. aggregate_of has member vector<T*>
ty = ty.replace("*", "")
# https://github.com/IfcOpenShell/IfcOpenShell/issues/2805
# We do support statically typed select types as aggregates when USE_VIRTUAL_INHERITANCE=True
if not USE_VIRTUAL_INHERITANCE and self.schema.is_select(attr_type.type):
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": bounds[0], "upper": bounds[1]}
else:
tmpl = templates.list_list_type if is_nested_list else templates.list_type
type_str = tmpl % {"instance_type": ty}
elif self.schema.is_entity(type_str) or self.schema.is_select(type_str):
type_str = "::%s::%s*" % (self.schema.name.capitalize(), attr_type)
is_ptr = True
if allow_optional and attr.optional and not is_ptr:
# pointers are still handled with nullptr for the time being
type_str = "boost::optional< %s >" % type_str
return type_str
def argument_count(self, t):
c = sum([self.argument_count(self.schema.entities[s]) for s in t.supertypes])
return c + len(t.attributes)
def arguments(self, t):
c = sum([self.arguments(self.schema.entities[s]) for s in t.supertypes], [])
return c + t.attributes
def derived_in_supertype(self, t):
c = sum([self.derived_in_supertype(self.schema.entities[s]) for s in t.supertypes], [])
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 not USE_VIRTUAL_INHERITANCE and self.schema.is_select(v):
return "IfcUtil::IfcBaseClass"
if str(v) in self.schema.types or str(v) in self.schema.entities:
return "::%s::%s" % (self.schema.name.capitalize(), v)
else:
return str(v)
if self.is_array(attr_type):
if not isinstance(attr_type, str) and self.is_array(attr_type.type):
if isinstance(attr_type.type, str):
return f(attr_type.type)
else:
return f(attr_type.type.type)
else:
if isinstance(attr_type, str):
return f(attr_type)
else:
return f(attr_type.type)
return None
def is_templated_list(self, attr):
attr_type = attr.type if isinstance(attr, nodes.ExplicitAttribute) else attr
if isinstance(attr, str):
return False
ty = self.list_instance_type(attr)
if ty is None:
return False
arr = self.is_array(attr_type)
simple = self.schema.is_simpletype(ty)
express = self.flatten_type_string(ty) in self.express_to_cpp_typemapping
select = ty == "IfcUtil::IfcBaseClass"
return arr and not simple and not express and not select
def get_assignable_arguments(self, t, include_derived=False):
count = self.argument_count(t)
num_inherited = count - len(t.attributes)
derived = set(self.derived_in_supertype(t))
attrs = enumerate(self.arguments(t))
def include(attr):
not_derived = include_derived or (attr.name not in derived)
supported = self.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN"
return not_derived and supported
return [
{
"index": i + 1,
"name": attr.name,
"full_type": self.get_parameter_type(attr),
"specialized_type": self.get_parameter_type(attr),
"non_optional_type": self.get_parameter_type(attr, allow_optional=False),
"list_instance_type": self.list_instance_type(attr),
"is_optional": attr.optional,
"is_inherited": i < num_inherited,
"is_enum": attr.type in self.schema.enumerations,
"is_array": self.is_array(attr.type),
"is_nested": self.is_array(attr.type)
and not isinstance(attr.type, str)
and self.is_array(attr.type.type),
"is_derived": attr.name in derived,
"is_templated_list": self.is_templated_list(attr),
"argument_type_enum": self.make_argument_type(attr),
"argument_entity": self.make_argument_entity(attr),
"argument_type": attr.type,
}
for i, attr in attrs
if include(attr)
]
@@ -0,0 +1,603 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import io
import string
import operator
import collections
import bootstrap
class Node:
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.dict_tokens = collections.defaultdict(list)
rules_as_list = set()
for t in self.tokens:
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):
return "%s[%s]" % (self.rule, ",".join("%s" % i for i in self.tokens))
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]
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.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)
def init(self):
assert hasattr(self, "TYPE")
self.where = []
clause = self.where_clause
if clause:
clause = list(clause[0])
self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]]
def __repr__(self):
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.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):
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 = list(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 map(list, list(clause)[1::2])]
def __repr__(self):
strm = io.StringIO()
print("ENTITY %s" % self.name, 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)
for a in self.attributes:
print(" ", a, ";", file=strm, sep="")
if self.derive:
print(" 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)
if self.where:
print(" WHERE", file=strm)
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 EnumerationType(Node):
values = property(lambda self: list(self.enumeration_type[2])[1::2])
def __repr__(self):
return "ENUMERATION OF (" + ",".join(self.values) + ")"
class NamedType(Node):
type = property(lambda self: self.simple_id)
def __repr__(self):
return self.type
def do_try(fn):
try:
return fn()
except:
pass
def get_rule_id(x):
if not isinstance(x, str):
x = type(x).__name__
matches = [k for k, v in bootstrap.actions.items() if v == 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])),
)
)
for k, v in bootstrap.express
}
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:
# production element from grammar is found in parsed data,
# return that.
# we now always explore other synonym, because more often than not we loose data otherwise
yield y
else:
# lookup rule
rule = [e for k, e in bootstrap.express if k == y][0]
def is_synonym(rl):
if isinstance(rl, bootstrap.Term) and isinstance(rl.contents, bootstrap.Keyword):
return rl.contents.contents
# is this a synonym? then processs that
if S := is_synonym(rule):
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])
):
S = is_synonym(rule.contents[0])
yield S
# Do this recursively
yield from replace_synonyms([S])
subrules = list(replace_synonyms(rule_dependencies[key]))
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
# such as IN, LIKE should be retained.
subrules = list(filter(str.islower, subrules))
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}:
return "".join(di)
return [simplify(v) for v in di]
elif isinstance(di, dict) and len(di) == 1 and next(iter(di.values())) == {}:
return next(iter(di.keys()))
elif isinstance(di, dict):
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":
# The definition of if statement if (roughy):
# 'if' expr 'then' stmt+ 'else' stmt+
# this causes stmt to be joined under the same
# dict key. The code below creates an artifical
# `else_stmt` that collects the second group
# of stmts.
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:
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:
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]
if else_stmt:
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]
if key is None:
return {get_rule_id(x): d}
return d
elif isinstance(x, Node):
d = to_tree(x.tokens, key=get_rule_id(x) or key)
if key is None:
return {get_rule_id(x): d}
return d
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)
}
return simplify(prune(d))
elif isinstance(x, list):
return [to_tree(v, key=key) for v in x]
else:
return x
class AggregationType(Node):
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
elif do_try(lambda: v.parameter_type.generalized_types.generic_type.generic_type[0].GENERIC):
return do_try(lambda: v.parameter_type.generalized_types.generic_type.generic_type[0].GENERIC)
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%s" % (self.aggregate_type, self.bounds, "unique " if self.unique else "", self.type)
class SelectType(Node):
values = property(lambda self: list(self.select_type[1])[1::2])
def __repr__(self):
return "SELECT (" + ",".join(map(str, self.values)) + ")"
class SuperTypeExpression(Node):
abstract = property(lambda self: self.abstract_supertype_declaration is not None)
def get_sub_types(self):
if self.abstract:
constraint = self.abstract_supertype_declaration[0]
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]
]
sub_types = property(get_sub_types)
def __repr__(self):
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.tokens[1:])
def __len__(self):
return len(self.tokens[1:])
class InverseAttribute(Node):
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):
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 __repr__(self):
return "binary"
class BoundSpecification(Node):
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.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)
class WidthSpec(Node):
fixed = property(lambda self: self.FIXED is not None)
def init(self):
self.width = int("".join(list(self.width)[0].flat))
def __repr__(self):
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
class ProcedureDeclaration(ListNode):
@property
def name(self):
return self.flat[1]
class FunctionDeclaration(ProcedureDeclaration):
pass
class RuleDeclaration(ProcedureDeclaration):
pass
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,299 @@
import os
import re
import ast
import collections
import ifcopenshell
from logging import Logger
from dataclasses import dataclass
from codegen import indent
def reverse_compile(s):
return re.sub(
r"\bself\b",
"SELF",
re.sub(
r"\s*\-\s*EXPRESS_ONE_BASED_INDEXING",
"",
re.sub(
", )?+.(, INDETERMINATE)\\"[::-1],
"]\\1[",
re.sub(
r", '(\w+)', INDETERMINATE\)",
".\\1",
s.strip()
.replace("len(", "SIZEOF(")
.replace("assert ", "")
.replace(" is not False", "")
.replace("express_getattr(", "")
.replace("express_getitem(", ""),
)[::-1],
)[::-1],
),
)
@dataclass
class error(Exception):
rule_name: str
rule_definition: str
violation: str
instance: ifcopenshell.entity_instance = None
def __str__(self):
inst = ""
if self.instance:
inst = f"On instance:\n{indent(4, str(self.instance))}\n"
if self.rule_name:
return f"{inst}Rule {self.rule_name}:\n{indent(4, self.rule_definition)}\nViolated by:\n{indent(4, self.violation)}"
else:
if inst:
inst = f"\n\n{inst}"
return f"{self.rule_definition}\n\nViolated by:\n{indent(4, self.violation)}{inst}"
def fix_type(v):
if isinstance(v, (list, tuple)):
# 1-based indexing:
#
# @todo this is not the best way, because it still allows to index the 0-th element,
# but given the existing body of rules this should be sufficient.
# return type(v)([None]) + type(v)(map(fix_type, v))
# We don't do this anymore, because it doesn't fix instance attribute lookups
# We now instead perform a -1 on the index qualifier in the code generation
pass
# @todo enrich entity instances with code to evaluate derived attributes
return v
def run(f: ifcopenshell.file, logger: Logger) -> None:
from _pytest import assertion
if hasattr(logger, "set_instance"):
# when using the json logger, we notify it of the relevant instance
pre_annotate_instance = lambda instance: (
logger.set_state("instance", instance) if hasattr(logger, "set_state") else None
)
post_annotate_instance = lambda instance: instance
pre_annotate_attribute = lambda attribute: (
logger.set_state("attribute", attribute) if hasattr(logger, "set_state") else None
)
post_annotate_attribute = lambda attribute: None
else:
# when using the normal text logger the instance is appended to the method
pre_annotate_instance = lambda instance: None
post_annotate_instance = lambda instance: instance
pre_annotate_attribute = lambda attribute: None
post_annotate_attribute = lambda attribute: attribute
orig = ifcopenshell.settings.unpack_non_aggregate_inverses
ifcopenshell.settings.unpack_non_aggregate_inverses = True
fn = os.path.join(os.path.dirname(__file__), "rules", f"{f.schema_identifier}.py")
try:
source = open(fn, "r").read()
except FileNotFoundError as e:
import sys
import time
import subprocess
current_dir_files = {fn.lower(): fn for fn in os.listdir(".")}
schema_name = str(f.schema_identifier).split(" ")[-1].lower()
schema_path = current_dir_files.get(schema_name + ".exp")
fn = schema_path[:-4] + ".py"
if not os.path.exists(fn):
subprocess.run([sys.executable, "-m", "ifcopenshell.express.rule_compiler", schema_path, fn], check=True)
time.sleep(1.0)
source = open(fn, "r").read()
a = ast.parse(source)
assertion.rewrite.rewrite_asserts(mod=a, source=source)
cd = compile(a, f"{f.schema_identifier}.py", "exec")
scope = {}
exec(cd, scope)
S = ifcopenshell.ifcopenshell_wrapper.schema_by_name(f.schema_identifier)
rules = list(filter(lambda x: hasattr(x, "SCOPE"), scope.values()))
if hasattr(logger, "set_state"):
logger.set_state("type", "global_rule")
for R in [r for r in rules if r.SCOPE == "file"]:
try:
R()(f)
except RecursionError as e:
logger.info(str(e))
except Exception as e:
ln = e.__traceback__.tb_next.tb_lineno
pre_annotate_attribute(R.__name__)
logger.error(
str(
error(
post_annotate_attribute(R.__name__),
reverse_compile(source.split("\n")[ln - 1]),
reverse_compile(e.args[0]),
)
)
)
if hasattr(logger, "set_state"):
logger.set_state("type", "simpletype_rule")
types = {}
subtypes = collections.defaultdict(list)
for d in S.declarations():
if isinstance(d, ifcopenshell.ifcopenshell_wrapper.type_declaration):
types[d.name()] = d
if isinstance(d.declared_type(), ifcopenshell.ifcopenshell_wrapper.named_type):
subtypes[d.declared_type().declared_type().name()].append(d.name())
D = collections.defaultdict(list)
for r in rules:
if r.SCOPE == "type":
def visit(nm):
D[nm].append(r)
for nm2 in subtypes[nm]:
visit(nm2)
visit(r.TYPE_NAME)
def type_name(ty):
if isinstance(ty, ifcopenshell.ifcopenshell_wrapper.named_type):
return type_name(ty.declared_type())
elif isinstance(ty, ifcopenshell.ifcopenshell_wrapper.aggregation_type):
# breakpoint()
pass
elif isinstance(ty, ifcopenshell.ifcopenshell_wrapper.simple_type):
pass
else:
return ty.name()
def check(value, type, instance):
if value is None:
return
if type_name(type) in D:
for R in D[type_name(type)]:
try:
R()(fix_type(value))
except RecursionError as e:
logger.info(str(e))
except Exception as e:
ln = e.__traceback__.tb_next.tb_lineno
pre_annotate_instance(instance)
pre_annotate_attribute(f"{R.TYPE_NAME}.{R.RULE_NAME}")
logger.error(
str(
error(
post_annotate_attribute(f"{R.TYPE_NAME}.{R.RULE_NAME}"),
reverse_compile(source.split("\n")[ln - 1]),
reverse_compile(e.args[0]),
post_annotate_instance(instance),
)
)
)
# @nb something can be a named type with rules and still be an aggregation.
# case in point IfcCompoundPlaneAngleMeasure. Therefore only unpack named
# type references from this point onwards.
while isinstance(
type,
(
ifcopenshell.ifcopenshell_wrapper.named_type,
ifcopenshell.ifcopenshell_wrapper.type_declaration,
),
):
type = type.declared_type()
if isinstance(value, (list, tuple)):
if isinstance(type, ifcopenshell.ifcopenshell_wrapper.aggregation_type):
ty = type.type_of_element()
for v in value:
check(v, ty, instance=inst)
else:
# Let's hope a schema validation error was reported for this case
pass
elif isinstance(value, ifcopenshell.entity_instance):
if isinstance(
S.declaration_by_name(value.is_a()),
ifcopenshell.ifcopenshell_wrapper.entity,
):
# top level entity instances will be checked on their own
pass
else:
# unpack the type instance
check(value[0], S.declaration_by_name(value.is_a()), instance=inst)
for inst in f:
try:
values = list(inst)
except RecursionError as e:
logger.info(str(e))
except Exception as e:
if hasattr(logger, "set_state"):
logger.error(str(e))
else:
logger.error("For instance:\n %s\n%s", inst, e)
continue
entity = S.declaration_by_name(inst.is_a())
attrs = entity.all_attributes()
for i, (attr, val, is_derived) in enumerate(zip(attrs, values, entity.derived())):
if is_derived:
# @todo
pass
else:
check(val, attr.type_of_attribute(), instance=inst)
if hasattr(logger, "set_state"):
logger.set_state("type", "entity_rule")
for R in [r for r in rules if r.SCOPE == "entity"]:
for inst in f.by_type(R.TYPE_NAME):
try:
R()(inst)
except RecursionError as e:
logger.info(str(e))
except Exception as e:
ln = e.__traceback__.tb_next.tb_lineno
pre_annotate_instance(inst)
pre_annotate_attribute(f"{R.TYPE_NAME}.{R.RULE_NAME}")
logger.error(
str(
error(
post_annotate_attribute(f"{R.TYPE_NAME}.{R.RULE_NAME}"),
reverse_compile(source.split("\n")[ln - 1]),
reverse_compile(e.args[0]),
post_annotate_instance(inst),
)
)
)
ifcopenshell.settings.unpack_non_aggregate_inverses = orig
if __name__ == "__main__":
import sys
import json
import logging
import ifcopenshell
from ifcopenshell.validate import json_logger
filenames = [x for x in sys.argv[1:] if not x.startswith("--")]
flags = set(x for x in sys.argv[1:] if x.startswith("--"))
for fn in filenames:
if "--json" in flags:
logger = json_logger()
else:
logger = logging.getLogger("validate")
logger.setLevel(logging.DEBUG)
f = ifcopenshell.open(fn)
run(f, logger)
if "--json" in flags:
print("\n".join(json.dumps(x, default=str) for x in logger.statements))
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,185 @@
@ECHO OFF
:: python bootstrap.py express.bnf > express_parser.py
IF EXIST IFC2X3_TC1.exp (
python express_parser.py IFC2X3_TC1.exp header implementation schema_class definitions
IF EXIST Ifc2x3-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc2x3.cpp txt/header_ifc2x3.txt Ifc2x3.cpp
python cat.py -o ..\..\..\ifcparse\Ifc2x3.h txt/header_ifc2x3.txt Ifc2x3.h
python cat.py -o ..\..\..\ifcparse\Ifc2x3-schema.cpp txt/header_ifc2x3.txt Ifc2x3-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc2x3-definitions.h txt/header_ifc2x3.txt Ifc2x3-definitions.h
) ELSE (
:: v0.5.0
python cat.py -o ..\..\..\ifcparse\Ifc2x3.cpp txt/header_ifc2x3.txt txt/ifndef_ifc4.txt Ifc2x3.cpp txt/endif.txt
python cat.py -o ..\..\..\ifcparse\Ifc2x3.h txt/header_ifc2x3.txt Ifc2x3.h
python cat.py -o ..\..\..\ifcparse\Ifc2x3enum.h txt/header_ifc2x3.txt Ifc2x3enum.h
python cat.py -o ..\..\..\ifcparse\Ifc2x3-latebound.cpp txt/header_ifc2x3.txt txt/ifndef_ifc4.txt Ifc2x3-latebound.cpp txt/endif.txt
python cat.py -o ..\..\..\ifcparse\Ifc2x3-latebound.h txt/header_ifc2x3.txt Ifc2x3-latebound.h
)
del *.cpp *.h
)
IF EXIST IFC4_ADD2TC1.exp (
python express_parser.py IFC4_ADD2TC1.exp header implementation schema_class definitions
IF EXIST Ifc4-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4.cpp txt/header_ifc4.txt Ifc4.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4.h txt/header_ifc4.txt Ifc4.h
python cat.py -o ..\..\..\ifcparse\Ifc4-schema.cpp txt/header_ifc4.txt Ifc4-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4-definitions.h txt/header_ifc4.txt Ifc4-definitions.h
) ELSE (
:: v0.5.0
python cat.py -o ..\..\..\ifcparse\Ifc4.cpp txt/header_ifc4.txt txt/ifdef_ifc4.txt Ifc4.cpp txt/endif.txt
python cat.py -o ..\..\..\ifcparse\Ifc4.h txt/header_ifc4.txt Ifc4.h
python cat.py -o ..\..\..\ifcparse\Ifc4enum.h txt/header_ifc4.txt Ifc4enum.h
python cat.py -o ..\..\..\ifcparse\Ifc4-latebound.cpp txt/header_ifc4.txt txt/ifdef_ifc4.txt Ifc4-latebound.cpp txt/endif.txt
python cat.py -o ..\..\..\ifcparse\Ifc4-latebound.h txt/header_ifc4.txt Ifc4-latebound.h
)
del *.cpp *.h
)
IF EXIST IFC4x1.exp (
python express_parser.py IFC4x1.exp header implementation schema_class definitions
IF EXIST Ifc4x1-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x1.cpp txt/header_ifc4x1.txt Ifc4x1.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x1.h txt/header_ifc4x1.txt Ifc4x1.h
python cat.py -o ..\..\..\ifcparse\Ifc4x1-schema.cpp txt/header_ifc4x1.txt Ifc4x1-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x1-definitions.h txt/header_ifc4x1.txt Ifc4x1-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4x2.exp (
python express_parser.py IFC4x2.exp header implementation schema_class definitions
IF EXIST Ifc4x2-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x2.cpp txt/header_ifc4x2.txt Ifc4x2.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x2.h txt/header_ifc4x2.txt Ifc4x2.h
python cat.py -o ..\..\..\ifcparse\Ifc4x2-schema.cpp txt/header_ifc4x2.txt Ifc4x2-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x2-definitions.h txt/header_ifc4x2.txt Ifc4x2-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4x3_RC1.exp (
python express_parser.py IFC4x3_RC1.exp header implementation schema_class definitions
IF EXIST Ifc4x3_rc1-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc1.cpp txt/header_ifc4x3_rc1.txt Ifc4x3_rc1.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc1.h txt/header_ifc4x3_rc1.txt Ifc4x3_rc1.h
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc1-schema.cpp txt/header_ifc4x3_rc1.txt Ifc4x3_rc1-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc1-definitions.h txt/header_ifc4x3_rc1.txt Ifc4x3_rc1-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4x3_RC2.exp (
python express_parser.py IFC4x3_RC2.exp header implementation schema_class definitions
IF EXIST Ifc4x3_rc2-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc2.cpp txt/header_ifc4x3_rc2.txt Ifc4x3_rc2.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc2.h txt/header_ifc4x3_rc2.txt Ifc4x3_rc2.h
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc2-schema.cpp txt/header_ifc4x3_rc2.txt Ifc4x3_rc2-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc2-definitions.h txt/header_ifc4x3_rc2.txt Ifc4x3_rc2-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4x3_RC3.exp (
python express_parser.py IFC4x3_RC3.exp header implementation schema_class definitions
IF EXIST Ifc4x3_rc3-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc3.cpp txt/header_ifc4x3_rc2.txt Ifc4x3_rc3.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc3.h txt/header_ifc4x3_rc2.txt Ifc4x3_rc3.h
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc3-schema.cpp txt/header_ifc4x3_rc2.txt Ifc4x3_rc3-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc3-definitions.h txt/header_ifc4x3_rc2.txt Ifc4x3_rc3-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4x3_RC4.exp (
python express_parser.py IFC4x3_RC4.exp header implementation schema_class definitions
IF EXIST Ifc4x3_rc4-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc4.cpp txt/header_ifc4x3_rc2.txt Ifc4x3_rc4.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc4.h txt/header_ifc4x3_rc2.txt Ifc4x3_rc4.h
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc4-schema.cpp txt/header_ifc4x3_rc2.txt Ifc4x3_rc4-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_rc4-definitions.h txt/header_ifc4x3_rc2.txt Ifc4x3_rc4-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4X3.exp (
python express_parser.py IFC4X3.exp header implementation schema_class definitions
IF EXIST Ifc4x3-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x3.cpp txt/header_ifc4x3_rc2.txt Ifc4x3.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3.h txt/header_ifc4x3_rc2.txt Ifc4x3.h
python cat.py -o ..\..\..\ifcparse\Ifc4x3-schema.cpp txt/header_ifc4x3_rc2.txt Ifc4x3-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3-definitions.h txt/header_ifc4x3_rc2.txt Ifc4x3-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4X3_TC1.exp (
python express_parser.py IFC4X3_TC1.exp header implementation schema_class definitions
IF EXIST Ifc4x3_tc1-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x3_tc1.cpp txt/header_ifc4x3_tc1.txt Ifc4x3_tc1.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_tc1.h txt/header_ifc4x3_tc1.txt Ifc4x3_tc1.h
python cat.py -o ..\..\..\ifcparse\Ifc4x3_tc1-schema.cpp txt/header_ifc4x3_tc1.txt Ifc4x3_tc1-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_tc1-definitions.h txt/header_ifc4x3_tc1.txt Ifc4x3_tc1-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4X3_ADD1.exp (
python express_parser.py IFC4X3_ADD1.exp header implementation schema_class definitions
IF EXIST Ifc4x3_add1-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x3_add1.cpp txt/header_ifc4x3_add1.txt Ifc4x3_add1.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_add1.h txt/header_ifc4x3_add1.txt Ifc4x3_add1.h
python cat.py -o ..\..\..\ifcparse\Ifc4x3_add1-schema.cpp txt/header_ifc4x3_add1.txt Ifc4x3_add1-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_add1-definitions.h txt/header_ifc4x3_add1.txt Ifc4x3_add1-definitions.h
)
del *.cpp *.h
)
IF EXIST IFC4X3_ADD2.exp (
python express_parser.py IFC4X3_ADD2.exp header implementation schema_class definitions
IF EXIST Ifc4x3_add2-schema.cpp (
:: v0.6.0
python cat.py -o ..\..\..\ifcparse\Ifc4x3_add2.cpp txt/header_ifc4x3_add2.txt Ifc4x3_add2.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_add2.h txt/header_ifc4x3_add2.txt Ifc4x3_add2.h
python cat.py -o ..\..\..\ifcparse\Ifc4x3_add2-schema.cpp txt/header_ifc4x3_add2.txt Ifc4x3_add2-schema.cpp
python cat.py -o ..\..\..\ifcparse\Ifc4x3_add2-definitions.h txt/header_ifc4x3_add2.txt Ifc4x3_add2-definitions.h
)
del *.cpp *.h
)
@@ -0,0 +1,122 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import nodes
import platform
import collections
import pyparsing
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_KeyObject(str):
def __eq__(self, other):
return self.lower() == other.lower()
def __hash__(self):
return hash(self.lower())
class OrderedCaseInsensitiveDict(collections.OrderedDict):
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))
def __delitem__(self, key):
return collections.OrderedDict.__delitem__(self, OrderedCaseInsensitiveDict_KeyObject(key))
class Schema:
def is_enumeration(self, v):
return str(v) in self.enumerations
def is_select(self, v):
return str(v) in self.selects
def is_simpletype(self, v):
return str(v) in self.simpletypes
def is_type(self, v):
return str(v) in self.types
def is_entity(self, v):
return str(v) in self.entities
def __len__(self):
return len(self.keys)
def __iter__(self):
return iter(self.keys)
def __getitem__(self, key):
return self.all_declarations[OrderedCaseInsensitiveDict_KeyObject(key)]
def __init__(self, parsetree: pyparsing.ParseResults):
self.tree = parsetree
schema = next(iter(parsetree.syntax[0]))
self.name = schema.simple_id
schema_declarations = list(schema.schema_body[0])
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"
]
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()
}
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, nodes.SimpleType, nodes.NamedType
)
assert len(self.enumerations) + len(self.selects) + len(self.simpletypes) == len(self.types)
@@ -0,0 +1,588 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import operator
import re
import nodes
import codegen
import templates
import mapping
from collections import defaultdict
try:
import ifcopenshell.ifcopenshell_wrapper as w
except:
pass
class LateBoundSchemaInstantiator:
def __init__(self, schema_name):
self.schema_name = schema_name
self.schema_name_title = schema_name.capitalize()
self.declarations = {}
self.names = []
# We need to make sure anonymous types are not gc'ed.
self.cache = []
def aggregation_type(self, aggr_type, bound1, bound2, decl_type):
self.cache.append(
w.aggregation_type(getattr(w.aggregation_type, aggr_type + "_type"), bound1, bound2, decl_type)
)
return self.cache[-1]
def simple_type(self, type):
self.cache.append(w.simple_type(getattr(w.simple_type, type + "_type")))
return self.cache[-1]
def named_type(self, type):
self.cache.append(w.named_type(self.declarations[str(type)]))
return self.cache[-1]
def declare(self, definition_type, name):
self.names.append(str(name))
def begin_schema(self):
self.names.sort(key=str.lower)
def typedef(self, name, declared_type):
index_in_schema = self.names.index(str(name))
self.declarations[str(name)] = w.type_declaration(name, index_in_schema, declared_type)
def enumeration(self, name, enum):
schema_name = self.schema_name
index_in_schema = self.names.index(str(name))
self.declarations[str(name)] = w.enumeration_type(name, index_in_schema, enum.values)
def entity(self, name, type):
index_in_schema = self.names.index(str(name))
supertype = None if len(type.supertypes) == 0 else self.declarations[str(type.supertypes[0])]
self.declarations[str(name)] = w.entity(name, type.abstract, index_in_schema, supertype)
def select(self, name, type):
index_in_schema = self.names.index(str(name))
children = [self.declarations[str(v)] for v in type.values]
self.declarations[str(name)] = w.select_type(name, index_in_schema, children)
def entity_attributes(self, name, attribute_definitions, is_derived):
attributes = []
for attr_name, decl_type, optional in attribute_definitions:
attributes.append(w.attribute(attr_name, decl_type, optional))
self.declarations[str(name)].set_attributes(attributes, is_derived)
self.cache.extend(attributes)
def inverse_attributes(self, name, inv_attrs):
attributes = []
for attr_name, aggr_type, bound1, bound2, entity_ref, attribute_entity, attribute_entity_index in inv_attrs:
en = self.declarations[str(entity_ref)]
attributes.append(
w.inverse_attribute(
attr_name,
getattr(w.inverse_attribute, aggr_type + "_type"),
bound1,
bound2,
en,
en.attributes()[attribute_entity_index],
)
)
self.cache.extend(attributes)
self.declarations[str(name)].set_inverse_attributes(attributes)
def entity_subtypes(self, name, tys):
self.declarations[str(name)].set_subtypes([self.declarations[str(v)] for v in tys])
def finalize(self, can_be_instantiated_set, override_schema_name=None):
self.schema = w.schema_definition(
override_schema_name or self.schema_name, list(self.declarations.values()), None
)
def disown(self):
for elem in self.cache + list(self.declarations.values()):
elem.this.disown()
class string_pool:
def __init__(self, fn):
self.di = {}
self.fn = fn
def append(self, v):
def _():
if i := self.di.get(v):
return i
else:
i = len(self.di)
self.di[v] = i
return i
return self.fn(_())
def __iter__(self):
return iter(self.di.keys())
class EarlyBoundCodeWriter:
def __init__(self, schema_name):
self.strings = string_pool(lambda i: "strings[%d]" % i)
self.schema_name = schema_name
self.schema_name_title = schema_name.capitalize()
self.statements = [
"",
'#include "../ifcparse/IfcSchema.h"',
'#include "../ifcparse/%(schema_name_title)s.h"' % self.__dict__,
"#include <string>",
"",
"using namespace std::string_literals;",
"using namespace IfcParse;",
"",
]
self.names = []
def aggregation_type(self, aggr_type, bound1, bound2, decl_type):
return (
"new aggregation_type(aggregation_type::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(decl_type)s)"
% locals()
)
def simple_type(self, type):
return "new simple_type(simple_type::%s_type)" % type
def named_type(self, type):
return "new named_type(%s_%s_type)" % (self.schema_name, type)
def declare(self, definition_type, name):
schema_name = self.schema_name
# self.statements.append("%(definition_type)s* %(schema_name)s_%(name)s_type = 0;" % locals())
self.names.append(name)
def begin_schema(self):
self.names.sort(key=str.lower)
schema_name = self.schema_name.upper()
num_names = len(self.names)
self.statements.append("declaration* %(schema_name)s_types[%(num_names)d] = {nullptr};" % locals())
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("IfcParse::schema_definition* %s_populate_schema() {" % self.schema_name.upper())
self.statements.append("{string_pool_placeholder}")
def typedef(self, name, declared_type):
schema_name = self.schema_name.upper()
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);"
% locals()
)
def enumeration(self, name, enum):
schema_name = self.schema_name.upper()
index_in_schema = self.names.index(name)
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});"
% locals()
)
def entity(self, name, type):
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]))
)
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);"
% locals()
)
def select(self, name, type):
schema_name = self.schema_name.upper()
index_in_schema = self.names.index(name)
ref = self.strings.append(name)
items = ",".join(
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});"
% 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()
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()
)
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()
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()
attributes = ",".join(_())
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()
)
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());"
% locals()
)
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.extend(
(
"static std::unique_ptr<schema_definition> schema;",
"",
"void %s::clear_schema() {" % schema_name_title,
" schema.reset();",
"}",
"",
)
)
self.statements.extend(
(
"const schema_definition& %s::get_schema() {" % schema_name_title,
" if (!schema) {",
" schema.reset(%(schema_name)s_populate_schema());" % locals(),
" }",
" return *schema;",
"}",
"",
"",
)
)
def can_be_instantiated(idx_name):
name = idx_name[1]
return name in can_be_instantiated_set
instance_mapping = """switch(decl->index_in_schema()) {
%s
default: throw IfcParse::IfcException(decl->name() + " cannot be instantiated");
}
""" % "\n ".join(
map(
lambda tup: ("case %%d: return new ::%s::%%s(std::move(data));" % schema_name_title) % tup,
filter(can_be_instantiated, enumerate(self.names)),
)
)
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()
""
self.statements[self.statements.index("{string_pool_placeholder}")] = """
const std::string strings[] = {%s};
""" % ",".join(map(lambda s: '"%s"s' % s, self.strings))
def __str__(self):
return "\n".join(self.statements)
class SchemaClass(codegen.Base):
def __init__(self, mapping: mapping.Mapping, code=EarlyBoundCodeWriter):
class UnmetDependenciesException(Exception):
pass
schema_name = mapping.schema.name
self.schema_name = schema_name_title = schema_name.capitalize()
declared_types = []
x = code(schema_name)
def transform_to_indexed(fn):
def wrapper(*args, **kwargs):
schema_name_upper = mapping.schema.name.upper()
declared_type = fn(*args, **kwargs)
if "simple_type" in declared_type:
pass
else:
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", "")
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()
)
return declared_type
return wrapper if code == EarlyBoundCodeWriter else fn
@transform_to_indexed
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)
bound1, bound2 = map(make_bound, (type.bounds.lower, type.bounds.upper))
decl_type = get_declared_type(type.type, emitted_names)
return x.aggregation_type(aggr_type, bound1, bound2, decl_type)
elif isinstance(type, nodes.BinaryType):
return x.simple_type("binary")
elif isinstance(type, nodes.StringType):
return x.simple_type("string")
elif isinstance(type, str):
if mapping.schema.is_type(type) or mapping.schema.is_entity(type):
if emitted_names is None or type.lower() in emitted_names:
return x.named_type(type)
else:
raise UnmetDependenciesException(type)
else:
return x.simple_type(type)
else:
raise ValueError("No declared type for <%r>" % type)
def find_inverse_name_and_index(entity_name, attribute_name):
entity_name_orig = entity_name
attributes_per_subtype = []
while True:
entity = mapping.schema.entities[entity_name]
attr_names = list(map(operator.attrgetter("name"), entity.attributes))
if len(attr_names):
attributes_per_subtype.append((entity_name, attr_names))
if len(entity.supertypes) != 1:
break
entity_name = entity.supertypes[0]
index = 0
for et, attrs in attributes_per_subtype[::-1]:
try:
return et, attrs.index(attribute_name)
except:
pass
else:
raise Exception("No attribute named %s.%s" % (entity_name_orig, attribute_name))
collections_by_type = (
("entity", mapping.schema.entities),
("type_declaration", mapping.schema.simpletypes),
("select_type", mapping.schema.selects),
("enumeration_type", mapping.schema.enumerations),
)
for definition_type, collection in collections_by_type:
for name in collection.keys():
x.declare(definition_type, name)
declarations_by_index = []
x.begin_schema()
emitted = set()
len_to_emit = len(mapping.schema) - len(mapping.schema.rules) - len(mapping.schema.functions)
def write_simpletype(schema_name, name, type):
try:
declared_type = get_declared_type(type, emitted)
except UnmetDependenciesException:
# @todo?
# print("Unmet", repr(name))
return False
x.typedef(name, declared_type)
def write_enumeration(schema_name, name, enum):
x.enumeration(name, enum)
def write_entity(schema_name, name, type):
if len(type.supertypes) == 0 or set(map(lambda s: s.lower(), type.supertypes)) < emitted:
x.entity(name, type)
else:
return False
def write_select(schema_name, name, type):
if set(map(lambda s: str(s).lower(), type.values)) < emitted:
x.select(name, type)
else:
return False
def write(name):
if mapping.schema.is_simpletype(name):
fn = write_simpletype
elif mapping.schema.is_enumeration(name):
fn = write_enumeration
elif mapping.schema.is_entity(name):
fn = write_entity
elif mapping.schema.is_select(name):
fn = write_select
elif name in mapping.schema.rules:
return
elif name in mapping.schema.functions:
return
decl = mapping.schema[name]
if isinstance(decl, nodes.TypeDeclaration):
decl = decl.type
return fn(schema_name, name, decl) is not False
while len(emitted) < len_to_emit:
for name in mapping.schema:
if name.lower() in emitted:
continue
if write(name):
emitted.add(name.lower())
declarations_by_index.append(name)
num_declarations = len(emitted)
for name, type in mapping.schema.entities.items():
derived = set(mapping.derived_in_supertype(type))
attribute_names = list(map(operator.attrgetter("name"), mapping.arguments(type)))
is_derived = [b in derived for b in attribute_names]
attribute_definitions = []
for attr in type.attributes:
decl_type = get_declared_type(attr.type)
attribute_definitions.append((attr.name, decl_type, attr.optional))
x.entity_attributes(name, attribute_definitions, is_derived)
for name, type in mapping.schema.entities.items():
if type.inverse:
inv_attrs = []
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))
else:
bound1, bound2 = -1, -1
attr_name, aggr_type, entity_ref = attr.name, attr.type, attr.entity
if aggr_type is None:
aggr_type = "unspecified"
attribute_entity, attribute_entity_index = find_inverse_name_and_index(entity_ref, attr.attribute)
inv_attrs.append(
(attr_name, aggr_type, bound1, bound2, entity_ref, attribute_entity, attribute_entity_index)
)
x.inverse_attributes(name, inv_attrs)
subtypes = defaultdict(list)
for name, type in mapping.schema.entities.items():
for ty in type.supertypes:
subtypes[ty].append(name)
for name, tys in subtypes.items():
x.entity_subtypes(name, tys)
can_be_instantiated_set = set(
list(mapping.schema.entities.keys())
+ list(mapping.schema.simpletypes.keys())
+ list(mapping.schema.enumerations.keys())
)
x.finalize(can_be_instantiated_set)
self.str = str(x)
self.file_name = "%s-schema.cpp" % self.schema_name
self.code = x
def __repr__(self):
return self.str
Generator = SchemaClass
@@ -0,0 +1,275 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
header = """
#ifndef %(schema_name_upper)s_H
#define %(schema_name_upper)s_H
#include <string>
#include <vector>
#include <boost/optional.hpp>
#include "../ifcparse/ifc_parse_api.h"
#include "../ifcparse/aggregate_of_instance.h"
#include "../ifcparse/IfcBaseClass.h"
#include "../ifcparse/IfcSchema.h"
#include "../ifcparse/IfcException.h"
#include "../ifcparse/Argument.h"
struct %(schema_name)s {
IFC_PARSE_API static const IfcParse::schema_definition& get_schema();
IFC_PARSE_API static void clear_schema();
static const char* const Identifier;
// Forward definitions
%(forward_definitions)s
%(declarations)s
%(class_definitions)s
};
#endif
"""
enum_header = """
#ifndef %(schema_name_upper)sENUM_H
#define %(schema_name_upper)sENUM_H
#include "../ifcparse/ifc_parse_api.h"
#include <string>
#include <boost/optional.hpp>
#endif
"""
lb_header = """"""
implementation = """
#include "../ifcparse/%(schema_name)s.h"
#include "../ifcparse/IfcSchema.h"
#include "../ifcparse/IfcException.h"
#include "../ifcparse/IfcFile.h"
#include <map>
const char* const %(schema_name)s::Identifier = "%(schema_name_upper)s";
using namespace IfcParse;
// External definitions
%(external_definitions)s
%(enumeration_functions)s
%(simple_type_impl)s
%(entity_implementations)s
"""
lb_implementation = """"""
entity_descriptor = """ current = entity_descriptor_map[Type::%(type)s] = new IfcEntityDescriptor(Type::%(type)s,%(parent_statement)s);
%(entity_descriptor_attributes)s"""
entity_descriptor_parent = "entity_descriptor_map.find(Type::%(type)s)->second"
entity_descriptor_attribute_without_entity = ' current->add("%(name)s",%(optional)s,%(type)s);'
entity_descriptor_attribute_with_entity = ' current->add("%(name)s",%(optional)s,%(type)s,Type::%(entity_name)s);'
enumeration_descriptor = """ values.clear(); values.reserve(128);
%(enumeration_descriptor_values)s
enumeration_descriptor_map[Type::%(type)s] = new IfcEnumerationDescriptor(Type::%(type)s, values);"""
enumeration_descriptor_value = ' values.push_back("%(name)s");'
derived_field_statement = " {std::set<int> idxs; %(statements)sderived_map[Type::%(type)s] = idxs;}"
derived_field_statement_attrs = "idxs.insert(%d); "
simpletype = """%(documentation)s
class IFC_PARSE_API %(name)s : %(superclass)s {
public:
virtual const IfcParse::type_declaration& declaration() const;
static const IfcParse::type_declaration& Class();
explicit %(name)s (IfcEntityInstanceData&& e);
%(name)s (%(type)s v);
operator %(type)s() const;
};
"""
simpletype_impl_comment = "// Function implementations for %(name)s"
simpletype_impl_argument_type = 'if (i == 0) { return %(attr_type)s; } else { throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); }'
simpletype_impl_argument = "return get_attribute_value(i);"
simpletype_impl_is_with_supertype = "return v == %(class_name)s_type || %(superclass)s::is(v);"
simpletype_impl_is_without_supertype = "return v == %(class_name)s_type;"
simpletype_impl_type = "return *((IfcParse::type_declaration*)%(schema_name_upper)s_types[%(index_in_schema)d]);"
simpletype_impl_class = "return *((IfcParse::type_declaration*)%(schema_name_upper)s_types[%(index_in_schema)d]);"
simpletype_impl_explicit_constructor = "data_ = e;"
simpletype_impl_constructor = (
"data_ = new IfcEntityInstanceData(%(schema_name_upper)s_types[%(index_in_schema)d]); set_attribute_value(0, v);"
)
simpletype_impl_constructor_templated = "data_ = new IfcEntityInstanceData(%(schema_name_upper)s_types[%(index_in_schema)d]); set_attribute_value(0, v->generalize());"
simpletype_impl_cast = "return get_attribute_value(0);"
simpletype_impl_cast_templated = (
"aggregate_of_instance::ptr es = get_attribute_value(0); return es->as< %(underlying_type)s >();"
)
simpletype_impl_declaration = "return *((IfcParse::type_declaration*)%(schema_name_upper)s_types[%(index_in_schema)d]);"
select_virtual = """%(documentation)s
class IFC_PARSE_API %(name)s : public virtual IfcUtil::IfcBaseInterface {
public:
static const IfcParse::select_type& Class();
typedef aggregate_of< %(name)s > list;
};
"""
select_plain = """%(documentation)s
typedef IfcUtil::IfcBaseClass %(name)s;
"""
enumeration = """class IFC_PARSE_API %(name)s : public IfcUtil::IfcBaseType {
%(documentation)s
public:
typedef enum {%(values)s} Value;
static const char* ToString(Value v);
static Value FromString(const std::string& s);
virtual const IfcParse::enumeration_type& declaration() const;
static const IfcParse::enumeration_type& Class();
%(name)s (IfcEntityInstanceData&& e);
%(name)s (Value v);
%(name)s (const std::string& v);
operator Value() const;
};
"""
entity = """%(documentation)s
class IFC_PARSE_API %(name)s : %(superclass)s {
public:
%(attributes)s %(inverse)s virtual const IfcParse::entity& declaration() const;
static const IfcParse::entity& Class();
%(name)s (IfcEntityInstanceData&& e);
%(name)s (%(constructor_arguments)s);
typedef aggregate_of< %(name)s > list;
};
"""
select_function = """
const IfcParse::select_type& %(schema_name)s::%(name)s::Class() { return *((IfcParse::select_type*)%(schema_name_upper)s_types[%(index_in_schema)d]); }
"""
enumeration_function = """
const IfcParse::enumeration_type& %(schema_name)s::%(name)s::declaration() const { return *((IfcParse::enumeration_type*)%(schema_name_upper)s_types[%(index_in_schema)d]); }
const IfcParse::enumeration_type& %(schema_name)s::%(name)s::Class() { return *((IfcParse::enumeration_type*)%(schema_name_upper)s_types[%(index_in_schema)d]); }
%(schema_name)s::%(name)s::%(name)s(IfcEntityInstanceData&& e)
: IfcBaseType(std::move(e))
{}
%(schema_name)s::%(name)s::%(name)s(Value v) {
set_attribute_value(0, EnumerationReference(&declaration(), static_cast<size_t>(v)));
}
%(schema_name)s::%(name)s::%(name)s(const std::string& v) {
set_attribute_value(0, EnumerationReference(&declaration(), declaration().lookup_enum_offset(v)));
}
const char* %(schema_name)s::%(name)s::ToString(Value v) {
return %(schema_name)s::%(name)s::%(name)s::Class().lookup_enum_value((size_t)v);
}
%(schema_name)s::%(name)s::Value %(schema_name)s::%(name)s::FromString(const std::string& s) {
return (%(schema_name)s::%(name)s::Value) %(schema_name)s::%(name)s::%(name)s::Class().lookup_enum_offset(s);
}
%(schema_name)s::%(name)s::operator %(schema_name)s::%(name)s::Value() const {
return (%(schema_name)s::%(name)s::Value) ((EnumerationReference) get_attribute_value(0)).index();
}
"""
entity_implementation = """// Function implementations for %(name)s
%(attributes)s
%(inverse)s
const IfcParse::entity& %(schema_name)s::%(name)s::declaration() const { return *((IfcParse::entity*)%(schema_name_upper)s_types[%(index_in_schema)d]); }
const IfcParse::entity& %(schema_name)s::%(name)s::Class() { return *((IfcParse::entity*)%(schema_name_upper)s_types[%(index_in_schema)d]); }
%(schema_name)s::%(name)s::%(name)s(IfcEntityInstanceData&& e) : %(superclass)s { }
%(schema_name)s::%(name)s::%(name)s(%(constructor_arguments)s) : %(superclass_num_attrs)s { %(constructor_implementation)s; populate_derived(); }
"""
# 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"
function = "%(return_type)s %(schema_name)s::%(class_name)s::%(name)s(%(arguments)s) { %(body)s }"
const_function = "%(return_type)s %(schema_name)s::%(class_name)s::%(name)s(%(arguments)s) const { %(body)s }"
constructor = "%(schema_name)s::%(class_name)s::%(class_name)s(%(arguments)s) { %(body)s }"
constructor_single_initlist = (
"%(schema_name)s::%(class_name)s::%(class_name)s(%(arguments)s) : %(superclass)s(%(superclass_init)s) { %(body)s }"
)
cast_function = "%(schema_name)s::%(class_name)s::operator %(return_type)s() const { %(body)s }"
array_type = "std::vector< %(instance_type)s > /*[%(lower)s:%(upper)s]*/"
nested_array_type = "std::vector< std::vector< %(instance_type)s > >"
list_type = "aggregate_of< %(instance_type)s >::ptr"
list_list_type = "aggregate_of_aggregate_of< %(instance_type)s >::ptr"
untyped_list = "aggregate_of_instance::ptr"
inverse_attr = "aggregate_of< %(entity)s >::ptr %(name)s() const; // INVERSE %(entity)s::%(attribute)s"
enum_from_string_stmt = ' if (s == "%(value)s") return ::%(schema_name)s::%(name)s::%(short_name)s_%(value)s;'
schema_entity_stmt = " case Type::%(name)s: return new %(name)s(e); break;"
string_map_statement = ' string_map["%(uppercase_name)s"%(padding)s] = Type::%(name)s;'
parent_type_stmt = " if(v==%(name)s%(padding)s) { return %(parent)s; }"
parent_type_test = " || %s::is(v)"
optional_attr_stmt = "return !get_attribute_value(%(index)d).isNull();"
get_attr_stmt = "%(null_check)s %(non_optional_type)s v = get_attribute_value(%(index)d); return v;"
get_attr_stmt_enum = "%(null_check)s return %(non_optional_type)s::FromString(get_attribute_value(%(index)d));"
get_attr_stmt_entity = "%(null_check)s return ((IfcUtil::IfcBaseClass*)(get_attribute_value(%(index)d)))->as<%(non_optional_type_no_pointer)s>(true);"
get_attr_stmt_array = "%(null_check)s aggregate_of_instance::ptr es = get_attribute_value(%(index)d); return es->as< %(list_instance_type)s >();"
get_attr_stmt_nested_array = "%(null_check)s aggregate_of_aggregate_of_instance::ptr es = get_attribute_value(%(index)d); return es->as< %(list_instance_type)s >();"
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<IfcUtil::IfcBaseClass>());%(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>() : (IfcUtil::IfcBaseClass*) nullptr);"
constructor_stmt_optional = " if (%(name)s) {%(stmt)s }"
inverse_implementation = ' inverse_map[Type::%(type)s].insert(std::make_pair("%(name)s", std::make_pair(Type::%(related_type)s, %(index)d)));'
def multi_line_comment(li):
return ("/// %s" % ("\n/// ".join(li))) if len(li) else ""
@@ -0,0 +1 @@
#endif
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC2X3_TC1.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC4.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC4x1.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC4x2.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC4X3_ADD1.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC4X3_ADD2.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC4x3_RC1.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC4x3_RC2.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,25 @@
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* This file has been generated from IFC4X3_TC1.exp. Do not make modifications *
* but instead modify the python script that has been used to generate this. *
* *
********************************************************************************/
@@ -0,0 +1,2 @@
#ifdef USE_IFC4
@@ -0,0 +1,2 @@
#ifdef USE_IFC4X1
@@ -0,0 +1,2 @@
#ifndef USE_IFC4
@@ -0,0 +1,2 @@
#ifndef USE_IFC4X1
@@ -0,0 +1,2 @@
#ifndef USE_IFC4X1