main.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. # Copyright (c) 2019-2024 Rocky Bernstein
  2. #
  3. # This program is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 3 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. """
  16. Common decompyle3 parser routines. From the outside, of the module
  17. you'll usually import a call something here, such as:
  18. * get_python_parser().parse(), or
  19. * python_parser() which does the above
  20. """
  21. import sys
  22. from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
  23. from xdis import iscode
  24. from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE, version_tuple_to_str
  25. from decompyle3.parsers.p37.heads import (
  26. Python37ParserEval,
  27. Python37ParserExec,
  28. Python37ParserExpr,
  29. Python37ParserLambda,
  30. Python37ParserSingle,
  31. )
  32. from decompyle3.parsers.p38.heads import (
  33. Python38ParserEval,
  34. Python38ParserExec,
  35. Python38ParserExpr,
  36. Python38ParserLambda,
  37. Python38ParserSingle,
  38. )
  39. from decompyle3.parsers.p38pypy.heads import (
  40. Python38PyPyParserEval,
  41. Python38PyPyParserExec,
  42. Python38PyPyParserExpr,
  43. Python38PyPyParserLambda,
  44. Python38PyPyParserSingle,
  45. )
  46. from decompyle3.parsers.treenode import SyntaxTree
  47. from decompyle3.show import maybe_show_asm
  48. def parse(p, tokens, customize, is_lambda: bool) -> SyntaxTree:
  49. was_lambda = p.is_lambda
  50. p.is_lambda = is_lambda
  51. p.customize_grammar_rules(tokens, customize)
  52. tree = p.parse(tokens)
  53. p.is_lambda = was_lambda
  54. # p.cleanup()
  55. return tree
  56. def get_python_parser(
  57. version, debug_parser=PARSER_DEFAULT_DEBUG, compile_mode="exec", is_pypy=False
  58. ):
  59. """
  60. Returns parser object for Python version 3.7, 3.8, etc. depending on the parameters
  61. passed.
  62. *compile_mode* is one of:
  63. * "lambda": is for the grammar that can appear in lambda statements.
  64. * "eval_expr:" is for grammar "expr" kinds of expressions - this is a smaller kind
  65. of "eval" that users only grammar inside lambdas.
  66. * "eval:" is for Python eval() kinds of expressions or eval compile mode
  67. * "exec": is for Python exec() kind of expressions, or exec compile mode
  68. * "single": is python compile "single" compile mode
  69. See https://docs.python.org/3/library/functions.html#compile for an
  70. explanation of the different modes.
  71. """
  72. # FIXME: there has to be a better way...
  73. # We could do this as a table lookup, but that would force us
  74. # in import all of the parsers all of the time. Perhaps there is
  75. # a lazy way of doing the import?
  76. version = version[:2]
  77. if version < (3, 7):
  78. raise RuntimeError(f"Unsupported Python version {version}")
  79. elif version == (3, 7):
  80. if compile_mode == "exec":
  81. p = Python37ParserExec(debug_parser=debug_parser)
  82. elif compile_mode == "single":
  83. p = Python37ParserSingle(debug_parser=debug_parser)
  84. elif compile_mode == "lambda":
  85. p = Python37ParserLambda(debug_parser=debug_parser)
  86. elif compile_mode == "eval":
  87. p = Python37ParserEval(debug_parser=debug_parser)
  88. elif compile_mode == "expr":
  89. p = Python37ParserExpr(debug_parser=debug_parser)
  90. else:
  91. p = Python37ParserSingle(debug_parser)
  92. elif version == (3, 8):
  93. if compile_mode == "exec":
  94. if is_pypy:
  95. p = Python38PyPyParserExec(debug_parser=debug_parser)
  96. else:
  97. p = Python38ParserExec(debug_parser=debug_parser)
  98. elif compile_mode == "single":
  99. if is_pypy:
  100. p = Python38PyPyParserSingle(debug_parser=debug_parser)
  101. else:
  102. p = Python38ParserSingle(debug_parser=debug_parser)
  103. elif compile_mode == "lambda":
  104. if is_pypy:
  105. p = Python38PyPyParserLambda(debug_parser=debug_parser)
  106. else:
  107. p = Python38ParserLambda(debug_parser=debug_parser)
  108. elif compile_mode == "eval":
  109. if is_pypy:
  110. p = Python38PyPyParserEval(debug_parser=debug_parser)
  111. else:
  112. p = Python38ParserEval(debug_parser=debug_parser)
  113. elif compile_mode == "expr":
  114. if is_pypy:
  115. p = Python38PyPyParserExpr(debug_parser=debug_parser)
  116. else:
  117. p = Python38ParserExpr(debug_parser=debug_parser)
  118. elif is_pypy:
  119. p = Python38PyPyParserSingle(debug_parser)
  120. else:
  121. p = Python38ParserSingle(debug_parser)
  122. elif version > (3, 8):
  123. raise RuntimeError(
  124. f"""Version {version_tuple_to_str(version)} is not supported."""
  125. )
  126. p.version = version
  127. # p.dump_grammar() # debug
  128. return p
  129. def python_parser(
  130. co,
  131. version: tuple = PYTHON_VERSION_TRIPLE,
  132. out=sys.stdout,
  133. showasm: bool = False,
  134. parser_debug=PARSER_DEFAULT_DEBUG,
  135. compile_mode: str = "exec",
  136. is_pypy: bool = False,
  137. is_lambda: bool = False,
  138. ) -> SyntaxTree:
  139. """
  140. Parse a code object to an abstract syntax tree representation.
  141. :param co: The code object to parse.
  142. :param version: The python version of this code is from as a float, for
  143. example, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5 etc.
  144. :param out: File like object to write the output to.
  145. :param showasm: Flag which determines whether the disassembled and
  146. ingested code is written to sys.stdout or not.
  147. :param parser_debug: dict containing debug flags for the spark parser.
  148. :param compile_mode: compile mode that we want to parse input `co` as.
  149. This is either "exec", "eval" or, "single".
  150. :param is_pypy: True if ``co`` comes is PyPy code
  151. :param is_lambda True if ``co`` is a lambda expression
  152. :return: Abstract syntax tree representation of the code object.
  153. """
  154. assert iscode(co)
  155. from decompyle3.scanner import get_scanner
  156. scanner = get_scanner(version, is_pypy)
  157. tokens, customize = scanner.ingest(co)
  158. maybe_show_asm(showasm, tokens)
  159. # For heavy grammar debugging
  160. # parser_debug = {'rules': True, 'transition': True, 'reduce' : True,
  161. # 'showstack': 'full'}
  162. p = get_python_parser(
  163. version, parser_debug, compile_mode=compile_mode, is_pypy=IS_PYPY
  164. )
  165. # FIXME: have p.insts update in a better way
  166. # modularity is broken here
  167. p.insts = scanner.insts
  168. p.offset2inst_index = scanner.offset2inst_index
  169. p.opc = scanner.opc
  170. return parse(p, tokens, customize, is_lambda)
  171. if __name__ == "__main__":
  172. def parse_test(co) -> None:
  173. tree = python_parser(co, (3, 8, 2), showasm=True, is_pypy=IS_PYPY)
  174. print(tree)
  175. print("+" * 30)
  176. return
  177. parse_test(parse_test.__code__)