def _parse_actions(self, actions): """ Analyze the given list of actions and extract pre/post actions, i.e. actions executed before and after handling children. :rtype: (list[EnvAction], list[EnvAction]) """ actions = list(actions) # If present, allow Do actions to come before SetInitialEnv self.pre_initial_env_actions = [] if any(isinstance(a, SetInitialEnv) for a in actions): while actions and isinstance(actions[0], Do): self.pre_initial_env_actions.append(actions.pop(0)) # After that, allow one call to SetInitialEnv self.initial_env = None if actions and isinstance(actions[0], SetInitialEnv): self.initial_env = actions.pop(0) pre, post = lsplit_by( lambda a: not isinstance(a, HandleChildren), actions ) # Get rid of the HandleChildren delimiter action post = post and post[1:] self.pre_actions = pre self.post_actions = post self.actions = self.pre_actions + self.post_actions
def _parse_actions(self, actions: List[EnvAction]) -> None: """ Analyze the given list of actions and extract pre/post actions, i.e. actions executed before and after handling children. """ def count(cls: Type[EnvAction], sequence: List[EnvAction]) -> int: """ Return the number of ``cls`` instances in ``sequence``. """ return len([a for a in sequence if isinstance(a, cls)]) # If present, allow Do actions to come before SetInitialEnv first_actions = [] if any(isinstance(a, SetInitialEnv) for a in actions): while actions and isinstance(actions[0], Do): first_actions.append(actions.pop(0)) # After that, allow at most one call to SetInitialEnv self.initial_env = None if actions and isinstance(actions[0], SetInitialEnv): check_source_language( isinstance(actions[0], SetInitialEnv), 'The initial environment must come first after the potential' ' do()' ) self.initial_env = cast(SetInitialEnv, actions.pop(0)) first_actions.append(self.initial_env) check_source_language( count(SetInitialEnv, actions) == 0, "set_initial_env can only be preceded by do()" ) check_source_language( count(AddEnv, actions) <= 1, "There can be at most one call to add_env()" ) # Separate actions that must occur before and after the handling of # children. Get also rid of the HandleChildren delimiter action. pre, post = lsplit_by(lambda a: not isinstance(a, HandleChildren), actions) post = post and post[1:] check_source_language( count(AddEnv, post) == 0, 'add_env() must occur before processing children' ) self.pre_actions = first_actions + pre self.post_actions = post self.actions = self.pre_actions + self.post_actions
def construct(self): check_multiple([ (self.pred_property.type.matches(T.Bool), 'Predicate property must return a boolean, got {}'.format( self.pred_property.type.dsl_name)), (self.pred_property.struct.matches(T.root_node), 'Predicate property must belong to a subtype of {}'.format( T.root_node.dsl_name)), ]) # Separate logic variable expressions from extra argument expressions exprs = [construct(e) for e in self.exprs] logic_var_exprs, closure_exprs = funcy.lsplit_by( lambda e: e.type == T.LogicVar, exprs) check_source_language( len(logic_var_exprs) > 0, "Predicate instantiation should have at " "least one logic variable expression") check_source_language( all(e.type != T.LogicVar for e in closure_exprs), 'Logic variable expressions should be grouped at the beginning,' ' and should not appear after non logic variable expressions') # Make sure this predicate will work on clean logic variables logic_var_exprs = [ResetLogicVar(expr) for expr in logic_var_exprs] # Compute the list of arguments to pass to the property (Self # included). args = ( [Argument(names.Name('Self'), self.pred_property.struct.entity)] + self.pred_property.natural_arguments) # Then check that 1) all extra passed actuals match what the property # arguments expect and that 2) arguments left without an actual have a # default value. default_passed_args = 0 for i, (expr, arg) in enumerate(zip_longest(exprs, args)): if expr is None: check_source_language( arg.default_value is not None, 'Missing an actual for argument #{} ({})'.format( i, arg.name.lower)) default_passed_args += 1 continue check_source_language( arg is not None, 'Too many actuals: at most {} expected, got {}'.format( len(args), len(exprs))) if expr.type == T.LogicVar: check_source_language( arg.type.matches(T.root_node.entity), "Argument #{} of predicate " "is a logic variable, the corresponding property formal " "has type {}, but should be a descendent of {}".format( i, arg.type.dsl_name, T.root_node.entity.dsl_name)) else: check_source_language( expr.type.matches(arg.type), "Argument #{} of predicate " "has type {}, should be {}".format(i, expr.type.dsl_name, arg.type.dsl_name)) DynamicVariable.check_call_bindings(self.pred_property, 'In predicate property {prop}') # Append dynamic variables to embed their values in the closure closure_exprs.extend( construct(dynvar) for dynvar in self.pred_property.dynamic_vars) pred_id = self.pred_property.do_generate_logic_predicate( tuple(e.type for e in closure_exprs), default_passed_args) args = " ({})".format(', '.join( ["{}" for _ in range(len(closure_exprs))])) if closure_exprs else "" predicate_expr = untyped_literal_expr( f"Create_{pred_id}_Predicate{args}", operands=closure_exprs) return Predicate.Expr(self.pred_property, pred_id, logic_var_exprs, predicate_expr, abstract_expr=self)