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.

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