def generate_actions(self, state): """ Determine all zero-cost action according to current state :param state: current State of the parser :return: generator of Action items to perform """ if not ((state.buffer or state.stack) and (self.edges_remaining or any( map(self.need_label, state.stack + list(state.buffer))))): yield Actions.Finish if state.stack and not self.need_label(state.stack[-1]): yield Actions.Reduce return self.found = False if state.stack: s0 = state.stack[-1] incoming = self.edges_remaining.intersection(s0.orig_node.incoming) outgoing = self.edges_remaining.intersection(s0.orig_node.outgoing) if not incoming and not outgoing and not self.need_label(s0): yield Actions.Reduce return else: # Check for node label action: if all terminals have already been connected if self.need_label(s0) and not any( e.tag == layer1.EdgeTags.Terminal for e in outgoing): self.found = True yield Actions.Label(0, orig_node=s0.orig_node, oracle=self) # Check for actions to create new nodes for edge in incoming: if edge.parent.ID in self.nodes_remaining and not edge.parent.attrib.get( "implicit" ) and (not edge.attrib.get("remote") or # Allow remote parent if all its children are remote/implicit all( e.attrib.get("remote") or e.child.attrib.get("implicit") for e in edge.parent)): yield self.action(edge, NODE, PARENT) # Node or RemoteNode for edge in outgoing: if edge.child.ID in self.nodes_remaining and edge.child.attrib.get( "implicit") and ( not edge.attrib.get("remote") ): # Allow implicit child if it is not remote yield self.action(edge, NODE, CHILD) # Implicit if len(state.stack) > 1: s1 = state.stack[-2] # Check for node label action: if all terminals have already been connected if self.need_label(s1) and not any( e.tag == layer1.EdgeTags.Terminal for e in self.edges_remaining.intersection( s1.orig_node.outgoing)): self.found = True yield Actions.Label(1, orig_node=s1.orig_node, oracle=self) # Check for actions to create binary edges for edge in incoming: if edge.parent.ID == s1.node_id: yield self.action( edge, EDGE, RIGHT) # RightEdge or RightRemote for edge in outgoing: if edge.child.ID == s1.node_id: yield self.action(edge, EDGE, LEFT) # LeftEdge or LeftRemote elif state.buffer and edge.child.ID == state.buffer[0].node_id and \ len(state.buffer[0].orig_node.incoming) == 1: yield Actions.Shift # Special case to allow getting rid of simple children quickly if not self.found: # Check if a swap is necessary, and how far (if compound swap is enabled) related = dict([(edge.child.ID, edge) for edge in outgoing] + [(edge.parent.ID, edge) for edge in incoming]) distance = None # Swap distance (how many nodes in the stack to swap) for i, s in enumerate( state.stack[-3::-1], start=1 ): # Skip top two: checked above, not related edge = related.pop(s.node_id, None) if edge is not None: swap = 'regular' max_swap = 3 if not swap == COMPOUND: # We have no chance to reach it, so stop trying self.remove(edge) continue if distance is None and swap == COMPOUND: # Save the first one distance = min( i, max_swap ) # Do not swap more than allowed if not related: # All related nodes are in the stack yield Actions.Swap(distance) return if state.buffer and not self.found: yield Actions.Shift