Guest User

Untitled

a guest
Nov 16th, 2013
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.70 KB | None | 0 0
  1. '''
  2. Created on Jun 13, 2013
  3.  
  4. @author: RSHELDON
  5.  
  6. NOTE: currently windows only because of the msvcrt module
  7. linux solution for immediate command line interpretation should be implemented
  8.  
  9. import sys
  10. import tty
  11. tty.setcbreak(sys.stdin)
  12. while True:
  13. print ord(sys.stdin.read(1))
  14. '''
  15. import sys,random,csv,msvcrt,re
  16. from collections import deque
  17.  
  18. class Stack:#currently no idea how to do x[3:5]
  19. def __init__(self,a=[]):
  20. self.list = a
  21. def push(self,val):
  22. self.list.append(val)
  23. def pop(self,n=""):
  24. if n == "":
  25. try:
  26. return self.list.pop()
  27. except:
  28. return 0
  29. else:
  30. self.list.pop(n)#will error if you try to pop something that doesnt exist. good idea?
  31. def __getitem__(self,i):
  32. return self.list[i]
  33. def __repr__(self):
  34. return str(self.list)
  35.  
  36. class BefungeInterpreter:
  37. def __init__(self):
  38. self.program = {}
  39. self.tick = 0
  40. self.stack = deque([Stack()])
  41. self.exitStateFound = False
  42. self.pointerPosition = (0,0)
  43. self.exitValue = 0
  44. self.storageOffset = (0,0)
  45. self.stringMode = False;
  46. self.stringCharacter = '"'
  47. self.jumpOverMode = False;
  48. self.jumpOverCharacter = ';'
  49. self.functionDictionary = {\
  50. '+' : self.add, '-' : self.subtract, '*' : self.multiply, '/' : self.divide,\
  51. '%' : self.modulus, '!' : self.logicalNot, '`' : self.greaterThan,\
  52. '>' : self.east, '<' : self.west, '^' : self.north, 'V' : self.south, 'v' : self.south, '?' : self.randomDirection,\
  53. '_' : self.westOrEast, '|' : self.northOrSouth,\
  54. ':' : self.duplicate, '\\' : self.swap, '$' : self.pop, '.' : self.intPrint, ',' : self.strPrint,\
  55. '#' : self.jump, 'p' : self.put, 'g' : self.get,\
  56. '&' : self.inputNumber, '~' : self.inputChar, '@' : self.end, "noop" : self.noop,\
  57. self.stringCharacter : self.string, 'r' : self.reverse, 'x' : self.popVector,\
  58. 'j' : self.jumpForward, 'q' : self.quit, 'k' : self.iterate,\
  59. '[' : self.turnLeft, ']' : self.turnRight, 'w' : self.compare,
  60. '{' : self.beginBlock, '}' : self.endBlock, "'" : self.fetchCharacter,
  61. 'n' : self.clearStack, 'u' : self.stackUnderStack
  62. }
  63. self.EAST = (1,0)
  64. self.WEST = (-1,0)
  65. self.NORTH = (0,1)
  66. self.SOUTH = (0,-1)
  67.  
  68. self.delta = self.EAST
  69.  
  70. if __name__ == "__main__":
  71. if '-f' in sys.argv:
  72. self.loadASCIIFile(sys.argv[sys.argv.index('-f')+1])
  73. if '-c' in sys.argv:
  74. self.loadCSVFile(sys.argv[sys.argv.index('-c')+1])
  75.  
  76. if '-v' in sys.argv:
  77. self.verbose = True
  78.  
  79. if len(self.program):
  80. self.run()
  81.  
  82. def clearProgram(self):
  83. self.program = {}
  84.  
  85. def loadASCIIFile(self,filePath):
  86. file = open(filePath)
  87. charinput = file.readlines()
  88. self.clearProgram()
  89. for y in range(0,len(charinput)):
  90. for x in range(0,len(charinput[y])-1):#1 to cut off newlines! check spec on this, we might need to account for /r/n
  91. self.program[(x,y)] = charinput[y][x]
  92.  
  93. def loadCSVFile(self,filePath):
  94. self.clearProgram()
  95. y = 0
  96. width = 0
  97. with open(filePath, newline='') as csvfile:
  98. reader = csv.reader(csvfile, delimiter="\t", quotechar='"')
  99. for row in reader:
  100. if self.verbose:
  101. print(row)
  102. width = max(len(row),width)
  103. for x in range(0,len(row)):
  104. char = row[x]
  105. if char == '':
  106. char = ' '
  107. self.program[(x,y)] = char
  108. y += 1
  109.  
  110.  
  111. def run(self):
  112. self.pointerPosition = (0,0)
  113.  
  114. while not self.exitStateFound:# and self.tick < 300:
  115. self.tick +=1
  116. if self.verbose:
  117. currentCommand = self.getCommand()
  118. print(self.stack)
  119. #print("pointer position: " + str(self.pointerPosition))
  120. #print("direction: " +str(self.delta))
  121. print("current command: " + currentCommand)
  122. #print('tick: '+ str(self.tick))
  123. print('-'*20)
  124. self.processCommand()
  125.  
  126. return self.exitValue
  127.  
  128. def getCommand(self):
  129. if self.stringMode:
  130. if self.program.get(self.pointerPosition," ") == " ":
  131. #return space and set program counter to place before the next value
  132. while self.program.get(self.pointerPosition," ") == " ":
  133. self.advance()
  134. self.retreat()
  135. return " "
  136. else:
  137. return self.program[self.pointerPosition]
  138. else:
  139. if self.program.get(self.pointerPosition," ") == ";":
  140. self.advance()
  141. while self.program.get(self.pointerPosition," ") != ";":
  142. self.advance()
  143. self.advance()
  144. return self.getCommand()
  145. elif self.program.get(self.pointerPosition," ") == " ":
  146. while self.program.get(self.pointerPosition," ") == " ":
  147. self.advance()
  148. return self.getCommand()
  149. else:
  150. return self.program[self.pointerPosition]
  151.  
  152. def processCommand(self):
  153. currentCommand = self.getCommand()
  154. if self.stringMode:
  155. self.string()
  156. elif currentCommand in set("1234567890abcdef"):#TODO: make these into functions cuz its faster
  157. self.push(int(currentCommand,16))#pretty sure we should do int here
  158. else:
  159. self.functionDictionary.get(currentCommand,self.error)()
  160. self.advance()
  161.  
  162. def error(self):
  163. print("unexpected character encountered, '" + str(self.getCommand()) + "' at " + str(self.pointerPosition))
  164. exit()
  165.  
  166. def advance(self):#TODO: implement lahey-space wraparound
  167. screenspaceDelta = (self.delta[0],-self.delta[1])#delta is stored as coordinate delta. in screenspace positive y is flipped. this affects the turning functions
  168. self.pointerPosition = tuple(map(lambda x,y: x+y,self.pointerPosition,screenspaceDelta))
  169.  
  170. def retreat(self):#technically I dont have to implement the wraparound for this function because its internal
  171. self.pointerPosition = tuple(map(lambda x,y: x-y,self.pointerPosition,self.delta))
  172.  
  173. def pop(self,n=""):#TODO: figure out how they do default n
  174. return self.stack[0].pop(n)
  175. def push(self,value):
  176. self.stack[0].push(value)
  177.  
  178. #command functions
  179. def add(self):
  180. a,b = self.pop(),self.pop()
  181. self.push(a+b)
  182. def subtract(self):
  183. a,b = self.pop(),self.pop()
  184. self.push(b-a)
  185. def multiply(self):
  186. a,b = self.pop(),self.pop()
  187. self.push(a*b)
  188. def divide(self):
  189. a,b = self.pop(),self.pop()
  190. self.push(b//a)#will error on 0 currently
  191. def modulus(self):
  192. a,b = self.pop(),self.pop()
  193. self.push(b%a)#will error on 0 currently
  194. def logicalNot(self):#not is reserved lol
  195. a = self.pop()
  196. if a==0:
  197. a = 1
  198. else:
  199. a = 0
  200. self.push(a)
  201. def greaterThan(self):
  202. a,b = self.pop(),self.pop()
  203. if b>a:
  204. self.push(1)
  205. else:
  206. self.push(0)
  207. def west(self):
  208. self.delta = self.WEST
  209. def east(self):
  210. self.delta = self.EAST
  211. def north(self):
  212. self.delta = self.NORTH
  213. def south(self):
  214. self.delta = self.SOUTH
  215. def randomDirection(self):
  216. self.delta = random.choice([self.WEST,self.EAST,self.NORTH,self.SOUTH])
  217. def westOrEast(self):
  218. a = self.pop()
  219. if a==0:
  220. self.delta = self.EAST
  221. else:
  222. self.delta = self.WEST
  223. def northOrSouth(self):
  224. a = self.pop()
  225. if a==0:
  226. self.delta = self.SOUTH
  227. else:
  228. self.delta = self.NORTH
  229. def string(self):
  230. if not self.stringMode:
  231. self.stringMode = True;
  232. else:
  233. currentCommand = self.getCommand()
  234. if currentCommand == self.stringCharacter:
  235. self.stringMode = False;
  236. else:
  237. self.push(ord(currentCommand))
  238. def duplicate(self):
  239. a = self.pop()
  240. self.push(a)
  241. self.push(a)
  242. def swap(self):
  243. x,y = self.pop(),self.pop()
  244. self.push(x)
  245. self.push(y)
  246. def intPrint(self):
  247. print(self.pop(),end="")
  248. def strPrint(self):
  249. print(chr(self.pop()),end="")
  250. def jump(self):
  251. self.advance()
  252. def put(self):
  253. y,x,v = self.pop(),self.pop(),self.pop()
  254. x,y = x+self.storageOffset[0],y+self.storageOffset[1]
  255.  
  256. self.program[(x,y)] = chr(v)
  257. def get(self):
  258. y,x = self.pop(),self.pop()
  259. x,y = x+self.storageOffset[0],y+self.storageOffset[1]
  260.  
  261. self.push(ord(self.program[(x,y)]))
  262. def inputNumber(self):#spec says to extract first contiguous base 10 number from input
  263. #msvcrt.getch() #wai wont u work
  264. m = re.search('\d+',input())
  265. self.push(int(m.group(0)))#currently errors if not found. maybe reflect()?
  266.  
  267. def inputChar(self):
  268. #self.push(ord(msvcrt.getch()))
  269. self.push(ord(input()))
  270. def end(self):
  271. self.exitStateFound = True
  272. def noop(self):
  273. ""
  274.  
  275. def turnLeft(self):
  276. x,y = self.delta
  277. y *= -1
  278. self.delta = (y,x)
  279. def turnRight(self):
  280. x,y = self.delta
  281. x *= -1
  282. self.delta = (y,x)
  283.  
  284. def reverse(self):#todo: hooray, I can do lambdas. they'll be the first to go when I start optomizing
  285. self.delta = tuple(map(lambda x: x*-1,self.delta))
  286.  
  287. def popVector(self):
  288. y,x = self.pop(),self.pop()
  289. self.delta = (x,y)
  290.  
  291. def jumpOver(self):#TODO: incorporate this and space into getCommand. they take 0 ticks, and certain instructions require getCommand to return the next actual valid character
  292. if not self.jumpOverMode:
  293. self.jumpOverMode = True
  294. else:
  295. if self.getCommand() == self.jumpOverCharacter:
  296. self.jumpOverMode = False
  297.  
  298. def jumpForward(self):
  299. num = self.pop()
  300. if num > 0:
  301. for i in range(0,num):
  302. self.advance()
  303. else:
  304. for i in range(0,num*-1):
  305. self.retreat()
  306.  
  307. def quit(self):
  308. self.exitStateFound = True
  309. self.exitValue = self.pop()
  310.  
  311. def iterate(self):
  312. num = self.pop()
  313. self.advance()
  314. command = self.getCommand()
  315. for i in range(0,num):
  316. self.functionDictionary[command]()
  317.  
  318. def compare(self):
  319. b,a = self.pop(),self.pop()
  320. if a < b:
  321. self.turnLeft()
  322. elif a == b:
  323. self.noop()
  324. else:
  325. self.turnRight()
  326.  
  327. #TODO: do pushthrough functions. 0-9 and a-f
  328. #TODO: in string mode, spaces are NOT ignored, but are truncated to a single space
  329. def fetchCharacter(self):
  330. self.advance()
  331. self.push(ord(self.getCommand()))
  332.  
  333. def store(self):
  334. self.advance()
  335. self.program[self.pointerPosition] = chr(self.pop())
  336.  
  337. def clearStack(self):
  338. self.stack[0]= Stack()
  339.  
  340. def beginBlock(self):
  341. n = self.pop()
  342. if n < 0:
  343. self.stack.prepend(Stack([0]*n))
  344. else:
  345. self.stack.appendleft(Stack(self.stack[0].list[-n:]))#have to use list here for now
  346. self.stack[1].push(self.storageOffset[0])
  347. self.stack[1].push(self.storageOffset[1])
  348. self.storageOffset = tuple(map(lambda x,y: x+y,self.pointerPosition,self.delta))
  349.  
  350. def endBlock(self):
  351. if len(self.stack) > 1:
  352. n = self.pop()
  353. y,x = self.stack[1].pop(),self.stack[1].pop()
  354. self.storageOffset = (x,y)
  355. if n < 0:
  356. for i in range(0,n):
  357. self.stack[1].pop()
  358. else:
  359. self.stack[1].list += self.stack[0].list[-n:]#using list here too
  360. self.stack.popleft()
  361. else:
  362. self.reverse();
  363.  
  364. def stackUnderStack(self):
  365. if len(self.stack) == 1:
  366. self.reverse()
  367. else:
  368. n = self.pop()
  369. if n > 0:
  370. for i in range(0,n):
  371. self.push(self.stack[1].pop())
  372. elif n < 0:
  373. for i in range(0,-n):
  374. self.stack[1].push(self.pop())#TODO: check if push is valid for python lists and get rid of all appends
  375. #more TODO: need to create class for stacks. pop() needs to return 0 if nothing is on the stack, which it wont right now
  376.  
  377.  
  378. #TO BE CONTINUED
  379.  
  380.  
  381. b = BefungeInterpreter()
  382. b.verbose = False#True
  383. b.loadCSVFile('dice.csv')
  384. #b.loadASCIIFile('befunge.txt')
  385.  
  386. b.run()
Advertisement
Add Comment
Please, Sign In to add comment