class xpattern_state_manager: """ And aggregation of multiple state machines, one for each registered pattern """ PARSER = XPatternParser.new() def __init__(self, xpatterns, nss): if not hasattr(xpatterns[0], "match"): self._xpatterns = [ (p, self.PARSER.parse(p)) for p in xpatterns ] else: self._xpatterns = [ (repr(xp), self.PARSER.parse(p)) for p in xpatterns ] self._machines = [ xpattern_state_machine(repr_xp, xp, nss) for repr_xp, xp in self._xpatterns ] return def event(self, is_start, ns, local): for machine in self._machines: machine.event(is_start, ns, local) #FIXME: Slow and clumsy self.entering_xpatterns = [] self.leaving_xpatterns = [] self.current_xpatterns = [] for m in self._machines: self.entering_xpatterns.extend(m.entering_xpatterns) self.leaving_xpatterns.extend(m.leaving_xpatterns) self.current_xpatterns.extend(m.current_xpatterns) #print "manager event", (self.entering_xpatterns, self.leaving_xpatterns, self.current_xpatterns) return def status(self): """ 1 if currently within an XPattern, 0 if not Calling code might also want to just check self.current_xpatterns directly """ return not not self.current_xpatterns
def match(self,args): if not len(args): self.outputHandler.display_error("'match' requires atleast one argument") return expr = string.join(args,',') con = self._copyContext(self.context) try: pattern = parser.new().parse(expr) rt = pattern.match(con,con.node) self.outputHandler.display_expressionResults(expr,rt) except SyntaxError: try: etype, value, tb = sys.exc_info() self.outputHandler.display_exception(etype,value,None) finally: etype = value = tb = None
class xpattern_state_machine: """ A simple state machine that interprets XPatterns A state is "live" when it represents the successful completion of an XPattern. """ PARSER = XPatternParser.new() def __init__(self, repr_xp, xp, nss): self._state_table = {START_STATE: {}} self._live_states = {} self._attrib_tests = {} self._ignored_subtree_states = [] self._push_states = [] self._substate_depth = 0 #States that should be hooked into for matching later XPatterns self._hook_states = {} newest_state = START_STATE last_state = START_STATE for subpat in xp.patterns: steps = subpat.steps[:] steps.reverse() for (step_count, (axis_type, node_test, ancestor)) in enumerate(steps): #Note: XSLT patterns only allow child or attribute axis handled = False attrib_test = None if axis_type == Node.ATTRIBUTE_NODE: if (isinstance(node_test, LocalNameTest) or isinstance(node_test, QualifiedNameTest) or isinstance(node_test, NamespaceTest) or isinstance(node_test, PrincipalTypeTest)): attrib_test = node_test start_event = (1, ATTRIB, None) end_event = (0, ATTRIB, None) handled = True elif isinstance(node_test, DocumentNodeTest): start_event = (1, None, None) end_event = (0, None, None) handled = True elif isinstance(node_test, LocalNameTest): if node_test.nodeType == Node.ELEMENT_NODE: start_event = (1, None, node_test._name) end_event = (0, None, node_test._name) handled = True elif isinstance(node_test, QualifiedNameTest): if node_test.nodeType == Node.ELEMENT_NODE: ns = nss[node_test._prefix] start_event = (1, ns, node_test._localName) end_event = (0, ns, node_test._localName) handled = True elif isinstance(node_test, PrincipalTypeTest): if node_test.nodeType == Node.ELEMENT_NODE: start_event = (1, ANY, EXPLICIT) end_event = (0, ANY, EXPLICIT) handled = True elif isinstance(node_test, NamespaceTest): if node_test.nodeType == Node.ELEMENT_NODE: ns = nss[node_test._prefix] start_event = (1, ns, ANY) end_event = (0, ns, ANY) handled = True if not (handled): import sys print >> sys.stderr, "Pattern step not supported:", ( axis_type, node_test, ancestor), "Node test class", node_test.__class__ continue #Say the full input is /u/v/w|/a/b/c and we're currently #Working the sub-pattern /a/b/c #top_state is the pattern #last_state is the last last_state = newest_state newest_state += 1 if not (step_count): if attrib_test: # #Because we want to treat @X as */@X # start_event = (1, ANY, EXPLICIT) # end_event = (0, ANY, EXPLICIT) attribute_test_state = newest_state self._state_table[newest_state] = {end_event: POP_STATE} if isinstance(node_test, DocumentNodeTest): self._state_table[START_STATE][ start_event] = newest_state else: for state in self._state_table: self._state_table[state][ start_event] = newest_state self._hook_states[newest_state] = start_event self._push_states.append(newest_state) #if attrib_test: #Because we want to treat @X as */@X # newest_state += 1 # start_event = (1, ATTRIB, None) # end_event = (0, ATTRIB, None) # self._state_table[newest_state] = {end_event: newest_state - 1} # self._state_table[newest_state -1][start_event] = newest_state else: if attrib_test: attribute_test_state = newest_state self._state_table[newest_state] = { end_event: parent_start_element_state } self._state_table[parent_start_element_state][ start_event] = newest_state for state in self._hook_states: self._state_table[newest_state][ self._hook_states[state]] = state start_element_state = newest_state #complete_state = top_state #The state representing completion of an XPattern if step_count and not ancestor and not isinstance( node_test, PrincipalTypeTest): #Insert a state, which handles any child element #Not explicitly matching some other state (so that #/a/b/c is not a mistaken match for XPattern /a/c) start_event = (1, ANY, IMPLICIT) end_event = (0, ANY, IMPLICIT) newest_state += 1 self._state_table[newest_state] = {} self._state_table[parent_start_element_state][ start_event] = newest_state self._state_table[newest_state][ end_event] = parent_start_element_state self._ignored_subtree_states.append(newest_state) #self._hook_states[newest_state] = start_event for state in self._hook_states: self._state_table[newest_state][ self._hook_states[state]] = state parent_start_element_state = start_element_state self._live_states[start_element_state] = repr_xp if attrib_test: self._attrib_tests[attribute_test_state] = Compile( '@' + repr(node_test)) self._state = START_STATE self.entering_xpatterns = [] self.leaving_xpatterns = [] self.current_xpatterns = [] self.tree_depth = 0 self.depth_marks = [] self.state_stack = [] #print self._state_table; print self._live_states; print self._push_states; print self._attrib_tests return def event(self, is_start, ns, local): """ Register an event and effect any state transitions found in the state table """ #We only have a chunk ready for the handler in #the explicit case below self.entering_xpatterns = [] self.leaving_xpatterns = [] self.attribute_test = None self.tree_depth += is_start and 1 or -1 #print "event", (is_start, ns, local), self._state, self.state_stack, self.tree_depth, self.depth_marks #An end event is never significant unless we know we're expecting it if not is_start and self.depth_marks and self.tree_depth != self.depth_marks[ -1]: return self._state lookup_from = self._state_table[self._state] #FIXME: second part should be an element node test "*", should not match, say, start document if not lookup_from.has_key( (is_start, ns, local)) and (ns, local) == (None, None): return self._state def process_transition(new_state): if new_state != self._state: if is_start and new_state in self._push_states: self.state_stack.append(self._state) elif new_state == POP_STATE: new_state = self.state_stack.pop() self._state = new_state if is_start: self.depth_marks.append(self.tree_depth - 1) elif self.depth_marks: self.depth_marks.pop() if is_start: attrib_state = self._state_table[new_state].get( (1, ATTRIB, None)) if attrib_state is not None: self.attribute_test = self._attrib_tests[attrib_state] if ((is_start, ns, local) in lookup_from) or ((is_start, ns, ANY) in lookup_from): try: new_state = lookup_from[(is_start, ns, local)] except KeyError: new_state = lookup_from[(is_start, ns, ANY)] if (new_state in self._live_states): #Entering a defined XPattern chunk self.entering_xpatterns.append(self._live_states[new_state]) self.current_xpatterns.append(self._live_states[new_state]) elif (self._state in self._live_states): #Leaving a defined XPattern chunk self.leaving_xpatterns.append(self.current_xpatterns.pop()) process_transition(new_state) elif (is_start, ANY, EXPLICIT) in lookup_from: new_state = lookup_from[(is_start, ANY, EXPLICIT)] if (new_state in self._live_states): #Entering a defined XPattern chunk self.entering_xpatterns.append(self._live_states[new_state]) self.current_xpatterns.append(self._live_states[new_state]) elif (self._state in self._live_states): #Leaving a defined XPattern chunk self.leaving_xpatterns.append(self.current_xpatterns.pop()) process_transition(new_state) elif (is_start, ANY, IMPLICIT) in lookup_from: new_state = lookup_from[(is_start, ANY, IMPLICIT)] process_transition(new_state) else: #Identity transition: from a state back to itself process_transition(self._state) #print self.entering_xpatterns,self.leaving_xpatterns,self.current_xpatterns return self._state def status(self): """ 1 if currently within an XPattern, 0 if not Calling code might also want to just check self.current_xpatterns directly """ return not not self.current_xpatterns
class xpattern_state_machine: """ A simple state machine that interprets XPatterns A state is "live" when it represents the successful completion of an XPattern. """ PARSER = XPatternParser.new() def __init__(self, repr_xp, xp, nss): self._state_table = {START_STATE: {}} self._live_states = {} self._ignored_subtree_states = [] self._substate_depth = 0 newest_state = START_STATE last_state = START_STATE for subpat in xp.patterns: steps = subpat.steps[:] steps.reverse() for (step_count, (axis_type, node_test, ancestor)) in enumerate(steps): if isinstance(node_test, DocumentNodeTest): start_event = (1, None, None) end_event = (0, None, None) elif isinstance(node_test, LocalNameTest): if node_test.nodeType == Node.ELEMENT_NODE: start_event = (1, None, node_test._name) end_event = (0, None, node_test._name) else: continue elif isinstance(node_test, QualifiedNameTest): if node_test.nodeType == Node.ELEMENT_NODE: ns = nss[node_test._prefix] start_event = (1, ns, node_test._localName) end_event = (0, ns, node_test._localName) else: continue elif isinstance(node_test, PrincipalTypeTest): if node_test.nodeType == Node.ELEMENT_NODE: start_event = (1, ANY, EXPLICIT) end_event = (0, ANY, EXPLICIT) else: continue elif isinstance(node_test, NamespaceTest): if node_test.nodeType == Node.ELEMENT_NODE: ns = nss[node_test._prefix] start_event = (1, ns, ANY) end_event = (0, ns, ANY) else: continue else: import sys; print >> sys.stderr, "Pattern step not supported:", (axis_type, node_test, ancestor), "Node test class", node_test.__class__ continue if self._state_table[last_state].has_key(start_event): top_state = self._state_table[last_state][start_event] else: newest_state += 1 top_state = newest_state self._state_table[top_state] = {} self._state_table[last_state][start_event] = top_state self._state_table[top_state][end_event] = last_state last_state = top_state complete_state = top_state #The state representing completion of an XPattern if step_count and not ancestor: #Insert a state, which handles any child element #Not explicitly matching some other state (so that #/a/b/c is not a mistaken match for XPattern /a/c) start_event = (1, ANY, IMPLICIT) end_event = (0, ANY, IMPLICIT) newest_state += 1 self._state_table[newest_state] = {} self._state_table[parent_start_element_event][start_event] = newest_state self._state_table[newest_state][end_event] = parent_start_element_event self._ignored_subtree_states.append(newest_state) parent_start_element_event = top_state self._live_states[top_state] = repr_xp #print self._state_table #print self._live_states self._state = START_STATE self.entering_xpatterns = [] self.leaving_xpatterns = [] self.current_xpatterns = [] self.tree_depth = 0 self.depth_marks = [] return def event(self, is_start, ns, local): """ Register an event and effect ant state transitions found in the state table """ #We only have a chunk ready for the handler in #the explicit case below self.entering_xpatterns = [] self.leaving_xpatterns = [] self.tree_depth += is_start and 1 or -1 #print "event", (is_start, ns, local), self._state, self.tree_depth, self.depth_marks #An end event is never significant unless we know we're expecting it if not is_start and self.depth_marks and self.tree_depth != self.depth_marks[-1]: return self._state lookup_from = self._state_table[self._state] #FIXME: second part should be an element node test "*", should not match, say, start document if not lookup_from.has_key((is_start, ns, local)) and (ns, local) == (None, None): return self._state if lookup_from.has_key((is_start, ns, local)) or lookup_from.has_key((is_start, ns, ANY)): try: new_state = lookup_from[(is_start, ns, local)] except KeyError: new_state = lookup_from[(is_start, ns, ANY)] if (new_state in self._live_states): #Entering a defined XPattern chunk self.entering_xpatterns.append(self._live_states[new_state]) self.current_xpatterns.append(self._live_states[new_state]) elif (self._state in self._live_states): #Leaving a defined XPattern chunk self.leaving_xpatterns.append(self.current_xpatterns.pop()) if is_start: self.depth_marks.append(self.tree_depth - 1) else: self.depth_marks.pop() self._state = new_state elif lookup_from.has_key((is_start, ANY, EXPLICIT)): new_state = lookup_from[(is_start, ANY, EXPLICIT)] if (new_state in self._live_states): #Entering a defined XPattern chunk self.entering_xpatterns.append(self._live_states[new_state]) self.current_xpatterns.append(self._live_states[new_state]) elif (self._state in self._live_states): #Leaving a defined XPattern chunk self.leaving_xpatterns.append(self.current_xpatterns.pop()) self._state = new_state if is_start: self.depth_marks.append(self.tree_depth - 1) else: self.depth_marks.pop() elif lookup_from.has_key((is_start, ANY, IMPLICIT)): new_state = lookup_from[(is_start, ANY, IMPLICIT)] self._state = new_state if is_start: self.depth_marks.append(self.tree_depth - 1) else: self.depth_marks.pop() #print self.entering_xpatterns,self.leaving_xpatterns,self.current_xpatterns return self._state def status(self): """ 1 if currently within an XPattern, 0 if not Calling code might also want to just check self.current_xpatterns directly """ return not not self.current_xpatterns