10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""
20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiHyperParser
30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi===========
40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiThis module defines the HyperParser class, which provides advanced parsing
50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiabilities for the ParenMatch and other extensions.
60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiThe HyperParser uses PyParser. PyParser is intended mostly to give information
70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yion the proper indentation of code. HyperParser gives some information on the
80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yistructure of code, used by extensions to help the user.
90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""
100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport string
120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport keyword
130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom idlelib import PyParse
140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass HyperParser:
160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def __init__(self, editwin, index):
180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Initialize the HyperParser to analyze the surroundings of the given
190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        index.
200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """
210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.editwin = editwin
230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.text = text = editwin.text
240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        parser = PyParse.Parser(editwin.indentwidth, editwin.tabwidth)
260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        def index2line(index):
280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return int(float(index))
290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        lno = index2line(text.index(index))
300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not editwin.context_use_ps1:
320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for context in editwin.num_context_lines:
330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                startat = max(lno - context, 1)
340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                startatindex = repr(startat) + ".0"
350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                stopatindex = "%d.end" % lno
360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # We add the newline because PyParse requires a newline at end.
370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # We add a space so that index won't be at end of line, so that
380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # its status will be the same as the char before it, if should.
390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                parser.set_str(text.get(startatindex, stopatindex)+' \n')
400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                bod = parser.find_good_parse_start(
410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                          editwin._build_char_in_string_func(startatindex))
420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if bod is not None or startat == 1:
430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    break
440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            parser.set_lo(bod or 0)
450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            r = text.tag_prevrange("console", index)
470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if r:
480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                startatindex = r[1]
490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                startatindex = "1.0"
510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            stopatindex = "%d.end" % lno
520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # We add the newline because PyParse requires a newline at end.
530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # We add a space so that index won't be at end of line, so that
540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # its status will be the same as the char before it, if should.
550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            parser.set_str(text.get(startatindex, stopatindex)+' \n')
560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            parser.set_lo(0)
570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # We want what the parser has, except for the last newline and space.
590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.rawtext = parser.str[:-2]
600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # As far as I can see, parser.str preserves the statement we are in,
610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # so that stopatindex can be used to synchronize the string with the
620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # text box indices.
630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.stopatindex = stopatindex
640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.bracketing = parser.get_last_stmt_bracketing()
650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # find which pairs of bracketing are openers. These always correspond
660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # to a character of rawtext.
670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.isopener = [i>0 and self.bracketing[i][1] > self.bracketing[i-1][1]
680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                         for i in range(len(self.bracketing))]
690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.set_index(index)
710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def set_index(self, index):
730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Set the index to which the functions relate. Note that it must be
740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        in the same statement.
750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """
760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        indexinrawtext = \
770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            len(self.rawtext) - len(self.text.get(index, self.stopatindex))
780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if indexinrawtext < 0:
790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise ValueError("The index given is before the analyzed statement")
800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.indexinrawtext = indexinrawtext
810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # find the rightmost bracket to which index belongs
820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.indexbracket = 0
830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        while self.indexbracket < len(self.bracketing)-1 and \
840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi              self.bracketing[self.indexbracket+1][0] < self.indexinrawtext:
850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.indexbracket += 1
860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.indexbracket < len(self.bracketing)-1 and \
870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and \
880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           not self.isopener[self.indexbracket+1]:
890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.indexbracket += 1
900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def is_in_string(self):
920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Is the index given to the HyperParser is in a string?"""
930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # The bracket to which we belong should be an opener.
940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # If it's an opener, it has to have a character.
950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return self.isopener[self.indexbracket] and \
960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi               self.rawtext[self.bracketing[self.indexbracket][0]] in ('"', "'")
970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def is_in_code(self):
990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Is the index given to the HyperParser is in a normal code?"""
1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return not self.isopener[self.indexbracket] or \
1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi               self.rawtext[self.bracketing[self.indexbracket][0]] not in \
1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                                                ('#', '"', "'")
1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def get_surrounding_brackets(self, openers='([{', mustclose=False):
1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """If the index given to the HyperParser is surrounded by a bracket
1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        defined in openers (or at least has one before it), return the
1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        indices of the opening bracket and the closing bracket (or the
1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        end of line, whichever comes first).
1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        If it is not surrounded by brackets, or the end of line comes before
1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        the closing bracket and mustclose is True, returns None.
1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """
1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        bracketinglevel = self.bracketing[self.indexbracket][1]
1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        before = self.indexbracket
1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        while not self.isopener[before] or \
1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi              self.rawtext[self.bracketing[before][0]] not in openers or \
1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi              self.bracketing[before][1] > bracketinglevel:
1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            before -= 1
1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if before < 0:
1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                return None
1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            bracketinglevel = min(bracketinglevel, self.bracketing[before][1])
1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        after = self.indexbracket + 1
1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        while after < len(self.bracketing) and \
1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi              self.bracketing[after][1] >= bracketinglevel:
1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            after += 1
1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        beforeindex = self.text.index("%s-%dc" %
1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            (self.stopatindex, len(self.rawtext)-self.bracketing[before][0]))
1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if after >= len(self.bracketing) or \
1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           self.bracketing[after][0] > len(self.rawtext):
1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if mustclose:
1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                return None
1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            afterindex = self.stopatindex
1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # We are after a real char, so it is a ')' and we give the index
1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # before it.
1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            afterindex = self.text.index("%s-%dc" %
1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                (self.stopatindex,
1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                 len(self.rawtext)-(self.bracketing[after][0]-1)))
1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return beforeindex, afterindex
1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # This string includes all chars that may be in a white space
1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    _whitespace_chars = " \t\n\\"
1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # This string includes all chars that may be in an identifier
1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    _id_chars = string.ascii_letters + string.digits + "_"
1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # This string includes all chars that may be the first char of an identifier
1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    _id_first_chars = string.ascii_letters + "_"
1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Given a string and pos, return the number of chars in the identifier
1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # which ends at pos, or 0 if there is no such one. Saved words are not
1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # identifiers.
1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _eat_identifier(self, str, limit, pos):
1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        i = pos
1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        while i > limit and str[i-1] in self._id_chars:
1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            i -= 1
1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if i < pos and (str[i] not in self._id_first_chars or \
1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        keyword.iskeyword(str[i:pos])):
1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            i = pos
1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return pos - i
1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def get_expression(self):
1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Return a string with the Python expression which ends at the given
1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        index, which is empty if there is no real one.
1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """
1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not self.is_in_code():
1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise ValueError("get_expression should only be called if index "\
1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                             "is inside a code.")
1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        rawtext = self.rawtext
1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        bracketing = self.bracketing
1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        brck_index = self.indexbracket
1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        brck_limit = bracketing[brck_index][0]
1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        pos = self.indexinrawtext
1750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        last_identifier_pos = pos
1770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        postdot_phase = True
1780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        while 1:
1800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Eat whitespaces, comments, and if postdot_phase is False - one dot
1810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            while 1:
1820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars:
1830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # Eat a whitespace
1840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    pos -= 1
1850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                elif not postdot_phase and \
1860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                     pos > brck_limit and rawtext[pos-1] == '.':
1870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # Eat a dot
1880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    pos -= 1
1890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    postdot_phase = True
1900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # The next line will fail if we are *inside* a comment, but we
1910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # shouldn't be.
1920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                elif pos == brck_limit and brck_index > 0 and \
1930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                     rawtext[bracketing[brck_index-1][0]] == '#':
1940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # Eat a comment
1950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    brck_index -= 2
1960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    brck_limit = bracketing[brck_index][0]
1970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    pos = bracketing[brck_index+1][0]
1980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                else:
1990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # If we didn't eat anything, quit.
2000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    break
2010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if not postdot_phase:
2030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # We didn't find a dot, so the expression end at the last
2040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # identifier pos.
2050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                break
2060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            ret = self._eat_identifier(rawtext, brck_limit, pos)
2080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if ret:
2090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # There is an identifier to eat
2100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                pos = pos - ret
2110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                last_identifier_pos = pos
2120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # Now, in order to continue the search, we must find a dot.
2130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                postdot_phase = False
2140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # (the loop continues now)
2150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            elif pos == brck_limit:
2170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # We are at a bracketing limit. If it is a closing bracket,
2180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # eat the bracket, otherwise, stop the search.
2190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                level = bracketing[brck_index][1]
2200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                while brck_index > 0 and bracketing[brck_index-1][1] > level:
2210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    brck_index -= 1
2220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if bracketing[brck_index][0] == brck_limit:
2230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # We were not at the end of a closing bracket
2240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    break
2250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                pos = bracketing[brck_index][0]
2260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                brck_index -= 1
2270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                brck_limit = bracketing[brck_index][0]
2280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                last_identifier_pos = pos
2290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if rawtext[pos] in "([":
2300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # [] and () may be used after an identifier, so we
2310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # continue. postdot_phase is True, so we don't allow a dot.
2320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    pass
2330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                else:
2340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    # We can't continue after other types of brackets
2350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    if rawtext[pos] in "'\"":
2360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        # Scan a string prefix
2370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        while pos > 0 and rawtext[pos - 1] in "rRbBuU":
2380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                            pos -= 1
2390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        last_identifier_pos = pos
2400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    break
2410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
2430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # We've found an operator or something.
2440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                break
2450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return rawtext[last_identifier_pos:self.indexinrawtext]
247