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.

227 lines
7.7 KiB

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