def gather_used_variables(model, variables, rewrite_table): """Gather the variables that are used within the statements""" class_name = model.__class__.__name__ if class_name in ["Assignment", "Expression", "ExprPrec1", "ExprPrec2", "ExprPrec3", "ExprPrec4"]: gather_used_variables(model.left, variables, rewrite_table) if model.right is not None: gather_used_variables(model.right, variables, rewrite_table) elif class_name == "ExpressionRef": if model.index is None: variables.update({(model.ref, None)}) else: # The index might hide a variable within it. variables.update({(model.ref, get_instruction(model.index, rewrite_table))}) gather_used_variables(model.index, variables, rewrite_table) elif class_name == "VariableRef": if model.index is None: variables.update({(model.var.name, None)}) else: # The index might hide a variable within it. variables.update({(model.var.name, get_instruction(model.index, rewrite_table))}) gather_used_variables(model.index, variables, rewrite_table) elif class_name == "Primary" and model.ref is not None: return gather_used_variables(model.ref, variables, rewrite_table) else: return set([])
def annotate_used_variables(o, global_variables, rewrite_table=None): """Add the used variables to Composites, Expressions and Assignments. The rewrite table is used to compensate for variable changes in composite statements.""" class_name = o.__class__.__name__ if class_name == "Class": for sm in o.statemachines: annotate_used_variables(sm, global_variables, rewrite_table) if class_name == "StateMachine": for transition in o.transitions: annotate_used_variables(transition, global_variables, rewrite_table) elif class_name == "Transition": o.variables = set([]) o.lock_requests = set([]) for statement in o.statements: annotate_used_variables(statement, global_variables, rewrite_table) o.variables.update(statement.variables) o.lock_requests.update(statement.lock_requests) elif class_name == "Composite": o.variables = set([]) o.lock_requests = set([]) rewrite_table = {} # Annotate the guard variable first. annotate_used_variables(o.guard, global_variables, rewrite_table) o.variables.update(o.guard.variables) o.lock_requests.update(o.guard.lock_requests) # Annotate the assignments and update the rewrite table. for assignment in o.assignments: annotate_used_variables(assignment, global_variables, rewrite_table) o.variables.update(assignment.variables) o.lock_requests.update(assignment.lock_requests) # Add a rewrite rule, changing the left-hand side into the right-hand side. # Note that in the left hand side, we only rewrite the index, if available. target_variable = assignment.left.var.name if assignment.left.index is not None: target_variable += "[" + get_instruction(assignment.left.index, rewrite_table) + "]" target_rewrite = get_instruction(assignment.right, rewrite_table) if ' ' in target_rewrite: rewrite_table[target_variable] = "(%s)" % target_rewrite else: rewrite_table[target_variable] = "%s" % target_rewrite elif class_name in ["Expression", "Assignment"]: o.variables = set([]) gather_used_variables(o, o.variables, rewrite_table) o.lock_requests = set([(name, sub) for name, sub in o.variables if name in global_variables])
def annotate_statement(s): """Transform the statement such that it provides all the data required for the code conversion""" class_name = s.__class__.__name__ if class_name == "Composite": # Always make sure that a guard expression exists, defaulting to a "True" expression. s.guard = get_true_expression() if s.guard is None else annotate_statement(s.guard) s.assignments = [annotate_statement(a) for a in s.assignments] elif class_name == "Expression": s.string_expression = get_instruction(s) # Provide equality and hash functions such that the expressions can be added to a set. s.__eq__ = lambda self, other: self.string_expression == other.string_expression s.__hash__ = lambda self: hash(self.__repr__()) # Give all statements a readable string representation corresponding to the associated java code. s.__repr__ = lambda self: get_instruction(self) return s
def get_true_expression(): """An expression that represents an object that is always true, used as replacement for empty guards""" return type("Expression", (object,), { "left": type("Primary", (object,), {"value": True, "sign": "", "body": None, "ref": None})(), "op": "", "right": None, "string_expression": "true", "is_trivially_satisfiable": True, "is_trivially_unsatisfiable": False, "lock_requests": set([]), "__repr__": lambda self: get_instruction(self), })()
def get_guard_statement(model): """Construct an or-code-clause encapsulating all guard expressions for the target decision block""" if model.__class__.__name__ == "TransitionBlock": return get_instruction(model.guard_expression) else: # Remove statements that have the same solution space. duplicate_expressions = set([]) for e_1 in model.guard_expressions: for e_2 in model.guard_expressions: if e_1 == e_2: break if z3_check_equality(e_1.z3py_expression, e_2.z3py_expression, True): duplicate_expressions.add(e_1) guard_expressions = [ e for e in model.guard_expressions if e not in duplicate_expressions ] # Check if expressions are superfluous, i.e., contained within another statement. implies_truth_matrix = {} for e_1 in guard_expressions: implies_truth_matrix[e_1] = {} for e_2 in guard_expressions: if e_1 == e_2: implies_truth_matrix[e_1][e_2] = False else: implies_truth_matrix[e_1][e_2] = z3_check_implies( e_1.z3py_expression, e_2.z3py_expression, True) # If any of the values is true, remove the guard expression from the result, since it is included in another. target_expressions = [] for e_1 in guard_expressions: if e_1 not in duplicate_expressions and not any( implies_truth_matrix[e_1].values()): target_expressions.append(e_1) # Construct a disjunction of statements. Brackets are not needed because of the precedence order. return " || ".join([get_instruction(e) for e in target_expressions])
def construct_decision_code(model, sm, no_current_state): """Convert the decision structure to Java code""" model_class = model.__class__.__name__ if model_class == "TransitionBlock": statements = [ construct_decision_code(s, sm, no_current_state) for s in model.statements ] statements = [s for s in statements if s != ""] return java_transition_template.render( statements=statements, target=model.target, state_machine_name=sm.name, always_fails=model.always_fails, comment=model.comment, add_performance_counters=settings.add_performance_counter, render_current_state_variable=no_current_state) elif model_class == "CompositeBlock": return java_composite_template.render(model=model, _c=sm.parent_class) elif model_class == "AssignmentBlock": return java_assignment_template.render(model=model, _c=sm.parent_class) elif model_class == "ExpressionBlock": return java_expression_template.render(model=model, _c=sm.parent_class) elif model_class == "ActionRef": return "// Execute action [%s]\n" % model.act.name elif model_class == "NonDeterministicBlock": # Several of the choices may have the same conversion string. Filter these out and merge. choices = [ construct_decision_code(choice, sm, no_current_state) for choice in model.choice_blocks ] choices.sort(key=lambda v: v[0]) # If only one choice remains, there is no reason to include an entire block. if len(choices) == 1: return choices[0] return java_pick_randomly_template.render(choices=choices, _c=sm.parent_class) elif model_class == "DeterministicIfThenElseBlock": # Order the choices such that the generated code is always the same. choices = [(construct_decision_code(choice, sm, no_current_state), get_guard_statement(choice)) for choice in model.choice_blocks] choices.sort(key=lambda v: v[0]) # Does the combination of all the guards always evaluate to true? else_choice = None if model.close_with_else: else_choice = choices[-1] choices = choices[:-1] return java_if_then_else_template.render(choices=choices, else_choice=else_choice, model=model, _c=sm.parent_class) elif model_class == "DeterministicCaseDistinctionBlock": # Several of the choices may have the same conversion string. Filter these out and merge. choices = [ (target, construct_decision_code(choice, sm, no_current_state), choice.comment if choice.__class__.__name__ == "TransitionBlock" else None) for (target, choice) in model.choice_blocks ] choices.sort(key=lambda v: v[1]) subject_expression = get_instruction(model.subject_expression) default_decision_tree = construct_decision_code( model.default_decision_tree, sm, no_current_state) return java_case_distinction_template.render( subject_expression=subject_expression, default_decision_tree=default_decision_tree, choices=choices, model=model, _c=sm.parent_class)