home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Lib / tokenize.py < prev    next >
Text File  |  1997-10-27  |  9KB  |  195 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. __version__ = "Ka-Ping Yee, 26 October 1997"
  13.  
  14. import string, re
  15. from token import *
  16.  
  17. COMMENT = N_TOKENS
  18. tok_name[COMMENT] = 'COMMENT'
  19.  
  20. # Changes from 1.3:
  21. #     Ignore now accepts \f as whitespace.  Operator now includes '**'.
  22. #     Ignore and Special now accept \n or \r\n at the end of a line.
  23. #     Imagnumber is new.  Expfloat is corrected to reject '0e4'.
  24. # Note: to quote a backslash in a regex, it must be doubled in a r'aw' string.
  25.  
  26. def group(*choices): return '(' + string.join(choices, '|') + ')'
  27. def any(*choices): return apply(group, choices) + '*'
  28. def maybe(*choices): return apply(group, choices) + '?'
  29.  
  30. Whitespace = r'[ \f\t]*'
  31. Comment = r'#[^\r\n]*'
  32. Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment)
  33. Name = r'[a-zA-Z_]\w*'
  34.  
  35. Hexnumber = r'0[xX][\da-fA-F]*[lL]?'
  36. Octnumber = r'0[0-7]*[lL]?'
  37. Decnumber = r'[1-9]\d*[lL]?'
  38. Intnumber = group(Hexnumber, Octnumber, Decnumber)
  39. Exponent = r'[eE][-+]?\d+'
  40. Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
  41. Expfloat = r'[1-9]\d*' + Exponent
  42. Floatnumber = group(Pointfloat, Expfloat)
  43. Imagnumber = group(r'0[jJ]', r'[1-9]\d*[jJ]', Floatnumber + r'[jJ]')
  44. Number = group(Imagnumber, Floatnumber, Intnumber)
  45.  
  46. Single = any(r"[^'\\]", r'\\.') + "'"
  47. Double = any(r'[^"\\]', r'\\.') + '"'
  48. Single3 = any(r"[^'\\]",r'\\.',r"'[^'\\]",r"'\\.",r"''[^'\\]",r"''\\.") + "'''"
  49. Double3 = any(r'[^"\\]',r'\\.',r'"[^"\\]',r'"\\.',r'""[^"\\]',r'""\\.') + '"""'
  50. Triple = group("[rR]?'''", '[rR]?"""')
  51. String = group("[rR]?'" + any(r"[^\n'\\]", r'\\.') + "'",
  52.                '[rR]?"' + any(r'[^\n"\\]', r'\\.') + '"')
  53.  
  54. Operator = group('\+', '\-', '\*\*', '\*', '\^', '~', '/', '%', '&', '\|',
  55.                  '<<', '>>', '==', '<=', '<>', '!=', '>=', '=', '<', '>')
  56. Bracket = '[][(){}]'
  57. Special = group(r'\r?\n', r'[:;.,`]')
  58. Funny = group(Operator, Bracket, Special)
  59.  
  60. PlainToken = group(Number, Funny, String, Name)
  61. Token = Ignore + PlainToken
  62.  
  63. ContStr = group("[rR]?'" + any(r'\\.', r"[^\n'\\]") + group("'", r'\\\r?\n'),
  64.                 '[rR]?"' + any(r'\\.', r'[^\n"\\]') + group('"', r'\\\r?\n'))
  65. PseudoExtras = group(r'\\\r?\n', Comment, Triple)
  66. PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name)
  67.  
  68. tokenprog, pseudoprog, single3prog, double3prog = map(
  69.     re.compile, (Token, PseudoToken, Single3, Double3))
  70. endprogs = {"'": re.compile(Single), '"': re.compile(Double),
  71.             "'''": single3prog, '"""': double3prog,
  72.             "r'''": single3prog, 'r"""': double3prog,
  73.             "R'''": single3prog, 'R"""': double3prog, 'r': None, 'R': None}
  74.  
  75. tabsize = 8
  76. TokenError = 'TokenError'
  77. def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing
  78.     print "%d,%d-%d,%d:\t%s\t%s" % \
  79.         (srow, scol, erow, ecol, tok_name[type], repr(token))
  80.  
  81. def tokenize(readline, tokeneater=printtoken):
  82.     lnum = parenlev = continued = 0
  83.     namechars, numchars = string.letters + '_', string.digits
  84.     contstr, needcont = '', 0
  85.     indents = [0]
  86.  
  87.     while 1:                                   # loop over lines in stream
  88.         line = readline()
  89.         lnum = lnum + 1
  90.         pos, max = 0, len(line)
  91.  
  92.         if contstr:                            # continued string
  93.             if not line:
  94.                 raise TokenError, ("EOF in multi-line string", strstart)
  95.             endmatch = endprog.match(line)
  96.             if endmatch:
  97.                 pos = end = endmatch.end(0)
  98.                 tokeneater(STRING, contstr + line[:end],
  99.                            strstart, (lnum, end), line)
  100.                 contstr, needcont = '', 0
  101.             elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n':
  102.                 tokeneater(ERRORTOKEN, contstr + line,
  103.                            strstart, (lnum, len(line)), line)
  104.                 contstr = ''
  105.                 continue
  106.             else:
  107.                 contstr = contstr + line
  108.                 continue
  109.  
  110.         elif parenlev == 0 and not continued:  # new statement
  111.             if not line: break
  112.             column = 0
  113.             while pos < max:                   # measure leading whitespace
  114.                 if line[pos] == ' ': column = column + 1
  115.                 elif line[pos] == '\t': column = (column/tabsize + 1)*tabsize
  116.                 elif line[pos] == '\f': column = 0
  117.                 else: break
  118.                 pos = pos + 1
  119.             if pos == max: break
  120.  
  121.             if line[pos] in '#\r\n':           # skip comments or blank lines
  122.                 tokeneater((NEWLINE, COMMENT)[line[pos] == '#'], line[pos:],
  123.                            (lnum, pos), (lnum, len(line)), line)
  124.                 continue
  125.  
  126.             if column > indents[-1]:           # count indents or dedents
  127.                 indents.append(column)
  128.                 tokeneater(INDENT, line[:pos], (lnum, 0), (lnum, pos), line)
  129.             while column < indents[-1]:
  130.                 indents = indents[:-1]
  131.                 tokeneater(DEDENT, '', (lnum, pos), (lnum, pos), line)
  132.  
  133.         else:                                  # continued statement
  134.             if not line:
  135.                 raise TokenError, ("EOF in multi-line statement", (lnum, 0))
  136.             continued = 0
  137.  
  138.         while pos < max:
  139.             pseudomatch = pseudoprog.match(line, pos)
  140.             if pseudomatch:                                # scan for tokens
  141.                 start, end = pseudomatch.span(1)
  142.                 spos, epos, pos = (lnum, start), (lnum, end), end
  143.                 token, initial = line[start:end], line[start]
  144.  
  145.                 if initial in numchars \
  146.                     or (initial == '.' and token != '.'):  # ordinary number
  147.                     tokeneater(NUMBER, token, spos, epos, line)
  148.                 elif initial in '\r\n':
  149.                     tokeneater(NEWLINE, token, spos, epos, line)
  150.                 elif initial == '#':
  151.                     tokeneater(COMMENT, token, spos, epos, line)
  152.                 elif token in ("'''", '"""',               # triple-quoted
  153.                                "r'''", 'r"""', "R'''", 'R"""'):
  154.                     endprog = endprogs[token]
  155.                     endmatch = endprog.match(line, pos)
  156.                     if endmatch:                           # all on one line
  157.                         pos = endmatch.end(0)
  158.                         token = line[start:pos]
  159.                         tokeneater(STRING, token, spos, (lnum, pos), line)
  160.                     else:
  161.                         strstart = (lnum, start)           # multiple lines
  162.                         contstr = line[start:]
  163.                         break
  164.                 elif initial in ("'", '"') or \
  165.                     token[:2] in ("r'", 'r"', "R'", 'R"'):
  166.                     if token[-1] == '\n':                  # continued string
  167.                         strstart = (lnum, start)
  168.                         endprog = endprogs[initial] or endprogs[token[1]]
  169.                         contstr, needcont = line[start:], 1
  170.                         break
  171.                     else:                                  # ordinary string
  172.                         tokeneater(STRING, token, spos, epos, line)
  173.                 elif initial in namechars:                 # ordinary name
  174.                     tokeneater(NAME, token, spos, epos, line)
  175.                 elif initial == '\\':                      # continued stmt
  176.                     continued = 1
  177.                 else:
  178.                     if initial in '([{': parenlev = parenlev + 1
  179.                     elif initial in ')]}': parenlev = parenlev - 1
  180.                     tokeneater(OP, token, spos, epos, line)
  181.             else:
  182.                 tokeneater(ERRORTOKEN, line[pos],
  183.                            (lnum, pos), (lnum, pos+1), line)
  184.                 pos = pos + 1
  185.  
  186.     for indent in indents[1:]:                 # pop remaining indent levels
  187.         tokeneater(DEDENT, '', (lnum, 0), (lnum, 0), '')
  188.     tokeneater(ENDMARKER, '', (lnum, 0), (lnum, 0), '')
  189.  
  190. if __name__ == '__main__':                     # testing
  191.     import sys
  192.     if len(sys.argv) > 1: tokenize(open(sys.argv[1]).readline)
  193.     else: tokenize(sys.stdin.readline)
  194.  
  195.