Generates random Python functions using Markov chains
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
6.5 KiB

  1. import opcode
  2. import sys
  3. import func_smash
  4. OP_HASLOAD = {"LOAD_CONST": True, "LOAD_FAST": False, "LOAD_GLOBAL": False}
  5. OP_HASBINARY = {"BINARY_ADD": "+", "BINARY_SUBTRACT": "-",
  6. "BINARY_MULTIPLY": "*", "BINARY_DIVIDE": "/",
  7. "BINARY_POWER": "**", "BINARY_MODULO": "%"}
  8. OP_HASBUILD = {"BUILD_TUPLE": ("(", ")"), "BUILD_LIST": ("[", "]"),
  9. "BUILD_SET": ("{", "}")}
  10. TAB = 4
  11. def prettify_function(func, indent=0):
  12. args = _get_func_args(func)
  13. _print(indent, "def {0}({1}):".format(func.func_name, args))
  14. prettify_code(func.__code__, indent=indent+TAB)
  15. def prettify_code(codeobj, indent=0):
  16. codes = []
  17. codestring = codeobj.co_code
  18. length = len(codestring)
  19. i = 0
  20. while i < length:
  21. code = ord(codestring[i])
  22. i += 1
  23. if code >= opcode.HAVE_ARGUMENT:
  24. arg = func_smash._get_argument(codeobj, codestring, i, code)
  25. i += 2
  26. codes.append((code, arg))
  27. else:
  28. codes.append((code, None))
  29. skip_next_line = False
  30. lines = _parse_codestring(codes)
  31. for i, line in enumerate(lines):
  32. if skip_next_line:
  33. skip_next_line = False
  34. continue
  35. added_indent, code = line[0], line[1:]
  36. if code[0] == "else:" and lines[i+2][0] >= added_indent:
  37. # Remove the automatic "pass" after each "if" as it's not needed
  38. skip_next_line = True
  39. _print(indent + added_indent, *code)
  40. def _parse_codestring(codes):
  41. indent = 0
  42. lines = []
  43. stack = []
  44. print_buffer = []
  45. block_dedent_at = []
  46. block_else_at = []
  47. i = 0
  48. for instruction in codes:
  49. code, arg = instruction
  50. opname = opcode.opname[code]
  51. if code >= opcode.HAVE_ARGUMENT:
  52. i += 3
  53. else:
  54. i += 1
  55. for x in block_dedent_at:
  56. if i >= x:
  57. indent -= TAB
  58. block_dedent_at.remove(x)
  59. for x in block_else_at:
  60. if i >= x:
  61. lines.append((indent, "else:"))
  62. indent += TAB
  63. lines.append((indent, "pass"))
  64. block_else_at.remove(x)
  65. _print(indent, i, opname, arg, debug=True)
  66. if opname in OP_HASLOAD:
  67. _push(stack, arg, literal=OP_HASLOAD[opname])
  68. elif opname in OP_HASBINARY:
  69. tos, tos1 = _pop(stack), _pop(stack)
  70. _push(stack, " ".join((tos1, OP_HASBINARY[opname], tos)))
  71. elif opname in OP_HASBUILD:
  72. args = []
  73. for i in xrange(arg):
  74. args.append(_pop(stack))
  75. args.reverse()
  76. start, end = OP_HASBUILD[opname]
  77. _push(stack, start + ", ".join(args) + end)
  78. elif opname == "BUILD_MAP":
  79. _push(stack, "{}")
  80. elif opname == "STORE_FAST":
  81. lines.append((indent, arg, "=", _pop(stack)))
  82. elif opname == "STORE_MAP":
  83. key, value = _pop(stack), _pop(stack)
  84. pair = ": ".join((key, value))
  85. oldmap = _pop(stack)
  86. if oldmap == "{}":
  87. newmap = "{" + pair + "}"
  88. else:
  89. newmap = oldmap[:-1] + ", " + pair + "}"
  90. _push(stack, newmap)
  91. elif opname == "LOAD_ATTR":
  92. tos = _pop(stack)
  93. new_tos = tos + "." + arg
  94. _push(stack, new_tos)
  95. elif opname == "POP_TOP":
  96. lines.append((indent, _pop(stack)))
  97. elif opname == "CALL_FUNCTION":
  98. numargs, numkwargs = arg
  99. args = []
  100. for i in xrange(numkwargs):
  101. value = _pop(stack)
  102. key = _pop(stack, never_literal=True)
  103. args.append("=".join((key, value)))
  104. for i in xrange(numargs):
  105. args.append(_pop(stack))
  106. args.reverse()
  107. funcname = _pop(stack)
  108. _push(stack, "{0}({1})".format(funcname, ", ".join(args)))
  109. elif opname == "PRINT_ITEM":
  110. print_buffer.append(_pop(stack))
  111. elif opname == "PRINT_NEWLINE":
  112. lines.append((indent, "print", ", ".join(print_buffer)))
  113. print_buffer = []
  114. elif opname == "RETURN_VALUE":
  115. lines.append((indent, "return", _pop(stack)))
  116. elif opname == "COMPARE_OP":
  117. tos, tos1 = _pop(stack), _pop(stack)
  118. compare = " ".join((tos1, arg, tos))
  119. _push(stack, compare)
  120. elif opname == "POP_JUMP_IF_FALSE":
  121. test = _pop(stack)
  122. block_dedent_at.append(arg)
  123. block_else_at.append(arg)
  124. lines.append((indent, "if {0}:".format(test)))
  125. indent += TAB
  126. lines.append((indent, "pass".format(test)))
  127. elif opname == "JUMP_ABSOLUTE":
  128. block_dedent_at.append(i)
  129. elif opname == "JUMP_FORWARD":
  130. block_dedent_at.append(i + arg)
  131. else:
  132. raise NotImplementedError(opname, arg, stack)
  133. return lines
  134. def _get_func_args(func):
  135. codeobj = func.__code__
  136. count = codeobj.co_argcount
  137. if count == 0:
  138. return ""
  139. return ", ".join([arg for arg in codeobj.co_varnames[:count]])
  140. def _push(stack, item, literal=False):
  141. stack.append((item, literal))
  142. def _pop(stack, never_literal=False):
  143. item, literal = stack.pop()
  144. return repr(item) if literal and not never_literal else item
  145. def _print(indentation, *args, **kwargs):
  146. argstring = " ".join([str(arg) for arg in args if str(arg)])
  147. if kwargs.get("debug"):
  148. # Ignore debug messages without -d flag
  149. if len(sys.argv) > 1 and sys.argv[1] == "-d":
  150. print " " * 50 + "#", argstring
  151. return
  152. print " " * indentation + argstring
  153. if __name__ == "__main__":
  154. def f1(a):
  155. b = a + 10 / 7.0
  156. d = long_func(a, b+9, c=42, d="43")
  157. print b, d
  158. ex_tuple = ("Hello", "world!", abcdef)
  159. ex_list = [1, 2, 3] * 3 + [4, 5, 6]
  160. ex_set = {99, 98, 97, 96, 95}
  161. ex_dict = {d: e, f: False, "testing": g}
  162. return ex_dict
  163. def f2(a, b, c):
  164. if cmp1:
  165. line1
  166. if cmp2:
  167. line2
  168. elif cmp3:
  169. line3
  170. elif cmp4:
  171. line4
  172. else:
  173. line5
  174. line6
  175. else:
  176. line7
  177. line8
  178. if cmp4:
  179. line9
  180. if cmp5:
  181. if cmp6:
  182. if cmp7:
  183. if cmp8:
  184. if cmp9:
  185. line10
  186. return line11
  187. prettify_function(f1)
  188. print
  189. prettify_function(f2)