/PyFuck.py

https://github.com/FDeSousa/PyTerpreter · Python · 192 lines · 124 code · 23 blank · 45 comment · 9 complexity · aeab43a897aa8d6d7f55b265ce5d5329 MD5 · raw file

  1. """ PyFuck - Brainfuck Interpreter in Python
  2. This module is the interpreter class itself, which can
  3. be covered by an interface for usefulness or to aide in
  4. expanding the program to interpret more languages later
  5. """
  6. __author__ = "Filipe De Sousa (filipe@desousa.com.pt)"
  7. __version__ = "$Revision: 0.1 $"
  8. __date__ = "$Date: 2011/09/03 21:16:20 $"
  9. __copyright__ = "Copyright (c) 2011 Filipe De Sousa"
  10. __license__ = "Python"
  11. import os, sys, re, types
  12. from PyTerpreter import Interpreter, Menu
  13. # The PyFuck menu class, to be created and instantiated when
  14. class PyMenuFucker(Menu):
  15. """ The interpreter's menu class, which displays and works
  16. on the selected options
  17. """
  18. def __init__(self):
  19. "Only initialises the super class currently"
  20. Menu.__init__(self)
  21. def menu(self):
  22. "Handle the menu options the user has for PyFuck"
  23. run_menu = True
  24. while run_menu:
  25. # Display a menu here once per time the loop runs
  26. print "%2d. %s" % (1, "Interactive Interpreter")
  27. print "%2d. %s" % (2, "Run from file")
  28. print "------------------------------"
  29. print "%2d. %s" % (0, "Exit PyFuck")
  30. print
  31. print "Enter option number:\t"
  32. # read what was entered here
  33. option = sys.stdin.readline()
  34. if option == '1': # Run the interactive interpreter
  35. interactive()
  36. elif option == '2': # Run a program from file
  37. runfromfile()
  38. elif option == '0': # Exit this interpreter, back to main menu
  39. run_menu = False
  40. return None
  41. else: # Unrecognised option, print simple error
  42. print "Invalid option, numbers only"
  43. # Must look at exceptions, and how to handle them
  44. # Add an extra empty line to clean up the output a bit
  45. print
  46. def run_interpreter(self, tointerpret):
  47. "Method to instantiate and run the interpreter"
  48. interpret = PyFucker(tointerpret)
  49. interpret.run()
  50. # The PyFuck interpreter class, to be created and instantiated per program
  51. class PyFucker(Interpreter):
  52. """ The interpreter class itself, which has the methods
  53. for doing all of the heavy lifting
  54. """
  55. # Since Python has no switch..case statement, using dictionary
  56. # to execute a function depending upon the current command
  57. # Inspiration came from: http://bytebaker.com/2008/11/03/switch-case-statement-in-python/
  58. commands = {'<' : dec_pointer,
  59. '>' : inc_pointer,
  60. '+' : inc_data,
  61. '-' : dec_data,
  62. '[' : loop_start,
  63. ']' : loop_end,
  64. '.' : print_char,
  65. ',' : read_char
  66. }
  67. def __init__(self, tointerpret):
  68. "Initialize program with the parsed-in program string"
  69. # Initialise the super class
  70. Interpreter.__init__(self, tointerpret)
  71. # Make a copy of the string to interpret while first replacing
  72. # digits/whitespace/word characters with nothing
  73. program = re.sub('[\d\s\w]', '', tointerpret.lower())
  74. def run(self):
  75. "The main run loop for the interpreter"
  76. # Run the super class run() method first off
  77. Interpreter.run(self)
  78. # The pointer and data array are unique to the run-time, so
  79. # initialise a pointer to 0 before starting interpreter:
  80. pointer = 0
  81. # and set our numbers array to all zeroes, 30,000 times over:
  82. arr = [0] * 30000
  83. # Should go through the program string and decide what
  84. # to do per-character
  85. # To allow iterating backwards through the string, we're
  86. # using i to hold the index in the string
  87. i = 0
  88. while i < len(self.program):
  89. # Hold the current char/command to execute in c
  90. c = self.program[i]
  91. try:
  92. # Try to execute the command here
  93. commands[c](self, pointer, arr, i)
  94. except KeyError:
  95. # If the command isn't found, dictionary will raise a
  96. # KeyError exception as the command is the key
  97. # We don't much mind, just ignore the key and carry on!
  98. pass
  99. def dec_pointer(self, pointer, arr, i):
  100. "Decrement the program pointer"
  101. # Function for the '<' command
  102. # Only decrement the pointer if it's more than 0
  103. if pointer > 0:
  104. pointer -= 1
  105. i += 1
  106. def inc_pointer(self, pointer, arr, i):
  107. "Increment the program pointer"
  108. # Function for the '>' command
  109. # Only increment the pointer if it's less than arr's length
  110. if pointer < len(arr)-1:
  111. pointer += 1
  112. i += 1
  113. def inc_data(self, pointer, arr, i):
  114. "Increment the data value in arr[pointer]"
  115. # Function for the '+' command
  116. # No conditional here, just increment the data at arr[pointer]
  117. arr[pointer] += 1
  118. i += 1
  119. def dec_data(self, pointer, arr, i):
  120. "Decrement the data value in arr[pointer]"
  121. # Function for the '-' command
  122. # No conditional here, just decrement the data at arr[pointer]
  123. arr[pointer] -= 1
  124. i += 1
  125. def loop_start(self, pointer, arr, i):
  126. "Check for loop start condition, start if possible"
  127. # Function for the '[' command
  128. # If data at arr[pointer] is >= 1, enter loop
  129. # Until I figure out how I want to do this, have just a pass
  130. pass
  131. def loop_end(self, pointer, arr, i):
  132. "Check for loop end condition, restart if possible"
  133. # Function for the ']' command
  134. # If data at arr[pointer] is >= 1, return into loop
  135. # Until I figure out how I want to do this, have just a pass
  136. pass
  137. def print_char(self, pointer, arr, i):
  138. "Print the char representating the data in arr[pointer]"
  139. # Function for the '.' command
  140. # Print char representation of arr[pointer]
  141. print chr(arr[pointer])
  142. i += 1
  143. def read_char(self, pointer, arr, i):
  144. "Read one char's associated value into arr[pointer]"
  145. # Function for the ',' command
  146. # Currently only reads one char at a time, and only after
  147. # pressing return/enter. To get rid of the '\n' from the
  148. # .read() buffer, we do another .read(1)
  149. # Must find a way to read just the one byte...
  150. # Get one char from keyboard into char_in
  151. char_in = ord(sys.stdin.read(1))
  152. # To make sure we don't get a '\n' next time, read another
  153. sys.stdin.read(1)
  154. # Now place char_in into arr[pointer]
  155. arr[pointer] = char_in
  156. i += 1