home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activepython / ActivePython-2.1.1.msi / Python21_Lib_tokenize.py < prev    next >
Encoding:
Python Source  |  2001-07-26  |  10.2 KB  |  241 lines

  1. """Tokenization help for Python programs.
  2.  
  3. This module exports a function called 'tokenize()' that breaks a stream of
  4. text into Python tokens.  It accepts a readline-like method which is called
  5. repeatedly to get the next line of input (or "" for EOF) and a "token-eater"
  6. function which is called once for each token found.  The latter function is
  7. passed the token type, a string containing the token, the starting and
  8. ending (row, column) coordinates of the token, and the original line.  It is
  9. designed to match the working of the Python tokenizer exactly, except that
  10. it produces COMMENT tokens for comments and gives type OP for all operators."""
  11.  
  12. __author__ = 'Ka-Ping Yee <ping@lfw.org>'
  13. __credits__ = \
  14.     'GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, Skip Montanaro'
  15.  
  16. import string, re
  17. from token import *
  18.  
  19. import token
  20. __all__ = [x for x in dir(token) if x[0] != '_'] + ["COMMENT", "tokenize", "NL"]
  21. del token
  22.  
  23. COMMENT = N_TOKENS
  24. tok_name[COMMENT] = 'COMMENT'
  25. NL = N_TOKENS + 1
  26. tok_name[NL] = 'NL'
  27. N_TOKENS += 2
  28.  
  29. def group(*choices): return '(' + '|'.join(choices) + ')'
  30. def any(*choices): return apply(group, choices) + '*'
  31. def maybe(*choices): return apply(group, choices) + '?'
  32.  
  33. Whitespace = r'[ \f\t]*'
  34. Comment = r'#[^\r\n]*'
  35. Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment)
  36. Name = r'[a-zA-Z_]\w*'
  37.  
  38. Hexnumber = r'0[xX][\da-fA-F]*[lL]?'
  39. Octnumber = r'0[0-7]*[lL]?'
  40. Decnumber = r'[1-9]\d*[lL]?'
  41. Intnumber = group(Hexnumber, Octnumber, Decnumber)
  42. Exponent = r'[eE][-+]?\d+'
  43. Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
  44. Expfloat = r'[1-9]\d*' + Exponent
  45. Floatnumber = group(Pointfloat, Expfloat)
  46. Imagnumber = group(r'0[jJ]', r'[1-9]\d*[jJ]', Floatnumber + r'[jJ]')
  47. Number = group(Imagnumber, Floatnumber, Intnumber)
  48.  
  49. # Tail end of ' string.
  50. Single = r"[^'\\]*(?:\\.[^'\\]*)*'"
  51. # Tail end of " string.
  52. Double = r'[^"\\]*(?:\\.[^"\\]*)*"'
  53. # Tail end of ''' string.
  54. Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''"
  55. # Tail end of """ string.
  56. Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""'
  57. Triple = group("[uU]?[rR]?'''", '[uU]?[rR]?"""')
  58. # Single-line ' or " string.
  59. String = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'",
  60.                r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"')
  61.  
  62. # Because of leftmost-then-longest match semantics, be sure to put the
  63. # longest operators first (e.g., if = came before ==, == would get
  64. # recognized as two instances of =).
  65. Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=",
  66.                  r"[+\-*/%&|^=<>]=?",
  67.                  r"~")
  68.  
  69. Bracket = '[][(){}]'
  70. Special = group(r'\r?\n', r'[:;.,`]')
  71. Funny = group(Operator, Bracket, Special)
  72.  
  73. PlainToken = group(Number, Funny, String, Name)
  74. Token = Ignore + PlainToken
  75.  
  76. # First (or only) line of ' or " string.
  77. ContStr = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
  78.                 group("'", r'\\\r?\n'),
  79.                 r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
  80.                 group('"', r'\\\r?\n'))
  81. PseudoExtras = group(r'\\\r?\n', Comment, Triple)
  82. PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name)
  83.  
  84. tokenprog, pseudoprog, single3prog, double3prog = map(
  85.     re.compile, (Token, PseudoToken, Single3, Double3))
  86. endprogs = {"'": re.compile(Single), '"': re.compile(Double),
  87.             "'''": single3prog, '"""': double3prog,
  88.             "r'''": single3prog, 'r"""': double3prog,
  89.             "u'''": single3prog, 'u"""': double3prog,
  90.             "ur'''": single3prog, 'ur"""': double3prog,
  91.             "R'''": single3prog, 'R"""': double3prog,
  92.             "U'''": single3prog, 'U"""': double3prog,
  93.             "uR'''": single3prog, 'uR"""': double3prog,
  94.             "Ur'''": single3prog, 'Ur"""': double3prog,
  95.             "UR'''": single3prog, 'UR"""': double3prog,
  96.             'r': None, 'R': None, 'u': None, 'U': None}
  97.  
  98. tabsize = 8
  99.  
  100. class TokenError(Exception): pass
  101.  
  102. class StopTokenizing(Exception): pass
  103.  
  104. def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing
  105.     print "%d,%d-%d,%d:\t%s\t%s" % \
  106.         (srow, scol, erow, ecol, tok_name[type], repr(token))
  107.  
  108. def tokenize(readline, tokeneater=printtoken):
  109.     try:
  110.         tokenize_loop(readline, tokeneater)
  111.     except StopTokenizing:
  112.         pass
  113.  
  114. def tokenize_loop(readline, tokeneater):
  115.     lnum = parenlev = continued = 0
  116.     namechars, numchars = string.letters + '_', string.digits
  117.     contstr, needcont = '', 0
  118.     contline = None
  119.     indents = [0]
  120.  
  121.     while 1:                                   # loop over lines in stream
  122.         line = readline()
  123.         lnum = lnum + 1
  124.         pos, max = 0, len(line)
  125.  
  126.         if contstr:                            # continued string
  127.             if not line:
  128.                 raise TokenError, ("EOF in multi-line string", strstart)
  129.             endmatch = endprog.match(line)
  130.             if endmatch:
  131.                 pos = end = endmatch.end(0)
  132.                 tokeneater(STRING, contstr + line[:end],
  133.                            strstart, (lnum, end), contline + line)
  134.                 contstr, needcont = '', 0
  135.                 contline = None
  136.             elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n':
  137.                 tokeneater(ERRORTOKEN, contstr + line,
  138.                            strstart, (lnum, len(line)), contline)
  139.                 contstr = ''
  140.                 contline = None
  141.                 continue
  142.             else:
  143.                 contstr = contstr + line
  144.                 contline = contline + line
  145.                 continue
  146.  
  147.         elif parenlev == 0 and not continued:  # new statement
  148.             if not line: break
  149.             column = 0
  150.             while pos < max:                   # measure leading whitespace
  151.                 if line[pos] == ' ': column = column + 1
  152.                 elif line[pos] == '\t': column = (column/tabsize + 1)*tabsize
  153.                 elif line[pos] == '\f': column = 0
  154.                 else: break
  155.                 pos = pos + 1
  156.             if pos == max: break
  157.  
  158.             if line[pos] in '#\r\n':           # skip comments or blank lines
  159.                 tokeneater((NL, COMMENT)[line[pos] == '#'], line[pos:],
  160.                            (lnum, pos), (lnum, len(line)), line)
  161.                 continue
  162.  
  163.             if column > indents[-1]:           # count indents or dedents
  164.                 indents.append(column)
  165.                 tokeneater(INDENT, line[:pos], (lnum, 0), (lnum, pos), line)
  166.             while column < indents[-1]:
  167.                 indents = indents[:-1]
  168.                 tokeneater(DEDENT, '', (lnum, pos), (lnum, pos), line)
  169.  
  170.         else:                                  # continued statement
  171.             if not line:
  172.                 raise TokenError, ("EOF in multi-line statement", (lnum, 0))
  173.             continued = 0
  174.  
  175.         while pos < max:
  176.             pseudomatch = pseudoprog.match(line, pos)
  177.             if pseudomatch:                                # scan for tokens
  178.                 start, end = pseudomatch.span(1)
  179.                 spos, epos, pos = (lnum, start), (lnum, end), end
  180.                 token, initial = line[start:end], line[start]
  181.  
  182.                 if initial in numchars or \
  183.                    (initial == '.' and token != '.'):      # ordinary number
  184.                     tokeneater(NUMBER, token, spos, epos, line)
  185.                 elif initial in '\r\n':
  186.                     tokeneater(parenlev > 0 and NL or NEWLINE,
  187.                                token, spos, epos, line)
  188.                 elif initial == '#':
  189.                     tokeneater(COMMENT, token, spos, epos, line)
  190.                 elif token in ("'''", '"""',               # triple-quoted
  191.                                "r'''", 'r"""', "R'''", 'R"""',
  192.                                "u'''", 'u"""', "U'''", 'U"""',
  193.                                "ur'''", 'ur"""', "Ur'''", 'Ur"""',
  194.                                "uR'''", 'uR"""', "UR'''", 'UR"""'):
  195.                     endprog = endprogs[token]
  196.                     endmatch = endprog.match(line, pos)
  197.                     if endmatch:                           # all on one line
  198.                         pos = endmatch.end(0)
  199.                         token = line[start:pos]
  200.                         tokeneater(STRING, token, spos, (lnum, pos), line)
  201.                     else:
  202.                         strstart = (lnum, start)           # multiple lines
  203.                         contstr = line[start:]
  204.                         contline = line
  205.                         break
  206.                 elif initial in ("'", '"') or \
  207.                     token[:2] in ("r'", 'r"', "R'", 'R"',
  208.                                   "u'", 'u"', "U'", 'U"') or \
  209.                     token[:3] in ("ur'", 'ur"', "Ur'", 'Ur"',
  210.                                   "uR'", 'uR"', "UR'", 'UR"' ):
  211.                     if token[-1] == '\n':                  # continued string
  212.                         strstart = (lnum, start)
  213.                         endprog = (endprogs[initial] or endprogs[token[1]] or
  214.                                    endprogs[token[2]])
  215.                         contstr, needcont = line[start:], 1
  216.                         contline = line
  217.                         break
  218.                     else:                                  # ordinary string
  219.                         tokeneater(STRING, token, spos, epos, line)
  220.                 elif initial in namechars:                 # ordinary name
  221.                     tokeneater(NAME, token, spos, epos, line)
  222.                 elif initial == '\\':                      # continued stmt
  223.                     continued = 1
  224.                 else:
  225.                     if initial in '([{': parenlev = parenlev + 1
  226.                     elif initial in ')]}': parenlev = parenlev - 1
  227.                     tokeneater(OP, token, spos, epos, line)
  228.             else:
  229.                 tokeneater(ERRORTOKEN, line[pos],
  230.                            (lnum, pos), (lnum, pos+1), line)
  231.                 pos = pos + 1
  232.  
  233.     for indent in indents[1:]:                 # pop remaining indent levels
  234.         tokeneater(DEDENT, '', (lnum, 0), (lnum, 0), '')
  235.     tokeneater(ENDMARKER, '', (lnum, 0), (lnum, 0), '')
  236.  
  237. if __name__ == '__main__':                     # testing
  238.     import sys
  239.     if len(sys.argv) > 1: tokenize(open(sys.argv[1]).readline)
  240.     else: tokenize(sys.stdin.readline)
  241.