PageRenderTime 121ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/qpy/compile_cpython.py

https://bitbucket.org/pfw/durusworks
Python | 241 lines | 188 code | 24 blank | 29 comment | 48 complexity | ee4b350855f44beeabc167aa682e09f0 MD5 | raw file
  1. """
  2. open/DurusWorks/qpy/compile_cpython.py
  3. """
  4. from pprint import pprint
  5. from symbol import sym_name
  6. from token import tok_name
  7. import re
  8. import sys
  9. import symbol
  10. from copy import deepcopy
  11. from os.path import splitext
  12. from qpy.translate import translate_tokens
  13. import py_compile
  14. import symbol
  15. import sys
  16. import token
  17. from parser import compilest, sequence2st, expr, suite
  18. if sys.version < "3":
  19. import __builtin__ as builtins
  20. else:
  21. import builtins
  22. def get_parse_tree(source, source_name=None):
  23. try:
  24. if source_name is None:
  25. return expr(source).tolist(True)
  26. else:
  27. return suite(source).tolist(True)
  28. except (TypeError, SyntaxError):
  29. e = sys.exc_info()[1]
  30. e.filename = source_name
  31. raise e
  32. def statement_tree(stmt):
  33. tree = get_parse_tree(stmt, 'fake')[1]
  34. #assert tree[0] == symbol.stmt, tree
  35. return tree
  36. def expr_tree(x):
  37. tree = get_parse_tree(x)[1]
  38. #assert tree[0] == symbol.testlist, tree
  39. return tree
  40. def symtree(t):
  41. if not isinstance(t, list):
  42. return t
  43. if t[0] in sym_name:
  44. return [(sym_name.get(t[0]), t[0])] + list(map(symtree, t[1:]))
  45. elif t[0] in tok_name:
  46. return [(tok_name.get(t[0]), t[0])] + list(map(symtree, t[1:]))
  47. else:
  48. assert 0, t
  49. def ptree(t):
  50. pprint(symtree(t))
  51. def get_argument(power):
  52. assert power[0] == symbol.power
  53. argument = power[2][2][1]
  54. assert argument[0] == symbol.argument
  55. return argument
  56. def is_future_import_statement(node):
  57. try:
  58. return (node[0] == symbol.stmt and
  59. node[1][1][1][1][0] == symbol.import_from and
  60. node[1][1][1][1][2][1][1] == '__future__')
  61. except IndexError:
  62. return False
  63. def is_atom_string(node):
  64. try:
  65. power = get_power(node)
  66. return (len(power) == 2 and
  67. power[1][0] == symbol.atom and
  68. power[1][1][0] == token.STRING)
  69. except IndexError:
  70. return False
  71. def get_funcdef_function_name_child(node):
  72. assert node[0] == symbol.funcdef
  73. for child in node[1:]:
  74. if child[0] == 1 and child[1] != 'def':
  75. return child
  76. def get_power(node):
  77. n = node
  78. while n[0] != symbol.power:
  79. n = n[1]
  80. return n
  81. class Transform (object):
  82. def __init__(self, tree):
  83. self.tree = tree
  84. self.template_type_stack = [None]
  85. self.module_start = statement_tree(
  86. 'from qpy.quoted import join_xml as _qpy_join_xml, '
  87. 'join_str as _qpy_join_str, xml as _qpy_xml')
  88. self.template_start = statement_tree(
  89. 'qpy_accumulation=[];qpy_append=qpy_accumulation.append')
  90. self.return_xml = statement_tree(
  91. 'return _qpy_join_xml(qpy_accumulation)')
  92. self.return_str = statement_tree(
  93. 'return _qpy_join_str(qpy_accumulation)')
  94. self.qpy_append_expr_stmt = statement_tree('qpy_append(X)')[1][1][1]
  95. assert self.qpy_append_expr_stmt[0] == symbol.expr_stmt
  96. self.xml_power = get_power(expr_tree('_qpy_xml("X")'))
  97. self.template_type_stack = [None]
  98. self.traverse_node(self.tree)
  99. self.line_number = 1
  100. self.rationalize_line_numbers(self.tree)
  101. def rationalize_line_numbers(self, node):
  102. if not isinstance(node, list):
  103. return
  104. if len(node) == 3 and node[0] in token.tok_name:
  105. if node[2] < self.line_number:
  106. node[2] = self.line_number
  107. else:
  108. self.line_number = node[2]
  109. else:
  110. for child in node[1:]:
  111. self.rationalize_line_numbers(child)
  112. def traverse_node(self, node):
  113. if not isinstance(node, list):
  114. return
  115. # If this is a funcdef, push 'xml', 'str', or None
  116. if node[0] == symbol.funcdef:
  117. function_name = get_funcdef_function_name_child(node)[1]
  118. if function_name.endswith('__xml_template__'):
  119. self.template_type_stack.append('xml')
  120. elif function_name.endswith('__str_template__'):
  121. self.template_type_stack.append('str')
  122. else:
  123. self.template_type_stack.append(None)
  124. # Traverse down before doing modifications.
  125. for child in node[1:]:
  126. self.traverse_node(child)
  127. # Modify the node as necessary.
  128. if node[0] == symbol.file_input:
  129. # Insert module-level import statement.
  130. # Skip over the module docstring and any __future__ imports.
  131. for index, child in enumerate(node):
  132. if index == 0:
  133. continue
  134. if is_atom_string(child):
  135. continue
  136. if is_future_import_statement(child):
  137. continue
  138. node.insert(index, deepcopy(self.module_start))
  139. break
  140. elif self.template_type_stack[-1] is None:
  141. pass # We're not in a template, so we're done.
  142. elif node[0] == symbol.expr_stmt and len(node) == 2:
  143. # Wrap this expression statement in a qpy_append call.
  144. stmt = deepcopy(self.qpy_append_expr_stmt)
  145. argument = get_argument(get_power(stmt))
  146. assert argument[1][0] == node[1][1][0]
  147. argument[1] = node[1][1]
  148. assert node[1][0] == stmt[1][0]
  149. node[1] = stmt[1]
  150. elif node[0] == symbol.funcdef:
  151. # This is a new template.
  152. # Insert the initialization of qpy_accumulation and qpy_append.
  153. func_suite = node[-1]
  154. for j, child in enumerate(func_suite[1:]):
  155. if child[0] == symbol.stmt:
  156. func_suite.insert(j+1, deepcopy(self.template_start))
  157. break
  158. # Add the appropriate return statement and patch function name.
  159. function_name_node = get_funcdef_function_name_child(node)
  160. function_name = function_name_node[1]
  161. if self.template_type_stack[-1] == 'xml':
  162. return_accumulation = deepcopy(self.return_xml)
  163. # trim __xml_template__
  164. function_name_node[1] = function_name_node[1][:-16]
  165. else:
  166. assert self.template_type_stack[-1] == 'str'
  167. return_accumulation = deepcopy(self.return_str)
  168. # trim __str_template__
  169. function_name_node[1] = function_name_node[1][:-16]
  170. func_suite.insert(-1, return_accumulation)
  171. elif (self.template_type_stack[-1] == 'xml' and
  172. node[0] == symbol.power and
  173. node[1][0] == symbol.atom and
  174. node[1][1][0] == token.STRING):
  175. # node looks like
  176. # [power [atom [STRING "Z" 1]] ...]
  177. xml_power = deepcopy(self.xml_power)
  178. # xml_power looks like
  179. # [power
  180. # [atom [STRING "_qpy_xml" 1]
  181. # [trailer
  182. # LPAR ...
  183. # [arglist ... [power [atom [STRING "X"]]]]
  184. # RPAR]]]
  185. argument = get_argument(xml_power)
  186. argument_power = get_power(argument)
  187. assert argument_power[1][0] == symbol.atom
  188. assert argument_power[1][0] == node[1][0]
  189. argument_power[1] = deepcopy(node[1]) # replace "X"
  190. node[1] = xml_power[1]
  191. node.insert(2, xml_power[2])
  192. # Pop the stack.
  193. if node[0] == symbol.funcdef:
  194. self.template_type_stack.pop()
  195. def get_st(self):
  196. return sequence2st(self.tree)
  197. PYC = ".pyc"
  198. def get_code(source, source_name):
  199. translated_source = translate_tokens(source)
  200. tree = get_parse_tree(translated_source, source_name)
  201. transformed = Transform(tree).get_st()
  202. code = compilest(transformed, filename=source_name)
  203. return code
  204. def compile(source_name):
  205. # Replace builtins.compile (temporarily) and use the py_compile module
  206. # to create the .pyc file using our special compile() function.
  207. output_name = splitext(source_name)[0] + PYC
  208. builtins_compile = builtins.compile
  209. try:
  210. def qpycompile(source, source_name, extra='exec', dont_inherit=False, optimize=-1):
  211. assert extra == 'exec'
  212. return get_code(source, source_name)
  213. builtins.compile = qpycompile
  214. py_compile.compile(source_name, cfile=output_name, doraise=True)
  215. finally:
  216. builtins.compile = builtins_compile