def tag_reachable_scripts(cls, scratch): """Tag each script with attribute reachable. The reachable attribute will be set false for any script that does not begin with a hat block. Additionally, any script that begins with a `when I receive` block whose event-name doesn't appear in a corresponding broadcast block is marked as unreachable. """ scratch.kelp_prepared = True reachable = set() untriggered_events = {} # Initial pass to find reachable and potentially reachable scripts for script in cls.iter_scripts(scratch): if not isinstance(script, kurt.Comment): starting_type = HairballPlugin.script_start_type(script) if starting_type == HairballPlugin.NO_HAT: script.reachable = False elif starting_type == HairballPlugin.HAT_WHEN_I_RECEIVE: script.reachable = False # Value will be updated if reachable message = script.blocks[0].args[0].lower() untriggered_events.setdefault(message, set()).add(script) else: script.reachable = True reachable.add(script) # Expand reachable states based on broadcast events while reachable: for event in HairballPlugin.get_broadcast_events(reachable.pop()): if event in untriggered_events: for script in untriggered_events.pop(event): script.reachable = True reachable.add(script)
def partition_scripts(scripts, start_type1, start_type2): """Return two lists of scripts out of the original `scripts` list. Scripts that begin with a `start_type1` or `start_type2` blocks are returned first. All other scripts are returned second. """ match, other = [], [] for script in scripts: if (HairballPlugin.script_start_type(script) == start_type1 or HairballPlugin.script_start_type(script) == start_type2): match.append(script) else: other.append(script) return match, other
def partition_scripts(scripts, start_type): """Return two lists of scripts out of the original scripts list. Scripts that begin with a 'start_type' block are returned first. All other scripts are returned second. """ match, other = [], [] for script in scripts: if HairballPlugin.script_start_type(script) == start_type: match.append(script) else: other.append(script) return match, other
def attribute_state(cls, scripts, attribute): """Return the state of the scripts for the given attribute. If there is more than one `when green flag clicked` script and they both modify the attribute, then the attribute is considered to not be initialized. """ green_flag, other = partition_scripts(scripts, cls.HAT_GREEN_FLAG, cls.HAT_CLONE) # add_scripts(green_flag, other, cls.HAT_CLONE) block_set = cls.BLOCKMAPPING[attribute] state = cls.STATE_NOT_MODIFIED # TODO: Any regular broadcast blocks encountered in the initialization # zone should be added to this loop for conflict checking. for script in green_flag: in_zone = True for name, level, _ in cls.iter_blocks(script.blocks): if name == 'broadcast %e and wait': # TODO: Follow the broadcast and wait scripts that occur in # the initialization zone in_zone = False if (name, 'absolute') in block_set: if in_zone and level == 0: # Success! if state == cls.STATE_NOT_MODIFIED: state = cls.STATE_INITIALIZED elif HairballPlugin.script_start_type(script) == cls.HAT_GREEN_FLAG: # Multiple when green flag clicked conflict state = cls.STATE_MODIFIED elif in_zone: continue # Conservative ignore for nested absolutes else: state = cls.STATE_MODIFIED break # The state of the script has been determined elif (name, 'relative') in block_set: state = cls.STATE_MODIFIED break if state != cls.STATE_NOT_MODIFIED: return state # Check the other scripts to see if the attribute was ever modified for script in other: if not isinstance(script, kurt.Comment): for name, _, _ in cls.iter_blocks(script.blocks): if name in [x[0] for x in block_set]: return cls.STATE_MODIFIED return cls.STATE_NOT_MODIFIED