fork download
  1. #!/usr/bin/python
  2.  
  3. # Author: mbomb007
  4. # Date: Sept. 26, 2015
  5. # Description: Interpret a Self-modifying Brainfuck program
  6. # - Language created by Simon Howard <fraggle@removethisbit.gmail.com>
  7. # References:
  8. # - [https://]esolangs.org/wiki/Self-modifying_Brainfuck
  9. # - [https://]soulsphere.org/hacks/smbf/
  10.  
  11. # Edits:
  12. # 10/08/2015 - Try/Except in Tape.__getitem__
  13. # - Implemented Tape.__len__
  14. # 10/09/2015 - Fixed bug with pc where dynamically created code wouldn't execute.
  15. # - Added debug print statments and dynamic code test
  16. # 10/13/2015 - Fixed input not working at all
  17. # 10/19/2015 - Changed error(objs) to error(*objs)
  18. # - Added errors checks for bracket mismatches
  19. # 03/03/2016 - Added MySTDIN for custom input. Works with non-printable ascii. by ZachGates.
  20. # 12/23/2016 - Added debug option to record instruction execution
  21. # 04/17/2017 - Updated Hello, World! example program to the golfed version.
  22. # 07/31/2018 - Added EOF option
  23. # 08/08/2018 - Fixed debug history not printing
  24.  
  25. from __future__ import print_function
  26. import os, sys
  27.  
  28. import io
  29.  
  30. class Tape(bytearray):
  31. def __init__(self):
  32. self.data = bytearray(b'\0' * 1000)
  33. self.center = len(self.data) // 2
  34. self.end = 0
  35.  
  36. def __len__(self):
  37. return len(self.data)
  38.  
  39. def __getitem__(self, index):
  40. try:
  41. return self.data[index + self.center]
  42. except:
  43. return 0
  44.  
  45. def __setitem__(self, index, val):
  46.  
  47. i = index + self.center
  48.  
  49. if i < 0 or i >= len(self.data):
  50. # resize the data array to be large enough
  51.  
  52. new_size = len(self.data)
  53.  
  54. while True:
  55. new_size *= 2
  56. test_index = index + (new_size // 2)
  57. if test_index >= 0 and test_index < new_size:
  58. # array is big enough now
  59. break
  60.  
  61. # generate the new array
  62. new_data = bytearray(b'\0' * new_size)
  63. new_center = new_size // 2
  64.  
  65. # copy old data into new array
  66. for j in range(0, len(self.data)):
  67. new_data[j - self.center + new_center] = self.data[j]
  68.  
  69. self.data = new_data
  70. self.center = new_center
  71.  
  72. self.data[index + self.center] = val & 0xff
  73. self.end = max(self.end, index+1)
  74.  
  75. class Interpreter():
  76. def __init__(self, data, eof=None, debug=False):
  77. self.tape = Tape()
  78.  
  79. # the value to write to the tape for end of file (EOF), or no change if None
  80. self.eof = eof
  81.  
  82. # whether to keep execution history
  83. self.debug = "" if debug else None
  84.  
  85. # copy the data into the tape
  86. for i in range(0, len(data)):
  87. self.tape[i - len(data)] = data[i]
  88.  
  89. # program start point
  90. self.entrypoint = -len(data)
  91.  
  92. def call(self):
  93. pc = self.entrypoint
  94. ptr = 0
  95.  
  96. while pc < self.tape.end:
  97. c = chr(self.tape[pc])
  98.  
  99. if self.debug != None:
  100. self.debug += c
  101.  
  102. if c == '>':
  103. ptr += 1
  104. elif c == '<':
  105. ptr -= 1
  106. elif c == '+':
  107. self.tape[ptr] += 1
  108. elif c == '-':
  109. self.tape[ptr] -= 1
  110. elif c == '.':
  111. print(chr(self.tape[ptr]), end="")
  112. elif c == ',':
  113. #self.tape[ptr] = ord(sys.stdin.read(1) or '\0')
  114. inChar = sys.stdin.read(1)
  115. if inChar:
  116. self.tape[ptr] = ord(inChar)
  117. else:
  118. if self.eof != None:
  119. self.tape[ptr] = self.eof
  120. elif c == '[':
  121. if self.tape[ptr] == 0:
  122. # advance to end of loop
  123. loop_level = 1
  124. while loop_level > 0:
  125. pc += 1
  126. if pc > self.tape.end:
  127. error("Bracket mismatch")
  128. if chr(self.tape[pc]) == '[': loop_level += 1
  129. elif chr(self.tape[pc]) == ']': loop_level -= 1
  130. elif c == ']':
  131. # rewind to the start of the loop
  132. loop_level = 1
  133. while loop_level > 0:
  134. pc -= 1
  135. if pc < -len(self.tape):
  136. error("Bracket mismatch")
  137. if chr(self.tape[pc]) == '[': loop_level -= 1
  138. elif chr(self.tape[pc]) == ']': loop_level += 1
  139. pc -= 1
  140. pc += 1
  141.  
  142. def load_script(filename):
  143. buf = bytearray(os.path.getsize(filename))
  144. with open(filename, "rb") as f:
  145. try:
  146. f.readinto(buf)
  147. return buf
  148. except Exception as e:
  149. error("Failed to load script file '%s'."%filename, e)
  150.  
  151. def error(*objs):
  152. print(*objs, file=sys.stderr)
  153. sys.exit(1)
  154.  
  155. # For testing with non-printable input
  156. class MySTDIN(list):
  157. def __init__(self, obj=''):
  158. if not isinstance(obj, str):
  159. raise TypeError("a string is required")
  160. self._init(obj)
  161.  
  162. def __repr__(self):
  163. return str(self)
  164.  
  165. def __str__(self):
  166. return "".join(self)
  167.  
  168. def _init(self, obj=''):
  169. for k in obj:
  170. list.append(self, k)
  171.  
  172. def read(self, n=1):
  173. retval = ""
  174. while n and self:
  175. retval += self.pop(0)
  176. n -= 1
  177. return retval
  178.  
  179. def main():
  180. #L = len(sys.argv)
  181. #if L < 2: error("Please provide a file to interpret.")
  182. #if L >=2: data = load_script(sys.argv[1])
  183. #if L > 3: sys.stdin = open(sys.argv[2])
  184.  
  185. # Override stdin. Helpful for non-printable input. Comment to use normal input.
  186. #sys.stdin = MySTDIN("<[.<]")
  187.  
  188. #data = bytearray(b',[>,]')
  189.  
  190. data = bytearray(b'<[.<]\x00!dlroW ,olleH')
  191.  
  192. # I/O without buffer
  193. #sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
  194. #sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
  195.  
  196. #intr = Interpreter(data, eof=0, debug=False)
  197. intr = Interpreter(data, eof=None, debug=False)
  198. #intr = Interpreter(data, eof=-1, debug=False)
  199. try:
  200. intr.call()
  201.  
  202. finally:
  203. sys.stdin = sys.__stdin__
  204.  
  205. if intr.debug:
  206. print("\n\nHISTORY: " + intr.debug)
  207.  
  208. #print(intr.tape.data, end="")
  209. #print(intr.tape.data.decode('ascii'), end="")
  210. #print(intr.tape.data.decode('ascii').replace('\0','_').strip('_'), end="")
  211. #print(bytearray(intr.tape.data.decode('ascii').strip('\0'),'ascii'), end="")
  212.  
  213. if __name__ == "__main__":
  214. main()
  215.  
Success #stdin #stdout 0.04s 9332KB
stdin
Standard input is empty
stdout
Hello, World!