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