Generates random Python functions using Markov chains
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

179 行
5.5 KiB

  1. from code import interact
  2. import imp
  3. import opcode
  4. import os
  5. import random
  6. import re
  7. import sys
  8. import types
  9. MARKOV_START = -1
  10. MARKOV_END = -2
  11. def make_chain(funcs):
  12. chain = {}
  13. for func in funcs:
  14. _parse_func(func, chain)
  15. return chain
  16. def make_function(chain, name, argcount=1):
  17. codes, constants, names, varnames = _make_codes(chain)
  18. codestring = "".join([chr(code) for code in codes])
  19. lnotab = ""
  20. code = types.CodeType(argcount, len(varnames), 1024, 0, codestring,
  21. constants, names, varnames, "<smash>", name, 1,
  22. lnotab)
  23. func = types.FunctionType(code, globals(), name)
  24. return func
  25. def print_chain(chain):
  26. print "{"
  27. for code in sorted(chain.keys()):
  28. name = _opcode_to_opname(code)
  29. target_counts = {}
  30. for tcode in chain[code]:
  31. target = _opcode_to_opname(tcode[0])
  32. if tcode[0] >= opcode.HAVE_ARGUMENT:
  33. target = "{0}({1!r})".format(target, tcode[1])
  34. try:
  35. target_counts[target] += 1
  36. except KeyError:
  37. target_counts[target] = 1
  38. targets = []
  39. for target, count in target_counts.iteritems():
  40. if count == 1:
  41. targets.append(target)
  42. else:
  43. targets.append("{0}x {1}".format(count, target))
  44. targets.sort()
  45. print name.rjust(20), "=> [{0}]".format(", ".join(targets))
  46. print "}"
  47. def print_function(func):
  48. codeobj = func.__code__
  49. codestring = codeobj.co_code
  50. length = len(codestring)
  51. i = 0
  52. while i < length:
  53. code = ord(codestring[i])
  54. i += 1
  55. print opcode.opname[code].rjust(20),
  56. if code >= opcode.HAVE_ARGUMENT:
  57. arg = _get_argument(codeobj, codestring, i, code)
  58. i += 2
  59. print " ({0!r})".format(arg)
  60. else:
  61. print
  62. def run():
  63. try:
  64. path, name = os.path.split(sys.argv[1])
  65. name = re.sub("\.pyc?$", "", name)
  66. except IndexError:
  67. raise RuntimeError("Needs a filename as a command-line argument")
  68. file_obj, path, desc = imp.find_module(name, [path])
  69. try:
  70. module = imp.load_module(name, file_obj, path, desc)
  71. finally:
  72. file_obj.close()
  73. _demo(module.corpus)
  74. def _parse_func(func, chain):
  75. codeobj = func.__code__
  76. codestring = codeobj.co_code
  77. length = len(codestring)
  78. i = 0
  79. prevcode = MARKOV_START
  80. while i < length:
  81. code = ord(codestring[i])
  82. i += 1
  83. if code >= opcode.HAVE_ARGUMENT:
  84. arg = _get_argument(codeobj, codestring, i, code)
  85. i += 2
  86. else:
  87. arg = None
  88. _chain_append(chain, prevcode, (code, arg))
  89. prevcode = code
  90. _chain_append(chain, code, (MARKOV_END, None))
  91. def _get_argument(codeobj, codestring, i, code):
  92. arg = ord(codestring[i]) + ord(codestring[i + 1]) * 256
  93. if code in opcode.hasconst:
  94. return codeobj.co_consts[arg]
  95. elif code in opcode.hasname:
  96. return codeobj.co_names[arg]
  97. elif code in opcode.haslocal:
  98. return codeobj.co_varnames[arg]
  99. elif code in opcode.hascompare:
  100. return opcode.cmp_op[arg]
  101. elif code == opcode.opmap["CALL_FUNCTION"]:
  102. return (ord(codestring[i]), ord(codestring[i + 1]))
  103. raise NotImplementedError(code, opcode.opname[code])
  104. def _chain_append(chain, first, second):
  105. try:
  106. chain[first].append(second)
  107. except KeyError:
  108. chain[first] = [second]
  109. def _make_codes(chain):
  110. codes = []
  111. instruction = random.choice(chain[MARKOV_START])
  112. constants, names, varnames = [], [], []
  113. while 1:
  114. code, arg = instruction
  115. if code == MARKOV_END:
  116. break
  117. codes.append(code)
  118. if code >= opcode.HAVE_ARGUMENT:
  119. if code in opcode.hasconst:
  120. if arg not in constants:
  121. constants.append(arg)
  122. _coerce_arg_into_codes(codes, constants.index(arg))
  123. elif code in opcode.hasname:
  124. if arg not in names:
  125. names.append(arg)
  126. _coerce_arg_into_codes(codes, names.index(arg))
  127. elif code in opcode.haslocal:
  128. if arg not in varnames:
  129. varnames.append(arg)
  130. _coerce_arg_into_codes(codes, varnames.index(arg))
  131. elif code in opcode.hascompare:
  132. _coerce_arg_into_codes(codes, opcode.cmp_op.index(arg))
  133. elif code == opcode.opmap["CALL_FUNCTION"]:
  134. codes.append(arg[0])
  135. codes.append(arg[1])
  136. else:
  137. raise NotImplementedError(code, opcode.opname[code])
  138. instruction = random.choice(chain[code])
  139. return codes, tuple(constants), tuple(names), tuple(varnames)
  140. def _opcode_to_opname(code):
  141. if code == MARKOV_START:
  142. return "START"
  143. elif code == MARKOV_END:
  144. return "END"
  145. return opcode.opname[code]
  146. def _coerce_arg_into_codes(codes, arg):
  147. codes.append(arg % 256)
  148. codes.append(arg // 256)
  149. def _demo(corpus, arg=12.0):
  150. chain = make_chain(corpus)
  151. func = make_function(chain, "func")
  152. print "Using {0}-function corpus.".format(len(corpus))
  153. print "Smashed function disassembly:"
  154. print_function(func)
  155. print
  156. print "func({0}) =".format(arg), func(arg)
  157. if len(sys.argv) > 2 and sys.argv[2] == "-i":
  158. variables = dict(globals().items() + locals().items())
  159. interact(banner="", local=variables)
  160. if __name__ == "__main__":
  161. run()