mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Feature: express rules (#2662)
* temp reasonable state, missing rule_head contents * better state, with init() and rule_id, but some missing parameters * performance; embed root in dict; fix lookup * Add ast_utils * Fixes related to listnode and typo * First steps with code gen * Finalize first steps towards working code generation * Backwards compat * Rule execution, error formatting, test cases * Where rule: support indexing, more expressions, test plane angle measure * positive length measure test cases * Venture into entity rules, test actor role * Fixed for not unary op and address test cases * Test group qualifier and air terminal type * typeof() implementation and test annotation curve occ * Flexible handling of expression, type set intersection, IfcAnnotationSurface test cases * Functions, derived attrs, fix branch order, allow to terminate branches, apply -1 to index * Implement express query(), test cases for 2x3 bspline curve * Function args, if-else, repeat, entity instance construction, cshape test cases * Multiple function call arguments and other small but influential changes. IfcDotProject() almost working except for variable case * Make lowercase and fix assignment to qualified lhs * Entity instance schema without file, aggregate assignment workaround, range fix, lowercase instance locals, extruded area direction test cases * Enumeration item handling, query() robustness, arbitrary profile test cases * Empty aggregate_initializer, xor and instance equality, nested expression fix in IfcCrossProduct, axis2-3d test cases * Numeric stable sort with indices greater then 10, get ast node parent, redeclared derived support, nested expression robustness, define entity functions, case statements, xor, mod, bool literals, nvl, unknown, conversion based unit test cases * Safeguard for schema name case norm, case norm query variable_id * free instance comparison, supertype serialization fix, include inherited attributes in locals, proper elif-else in case stmt, loindex, shape rep test cases * Union operator on set, retain general_aggr_types in parse tree, test cases for property set * Escape stmt, rule locals and statements * Return calculated values for redeclared derived attributes * fix for rules without stmt, add derived and inverse attributes to locals, filter unused locals, wrap exists() arg with lambda to catch indexerror, support repeated aggr init element, typeof() none check, enum namespace uppercase * Regen 2x3 rules * Rule test filter only on basename * Update fixtures for compliance with full body of rules * Add rule support to ifcopenshell.validate * Implement usedin() function, test cases for IfcWallSC MLS * blength * Run code generation on all schemas * Try to eliminate runtime pyparsing dep * Try to eliminate runtime pyparsing dep
This commit is contained in:
@@ -22,6 +22,7 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import functools
|
||||
import importlib
|
||||
import numbers
|
||||
import itertools
|
||||
|
||||
@@ -33,7 +34,7 @@ except ImportError as e:
|
||||
logging = type("logger", (object,), {"exception": staticmethod(lambda s: print(s))})
|
||||
|
||||
|
||||
def set_derived_atribute(*args):
|
||||
def set_derived_attribute(*args):
|
||||
raise TypeError("Unable to set derived attribute")
|
||||
|
||||
|
||||
@@ -73,7 +74,7 @@ def register_schema_attributes(schema):
|
||||
|
||||
# resolve to actual functions in wrapper
|
||||
functions = [
|
||||
set_derived_atribute
|
||||
set_derived_attribute
|
||||
if mname == "setArgumentAsDerived"
|
||||
else getattr(ifcopenshell_wrapper.entity_instance, mname)
|
||||
for mname in fn_names
|
||||
@@ -123,17 +124,30 @@ class entity_instance(object):
|
||||
INVALID, FORWARD, INVERSE = range(3)
|
||||
attr_cat = self.wrapped_data.get_attribute_category(name)
|
||||
if attr_cat == FORWARD:
|
||||
return entity_instance.wrap_value(
|
||||
self.wrapped_data.get_argument(
|
||||
self.wrapped_data.get_argument_index(name)
|
||||
),
|
||||
self.wrapped_data.file,
|
||||
)
|
||||
idx = self.wrapped_data.get_argument_index(name)
|
||||
if _method_dict[self.is_a(True)][idx] != set_derived_attribute:
|
||||
# A bit ugly, but we fall through to derived attribute handling below
|
||||
return entity_instance.wrap_value(
|
||||
self.wrapped_data.get_argument(idx), self.wrapped_data.file
|
||||
)
|
||||
elif attr_cat == INVERSE:
|
||||
return entity_instance.wrap_value(
|
||||
self.wrapped_data.get_inverse(name), self.wrapped_data.file
|
||||
)
|
||||
else:
|
||||
return entity_instance.wrap_value(self.wrapped_data.get_inverse(name), self.wrapped_data.file)
|
||||
|
||||
# derived attribute perhaps?
|
||||
schema_name = self.wrapped_data.is_a(True).split('.')[0]
|
||||
rules = importlib.import_module(f"ifcopenshell.express.rules.{schema_name}")
|
||||
def yield_supertypes():
|
||||
decl = ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(self.is_a())
|
||||
while decl:
|
||||
yield decl.name()
|
||||
decl = decl.supertype()
|
||||
|
||||
for sty in yield_supertypes():
|
||||
fn = getattr(rules, f"calc_{sty}_{name}", None)
|
||||
if fn:
|
||||
return fn(self)
|
||||
|
||||
if attr_cat != FORWARD:
|
||||
raise AttributeError(
|
||||
"entity instance of type '%s' has no attribute '%s'"
|
||||
% (self.wrapped_data.is_a(True), name)
|
||||
@@ -218,7 +232,7 @@ class entity_instance(object):
|
||||
method = self.method_list[idx]
|
||||
|
||||
if value is None:
|
||||
if method is not set_derived_atribute:
|
||||
if method is not set_derived_attribute:
|
||||
self.wrapped_data.setArgumentAsNull(idx)
|
||||
else:
|
||||
self.method_list[idx](
|
||||
@@ -275,17 +289,22 @@ class entity_instance(object):
|
||||
def __eq__(self, other):
|
||||
if not isinstance(self, type(other)):
|
||||
return False
|
||||
# Proper entity instances have a stable identity by means of the numeric
|
||||
# step id. Selected type instances (such as IfcPropertySingleValue.NominalValue
|
||||
# always have id=0, so we compare <type, value, file pointer>
|
||||
if self.id():
|
||||
return self.wrapped_data == other.wrapped_data
|
||||
elif None in (self.wrapped_data.file, other.wrapped_data.file):
|
||||
# when not added to a file, we can only compare attribute values
|
||||
# and we need this for where rule evaluation
|
||||
return self.get_info(recursive=True, include_identifier=False) == other.get_info(recursive=True, include_identifier=False)
|
||||
else:
|
||||
return (self.is_a(), self[0], self.wrapped_data.file_pointer()) == (
|
||||
other.is_a(),
|
||||
other[0],
|
||||
other.wrapped_data.file_pointer(),
|
||||
)
|
||||
# Proper entity instances have a stable identity by means of the numeric
|
||||
# step id. Selected type instances (such as IfcPropertySingleValue.NominalValue
|
||||
# always have id=0, so we compare <type, value, file pointer>
|
||||
if self.id():
|
||||
return self.wrapped_data == other.wrapped_data
|
||||
else:
|
||||
return (self.is_a(), self[0], self.wrapped_data.file_pointer()) == (
|
||||
other.is_a(),
|
||||
other[0],
|
||||
other.wrapped_data.file_pointer(),
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
# Proper entity instances have a stable identity by means of the numeric
|
||||
|
||||
@@ -29,11 +29,9 @@ 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)
|
||||
|
||||
import express_parser
|
||||
import schema_class
|
||||
import ifcopenshell.ifcopenshell_wrapper
|
||||
|
||||
|
||||
def parse(fn):
|
||||
import express_parser
|
||||
import schema_class
|
||||
mapping = express_parser.parse(fn)
|
||||
return schema_class.SchemaClass(mapping, schema_class.LateBoundSchemaInstantiator).code
|
||||
|
||||
@@ -151,6 +151,8 @@ actions = {
|
||||
"string_type": "StringType",
|
||||
"named_types": "NamedType",
|
||||
"simple_types": "SimpleType",
|
||||
"function_decl": "FunctionDeclaration",
|
||||
"rule_decl": "RuleDeclaration",
|
||||
}
|
||||
|
||||
to_emit = set(id for id, expr in express)
|
||||
@@ -192,7 +194,9 @@ for id in to_emit:
|
||||
if id in to_combine:
|
||||
stmt = "Suppress%s" % stmt
|
||||
if id not in no_action and not isinstance(expr.contents, Keyword):
|
||||
node_type = "ListNode" if "ZeroOrMore" in stmt else "Node"
|
||||
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)
|
||||
)
|
||||
|
||||
@@ -16,6 +16,18 @@
|
||||
# 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(object):
|
||||
"""
|
||||
|
||||
@@ -152,7 +152,7 @@ def parse(fn):
|
||||
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("abstract") + ~CaselessKeyword("reference") + ~CaselessKeyword("pi") + ~CaselessKeyword("andor") + ~CaselessKeyword("loindex") + ~CaselessKeyword("aggregate") + ~CaselessKeyword("self") + ~CaselessKeyword("length") + ~CaselessKeyword("optional") + ~CaselessKeyword("for") + ~CaselessKeyword("end") + ~CaselessKeyword("local") + ~CaselessKeyword("true") + ~CaselessKeyword("logical") + ~CaselessKeyword("constant") + ~CaselessKeyword("nvl") + ~CaselessKeyword("bag") + ~CaselessKeyword("repeat") + ~CaselessKeyword("boolean") + ~CaselessKeyword("otherwise") + ~CaselessKeyword("type") + ~CaselessKeyword("typeof") + ~CaselessKeyword("alias") + ~CaselessKeyword("in") + ~CaselessKeyword("mod") + ~CaselessKeyword("escape") + ~CaselessKeyword("or") + ~CaselessKeyword("of") + ~CaselessKeyword("like") + ~CaselessKeyword("value_unique") + ~CaselessKeyword("tan") + ~CaselessKeyword("oneof") + ~CaselessKeyword("log") + ~CaselessKeyword("schema") + ~CaselessKeyword("fixed") + ~CaselessKeyword("by") + ~CaselessKeyword("integer") + ~CaselessKeyword("div") + ~CaselessKeyword("log10") + ~CaselessKeyword("not") + ~CaselessKeyword("skip") + ~CaselessKeyword("odd") + ~CaselessKeyword("return") + ~CaselessKeyword("end_subtype_constraint") + ~CaselessKeyword("end_alias") + ~CaselessKeyword("remove") + ~CaselessKeyword("unknown") + ~CaselessKeyword("end_repeat") + ~CaselessKeyword("enumeration") + ~CaselessKeyword("query") + ~CaselessKeyword("function") + ~CaselessKeyword("list") + ~CaselessKeyword("end_local") + ~CaselessKeyword("cos") + ~CaselessKeyword("atan") + ~CaselessKeyword("hibound") + ~CaselessKeyword("rolesof") + ~CaselessKeyword("end_function") + ~CaselessKeyword("abs") + ~CaselessKeyword("renamed") + ~CaselessKeyword("select") + ~CaselessKeyword("end_if") + ~CaselessKeyword("case") + ~CaselessKeyword("sizeof") + ~CaselessKeyword("var") + ~CaselessKeyword("end_case") + ~CaselessKeyword("acos") + ~CaselessKeyword("supertype") + ~CaselessKeyword("then") + ~CaselessKeyword("inverse") + ~CaselessKeyword("hiindex") + ~CaselessKeyword("false") + ~CaselessKeyword("generic") + ~CaselessKeyword("as") + ~CaselessKeyword("use") + ~CaselessKeyword("end_entity") + ~CaselessKeyword("rule") + ~CaselessKeyword("derive") + ~CaselessKeyword("set") + ~CaselessKeyword("subtype") + ~CaselessKeyword("unique") + ~CaselessKeyword("subtype_constraint") + ~CaselessKeyword("where") + ~CaselessKeyword("until") + ~CaselessKeyword("usedin") + ~CaselessKeyword("value") + ~CaselessKeyword("array") + ~CaselessKeyword("sqrt") + ~CaselessKeyword("value_in") + ~CaselessKeyword("to") + ~CaselessKeyword("xor") + ~CaselessKeyword("sin") + ~CaselessKeyword("while") + ~CaselessKeyword("with") + ~CaselessKeyword("string") + ~CaselessKeyword("total_over") + ~CaselessKeyword("binary") + ~CaselessKeyword("exp") + ~CaselessKeyword("and") + ~CaselessKeyword("number") + ~CaselessKeyword("generic_entity") + ~CaselessKeyword("const_e") + ~CaselessKeyword("end_type") + ~CaselessKeyword("log2") + ~CaselessKeyword("lobound") + ~CaselessKeyword("end_procedure") + ~CaselessKeyword("end_schema") + ~CaselessKeyword("end_constant") + ~CaselessKeyword("procedure") + ~CaselessKeyword("else") + ~CaselessKeyword("end_rule") + ~CaselessKeyword("if") + ~CaselessKeyword("based_on") + ~CaselessKeyword("exists") + ~CaselessKeyword("asin") + ~CaselessKeyword("blength") + ~CaselessKeyword("entity") + ~CaselessKeyword("from") + ~CaselessKeyword("format") + ~CaselessKeyword("insert") + ~CaselessKeyword("begin") + ~CaselessKeyword("extensible") + ~CaselessKeyword("real") + originalTextFor(Combine((letter + ZeroOrMore((letter | digit | CaselessLiteral("_"))))))("simple_id")
|
||||
simple_id = ~CaselessKeyword("bag") + ~CaselessKeyword("lobound") + ~CaselessKeyword("aggregate") + ~CaselessKeyword("reference") + ~CaselessKeyword("abstract") + ~CaselessKeyword("value_unique") + ~CaselessKeyword("if") + ~CaselessKeyword("loindex") + ~CaselessKeyword("format") + ~CaselessKeyword("true") + ~CaselessKeyword("insert") + ~CaselessKeyword("exp") + ~CaselessKeyword("end_type") + ~CaselessKeyword("end") + ~CaselessKeyword("optional") + ~CaselessKeyword("in") + ~CaselessKeyword("like") + ~CaselessKeyword("type") + ~CaselessKeyword("end_rule") + ~CaselessKeyword("repeat") + ~CaselessKeyword("nvl") + ~CaselessKeyword("otherwise") + ~CaselessKeyword("procedure") + ~CaselessKeyword("number") + ~CaselessKeyword("boolean") + ~CaselessKeyword("exists") + ~CaselessKeyword("andor") + ~CaselessKeyword("alias") + ~CaselessKeyword("entity") + ~CaselessKeyword("constant") + ~CaselessKeyword("tan") + ~CaselessKeyword("or") + ~CaselessKeyword("oneof") + ~CaselessKeyword("from") + ~CaselessKeyword("escape") + ~CaselessKeyword("typeof") + ~CaselessKeyword("extensible") + ~CaselessKeyword("div") + ~CaselessKeyword("then") + ~CaselessKeyword("by") + ~CaselessKeyword("unknown") + ~CaselessKeyword("var") + ~CaselessKeyword("pi") + ~CaselessKeyword("inverse") + ~CaselessKeyword("skip") + ~CaselessKeyword("array") + ~CaselessKeyword("end_subtype_constraint") + ~CaselessKeyword("use") + ~CaselessKeyword("self") + ~CaselessKeyword("end_alias") + ~CaselessKeyword("select") + ~CaselessKeyword("for") + ~CaselessKeyword("sizeof") + ~CaselessKeyword("fixed") + ~CaselessKeyword("local") + ~CaselessKeyword("remove") + ~CaselessKeyword("enumeration") + ~CaselessKeyword("end_local") + ~CaselessKeyword("not") + ~CaselessKeyword("function") + ~CaselessKeyword("cos") + ~CaselessKeyword("logical") + ~CaselessKeyword("query") + ~CaselessKeyword("atan") + ~CaselessKeyword("return") + ~CaselessKeyword("schema") + ~CaselessKeyword("hiindex") + ~CaselessKeyword("rolesof") + ~CaselessKeyword("log10") + ~CaselessKeyword("end_function") + ~CaselessKeyword("abs") + ~CaselessKeyword("length") + ~CaselessKeyword("renamed") + ~CaselessKeyword("acos") + ~CaselessKeyword("end_case") + ~CaselessKeyword("case") + ~CaselessKeyword("mod") + ~CaselessKeyword("end_if") + ~CaselessKeyword("list") + ~CaselessKeyword("end_repeat") + ~CaselessKeyword("generic") + ~CaselessKeyword("of") + ~CaselessKeyword("supertype") + ~CaselessKeyword("false") + ~CaselessKeyword("end_entity") + ~CaselessKeyword("odd") + ~CaselessKeyword("integer") + ~CaselessKeyword("hibound") + ~CaselessKeyword("rule") + ~CaselessKeyword("as") + ~CaselessKeyword("derive") + ~CaselessKeyword("log") + ~CaselessKeyword("set") + ~CaselessKeyword("subtype_constraint") + ~CaselessKeyword("unique") + ~CaselessKeyword("value") + ~CaselessKeyword("subtype") + ~CaselessKeyword("until") + ~CaselessKeyword("with") + ~CaselessKeyword("sqrt") + ~CaselessKeyword("where") + ~CaselessKeyword("value_in") + ~CaselessKeyword("to") + ~CaselessKeyword("xor") + ~CaselessKeyword("sin") + ~CaselessKeyword("while") + ~CaselessKeyword("string") + ~CaselessKeyword("usedin") + ~CaselessKeyword("total_over") + ~CaselessKeyword("binary") + ~CaselessKeyword("and") + ~CaselessKeyword("end_schema") + ~CaselessKeyword("generic_entity") + ~CaselessKeyword("end_constant") + ~CaselessKeyword("const_e") + ~CaselessKeyword("based_on") + ~CaselessKeyword("else") + ~CaselessKeyword("asin") + ~CaselessKeyword("blength") + ~CaselessKeyword("real") + ~CaselessKeyword("end_procedure") + ~CaselessKeyword("log2") + ~CaselessKeyword("begin") + 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")
|
||||
@@ -250,224 +250,224 @@ def parse(fn):
|
||||
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")
|
||||
list_type = Forward()("list_type")
|
||||
parameter_type = Forward()("parameter_type")
|
||||
qualifiable_factor = Forward()("qualifiable_factor")
|
||||
supertype_expression = Forward()("supertype_expression")
|
||||
precision_spec = Forward()("precision_spec")
|
||||
element = Forward()("element")
|
||||
subtype_constraint_decl = Forward()("subtype_constraint_decl")
|
||||
derived_attr = Forward()("derived_attr")
|
||||
general_bag_type = Forward()("general_bag_type")
|
||||
aggregation_types = Forward()("aggregation_types")
|
||||
entity_decl = Forward()("entity_decl")
|
||||
rule_decl = Forward()("rule_decl")
|
||||
numeric_expression = Forward()("numeric_expression")
|
||||
increment = Forward()("increment")
|
||||
supertype_constraint = Forward()("supertype_constraint")
|
||||
concrete_types = Forward()("concrete_types")
|
||||
remark = Forward()("remark")
|
||||
width = Forward()("width")
|
||||
index = Forward()("index")
|
||||
selector = Forward()("selector")
|
||||
simple_factor = Forward()("simple_factor")
|
||||
actual_parameter_list = Forward()("actual_parameter_list")
|
||||
general_set_type = Forward()("general_set_type")
|
||||
declaration = Forward()("declaration")
|
||||
repeat_stmt = Forward()("repeat_stmt")
|
||||
until_control = Forward()("until_control")
|
||||
supertype_factor = Forward()("supertype_factor")
|
||||
one_of = Forward()("one_of")
|
||||
stmt = Forward()("stmt")
|
||||
general_aggregation_types = Forward()("general_aggregation_types")
|
||||
procedure_call_stmt = Forward()("procedure_call_stmt")
|
||||
repetition = Forward()("repetition")
|
||||
case_action = Forward()("case_action")
|
||||
inverse_attr = Forward()("inverse_attr")
|
||||
aggregate_initializer = Forward()("aggregate_initializer")
|
||||
bound_spec = Forward()("bound_spec")
|
||||
interval_high = Forward()("interval_high")
|
||||
algorithm_head = Forward()("algorithm_head")
|
||||
real_type = Forward()("real_type")
|
||||
case_stmt = Forward()("case_stmt")
|
||||
constant_decl = Forward()("constant_decl")
|
||||
alias_stmt = Forward()("alias_stmt")
|
||||
function_head = Forward()("function_head")
|
||||
interval_low = Forward()("interval_low")
|
||||
aggregate_source = Forward()("aggregate_source")
|
||||
query_expression = Forward()("query_expression")
|
||||
derived_attr = Forward()("derived_attr")
|
||||
width_spec = Forward()("width_spec")
|
||||
interval_item = Forward()("interval_item")
|
||||
subsuper = Forward()("subsuper")
|
||||
array_type = Forward()("array_type")
|
||||
primary = Forward()("primary")
|
||||
case_label = Forward()("case_label")
|
||||
index_1 = Forward()("index_1")
|
||||
entity_constructor = Forward()("entity_constructor")
|
||||
subtype_constraint_body = Forward()("subtype_constraint_body")
|
||||
constant_body = Forward()("constant_body")
|
||||
actual_parameter_list = Forward()("actual_parameter_list")
|
||||
type_decl = Forward()("type_decl")
|
||||
selector = Forward()("selector")
|
||||
abstract_supertype_declaration = Forward()("abstract_supertype_declaration")
|
||||
explicit_attr = Forward()("explicit_attr")
|
||||
entity_body = Forward()("entity_body")
|
||||
repeat_control = Forward()("repeat_control")
|
||||
expression = Forward()("expression")
|
||||
if_stmt = Forward()("if_stmt")
|
||||
subtype_constraint = Forward()("subtype_constraint")
|
||||
while_control = Forward()("while_control")
|
||||
logical_expression = Forward()("logical_expression")
|
||||
aggregate_type = Forward()("aggregate_type")
|
||||
interval = Forward()("interval")
|
||||
general_array_type = Forward()("general_array_type")
|
||||
aggregation_types = Forward()("aggregation_types")
|
||||
domain_rule = Forward()("domain_rule")
|
||||
entity_constructor = Forward()("entity_constructor")
|
||||
function_call = Forward()("function_call")
|
||||
numeric_expression = Forward()("numeric_expression")
|
||||
general_set_type = Forward()("general_set_type")
|
||||
qualifier = Forward()("qualifier")
|
||||
formal_parameter = Forward()("formal_parameter")
|
||||
index_1 = Forward()("index_1")
|
||||
underlying_type = Forward()("underlying_type")
|
||||
instantiable_type = Forward()("instantiable_type")
|
||||
supertype_rule = Forward()("supertype_rule")
|
||||
generalized_types = Forward()("generalized_types")
|
||||
local_variable = Forward()("local_variable")
|
||||
schema_body = Forward()("schema_body")
|
||||
general_list_type = Forward()("general_list_type")
|
||||
assignment_stmt = Forward()("assignment_stmt")
|
||||
bound_2 = Forward()("bound_2")
|
||||
binary_type = Forward()("binary_type")
|
||||
syntax = Forward()("syntax")
|
||||
aggregate_source = Forward()("aggregate_source")
|
||||
while_control = Forward()("while_control")
|
||||
interval_low = Forward()("interval_low")
|
||||
parameter = Forward()("parameter")
|
||||
string_type = Forward()("string_type")
|
||||
supertype_term = Forward()("supertype_term")
|
||||
embedded_remark = Forward()("embedded_remark")
|
||||
increment_control = Forward()("increment_control")
|
||||
local_decl = Forward()("local_decl")
|
||||
precision_spec = Forward()("precision_spec")
|
||||
one_of = Forward()("one_of")
|
||||
subtype_constraint_body = Forward()("subtype_constraint_body")
|
||||
general_array_type = Forward()("general_array_type")
|
||||
list_type = Forward()("list_type")
|
||||
subtype_constraint_decl = Forward()("subtype_constraint_decl")
|
||||
schema_decl = Forward()("schema_decl")
|
||||
algorithm_head = Forward()("algorithm_head")
|
||||
query_expression = Forward()("query_expression")
|
||||
primary = Forward()("primary")
|
||||
repeat_control = Forward()("repeat_control")
|
||||
factor = Forward()("factor")
|
||||
procedure_head = Forward()("procedure_head")
|
||||
function_decl = Forward()("function_decl")
|
||||
type_decl = Forward()("type_decl")
|
||||
general_aggregation_types = Forward()("general_aggregation_types")
|
||||
domain_rule = Forward()("domain_rule")
|
||||
schema_decl = Forward()("schema_decl")
|
||||
derive_clause = Forward()("derive_clause")
|
||||
return_stmt = Forward()("return_stmt")
|
||||
bag_type = Forward()("bag_type")
|
||||
procedure_decl = Forward()("procedure_decl")
|
||||
abstract_supertype_declaration = Forward()("abstract_supertype_declaration")
|
||||
inverse_clause = Forward()("inverse_clause")
|
||||
index_qualifier = Forward()("index_qualifier")
|
||||
bound_1 = Forward()("bound_1")
|
||||
compound_stmt = Forward()("compound_stmt")
|
||||
set_type = Forward()("set_type")
|
||||
where_clause = Forward()("where_clause")
|
||||
qualifier = Forward()("qualifier")
|
||||
entity_head = Forward()("entity_head")
|
||||
stmt = Forward()("stmt")
|
||||
index_2 = Forward()("index_2")
|
||||
term = Forward()("term")
|
||||
function_call = Forward()("function_call")
|
||||
formal_parameter = Forward()("formal_parameter")
|
||||
aggregate_type = Forward()("aggregate_type")
|
||||
repeat_stmt = Forward()("repeat_stmt")
|
||||
entity_body = Forward()("entity_body")
|
||||
interval_high = Forward()("interval_high")
|
||||
logical_expression = Forward()("logical_expression")
|
||||
simple_expression = Forward()("simple_expression")
|
||||
remark = Forward()("remark")
|
||||
simple_factor = Forward()("simple_factor")
|
||||
case_stmt = Forward()("case_stmt")
|
||||
derive_clause = Forward()("derive_clause")
|
||||
supertype_constraint = Forward()("supertype_constraint")
|
||||
assignment_stmt = Forward()("assignment_stmt")
|
||||
entity_head = Forward()("entity_head")
|
||||
set_type = Forward()("set_type")
|
||||
instantiable_type = Forward()("instantiable_type")
|
||||
declaration = Forward()("declaration")
|
||||
binary_type = Forward()("binary_type")
|
||||
interval = Forward()("interval")
|
||||
parameter_type = Forward()("parameter_type")
|
||||
term = Forward()("term")
|
||||
index = Forward()("index")
|
||||
expression = Forward()("expression")
|
||||
bag_type = Forward()("bag_type")
|
||||
schema_body = Forward()("schema_body")
|
||||
until_control = Forward()("until_control")
|
||||
simple_types = Forward()("simple_types")
|
||||
list_type << (((LIST + Optional(bound_spec) + OF + Optional(UNIQUE) + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="list_type"))
|
||||
parameter_type << (((generalized_types | simple_types | named_types))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="parameter_type"))
|
||||
qualifiable_factor << (((function_call | attribute_ref | constant_factor | general_ref | population))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="qualifiable_factor"))
|
||||
supertype_expression << (((supertype_factor + ZeroOrMore((ANDOR + supertype_factor))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="supertype_expression"))
|
||||
precision_spec << (numeric_expression)
|
||||
element << (((expression + Optional((CaselessLiteral(":") + repetition))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="element"))
|
||||
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"))
|
||||
derived_attr << (((attribute_decl + CaselessLiteral(":") + parameter_type + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="derived_attr"))
|
||||
general_bag_type << (((BAG + Optional(bound_spec) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_bag_type"))
|
||||
aggregation_types << (((array_type | bag_type | list_type | set_type))).setParseAction(AggregationType)
|
||||
entity_decl << (((entity_head + entity_body + END_ENTITY + CaselessLiteral(";")))).setParseAction(EntityDeclaration)
|
||||
rule_decl << (((rule_head + algorithm_head + ZeroOrMore(stmt) + where_clause + END_RULE + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="rule_decl"))
|
||||
numeric_expression << (simple_expression)
|
||||
increment << (numeric_expression)
|
||||
supertype_constraint << (((abstract_supertype_declaration | abstract_entity_declaration | supertype_rule))).setParseAction(SuperTypeExpression)
|
||||
concrete_types << (((aggregation_types | simple_types | type_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="concrete_types"))
|
||||
remark << (((embedded_remark | tail_remark))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="remark"))
|
||||
width << (numeric_expression)
|
||||
index << (numeric_expression)
|
||||
selector << (expression)
|
||||
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"))
|
||||
actual_parameter_list << (((CaselessLiteral("(") + Optional(parameter) + ZeroOrMore((CaselessLiteral(",") + parameter)) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="actual_parameter_list"))
|
||||
general_set_type << (((SET + Optional(bound_spec) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_set_type"))
|
||||
declaration << (((entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="declaration"))
|
||||
repeat_stmt << (((REPEAT + repeat_control + CaselessLiteral(";") + stmt + ZeroOrMore(stmt) + END_REPEAT + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="repeat_stmt"))
|
||||
until_control << (((UNTIL + logical_expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="until_control"))
|
||||
supertype_factor << (((supertype_term + ZeroOrMore((AND + supertype_term))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="supertype_factor"))
|
||||
one_of << (((ONEOF + CaselessLiteral("(") + supertype_expression + ZeroOrMore((CaselessLiteral(",") + supertype_expression)) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="one_of"))
|
||||
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"))
|
||||
repetition << (numeric_expression)
|
||||
case_action << (((case_label + ZeroOrMore((CaselessLiteral(",") + case_label)) + CaselessLiteral(":") + stmt))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="case_action"))
|
||||
inverse_attr << (((attribute_decl + CaselessLiteral(":") + Optional(((SET | BAG) + Optional(bound_spec) + OF)) + entity_ref + FOR + Optional((entity_ref + CaselessLiteral("."))) + attribute_ref + CaselessLiteral(";")))).setParseAction(InverseAttribute)
|
||||
aggregate_initializer << (((CaselessLiteral("[") + Optional((element + ZeroOrMore((CaselessLiteral(",") + element)))) + CaselessLiteral("]")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="aggregate_initializer"))
|
||||
bound_spec << (((CaselessLiteral("[") + bound_1 + CaselessLiteral(":") + bound_2 + CaselessLiteral("]")))).setParseAction(BoundSpecification)
|
||||
interval_high << (simple_expression)
|
||||
algorithm_head << (((ZeroOrMore(declaration) + Optional(constant_decl) + Optional(local_decl)))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="algorithm_head"))
|
||||
real_type << (((REAL + Optional((CaselessLiteral("(") + precision_spec + CaselessLiteral(")")))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="real_type"))
|
||||
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"))
|
||||
constant_decl << (((CONSTANT + constant_body + ZeroOrMore(constant_body) + END_CONSTANT + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="constant_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"))
|
||||
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"))
|
||||
interval_low << (simple_expression)
|
||||
aggregate_source << (simple_expression)
|
||||
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"))
|
||||
width_spec << (((CaselessLiteral("(") + width + CaselessLiteral(")") + Optional(FIXED)))).setParseAction(WidthSpec)
|
||||
interval_item << (simple_expression)
|
||||
subsuper << (((Optional(supertype_constraint) + Optional(subtype_declaration)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subsuper"))
|
||||
array_type << (((ARRAY + bound_spec + OF + Optional(OPTIONAL) + Optional(UNIQUE) + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="array_type"))
|
||||
primary << (((literal | (qualifiable_factor + ZeroOrMore(qualifier))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="primary"))
|
||||
case_label << (expression)
|
||||
index_1 << (index)
|
||||
entity_constructor << (((entity_ref + CaselessLiteral("(") + Optional((expression + ZeroOrMore((CaselessLiteral(",") + expression)))) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="entity_constructor"))
|
||||
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"))
|
||||
constant_body << (((constant_id + CaselessLiteral(":") + instantiable_type + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="constant_body"))
|
||||
explicit_attr << (((attribute_decl + ZeroOrMore((CaselessLiteral(",") + attribute_decl)) + CaselessLiteral(":") + Optional(OPTIONAL) + parameter_type + CaselessLiteral(";")))).setParseAction(ExplicitAttribute)
|
||||
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"))
|
||||
repeat_control << (((Optional(increment_control) + Optional(while_control) + Optional(until_control)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="repeat_control"))
|
||||
expression << (((simple_expression + Optional((rel_op_extended + simple_expression))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="expression"))
|
||||
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"))
|
||||
subtype_constraint << (((OF + CaselessLiteral("(") + supertype_expression + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subtype_constraint"))
|
||||
while_control << (((WHILE + logical_expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="while_control"))
|
||||
logical_expression << (expression)
|
||||
aggregate_type << (((AGGREGATE + Optional((CaselessLiteral(":") + type_label)) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="aggregate_type"))
|
||||
interval << (((CaselessLiteral("{") + interval_low + interval_op + interval_item + interval_op + interval_high + CaselessLiteral("}")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="interval"))
|
||||
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"))
|
||||
underlying_type << (((constructed_types | concrete_types))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="underlying_type"))
|
||||
instantiable_type << (((concrete_types | entity_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="instantiable_type"))
|
||||
supertype_rule << (((SUPERTYPE + subtype_constraint))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="supertype_rule"))
|
||||
generalized_types << (((aggregate_type | general_aggregation_types | generic_entity_type | generic_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="generalized_types"))
|
||||
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"))
|
||||
schema_body << (((ZeroOrMore(interface_specification) + Optional(constant_decl) + ZeroOrMore((declaration | rule_decl))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="schema_body"))
|
||||
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"))
|
||||
assignment_stmt << (((general_ref + ZeroOrMore(qualifier) + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="assignment_stmt"))
|
||||
bound_2 << (numeric_expression)
|
||||
binary_type << (((BINARY + Optional(width_spec)))).setParseAction(BinaryType)
|
||||
syntax << (((schema_decl + ZeroOrMore(schema_decl)))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="syntax"))
|
||||
parameter << (expression)
|
||||
string_type << (((STRING + Optional(width_spec)))).setParseAction(StringType)
|
||||
supertype_term << (((one_of | (CaselessLiteral("(") + supertype_expression + CaselessLiteral(")")) | entity_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="supertype_term"))
|
||||
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"))
|
||||
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_decl << (((LOCAL + local_variable + ZeroOrMore(local_variable) + END_LOCAL + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="local_decl"))
|
||||
factor << (((simple_factor + Optional((CaselessLiteral("**") + simple_factor))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="factor"))
|
||||
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(lambda s, loc, t: ListNode(s, loc, t, rule="function_decl"))
|
||||
type_decl << (((TYPE + type_id + CaselessLiteral("=") + underlying_type + CaselessLiteral(";") + Optional(where_clause) + END_TYPE + CaselessLiteral(";")))).setParseAction(TypeDeclaration)
|
||||
general_aggregation_types << (((general_array_type | general_bag_type | general_list_type | general_set_type))).setParseAction(AggregationType)
|
||||
domain_rule << (((Optional((rule_label_id + CaselessLiteral(":"))) + expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="domain_rule"))
|
||||
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"))
|
||||
derive_clause << (((DERIVE + derived_attr + ZeroOrMore(derived_attr)))).setParseAction(AttributeList)
|
||||
return_stmt << (((RETURN + Optional((CaselessLiteral("(") + expression + CaselessLiteral(")"))) + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="return_stmt"))
|
||||
bag_type << (((BAG + Optional(bound_spec) + OF + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="bag_type"))
|
||||
procedure_decl << (((procedure_head + algorithm_head + ZeroOrMore(stmt) + END_PROCEDURE + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="procedure_decl"))
|
||||
abstract_supertype_declaration << (((ABSTRACT + SUPERTYPE + Optional(subtype_constraint)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="abstract_supertype_declaration"))
|
||||
inverse_clause << (((INVERSE + inverse_attr + ZeroOrMore(inverse_attr)))).setParseAction(AttributeList)
|
||||
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)
|
||||
compound_stmt << (((BEGIN + stmt + ZeroOrMore(stmt) + END + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="compound_stmt"))
|
||||
set_type << (((SET + Optional(bound_spec) + OF + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="set_type"))
|
||||
where_clause << (((WHERE + domain_rule + CaselessLiteral(";") + ZeroOrMore((domain_rule + CaselessLiteral(";")))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="where_clause"))
|
||||
qualifier << (((attribute_qualifier | group_qualifier | index_qualifier))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="qualifier"))
|
||||
entity_head << (((ENTITY + entity_id + subsuper + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="entity_head"))
|
||||
subsuper = Forward()("subsuper")
|
||||
entity_decl = Forward()("entity_decl")
|
||||
concrete_types = Forward()("concrete_types")
|
||||
element = Forward()("element")
|
||||
general_bag_type = Forward()("general_bag_type")
|
||||
interval_item = Forward()("interval_item")
|
||||
constant_body = Forward()("constant_body")
|
||||
increment = Forward()("increment")
|
||||
case_label = Forward()("case_label")
|
||||
case_action = Forward()("case_action")
|
||||
width = Forward()("width")
|
||||
procedure_decl = Forward()("procedure_decl")
|
||||
increment_control = Forward()("increment_control")
|
||||
index_qualifier = Forward()("index_qualifier")
|
||||
constant_decl = Forward()("constant_decl")
|
||||
supertype_rule = Forward()("supertype_rule")
|
||||
syntax = Forward()("syntax")
|
||||
function_head = Forward()("function_head")
|
||||
repetition = Forward()("repetition")
|
||||
if_stmt = Forward()("if_stmt")
|
||||
supertype_expression = Forward()("supertype_expression")
|
||||
inverse_clause = Forward()("inverse_clause")
|
||||
aggregate_initializer = Forward()("aggregate_initializer")
|
||||
return_stmt = Forward()("return_stmt")
|
||||
generalized_types = Forward()("generalized_types")
|
||||
bound_2 = Forward()("bound_2")
|
||||
real_type = Forward()("real_type")
|
||||
index_2 = Forward()("index_2")
|
||||
array_type = Forward()("array_type")
|
||||
local_decl = Forward()("local_decl")
|
||||
supertype_term = Forward()("supertype_term")
|
||||
where_clause = Forward()("where_clause")
|
||||
embedded_remark = Forward()("embedded_remark")
|
||||
compound_stmt = Forward()("compound_stmt")
|
||||
bound_1 = Forward()("bound_1")
|
||||
alias_stmt = Forward()("alias_stmt")
|
||||
subtype_constraint = Forward()("subtype_constraint")
|
||||
string_type = Forward()("string_type")
|
||||
function_decl = Forward()("function_decl")
|
||||
general_list_type = Forward()("general_list_type")
|
||||
supertype_factor = Forward()("supertype_factor")
|
||||
rule_decl = Forward()("rule_decl")
|
||||
qualifiable_factor = Forward()("qualifiable_factor")
|
||||
bound_spec = Forward()("bound_spec")
|
||||
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"))
|
||||
index_2 << (index)
|
||||
term << (((factor + ZeroOrMore((multiplication_like_op + factor))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="term"))
|
||||
general_aggregation_types << (((general_array_type | general_bag_type | general_list_type | general_set_type))).setParseAction(AggregationType)
|
||||
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"))
|
||||
inverse_attr << (((attribute_decl + CaselessLiteral(":") + Optional(((SET | BAG) + Optional(bound_spec) + OF)) + entity_ref + FOR + Optional((entity_ref + CaselessLiteral("."))) + attribute_ref + CaselessLiteral(";")))).setParseAction(InverseAttribute)
|
||||
derived_attr << (((attribute_decl + CaselessLiteral(":") + parameter_type + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="derived_attr"))
|
||||
width_spec << (((CaselessLiteral("(") + width + CaselessLiteral(")") + Optional(FIXED)))).setParseAction(WidthSpec)
|
||||
actual_parameter_list << (((CaselessLiteral("(") + Optional(parameter) + ZeroOrMore((CaselessLiteral(",") + parameter)) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="actual_parameter_list"))
|
||||
type_decl << (((TYPE + type_id + CaselessLiteral("=") + underlying_type + CaselessLiteral(";") + Optional(where_clause) + END_TYPE + CaselessLiteral(";")))).setParseAction(TypeDeclaration)
|
||||
selector << (expression)
|
||||
abstract_supertype_declaration << (((ABSTRACT + SUPERTYPE + Optional(subtype_constraint)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="abstract_supertype_declaration"))
|
||||
explicit_attr << (((attribute_decl + ZeroOrMore((CaselessLiteral(",") + attribute_decl)) + CaselessLiteral(":") + Optional(OPTIONAL) + parameter_type + CaselessLiteral(";")))).setParseAction(ExplicitAttribute)
|
||||
aggregation_types << (((array_type | bag_type | list_type | set_type))).setParseAction(AggregationType)
|
||||
domain_rule << (((Optional((rule_label_id + CaselessLiteral(":"))) + expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="domain_rule"))
|
||||
entity_constructor << (((entity_ref + CaselessLiteral("(") + Optional((expression + ZeroOrMore((CaselessLiteral(",") + expression)))) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="entity_constructor"))
|
||||
function_call << ((((built_in_function | function_ref) + actual_parameter_list))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="function_call"))
|
||||
numeric_expression << (simple_expression)
|
||||
general_set_type << (((SET + Optional(bound_spec) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_set_type"))
|
||||
qualifier << (((attribute_qualifier | group_qualifier | index_qualifier))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="qualifier"))
|
||||
formal_parameter << (((parameter_id + ZeroOrMore((CaselessLiteral(",") + parameter_id)) + CaselessLiteral(":") + parameter_type))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="formal_parameter"))
|
||||
index_1 << (index)
|
||||
underlying_type << (((constructed_types | concrete_types))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="underlying_type"))
|
||||
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"))
|
||||
aggregate_source << (simple_expression)
|
||||
while_control << (((WHILE + logical_expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="while_control"))
|
||||
interval_low << (simple_expression)
|
||||
parameter << (expression)
|
||||
precision_spec << (numeric_expression)
|
||||
one_of << (((ONEOF + CaselessLiteral("(") + supertype_expression + ZeroOrMore((CaselessLiteral(",") + supertype_expression)) + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="one_of"))
|
||||
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"))
|
||||
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"))
|
||||
list_type << (((LIST + Optional(bound_spec) + OF + Optional(UNIQUE) + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="list_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"))
|
||||
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"))
|
||||
algorithm_head << (((ZeroOrMore(declaration) + Optional(constant_decl) + Optional(local_decl)))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="algorithm_head"))
|
||||
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"))
|
||||
primary << (((literal | (qualifiable_factor + ZeroOrMore(qualifier))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="primary"))
|
||||
repeat_control << (((Optional(increment_control) + Optional(while_control) + Optional(until_control)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="repeat_control"))
|
||||
factor << (((simple_factor + Optional((CaselessLiteral("**") + simple_factor))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="factor"))
|
||||
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"))
|
||||
aggregate_type << (((AGGREGATE + Optional((CaselessLiteral(":") + type_label)) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="aggregate_type"))
|
||||
repeat_stmt << (((REPEAT + repeat_control + CaselessLiteral(";") + stmt + ZeroOrMore(stmt) + END_REPEAT + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="repeat_stmt"))
|
||||
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"))
|
||||
interval_high << (simple_expression)
|
||||
logical_expression << (expression)
|
||||
simple_expression << (((term + ZeroOrMore((add_like_op + term))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="simple_expression"))
|
||||
remark << (((embedded_remark | tail_remark))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="remark"))
|
||||
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"))
|
||||
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"))
|
||||
derive_clause << (((DERIVE + derived_attr + ZeroOrMore(derived_attr)))).setParseAction(AttributeList)
|
||||
supertype_constraint << (((abstract_supertype_declaration | abstract_entity_declaration | supertype_rule))).setParseAction(SuperTypeExpression)
|
||||
assignment_stmt << (((general_ref + ZeroOrMore(qualifier) + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="assignment_stmt"))
|
||||
entity_head << (((ENTITY + entity_id + subsuper + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="entity_head"))
|
||||
set_type << (((SET + Optional(bound_spec) + OF + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="set_type"))
|
||||
instantiable_type << (((concrete_types | entity_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="instantiable_type"))
|
||||
declaration << (((entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="declaration"))
|
||||
binary_type << (((BINARY + Optional(width_spec)))).setParseAction(BinaryType)
|
||||
interval << (((CaselessLiteral("{") + interval_low + interval_op + interval_item + interval_op + interval_high + CaselessLiteral("}")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="interval"))
|
||||
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"))
|
||||
index << (numeric_expression)
|
||||
expression << (((simple_expression + Optional((rel_op_extended + simple_expression))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="expression"))
|
||||
bag_type << (((BAG + Optional(bound_spec) + OF + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="bag_type"))
|
||||
schema_body << (((ZeroOrMore(interface_specification) + Optional(constant_decl) + ZeroOrMore((declaration | rule_decl))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="schema_body"))
|
||||
until_control << (((UNTIL + logical_expression))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="until_control"))
|
||||
simple_types << (((binary_type | boolean_type | integer_type | logical_type | number_type | real_type | string_type))).setParseAction(SimpleType)
|
||||
subsuper << (((Optional(supertype_constraint) + Optional(subtype_declaration)))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subsuper"))
|
||||
entity_decl << (((entity_head + entity_body + END_ENTITY + CaselessLiteral(";")))).setParseAction(EntityDeclaration)
|
||||
concrete_types << (((aggregation_types | simple_types | type_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="concrete_types"))
|
||||
element << (((expression + Optional((CaselessLiteral(":") + repetition))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="element"))
|
||||
general_bag_type << (((BAG + Optional(bound_spec) + OF + parameter_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="general_bag_type"))
|
||||
interval_item << (simple_expression)
|
||||
constant_body << (((constant_id + CaselessLiteral(":") + instantiable_type + CaselessLiteral(":=") + expression + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="constant_body"))
|
||||
increment << (numeric_expression)
|
||||
case_label << (expression)
|
||||
case_action << (((case_label + ZeroOrMore((CaselessLiteral(",") + case_label)) + CaselessLiteral(":") + stmt))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="case_action"))
|
||||
width << (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"))
|
||||
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"))
|
||||
index_qualifier << (((CaselessLiteral("[") + index_1 + Optional((CaselessLiteral(":") + index_2)) + CaselessLiteral("]")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="index_qualifier"))
|
||||
constant_decl << (((CONSTANT + constant_body + ZeroOrMore(constant_body) + END_CONSTANT + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="constant_decl"))
|
||||
supertype_rule << (((SUPERTYPE + subtype_constraint))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="supertype_rule"))
|
||||
syntax << (((schema_decl + ZeroOrMore(schema_decl)))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="syntax"))
|
||||
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"))
|
||||
repetition << (numeric_expression)
|
||||
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"))
|
||||
supertype_expression << (((supertype_factor + ZeroOrMore((ANDOR + supertype_factor))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="supertype_expression"))
|
||||
inverse_clause << (((INVERSE + inverse_attr + ZeroOrMore(inverse_attr)))).setParseAction(AttributeList)
|
||||
aggregate_initializer << (((CaselessLiteral("[") + Optional((element + ZeroOrMore((CaselessLiteral(",") + element)))) + CaselessLiteral("]")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="aggregate_initializer"))
|
||||
return_stmt << (((RETURN + Optional((CaselessLiteral("(") + expression + CaselessLiteral(")"))) + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="return_stmt"))
|
||||
generalized_types << (((aggregate_type | general_aggregation_types | generic_entity_type | generic_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="generalized_types"))
|
||||
bound_2 << (numeric_expression)
|
||||
real_type << (((REAL + Optional((CaselessLiteral("(") + precision_spec + CaselessLiteral(")")))))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="real_type"))
|
||||
index_2 << (index)
|
||||
array_type << (((ARRAY + bound_spec + OF + Optional(OPTIONAL) + Optional(UNIQUE) + instantiable_type))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="array_type"))
|
||||
local_decl << (((LOCAL + local_variable + ZeroOrMore(local_variable) + END_LOCAL + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="local_decl"))
|
||||
supertype_term << (((one_of | (CaselessLiteral("(") + supertype_expression + CaselessLiteral(")")) | entity_ref))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="supertype_term"))
|
||||
where_clause << (((WHERE + domain_rule + CaselessLiteral(";") + ZeroOrMore((domain_rule + CaselessLiteral(";")))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="where_clause"))
|
||||
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"))
|
||||
compound_stmt << (((BEGIN + stmt + ZeroOrMore(stmt) + END + CaselessLiteral(";")))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="compound_stmt"))
|
||||
bound_1 << (numeric_expression)
|
||||
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"))
|
||||
subtype_constraint << (((OF + CaselessLiteral("(") + supertype_expression + CaselessLiteral(")")))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="subtype_constraint"))
|
||||
string_type << (((STRING + Optional(width_spec)))).setParseAction(StringType)
|
||||
function_decl << (((function_head + algorithm_head + stmt + ZeroOrMore(stmt) + END_FUNCTION + CaselessLiteral(";")))).setParseAction(FunctionDeclaration)
|
||||
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"))
|
||||
supertype_factor << (((supertype_term + ZeroOrMore((AND + supertype_term))))).setParseAction(lambda s, loc, t: ListNode(s, loc, t, rule="supertype_factor"))
|
||||
rule_decl << (((rule_head + algorithm_head + ZeroOrMore(stmt) + where_clause + END_RULE + CaselessLiteral(";")))).setParseAction(RuleDeclaration)
|
||||
qualifiable_factor << (((function_call | attribute_ref | constant_factor | general_ref | population))).setParseAction(lambda s, loc, t: Node(s, loc, t, rule="qualifiable_factor"))
|
||||
bound_spec << (((CaselessLiteral("[") + bound_1 + CaselessLiteral(":") + bound_2 + CaselessLiteral("]")))).setParseAction(BoundSpecification)
|
||||
|
||||
syntax.ignore("--" + restOfLine)
|
||||
syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))"))
|
||||
|
||||
@@ -24,6 +24,7 @@ import string
|
||||
import operator
|
||||
import collections
|
||||
|
||||
import bootstrap
|
||||
|
||||
class Node:
|
||||
def __init__(self, s, loc, tokens, rule=None):
|
||||
@@ -57,10 +58,18 @@ class ListNode:
|
||||
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:
|
||||
self.dict_tokens[r].append(t)
|
||||
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):
|
||||
@@ -68,9 +77,10 @@ class ListNode:
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.tokens)
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.tokens[i]
|
||||
|
||||
# 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
|
||||
@@ -115,7 +125,7 @@ class TypeDeclaration(Node):
|
||||
self.where = []
|
||||
clause = self.where_clause
|
||||
if clause:
|
||||
clause = clause[0]
|
||||
clause = list(clause[0])
|
||||
|
||||
self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]]
|
||||
|
||||
@@ -170,7 +180,7 @@ class EntityDeclaration(Node):
|
||||
self.where = []
|
||||
clause = [r for r in self.entity_body[0] if r.rule == "where_clause"]
|
||||
if clause:
|
||||
clause = clause[0]
|
||||
clause = list(clause[0])
|
||||
|
||||
self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]]
|
||||
|
||||
@@ -178,7 +188,7 @@ class EntityDeclaration(Node):
|
||||
clause = [r for r in self.entity_body[0] if r.rule == "unique_clause"]
|
||||
if clause:
|
||||
clause = clause[0]
|
||||
self.unique = [(r[0], r[2].simple_id) for r in clause[1::2]]
|
||||
self.unique = [(r[0], r[2].simple_id) for r in map(list, list(clause)[1::2])]
|
||||
|
||||
def __repr__(self):
|
||||
strm = io.StringIO()
|
||||
@@ -222,7 +232,7 @@ class EntityDeclaration(Node):
|
||||
|
||||
|
||||
class EnumerationType(Node):
|
||||
values = property(lambda self: self.enumeration_type[2][1::2])
|
||||
values = property(lambda self: list(self.enumeration_type[2])[1::2])
|
||||
|
||||
def __repr__(self):
|
||||
return "ENUMERATION OF (" + ",".join(self.values) + ")"
|
||||
@@ -241,29 +251,160 @@ def do_try(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 get_rule_id(x):
|
||||
from bootstrap import actions
|
||||
ty = type(x).__name__
|
||||
matches = [k for k, v in actions.items() if v == ty]
|
||||
if matches:
|
||||
return matches[0]
|
||||
|
||||
def prune(di):
|
||||
import bootstrap
|
||||
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
|
||||
}
|
||||
subrules = list(filter(str.islower, rule_dependencies[key]))
|
||||
return {k: v for k, v in di.items() if k in subrules}
|
||||
# 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):
|
||||
return to_tree(x.dict_tokens, key=get_rule_id(x) or key)
|
||||
if isinstance(x, Node,):
|
||||
return to_tree(x.tokens, key=get_rule_id(x) or key)
|
||||
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):
|
||||
return prune({k: to_tree(v, key=k) for k, v in x.items()})
|
||||
# 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:
|
||||
@@ -306,7 +447,7 @@ class AggregationType(Node):
|
||||
|
||||
|
||||
class SelectType(Node):
|
||||
values = property(lambda self: self.select_type[1][1::2])
|
||||
values = property(lambda self: list(self.select_type[1])[1::2])
|
||||
|
||||
def __repr__(self):
|
||||
return "SELECT (" + ",".join(map(str, self.values)) + ")"
|
||||
@@ -321,7 +462,7 @@ class SuperTypeExpression(Node):
|
||||
else:
|
||||
constraint = self.supertype_rule[0]
|
||||
return [
|
||||
s[0][0].simple_id for s in constraint.subtype_constraint[0].supertype_expression[0][0][0].one_of[0][2::2]
|
||||
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)
|
||||
@@ -418,7 +559,7 @@ class WidthSpec(Node):
|
||||
fixed = property(lambda self: self.FIXED is not None)
|
||||
|
||||
def init(self):
|
||||
self.width = int("".join(self.width[0].flat))
|
||||
self.width = int("".join(list(self.width)[0].flat))
|
||||
|
||||
def __repr__(self):
|
||||
return "(%d)%s" % (self.width, " fixed" if self.fixed else "")
|
||||
@@ -432,3 +573,16 @@ class StringType(Node):
|
||||
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
|
||||
|
||||
@@ -0,0 +1,703 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import hashlib
|
||||
import operator
|
||||
import functools
|
||||
import itertools
|
||||
|
||||
import ifcopenshell.express
|
||||
|
||||
import networkx as nx
|
||||
|
||||
from codegen import indent
|
||||
|
||||
DEBUG = False
|
||||
|
||||
def to_graph(tree):
|
||||
g = nx.DiGraph()
|
||||
|
||||
# Convert
|
||||
def write_to_graph(val, name=None):
|
||||
if isinstance(val, list):
|
||||
pairs = ((None, v) for v in val)
|
||||
elif isinstance(val, dict):
|
||||
pairs = val.items()
|
||||
else:
|
||||
assert name
|
||||
g.add_edge(name, name + "_value")
|
||||
return g.add_node(name + "_value", label=val)
|
||||
|
||||
for i, (k, v) in enumerate(pairs):
|
||||
i = f"{i:03d}"
|
||||
nid = f"{name or 'root'}_{k or i}"
|
||||
g.add_node(nid, label=k)
|
||||
if name:
|
||||
g.add_edge(name, nid)
|
||||
write_to_graph(v, nid)
|
||||
|
||||
write_to_graph(tree)
|
||||
|
||||
to_remove = set()
|
||||
|
||||
# Remove intermediate anonymous nodes. Often the result of ZeroOrMore() productions in
|
||||
# bootstrap.py that result in an intermediate list index node in to_tree()
|
||||
|
||||
# Start with the intermediate nodes and filter out root (needs to have predecessors)
|
||||
intermediate = [n for n in g.nodes if g.nodes[n].get('label') is None and list(g.predecessors(n))]
|
||||
|
||||
for n in intermediate:
|
||||
pr = list(g.predecessors(n))
|
||||
if len(pr) == 1 and g.nodes[pr[0]].get('label'):
|
||||
# when eliminating a grouping node with heterogeneous content
|
||||
# rather copy the predecessor node label to the grouping node
|
||||
# and later delete the predecessor
|
||||
sc = list(g.successors(n))
|
||||
if len(sc) > 1:
|
||||
sc_labels = list(map(lambda x: g.nodes[x].get('label'), sc))
|
||||
if len(set(sc_labels)) > 1 and None not in sc_labels:
|
||||
g.nodes[n]['label'] = g.nodes[pr[0]].get('label')
|
||||
to_remove.add(pr[0])
|
||||
continue
|
||||
|
||||
for ab in itertools.product(g.predecessors(n), g.successors(n)):
|
||||
g.add_edge(*ab)
|
||||
g.remove_node(n)
|
||||
|
||||
# The removal process above can decide to not fold the anonymous node, but rather
|
||||
# the predecessor of it, in which case it is deleted in this step.
|
||||
for n in to_remove:
|
||||
for ab in itertools.product(g.predecessors(n), g.successors(n)):
|
||||
g.add_edge(*ab)
|
||||
g.remove_node(n)
|
||||
|
||||
for n in g.nodes:
|
||||
if len(list(g.successors(n))) == 0 and g.nodes[n].get('label') not in ifcopenshell.express.express_parser.all_rules:
|
||||
g.nodes[n]['is_terminal'] = True
|
||||
|
||||
return g
|
||||
|
||||
def write_dot(fn, g):
|
||||
|
||||
with open(fn, "w") as f:
|
||||
|
||||
def w(*args, **kwargs):
|
||||
print(*args, file=f, **kwargs)
|
||||
|
||||
w("digraph", "{")
|
||||
|
||||
def nodename(n):
|
||||
return "N"+hashlib.md5(n.encode()).hexdigest()
|
||||
|
||||
def format(di):
|
||||
Q = '"'
|
||||
inner = ",".join(f"{k}={'' if v.startswith('<') else Q}{v}{'' if v.startswith('<') else Q}" for k, v in di.items())
|
||||
if inner:
|
||||
inner = f"[{inner}]"
|
||||
return inner
|
||||
|
||||
for n in g.nodes:
|
||||
lbl = g.nodes[n].get('label')
|
||||
if lbl:
|
||||
attrs = {"label": lbl}
|
||||
else:
|
||||
attrs = {"label": n}
|
||||
|
||||
if g.nodes[n].get('is_terminal'):
|
||||
attrs["shape"] = "rect"
|
||||
attrs["label"] = f"\\\"{attrs['label']}\\\""
|
||||
else:
|
||||
attrs["shape"] = "none"
|
||||
|
||||
w(nodename(n), format(attrs), ";", sep="")
|
||||
|
||||
for a,b in g.edges:
|
||||
w(nodename(a), "->", nodename(b), ";")
|
||||
|
||||
w("}", flush=True)
|
||||
|
||||
|
||||
from pyparsing import *
|
||||
SLASH = Suppress("/")
|
||||
identifier = Word(alphanums + "_")
|
||||
rule = identifier + (ZeroOrMore(SLASH + identifier))
|
||||
|
||||
def paths(G, root, length):
|
||||
if length == 1:
|
||||
yield (G.nodes[root].get('label'),)
|
||||
return
|
||||
|
||||
sd = dict(nx.bfs_successors(G, root, depth_limit=length-1))
|
||||
def r(x, p=None):
|
||||
if p and len(p) == length:
|
||||
yield tuple(map(lambda n: G.nodes[n].get('label'), p))
|
||||
else:
|
||||
for y in sd.get(x, []):
|
||||
yield from r(y, (p or [x])+[y])
|
||||
yield from r(root)
|
||||
|
||||
|
||||
class context:
|
||||
def __init__(self, graph, rules):
|
||||
self.graph = graph
|
||||
self.rules = rules
|
||||
|
||||
def __getattr__(self, k):
|
||||
def inner():
|
||||
for r in self.rules:
|
||||
label_id_pairs = map(
|
||||
lambda n: (self.graph.nodes[n].get('label'), n),
|
||||
# itertools.chain.from_iterable(
|
||||
# dict(nx.bfs_successors(self.graph, r)).values()
|
||||
# )
|
||||
self.graph.successors(r)
|
||||
)
|
||||
matching = filter(lambda p: p[0] == k, label_id_pairs)
|
||||
yield from map(operator.itemgetter(1), matching)
|
||||
|
||||
return context(self.graph, list(inner()))
|
||||
|
||||
def has_inverse(self, a):
|
||||
for r in self.rules:
|
||||
if a in map(
|
||||
lambda n: self.graph.nodes[n].get('label'),
|
||||
self.graph.predecessors(r)
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __iter__(self):
|
||||
for r in self.rules:
|
||||
yield context(self.graph, [r])
|
||||
|
||||
def descendants(self):
|
||||
return [b.rules[0][len(self.rules[0])+1:] for b in self.branches(allow_multiple=True)]
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
s = "\n\n"+str(self)
|
||||
except:
|
||||
s = ""
|
||||
return f"<rule_context ({' '.join(self.descendants())})>{s}"
|
||||
|
||||
def __str__(self):
|
||||
assert len(self.rules) == 1
|
||||
nodes = itertools.chain(self.rules, itertools.chain.from_iterable(
|
||||
dict(nx.bfs_successors(self.graph, self.rules[0])).values()
|
||||
))
|
||||
terminals_or_values = list(filter(
|
||||
lambda n: self.graph.nodes[n].get('is_terminal') or self.graph.nodes[n].get('value'),
|
||||
nodes
|
||||
))
|
||||
# assert len(terminals) == 1
|
||||
attrs = [self.graph.nodes[tv] for tv in terminals_or_values]
|
||||
attrs = [a.get('value', a['label']) for a in attrs]
|
||||
attr_types = list(map(type, attrs))
|
||||
if empty in attr_types[0:1]:
|
||||
return ""
|
||||
attrs = list(filter(lambda s: isinstance(s, str), attrs))
|
||||
return attrs[0]
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.graph == other.graph and self.rules == other.rules
|
||||
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.rules)
|
||||
|
||||
|
||||
def branches(self, allow_multiple=False, exclude=()):
|
||||
if not allow_multiple:
|
||||
assert len(self.rules) == 1
|
||||
combined = sum([sorted((context(self.graph, [n]) for n in self.graph.successors(R)), key=lambda c: c.rules[0] if c.rules else "") for R in self.rules], [])
|
||||
return [c for c in combined if c not in exclude]
|
||||
|
||||
def parent(self):
|
||||
assert len(self.rules) == 1
|
||||
return context(self.graph, list(self.graph.predecessors(self.rules[0])))
|
||||
|
||||
def branch(self, i):
|
||||
return self.branches()[i]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.rules)
|
||||
|
||||
def __getitem__(self, k):
|
||||
return list(self)[k]
|
||||
|
||||
|
||||
# @todo
|
||||
context_class = context
|
||||
|
||||
class codegen_rule:
|
||||
def __init__(self, pattern, fn):
|
||||
self.pattern = tuple(rule.parseString(pattern))
|
||||
self.fn = fn
|
||||
if not hasattr(codegen_rule, 'all_rules'):
|
||||
codegen_rule.all_rules = []
|
||||
codegen_rule.all_rules.append(self)
|
||||
|
||||
def __call__(self, graph, node):
|
||||
# try:
|
||||
v = self.fn(context(graph, [node]))
|
||||
# except:
|
||||
# v = "ERROR!!"
|
||||
graph.nodes[node]['value'] = v
|
||||
return v
|
||||
|
||||
@staticmethod
|
||||
def apply(G):
|
||||
v = None
|
||||
for n in reversed(list(nx.topological_sort(G))):
|
||||
for r in codegen_rule.all_rules:
|
||||
if r.pattern in paths(G, n, len(r.pattern)):
|
||||
v = r(G, n)
|
||||
return v
|
||||
|
||||
def process_rule_decl(context):
|
||||
return f"""
|
||||
class {context.rule_head.rule_id}:
|
||||
SCOPE = "file"
|
||||
|
||||
@staticmethod
|
||||
def __call__(file):
|
||||
{context.rule_head.entity_ref} = file.by_type("{context.rule_head.entity_ref}")
|
||||
{indent(8, context.algorithm_head.local_decl)}
|
||||
{indent(8, context.stmt.branches()) if context.stmt else ''}
|
||||
{indent(8, context.where_clause.domain_rule)}
|
||||
"""
|
||||
|
||||
class empty:
|
||||
pass
|
||||
|
||||
wb = r"\b"
|
||||
|
||||
def process_type_decl(scope, context):
|
||||
class_name = context.type_id if scope == 'type' else context.entity_head.entity_id
|
||||
|
||||
attributes = []
|
||||
|
||||
if scope == 'entity':
|
||||
|
||||
def get_attributes(nm):
|
||||
ent = schema.entities[nm]
|
||||
if ent.supertypes:
|
||||
yield from get_attributes(ent.supertypes[0])
|
||||
yield from [a.name for a in ent.attributes]
|
||||
yield from [a.name for a in ent.inverse]
|
||||
# redeclared do not need to be printed, because they're emitted
|
||||
# as part of supertype
|
||||
yield from [a[0] for a in ent.derive if isinstance(a[0], str)]
|
||||
|
||||
# @todo derived and inverse attributes
|
||||
attributes = list(get_attributes(class_name))
|
||||
|
||||
def format_rule(domain_rule):
|
||||
return f"""
|
||||
class {class_name}_{domain_rule.rule_label_id}:
|
||||
SCOPE = "{scope}"
|
||||
TYPE_NAME = "{class_name}"
|
||||
RULE_NAME = "{domain_rule.rule_label_id}"
|
||||
|
||||
@staticmethod
|
||||
def __call__(self):
|
||||
{indent(8, (f"{a.lower()} = self.{a}" for a in attributes if re.search(f'{wb}{a.lower()}{wb}', str(domain_rule))))}
|
||||
{indent(8, domain_rule)}
|
||||
"""
|
||||
|
||||
rule_parent = context if scope == 'type' else context.entity_body
|
||||
|
||||
statements = []
|
||||
|
||||
if rule_parent.where_clause:
|
||||
# @todo should we not try to maintain a 1-1 correspondence?
|
||||
statements.extend(map(format_rule, rule_parent.where_clause.branches()))
|
||||
|
||||
if scope == 'entity':
|
||||
def format_derived(derived_attr):
|
||||
slash = "\\"
|
||||
return f"""
|
||||
def calc_{class_name}_{str(derived_attr.attribute_decl.redeclared_attribute.qualified_attribute.attribute_qualifier)[1:] if derived_attr.attribute_decl.redeclared_attribute else derived_attr.attribute_decl}(self):
|
||||
{indent(4, (f"{a.lower()} = self.{a}" for a in attributes if re.search(f'{wb}{a.lower()}{wb}', str(derived_attr.expression))))}
|
||||
{indent(4, f"return {slash}")}
|
||||
{indent(4, derived_attr.expression)}
|
||||
"""
|
||||
if context.entity_body.derive_clause:
|
||||
statements.extend(map(format_derived, context.entity_body.derive_clause.branches()))
|
||||
|
||||
return "\n\n".join(statements)
|
||||
|
||||
def process_domain_rule(context):
|
||||
return f"""
|
||||
assert {context.expression}
|
||||
"""
|
||||
|
||||
def process_expression(context):
|
||||
def wrap(s):
|
||||
s = str(s)
|
||||
if " " in s:
|
||||
s = '(%s)' % s
|
||||
return s
|
||||
|
||||
def concat(a, b, **kwargs):
|
||||
return " ".join(map(str, sum(zip(
|
||||
[None] + a.branches(**kwargs),
|
||||
map(wrap, b.branches(**kwargs))
|
||||
), ())[1:]))
|
||||
|
||||
if context.rel_op_extended:
|
||||
if context.term:
|
||||
# IfcSameValue
|
||||
return concat(context.rel_op_extended, context, allow_multiple=True, exclude=[context.rel_op_extended])
|
||||
else:
|
||||
return concat(context.rel_op_extended, context.simple_expression)
|
||||
elif context.multiplication_like_op:
|
||||
if str(context.multiplication_like_op.branches()[0]) == '||':
|
||||
all_args = {}
|
||||
most_concrete_type = None
|
||||
most_concrete_type_inheritance_chain_length = -1
|
||||
|
||||
for s in context.factor.branches():
|
||||
typename, args = str(s).split('(', 1)
|
||||
args = args[:-1]
|
||||
|
||||
break_points = [[0]]
|
||||
bracket_nesting = 0
|
||||
for i, tk in enumerate(args):
|
||||
if tk in '[(': bracket_nesting += 1
|
||||
if tk in ')]': bracket_nesting -= 1
|
||||
if tk == ',' and bracket_nesting == 0:
|
||||
break_points[-1].append(i)
|
||||
break_points.append([i+1])
|
||||
|
||||
break_points[-1].append(len(args))
|
||||
|
||||
S = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema.name)
|
||||
entity = S.declaration_by_name(typename)
|
||||
entity_attributes = entity.attributes()
|
||||
|
||||
def count_chain_length(ent):
|
||||
length = 0
|
||||
while ent:
|
||||
ent = ent.supertype()
|
||||
length += 1
|
||||
return length
|
||||
|
||||
args = [args[slice(*x)] for x in break_points]
|
||||
|
||||
for i, arg in filter(lambda p: p[1], enumerate(args)):
|
||||
all_args[entity_attributes[i].name()] = arg
|
||||
|
||||
cl = count_chain_length(entity)
|
||||
if cl > most_concrete_type_inheritance_chain_length:
|
||||
most_concrete_type = entity.name()
|
||||
most_concrete_type_inheritance_chain_length = cl
|
||||
|
||||
return f"{most_concrete_type}({', '.join(f'{a[0]}={a[1]}' for a in all_args.items())})"
|
||||
else:
|
||||
return concat(context.multiplication_like_op, context.factor)
|
||||
elif context.add_like_op:
|
||||
if context.factor or len(context.term) > 1:
|
||||
# @todo now sure why this is required (in IfcCrossProduct)
|
||||
# @todo not sure what's going on here, why we have both factor and term as direct child productions of simple_expression (in IfcDotProduct)
|
||||
return concat(context.add_like_op, context, allow_multiple=True, exclude=[context.add_like_op])
|
||||
else:
|
||||
return concat(context.add_like_op, context.term)
|
||||
|
||||
|
||||
def process_interval(context):
|
||||
op0, op1 = context.interval_op.branches()
|
||||
return " ".join(map(str, (
|
||||
context.interval_low,
|
||||
op0,
|
||||
context.interval_item,
|
||||
op1,
|
||||
context.interval_high
|
||||
)))
|
||||
|
||||
|
||||
def simple_concat(context):
|
||||
# simple_factor:
|
||||
# only to join unary op (-) with number literal
|
||||
# primary:
|
||||
# only to join index with qualifyable operand
|
||||
|
||||
def qualifier_position(s):
|
||||
# @todo this is a really ugly hack, can we not depend on stable branch order and why?
|
||||
|
||||
# unary operators
|
||||
if s in ("-", "+", "not"): return -1
|
||||
|
||||
# qualifiers
|
||||
if s and s[0] in ('.', '['): return 1
|
||||
|
||||
# default
|
||||
return 0
|
||||
|
||||
branches = sorted(map(str, context.branches()), key=qualifier_position)
|
||||
|
||||
# sorting no longer necessary as we sort in branches() now
|
||||
# correction: still necessary, apparently.
|
||||
# branches = list(map(str, context.branches()))
|
||||
|
||||
concat = ""
|
||||
if len(branches) == 2 and branches[0] == 'not':
|
||||
concat = " "
|
||||
|
||||
v = concat.join(branches)
|
||||
|
||||
return v
|
||||
|
||||
|
||||
def process_rel_op(context):
|
||||
# @todo the distinction between value comparison and instance comparison
|
||||
if str(context) == "<>" or str(context) == ":<>:":
|
||||
return "!="
|
||||
elif str(context) == "=" or str(context) == ":=:":
|
||||
return "=="
|
||||
|
||||
|
||||
def process_if_stmt(context):
|
||||
s = f"if {context.logical_expression if context.logical_expression.branches() else context.expression}:\n{indent(4, context.stmt.branches())}"
|
||||
if context.else_stmt:
|
||||
s += f"\nelse:\n{indent(4, context.else_stmt.branches())}"
|
||||
return s
|
||||
|
||||
|
||||
def process_repeat_stmt(context):
|
||||
ic = context.repeat_control.increment_control
|
||||
return f"for {ic.variable_id} in range({ic.bound_1}, {ic.bound_2} + 1):\n{indent(4, context.stmt.branches())}"
|
||||
|
||||
|
||||
def process_function_decl(context):
|
||||
arguments = map(str.lower, map(str, context.function_head.formal_parameter.parameter_id.branches(allow_multiple=True)))
|
||||
return f"def {context.function_head.function_id}({', '.join(arguments)}):\n{indent(4, context.algorithm_head.local_decl)}\n{indent(4, context.stmt.branches())}"
|
||||
|
||||
def process_query(context):
|
||||
return f"[{str(context.variable_id).lower()} for {str(context.variable_id).lower()} in {context.aggregate_source} if {context.logical_expression if context.logical_expression and context.logical_expression.branches() else context.expression}]"
|
||||
|
||||
def process_local_variable(context):
|
||||
if context.expression:
|
||||
expr = str(context.expression)
|
||||
if context.parameter_type.generalized_types.general_aggregation_types.general_set_type:
|
||||
expr = re.sub('(\[[^\]]*\])', 'express_set(\\1)', expr)
|
||||
|
||||
return '%s = %s' % (str(context.variable_id).lower(), expr)
|
||||
else:
|
||||
return empty()
|
||||
|
||||
|
||||
def process_function_call(context):
|
||||
nm = f"{context.built_in_function if context.built_in_function else context.function_ref}"
|
||||
args = f"{context.actual_parameter_list if context.actual_parameter_list and context.actual_parameter_list.branches() else ''}"
|
||||
if nm == "exists" and '[' in args:
|
||||
# exists check if it receives a callable to catch IndexError, because express semantics
|
||||
# dictate that out of bounds index returned unknown (IfcTypeObject_WR1)
|
||||
wrap = "lambda: "
|
||||
else:
|
||||
wrap = ""
|
||||
return f"{nm}({wrap}{args})"
|
||||
|
||||
|
||||
def make_lowercase(context):
|
||||
return str(context).lower()
|
||||
|
||||
|
||||
def make_lowercase_if(fn):
|
||||
def inner(context):
|
||||
if fn(context):
|
||||
return make_lowercase(context)
|
||||
return inner
|
||||
|
||||
|
||||
def process_assignment(context):
|
||||
lhs = str(context.general_ref)
|
||||
if context.qualifier:
|
||||
lhs += str(context.qualifier)
|
||||
if m := re.match(r'^([^\[]+)\[([^\[]+)\]$', lhs):
|
||||
# @todo ugly regex hack
|
||||
aggr, index = m.groups()
|
||||
return f"temp = list({aggr})\ntemp[{index}] = {context.expression}\n{aggr} = temp"
|
||||
else:
|
||||
return '%s = %s' % (lhs, context.expression)
|
||||
|
||||
def process_case_action(context):
|
||||
first = context.parent().branches().index(context)
|
||||
pred = "elif" if first else "if"
|
||||
if re.match(r"^'[a-z0-9]+'$", str(context.expression)):
|
||||
# @todo this is yet again an ugly hack
|
||||
lower = '.lower()'
|
||||
else:
|
||||
lower = ''
|
||||
return f"{pred} {context.parent().expression}{lower} == {context.expression}:\n{indent(4, context.stmt.branches())}"
|
||||
|
||||
|
||||
def process_case_statement(context):
|
||||
branches = context.branches(exclude=[getattr(context, v) for v in context.descendants() if not v.startswith('case_action')])
|
||||
if context.stmt and context.stmt.branches():
|
||||
branches += [f"else:\n{indent(4, context.stmt)}"]
|
||||
return'\n'.join(map(str, branches))
|
||||
|
||||
|
||||
def process_aggregate_initializer(context):
|
||||
if context.element.repetition:
|
||||
return '([%s] * %s)' % (context.element.expression, context.element.repetition)
|
||||
else:
|
||||
return '[%s]' % ','.join(map(str, context.element.branches() if context.element else ()))
|
||||
|
||||
# implemented sizeof() function in generated code
|
||||
# codegen_rule("built_in_function/SIZEOF", lambda context: f"len")
|
||||
# @todo
|
||||
codegen_rule("function_call", process_function_call)
|
||||
codegen_rule("actual_parameter_list", lambda context: ','.join(map(str, context.expression.branches() if context.expression else [])))
|
||||
codegen_rule("entity_decl", functools.partial(process_type_decl, 'entity'))
|
||||
codegen_rule("rule_decl", process_rule_decl)
|
||||
codegen_rule("type_decl", functools.partial(process_type_decl, 'type'))
|
||||
codegen_rule("function_decl", process_function_decl)
|
||||
codegen_rule("domain_rule", process_domain_rule)
|
||||
codegen_rule("expression", process_expression)
|
||||
codegen_rule("simple_expression", process_expression)
|
||||
codegen_rule("logical_expression", process_expression)
|
||||
codegen_rule("term", process_expression)
|
||||
codegen_rule("query_expression", process_query)
|
||||
codegen_rule("aggregate_initializer", process_aggregate_initializer)
|
||||
codegen_rule("interval", process_interval)
|
||||
codegen_rule("simple_factor", simple_concat)
|
||||
codegen_rule("primary", simple_concat)
|
||||
codegen_rule("qualifier", simple_concat)
|
||||
codegen_rule("return_stmt", lambda context: 'return %s' % context)
|
||||
codegen_rule("compound_stmt", lambda context: '\n'.join(map(str, context.stmt.branches())))
|
||||
codegen_rule("if_stmt", process_if_stmt)
|
||||
codegen_rule("repeat_stmt", process_repeat_stmt)
|
||||
# codegen_rule("index", lambda context: '**express_index(%s)' % context)
|
||||
codegen_rule("index", lambda context: '[%s - 1]' % context)
|
||||
codegen_rule("group_qualifier", lambda context: empty())
|
||||
codegen_rule("attribute_qualifier", lambda context: '.%s' % context)
|
||||
codegen_rule("rel_op", process_rel_op)
|
||||
codegen_rule("built_in_constant", lambda context: "None" if str(context) == "?" else str(context))
|
||||
codegen_rule("assignment_stmt", process_assignment)
|
||||
codegen_rule("local_variable", process_local_variable)
|
||||
codegen_rule("local_decl", lambda context: '\n'.join(map(str, context.branches())))
|
||||
codegen_rule("general_ref/parameter_ref", make_lowercase)
|
||||
codegen_rule("qualifiable_factor/attribute_ref", make_lowercase_if(lambda context: str(context) not in set(map(str, schema.all_declarations.keys()))))
|
||||
codegen_rule("case_action", process_case_action)
|
||||
codegen_rule("case_stmt", process_case_statement)
|
||||
codegen_rule("escape_stmt", lambda context: "break")
|
||||
|
||||
codegen_rule("XOR", lambda context: "^")
|
||||
codegen_rule("MOD", lambda context: "%")
|
||||
codegen_rule("TRUE", lambda context: "True")
|
||||
codegen_rule("FALSE", lambda context: "False")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
schema = ifcopenshell.express.express_parser.parse(sys.argv[1]).schema
|
||||
ofn = os.path.join(os.path.dirname(__file__), "rules", f"{schema.name}.py")
|
||||
output = open(ofn, "w")
|
||||
|
||||
print("import ifcopenshell", file=output, sep='\n')
|
||||
|
||||
print("""
|
||||
def exists(v):
|
||||
if callable(v):
|
||||
try: return v() is not None
|
||||
except IndexError as e: return False
|
||||
else: return v is not None
|
||||
""", "\n", file=output, sep='\n')
|
||||
print("def nvl(v, default): return v if v is not None else default", "\n", file=output, sep='\n')
|
||||
|
||||
print("sizeof = len", file=output, sep='\n')
|
||||
print("hiindex = len", file=output, sep='\n')
|
||||
print("blength = len", file=output, sep='\n')
|
||||
print("loindex = lambda x: 1", file=output, sep='\n')
|
||||
print("from math import *", file=output, sep='\n')
|
||||
|
||||
# @todo this will get us in trouble when evaluating the truthness
|
||||
print("unknown = 'UNKNOWN'", file=output, sep='\n')
|
||||
|
||||
print("""
|
||||
def usedin(inst, ref_name):
|
||||
if inst is None:
|
||||
return []
|
||||
_, __, attr = ref_name.split('.')
|
||||
def filter():
|
||||
for ref, attr_idx in inst.wrapped_data.file.get_inverse(inst, allow_duplicate=True, with_attribute_indices=True):
|
||||
if ref.wrapped_data.get_attribute_names()[attr_idx].lower() == attr:
|
||||
yield ref
|
||||
return list(filter())
|
||||
|
||||
|
||||
class express_set(set):
|
||||
def __rmul__(self, other):
|
||||
return express_set(set(other) & self)
|
||||
def __add__(self, other):
|
||||
def make_list(v):
|
||||
# Comply with 12.6.3 Union operator
|
||||
if isinstance(v, (list, tuple, set, express_set)):
|
||||
return list(v)
|
||||
else:
|
||||
return [v]
|
||||
return express_set(list(self) + make_list(other))
|
||||
__radd__ = __add__
|
||||
def __repr__(self):
|
||||
return repr(set(self))
|
||||
|
||||
|
||||
def typeof(inst):
|
||||
if not inst:
|
||||
# If V evaluates to indeterminate (?), an empty set is returned.
|
||||
return express_set([])
|
||||
schema_name = inst.is_a(True).split('.')[0].lower()
|
||||
def inner():
|
||||
decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a())
|
||||
while decl:
|
||||
yield '.'.join((schema_name, decl.name().lower()))
|
||||
decl = decl.supertype()
|
||||
return express_set(inner())
|
||||
""", file=output, sep='\n')
|
||||
|
||||
print("class enum_namespace:\n def __getattr__(self, k):\n return k.upper()", "\n", file=output, sep='\n')
|
||||
|
||||
for k, v in schema.enumerations.items():
|
||||
print(f"{k} = enum_namespace()", "\n", file=output, sep='\n')
|
||||
|
||||
for vi in v.values:
|
||||
print(f"{vi.lower()} = {k}.{vi}", "\n", file=output, sep='\n')
|
||||
|
||||
for k in schema.entities.keys():
|
||||
print(f"def {k}(*args, **kwargs): return ifcopenshell.create_entity({k!r}, {schema.name!r}, *args, **kwargs)", "\n", file=output, sep='\n')
|
||||
|
||||
for nm in schema.all_declarations.keys():
|
||||
print(nm)
|
||||
|
||||
tree = ifcopenshell.express.express_parser.to_tree(schema[nm])
|
||||
|
||||
if DEBUG:
|
||||
with open(f"{nm}.json", "w") as f:
|
||||
json.dump(tree, f, indent=2)
|
||||
|
||||
G = to_graph(tree)
|
||||
rule_code = codegen_rule.apply(G)
|
||||
|
||||
if DEBUG:
|
||||
for n in G.nodes.values():
|
||||
if v := n.get('value'):
|
||||
if isinstance(v, str):
|
||||
nl = "\n"
|
||||
es = "\\n"
|
||||
n['label'] = f'<<table cellborder="0" cellpadding="0"><tr><td><b>{n.get("label")}</b></td></tr><tr><td align="left" balign="left">{v.replace("<", "<").replace(">", ">").replace(nl, "<br/>")}</td></tr></table>>'
|
||||
elif isinstance(v, empty):
|
||||
n['label'] = f'<<table cellborder="0" cellpadding="0"><tr><td><b>{n.get("label")}</b></td></tr><tr><td align="left" balign="left">---</td></tr></table>>'
|
||||
|
||||
fn = f"{nm}.dot"
|
||||
write_dot(fn, G)
|
||||
subprocess.call([shutil.which("dot") or "dot", fn, "-O", "-Tpng"])
|
||||
|
||||
print(rule_code, "\n", file=output, sep='\n')
|
||||
|
||||
output.close()
|
||||
@@ -0,0 +1,180 @@
|
||||
import os
|
||||
import ast
|
||||
import collections
|
||||
|
||||
from dataclasses import dataclass
|
||||
from _pytest import assertion
|
||||
|
||||
import ifcopenshell
|
||||
from ifcopenshell.validate import json_logger
|
||||
|
||||
from codegen import indent
|
||||
|
||||
def reverse_compile(s):
|
||||
return s.strip().replace('len(', 'SIZEOF(').replace('assert ', '')
|
||||
|
||||
|
||||
@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"
|
||||
return f"{inst}Rule {self.rule_name}:\n{indent(4, self.rule_definition)}\nViolated by:\n{indent(4, self.violation)}"
|
||||
|
||||
|
||||
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, logger):
|
||||
fn = os.path.join(os.path.dirname(__file__), "rules", f"{f.schema}.py")
|
||||
source = open(fn, "r").read()
|
||||
a = ast.parse(source)
|
||||
assertion.rewrite.rewrite_asserts(mod=a, source=source)
|
||||
cd = compile(a, f"{f.schema}.py", 'exec')
|
||||
scope = {}
|
||||
exec(cd, scope)
|
||||
S = ifcopenshell.ifcopenshell_wrapper.schema_by_name(f.schema)
|
||||
|
||||
rules = list(filter(lambda x: hasattr(x, 'SCOPE'), scope.values()))
|
||||
|
||||
for R in [r for r in rules if r.SCOPE == 'file']:
|
||||
try:
|
||||
R()(f)
|
||||
except Exception as e:
|
||||
ln = e.__traceback__.tb_next.tb_lineno
|
||||
logger.error(str(error(
|
||||
R.__name__,
|
||||
reverse_compile(source.split("\n")[ln-1]),
|
||||
reverse_compile(e.args[0])
|
||||
)))
|
||||
|
||||
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 Exception as e:
|
||||
ln = e.__traceback__.tb_next.tb_lineno
|
||||
logger.error(str(error(
|
||||
R.__name__,
|
||||
reverse_compile(source.split("\n")[ln-1]),
|
||||
reverse_compile(e.args[0]),
|
||||
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)):
|
||||
assert isinstance(type, ifcopenshell.ifcopenshell_wrapper.aggregation_type)
|
||||
ty = type.type_of_element()
|
||||
for v in value:
|
||||
check(v, ty, instance=inst)
|
||||
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:
|
||||
values = list(inst)
|
||||
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)
|
||||
|
||||
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 Exception as e:
|
||||
ln = e.__traceback__.tb_next.tb_lineno
|
||||
logger.error(str(error(
|
||||
R.__name__,
|
||||
reverse_compile(source.split("\n")[ln-1]),
|
||||
reverse_compile(e.args[0]),
|
||||
inst
|
||||
)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
import ifcopenshell
|
||||
|
||||
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
@@ -81,25 +81,33 @@ class Schema:
|
||||
return iter(self.keys)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.types_entities[key]
|
||||
return self.all_declarations[OrderedCaseInsensitiveDict_KeyObject(key)]
|
||||
|
||||
def __init__(self, parsetree):
|
||||
self.tree = parsetree
|
||||
self.name = parsetree.syntax[0][0].simple_id
|
||||
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 parsetree.syntax[0][0].schema_body[0]
|
||||
if d.rule == "declaration" and d.any()[0].rule != "function_decl"
|
||||
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())
|
||||
self.types_entities = {k: v for d in (self.types, self.entities) for k, v in d.items()}
|
||||
self.keys = list(self.types.keys()) + list(self.entities.keys()) + list(self.rules.keys()) + list(self.functions.keys())
|
||||
self.all_declarations = {k: v for d in (self.types, self.entities, self.rules, self.functions) for k, v in d.items()}
|
||||
|
||||
of_type = lambda *types: sort(
|
||||
[(a, b.type) for a, b in self.types.items() if any(isinstance(b.type, ty) for ty in types)]
|
||||
|
||||
@@ -410,7 +410,7 @@ class SchemaClass(codegen.Base):
|
||||
x.begin_schema()
|
||||
|
||||
emitted = set()
|
||||
len_to_emit = len(mapping.schema)
|
||||
len_to_emit = len(mapping.schema) - len(mapping.schema.rules) - len(mapping.schema.functions)
|
||||
|
||||
def write_simpletype(schema_name, name, type):
|
||||
try:
|
||||
@@ -446,6 +446,10 @@ class SchemaClass(codegen.Base):
|
||||
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):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@ import functools
|
||||
from collections import namedtuple
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.express.rule_executor
|
||||
|
||||
named_type = ifcopenshell.ifcopenshell_wrapper.named_type
|
||||
aggregation_type = ifcopenshell.ifcopenshell_wrapper.aggregation_type
|
||||
@@ -265,7 +266,7 @@ def get_entity_attributes(schema, entity):
|
||||
return entity_attrs
|
||||
|
||||
|
||||
def validate(f, logger):
|
||||
def validate(f, logger, express_rules=False):
|
||||
"""
|
||||
For an IFC population model `f` (or filepath to such a file) validate whether the entity attribute values are correctly supplied. As this
|
||||
is a function that is applied after a file has been parsed, certain types of errors in syntax, duplicate
|
||||
@@ -402,6 +403,9 @@ def validate(f, logger):
|
||||
# Restore the original value for 'use_attribute_value_derived'
|
||||
ifcopenshell.ifcopenshell_wrapper.set_feature('use_attribute_value_derived', attribute_value_derived_org)
|
||||
|
||||
if express_rules:
|
||||
ifcopenshell.express.rule_executor.run(f, logger)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import logging
|
||||
@@ -417,8 +421,7 @@ if __name__ == "__main__":
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
print("Validating", fn, file=sys.stderr)
|
||||
|
||||
validate(fn, logger)
|
||||
validate(fn, logger, "--rules" in flags)
|
||||
|
||||
if "--json" in flags:
|
||||
conv = str
|
||||
|
||||
Reference in New Issue
Block a user