Beispiel #1
0
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
Beispiel #4
0
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