PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/pypy/interpreter/pycompiler.py

https://bitbucket.org/kkris/pypy
Python | 160 lines | 144 code | 11 blank | 5 comment | 15 complexity | 9b5e23491ecd6e87252e10bbd8670722 MD5 | raw file
  1. """
  2. General classes for bytecode compilers.
  3. Compiler instances are stored into 'space.getexecutioncontext().compiler'.
  4. """
  5. from pypy.interpreter import pycode
  6. from pypy.interpreter.pyparser import future, pyparse, error as parseerror
  7. from pypy.interpreter.astcompiler import (astbuilder, codegen, consts, misc,
  8. optimize, ast)
  9. from pypy.interpreter.error import OperationError
  10. class AbstractCompiler(object):
  11. """Abstract base class for a bytecode compiler."""
  12. # The idea is to grow more methods here over the time,
  13. # e.g. to handle .pyc files in various ways if we have multiple compilers.
  14. def __init__(self, space):
  15. self.space = space
  16. self.w_compile_hook = space.w_None
  17. def compile(self, source, filename, mode, flags):
  18. """Compile and return an pypy.interpreter.eval.Code instance."""
  19. raise NotImplementedError
  20. def getcodeflags(self, code):
  21. """Return the __future__ compiler flags that were used to compile
  22. the given code object."""
  23. return 0
  24. def compile_command(self, source, filename, mode, flags):
  25. """Same as compile(), but tries to compile a possibly partial
  26. interactive input. If more input is needed, it returns None.
  27. """
  28. # Hackish default implementation based on the stdlib 'codeop' module.
  29. # See comments over there.
  30. space = self.space
  31. flags |= consts.PyCF_DONT_IMPLY_DEDENT
  32. # Check for source consisting of only blank lines and comments
  33. if mode != "eval":
  34. in_comment = False
  35. for c in source:
  36. if c in ' \t\f\v': # spaces
  37. pass
  38. elif c == '#':
  39. in_comment = True
  40. elif c in '\n\r':
  41. in_comment = False
  42. elif not in_comment:
  43. break # non-whitespace, non-comment character
  44. else:
  45. source = "pass" # Replace it with a 'pass' statement
  46. try:
  47. code = self.compile(source, filename, mode, flags)
  48. return code # success
  49. except OperationError, err:
  50. if not err.match(space, space.w_SyntaxError):
  51. raise
  52. try:
  53. self.compile(source + "\n", filename, mode, flags)
  54. return None # expect more
  55. except OperationError, err1:
  56. if not err1.match(space, space.w_SyntaxError):
  57. raise
  58. try:
  59. self.compile(source + "\n\n", filename, mode, flags)
  60. raise # uh? no error with \n\n. re-raise the previous error
  61. except OperationError, err2:
  62. if not err2.match(space, space.w_SyntaxError):
  63. raise
  64. if space.eq_w(err1.get_w_value(space), err2.get_w_value(space)):
  65. raise # twice the same error, re-raise
  66. return None # two different errors, expect more
  67. class PyCodeCompiler(AbstractCompiler):
  68. """Base class for compilers producing PyCode objects."""
  69. def getcodeflags(self, code):
  70. """Return the __future__ compiler flags that were used to compile
  71. the given code object."""
  72. if isinstance(code, pycode.PyCode):
  73. return code.co_flags & self.compiler_flags
  74. else:
  75. return 0
  76. class PythonAstCompiler(PyCodeCompiler):
  77. """Uses the stdlib's python implementation of compiler
  78. XXX: This class should override the baseclass implementation of
  79. compile_command() in order to optimize it, especially in case
  80. of incomplete inputs (e.g. we shouldn't re-compile from sracth
  81. the whole source after having only added a new '\n')
  82. """
  83. def __init__(self, space, override_version=None):
  84. PyCodeCompiler.__init__(self, space)
  85. self.future_flags = future.futureFlags_2_7
  86. self.parser = pyparse.PythonParser(space, self.future_flags)
  87. self.additional_rules = {}
  88. self.compiler_flags = self.future_flags.allowed_flags
  89. def compile_ast(self, node, filename, mode, flags):
  90. if mode == 'eval':
  91. check = isinstance(node, ast.Expression)
  92. elif mode == 'exec':
  93. check = isinstance(node, ast.Module)
  94. elif mode == 'input':
  95. check = isinstance(node, ast.Interactive)
  96. else:
  97. check = True
  98. if not check:
  99. raise OperationError(self.space.w_TypeError, self.space.wrap(
  100. "invalid node type"))
  101. fut = misc.parse_future(node, self.future_flags.compiler_features)
  102. f_flags, f_lineno, f_col = fut
  103. future_pos = f_lineno, f_col
  104. flags |= f_flags
  105. info = pyparse.CompileInfo(filename, mode, flags, future_pos)
  106. return self._compile_ast(node, info)
  107. def _compile_ast(self, node, info):
  108. space = self.space
  109. try:
  110. mod = optimize.optimize_ast(space, node, info)
  111. code = codegen.compile_ast(space, mod, info)
  112. except parseerror.SyntaxError, e:
  113. raise OperationError(space.w_SyntaxError,
  114. e.wrap_info(space))
  115. return code
  116. def compile_to_ast(self, source, filename, mode, flags):
  117. info = pyparse.CompileInfo(filename, mode, flags)
  118. return self._compile_to_ast(source, info)
  119. def _compile_to_ast(self, source, info):
  120. space = self.space
  121. try:
  122. parse_tree = self.parser.parse_source(source, info)
  123. mod = astbuilder.ast_from_node(space, parse_tree, info)
  124. except parseerror.IndentationError, e:
  125. raise OperationError(space.w_IndentationError,
  126. e.wrap_info(space))
  127. except parseerror.SyntaxError, e:
  128. raise OperationError(space.w_SyntaxError,
  129. e.wrap_info(space))
  130. return mod
  131. def compile(self, source, filename, mode, flags, hidden_applevel=False):
  132. info = pyparse.CompileInfo(filename, mode, flags,
  133. hidden_applevel=hidden_applevel)
  134. mod = self._compile_to_ast(source, info)
  135. return self._compile_ast(mod, info)