basinterp.py (4479:61d3ed46e373) basinterp.py (6498:e21e9ab5fad0)
1# This file provides the runtime support for running a basic program
2# Assumes the program has been parsed using basparse.py
3
4import sys
5import math
6import random
7
8class BasicInterpreter:

--- 26 unchanged lines hidden (view full) ---

35
36 # Check for end statements
37 def check_end(self):
38 has_end = 0
39 for lineno in self.stat:
40 if self.prog[lineno][0] == 'END' and not has_end:
41 has_end = lineno
42 if not has_end:
1# This file provides the runtime support for running a basic program
2# Assumes the program has been parsed using basparse.py
3
4import sys
5import math
6import random
7
8class BasicInterpreter:

--- 26 unchanged lines hidden (view full) ---

35
36 # Check for end statements
37 def check_end(self):
38 has_end = 0
39 for lineno in self.stat:
40 if self.prog[lineno][0] == 'END' and not has_end:
41 has_end = lineno
42 if not has_end:
43 print "NO END INSTRUCTION"
43 print("NO END INSTRUCTION")
44 self.error = 1
44 self.error = 1
45 return
45 if has_end != lineno:
46 if has_end != lineno:
46 print "END IS NOT LAST"
47 print("END IS NOT LAST")
47 self.error = 1
48
49 # Check loops
50 def check_loops(self):
51 for pc in range(len(self.stat)):
52 lineno = self.stat[pc]
53 if self.prog[lineno][0] == 'FOR':
54 forinst = self.prog[lineno]
55 loopvar = forinst[1]
56 for i in range(pc+1,len(self.stat)):
57 if self.prog[self.stat[i]][0] == 'NEXT':
58 nextvar = self.prog[self.stat[i]][1]
59 if nextvar != loopvar: continue
60 self.loopend[pc] = i
61 break
62 else:
48 self.error = 1
49
50 # Check loops
51 def check_loops(self):
52 for pc in range(len(self.stat)):
53 lineno = self.stat[pc]
54 if self.prog[lineno][0] == 'FOR':
55 forinst = self.prog[lineno]
56 loopvar = forinst[1]
57 for i in range(pc+1,len(self.stat)):
58 if self.prog[self.stat[i]][0] == 'NEXT':
59 nextvar = self.prog[self.stat[i]][1]
60 if nextvar != loopvar: continue
61 self.loopend[pc] = i
62 break
63 else:
63 print "FOR WITHOUT NEXT AT LINE" % self.stat[pc]
64 print("FOR WITHOUT NEXT AT LINE %s" % self.stat[pc])
64 self.error = 1
65 self.error = 1
65
66
66 # Evaluate an expression
67 def eval(self,expr):
68 etype = expr[0]
69 if etype == 'NUM': return expr[1]
70 elif etype == 'GROUP': return self.eval(expr[1])
71 elif etype == 'UNARY':
72 if expr[1] == '-': return -self.eval(expr[2])
73 elif etype == 'BINOP':
74 if expr[1] == '+': return self.eval(expr[2])+self.eval(expr[3])
75 elif expr[1] == '-': return self.eval(expr[2])-self.eval(expr[3])
76 elif expr[1] == '*': return self.eval(expr[2])*self.eval(expr[3])
77 elif expr[1] == '/': return float(self.eval(expr[2]))/self.eval(expr[3])
78 elif expr[1] == '^': return abs(self.eval(expr[2]))**self.eval(expr[3])
79 elif etype == 'VAR':
80 var,dim1,dim2 = expr[1]
81 if not dim1 and not dim2:
67 # Evaluate an expression
68 def eval(self,expr):
69 etype = expr[0]
70 if etype == 'NUM': return expr[1]
71 elif etype == 'GROUP': return self.eval(expr[1])
72 elif etype == 'UNARY':
73 if expr[1] == '-': return -self.eval(expr[2])
74 elif etype == 'BINOP':
75 if expr[1] == '+': return self.eval(expr[2])+self.eval(expr[3])
76 elif expr[1] == '-': return self.eval(expr[2])-self.eval(expr[3])
77 elif expr[1] == '*': return self.eval(expr[2])*self.eval(expr[3])
78 elif expr[1] == '/': return float(self.eval(expr[2]))/self.eval(expr[3])
79 elif expr[1] == '^': return abs(self.eval(expr[2]))**self.eval(expr[3])
80 elif etype == 'VAR':
81 var,dim1,dim2 = expr[1]
82 if not dim1 and not dim2:
82 if self.vars.has_key(var):
83 if var in self.vars:
83 return self.vars[var]
84 else:
84 return self.vars[var]
85 else:
85 print "UNDEFINED VARIABLE", var, "AT LINE", self.stat[self.pc]
86 print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))
86 raise RuntimeError
87 # May be a list lookup or a function evaluation
88 if dim1 and not dim2:
87 raise RuntimeError
88 # May be a list lookup or a function evaluation
89 if dim1 and not dim2:
89 if self.functions.has_key(var):
90 if var in self.functions:
90 # A function
91 return self.functions[var](dim1)
92 else:
93 # A list evaluation
91 # A function
92 return self.functions[var](dim1)
93 else:
94 # A list evaluation
94 if self.lists.has_key(var):
95 if var in self.lists:
95 dim1val = self.eval(dim1)
96 if dim1val < 1 or dim1val > len(self.lists[var]):
96 dim1val = self.eval(dim1)
97 if dim1val < 1 or dim1val > len(self.lists[var]):
97 print "LIST INDEX OUT OF BOUNDS AT LINE", self.stat[self.pc]
98 print("LIST INDEX OUT OF BOUNDS AT LINE %s" % self.stat[self.pc])
98 raise RuntimeError
99 return self.lists[var][dim1val-1]
100 if dim1 and dim2:
99 raise RuntimeError
100 return self.lists[var][dim1val-1]
101 if dim1 and dim2:
101 if self.tables.has_key(var):
102 if var in self.tables:
102 dim1val = self.eval(dim1)
103 dim2val = self.eval(dim2)
104 if dim1val < 1 or dim1val > len(self.tables[var]) or dim2val < 1 or dim2val > len(self.tables[var][0]):
103 dim1val = self.eval(dim1)
104 dim2val = self.eval(dim2)
105 if dim1val < 1 or dim1val > len(self.tables[var]) or dim2val < 1 or dim2val > len(self.tables[var][0]):
105 print "TABLE INDEX OUT OUT BOUNDS AT LINE", self.stat[self.pc]
106 print("TABLE INDEX OUT OUT BOUNDS AT LINE %s" % self.stat[self.pc])
106 raise RuntimeError
107 return self.tables[var][dim1val-1][dim2val-1]
107 raise RuntimeError
108 return self.tables[var][dim1val-1][dim2val-1]
108 print "UNDEFINED VARIABLE", var, "AT LINE", self.stat[self.pc]
109 print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))
109 raise RuntimeError
110
111 # Evaluate a relational expression
112 def releval(self,expr):
113 etype = expr[1]
114 lhs = self.eval(expr[2])
115 rhs = self.eval(expr[3])
116 if etype == '<':

--- 23 unchanged lines hidden (view full) ---

140 # Assignment
141 def assign(self,target,value):
142 var, dim1, dim2 = target
143 if not dim1 and not dim2:
144 self.vars[var] = self.eval(value)
145 elif dim1 and not dim2:
146 # List assignment
147 dim1val = self.eval(dim1)
110 raise RuntimeError
111
112 # Evaluate a relational expression
113 def releval(self,expr):
114 etype = expr[1]
115 lhs = self.eval(expr[2])
116 rhs = self.eval(expr[3])
117 if etype == '<':

--- 23 unchanged lines hidden (view full) ---

141 # Assignment
142 def assign(self,target,value):
143 var, dim1, dim2 = target
144 if not dim1 and not dim2:
145 self.vars[var] = self.eval(value)
146 elif dim1 and not dim2:
147 # List assignment
148 dim1val = self.eval(dim1)
148 if not self.lists.has_key(var):
149 if not var in self.lists:
149 self.lists[var] = [0]*10
150
151 if dim1val > len(self.lists[var]):
150 self.lists[var] = [0]*10
151
152 if dim1val > len(self.lists[var]):
152 print "DIMENSION TOO LARGE AT LINE", self.stat[self.pc]
153 print ("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
153 raise RuntimeError
154 self.lists[var][dim1val-1] = self.eval(value)
155 elif dim1 and dim2:
156 dim1val = self.eval(dim1)
157 dim2val = self.eval(dim2)
154 raise RuntimeError
155 self.lists[var][dim1val-1] = self.eval(value)
156 elif dim1 and dim2:
157 dim1val = self.eval(dim1)
158 dim2val = self.eval(dim2)
158 if not self.tables.has_key(var):
159 if not var in self.tables:
159 temp = [0]*10
160 v = []
161 for i in range(10): v.append(temp[:])
162 self.tables[var] = v
163 # Variable already exists
164 if dim1val > len(self.tables[var]) or dim2val > len(self.tables[var][0]):
160 temp = [0]*10
161 v = []
162 for i in range(10): v.append(temp[:])
163 self.tables[var] = v
164 # Variable already exists
165 if dim1val > len(self.tables[var]) or dim2val > len(self.tables[var][0]):
165 print "DIMENSION TOO LARGE AT LINE", self.stat[self.pc]
166 print("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
166 raise RuntimeError
167 self.tables[var][dim1val-1][dim2val-1] = self.eval(value)
168
169 # Change the current line number
170 def goto(self,linenum):
167 raise RuntimeError
168 self.tables[var][dim1val-1][dim2val-1] = self.eval(value)
169
170 # Change the current line number
171 def goto(self,linenum):
171 if not self.prog.has_key(linenum):
172 print "UNDEFINED LINE NUMBER %d AT LINE %d" % (linenum, self.stat[self.pc])
172 if not linenum in self.prog:
173 print("UNDEFINED LINE NUMBER %d AT LINE %d" % (linenum, self.stat[self.pc]))
173 raise RuntimeError
174 self.pc = self.stat.index(linenum)
175
176 # Run it
177 def run(self):
178 self.vars = { } # All variables
179 self.lists = { } # List variables
180 self.tables = { } # Tables
181 self.loops = [ ] # Currently active loops
182 self.loopend= { } # Mapping saying where loops end
183 self.gosub = None # Gosub return point (if any)
184 self.error = 0 # Indicates program error
185
174 raise RuntimeError
175 self.pc = self.stat.index(linenum)
176
177 # Run it
178 def run(self):
179 self.vars = { } # All variables
180 self.lists = { } # List variables
181 self.tables = { } # Tables
182 self.loops = [ ] # Currently active loops
183 self.loopend= { } # Mapping saying where loops end
184 self.gosub = None # Gosub return point (if any)
185 self.error = 0 # Indicates program error
186
186 self.stat = self.prog.keys() # Ordered list of all line numbers
187 self.stat = list(self.prog) # Ordered list of all line numbers
187 self.stat.sort()
188 self.pc = 0 # Current program counter
189
190 # Processing prior to running
191
192 self.collect_data() # Collect all of the data statements
193 self.check_end()
194 self.check_loops()
195
196 if self.error: raise RuntimeError
197
198 while 1:
199 line = self.stat[self.pc]
200 instr = self.prog[line]
188 self.stat.sort()
189 self.pc = 0 # Current program counter
190
191 # Processing prior to running
192
193 self.collect_data() # Collect all of the data statements
194 self.check_end()
195 self.check_loops()
196
197 if self.error: raise RuntimeError
198
199 while 1:
200 line = self.stat[self.pc]
201 instr = self.prog[line]
201
202
202 op = instr[0]
203
204 # END and STOP statements
205 if op == 'END' or op == 'STOP':
206 break # We're done
207
208 # GOTO statement
209 elif op == 'GOTO':

--- 10 unchanged lines hidden (view full) ---

220 out += ' '*(15 - (len(out) % 15))
221 out += label
222 if val:
223 if label: out += " "
224 eval = self.eval(val)
225 out += str(eval)
226 sys.stdout.write(out)
227 end = instr[2]
203 op = instr[0]
204
205 # END and STOP statements
206 if op == 'END' or op == 'STOP':
207 break # We're done
208
209 # GOTO statement
210 elif op == 'GOTO':

--- 10 unchanged lines hidden (view full) ---

221 out += ' '*(15 - (len(out) % 15))
222 out += label
223 if val:
224 if label: out += " "
225 eval = self.eval(val)
226 out += str(eval)
227 sys.stdout.write(out)
228 end = instr[2]
228 if not (end == ',' or end == ';'):
229 if not (end == ',' or end == ';'):
229 sys.stdout.write("\n")
230 if end == ',': sys.stdout.write(" "*(15-(len(out) % 15)))
231 if end == ';': sys.stdout.write(" "*(3-(len(out) % 3)))
230 sys.stdout.write("\n")
231 if end == ',': sys.stdout.write(" "*(15-(len(out) % 15)))
232 if end == ';': sys.stdout.write(" "*(3-(len(out) % 3)))
232
233
233 # LET statement
234 elif op == 'LET':
235 target = instr[1]
236 value = instr[2]
237 self.assign(target,value)
238
239 # READ statement
240 elif op == 'READ':

--- 12 unchanged lines hidden (view full) ---

253 self.goto(newline)
254 continue
255
256 elif op == 'FOR':
257 loopvar = instr[1]
258 initval = instr[2]
259 finval = instr[3]
260 stepval = instr[4]
234 # LET statement
235 elif op == 'LET':
236 target = instr[1]
237 value = instr[2]
238 self.assign(target,value)
239
240 # READ statement
241 elif op == 'READ':

--- 12 unchanged lines hidden (view full) ---

254 self.goto(newline)
255 continue
256
257 elif op == 'FOR':
258 loopvar = instr[1]
259 initval = instr[2]
260 finval = instr[3]
261 stepval = instr[4]
261
262
262 # Check to see if this is a new loop
263 if not self.loops or self.loops[-1][0] != self.pc:
264 # Looks like a new loop. Make the initial assignment
265 newvalue = initval
266 self.assign((loopvar,None,None),initval)
267 if not stepval: stepval = ('NUM',1)
268 stepval = self.eval(stepval) # Evaluate step here
269 self.loops.append((self.pc,stepval))

--- 9 unchanged lines hidden (view full) ---

279 # Loop is done. Jump to the NEXT
280 self.pc = self.loopend[self.pc]
281 self.loops.pop()
282 else:
283 self.assign((loopvar,None,None),newvalue)
284
285 elif op == 'NEXT':
286 if not self.loops:
263 # Check to see if this is a new loop
264 if not self.loops or self.loops[-1][0] != self.pc:
265 # Looks like a new loop. Make the initial assignment
266 newvalue = initval
267 self.assign((loopvar,None,None),initval)
268 if not stepval: stepval = ('NUM',1)
269 stepval = self.eval(stepval) # Evaluate step here
270 self.loops.append((self.pc,stepval))

--- 9 unchanged lines hidden (view full) ---

280 # Loop is done. Jump to the NEXT
281 self.pc = self.loopend[self.pc]
282 self.loops.pop()
283 else:
284 self.assign((loopvar,None,None),newvalue)
285
286 elif op == 'NEXT':
287 if not self.loops:
287 print "NEXT WITHOUT FOR AT LINE",line
288 print("NEXT WITHOUT FOR AT LINE %s" % line)
288 return
289 return
289
290
290 nextvar = instr[1]
291 self.pc = self.loops[-1][0]
292 loopinst = self.prog[self.stat[self.pc]]
293 forvar = loopinst[1]
294 if nextvar != forvar:
291 nextvar = instr[1]
292 self.pc = self.loops[-1][0]
293 loopinst = self.prog[self.stat[self.pc]]
294 forvar = loopinst[1]
295 if nextvar != forvar:
295 print "NEXT DOESN'T MATCH FOR AT LINE", line
296 print("NEXT DOESN'T MATCH FOR AT LINE %s" % line)
296 return
297 continue
298 elif op == 'GOSUB':
299 newline = instr[1]
300 if self.gosub:
297 return
298 continue
299 elif op == 'GOSUB':
300 newline = instr[1]
301 if self.gosub:
301 print "ALREADY IN A SUBROUTINE AT LINE", line
302 print("ALREADY IN A SUBROUTINE AT LINE %s" % line)
302 return
303 self.gosub = self.stat[self.pc]
304 self.goto(newline)
305 continue
306
307 elif op == 'RETURN':
308 if not self.gosub:
303 return
304 self.gosub = self.stat[self.pc]
305 self.goto(newline)
306 continue
307
308 elif op == 'RETURN':
309 if not self.gosub:
309 print "RETURN WITHOUT A GOSUB AT LINE",line
310 print("RETURN WITHOUT A GOSUB AT LINE %s" % line)
310 return
311 self.goto(self.gosub)
312 self.gosub = None
313
314 elif op == 'FUNC':
315 fname = instr[1]
316 pname = instr[2]
317 expr = instr[3]

--- 10 unchanged lines hidden (view full) ---

328 else:
329 # Double dimension variable
330 temp = [0]*y
331 v = []
332 for i in range(x):
333 v.append(temp[:])
334 self.tables[vname] = v
335
311 return
312 self.goto(self.gosub)
313 self.gosub = None
314
315 elif op == 'FUNC':
316 fname = instr[1]
317 pname = instr[2]
318 expr = instr[3]

--- 10 unchanged lines hidden (view full) ---

329 else:
330 # Double dimension variable
331 temp = [0]*y
332 v = []
333 for i in range(x):
334 v.append(temp[:])
335 self.tables[vname] = v
336
336 self.pc += 1
337 self.pc += 1
337
338 # Utility functions for program listing
339 def expr_str(self,expr):
340 etype = expr[0]
341 if etype == 'NUM': return str(expr[1])
342 elif etype == 'GROUP': return "(%s)" % self.expr_str(expr[1])
343 elif etype == 'UNARY':
344 if expr[1] == '-': return "-"+str(expr[2])

--- 8 unchanged lines hidden (view full) ---

353 def var_str(self,var):
354 varname,dim1,dim2 = var
355 if not dim1 and not dim2: return varname
356 if dim1 and not dim2: return "%s(%s)" % (varname, self.expr_str(dim1))
357 return "%s(%s,%s)" % (varname, self.expr_str(dim1),self.expr_str(dim2))
358
359 # Create a program listing
360 def list(self):
338
339 # Utility functions for program listing
340 def expr_str(self,expr):
341 etype = expr[0]
342 if etype == 'NUM': return str(expr[1])
343 elif etype == 'GROUP': return "(%s)" % self.expr_str(expr[1])
344 elif etype == 'UNARY':
345 if expr[1] == '-': return "-"+str(expr[2])

--- 8 unchanged lines hidden (view full) ---

354 def var_str(self,var):
355 varname,dim1,dim2 = var
356 if not dim1 and not dim2: return varname
357 if dim1 and not dim2: return "%s(%s)" % (varname, self.expr_str(dim1))
358 return "%s(%s,%s)" % (varname, self.expr_str(dim1),self.expr_str(dim2))
359
360 # Create a program listing
361 def list(self):
361 stat = self.prog.keys() # Ordered list of all line numbers
362 stat = list(self.prog) # Ordered list of all line numbers
362 stat.sort()
363 for line in stat:
364 instr = self.prog[line]
365 op = instr[0]
366 if op in ['END','STOP','RETURN']:
363 stat.sort()
364 for line in stat:
365 instr = self.prog[line]
366 op = instr[0]
367 if op in ['END','STOP','RETURN']:
367 print line, op
368 print("%s %s" % (line, op))
368 continue
369 elif op == 'REM':
369 continue
370 elif op == 'REM':
370 print line, instr[1]
371 print("%s %s" % (line, instr[1]))
371 elif op == 'PRINT':
372 elif op == 'PRINT':
372 print line, op,
373 _out = "%s %s " % (line, op)
373 first = 1
374 for p in instr[1]:
374 first = 1
375 for p in instr[1]:
375 if not first: print ",",
376 if p[0] and p[1]: print '"%s"%s' % (p[0],self.expr_str(p[1])),
377 elif p[1]: print self.expr_str(p[1]),
378 else: print '"%s"' % (p[0],),
376 if not first: _out += ", "
377 if p[0] and p[1]: _out += '"%s"%s' % (p[0],self.expr_str(p[1]))
378 elif p[1]: _out += self.expr_str(p[1])
379 else: _out += '"%s"' % (p[0],)
379 first = 0
380 first = 0
380 if instr[2]: print instr[2]
381 else: print
381 if instr[2]: _out += instr[2]
382 print(_out)
382 elif op == 'LET':
383 elif op == 'LET':
383 print line,"LET",self.var_str(instr[1]),"=",self.expr_str(instr[2])
384 print("%s LET %s = %s" % (line,self.var_str(instr[1]),self.expr_str(instr[2])))
384 elif op == 'READ':
385 elif op == 'READ':
385 print line,"READ",
386 _out = "%s READ " % line
386 first = 1
387 for r in instr[1]:
387 first = 1
388 for r in instr[1]:
388 if not first: print ",",
389 print self.var_str(r),
389 if not first: _out += ","
390 _out += self.var_str(r)
390 first = 0
391 first = 0
391 print ""
392 print(_out)
392 elif op == 'IF':
393 elif op == 'IF':
393 print line,"IF %s THEN %d" % (self.relexpr_str(instr[1]),instr[2])
394 print("%s IF %s THEN %d" % (line,self.relexpr_str(instr[1]),instr[2]))
394 elif op == 'GOTO' or op == 'GOSUB':
395 elif op == 'GOTO' or op == 'GOSUB':
395 print line, op, instr[1]
396 print("%s %s %s" % (line, op, instr[1]))
396 elif op == 'FOR':
397 elif op == 'FOR':
397 print line,"FOR %s = %s TO %s" % (instr[1],self.expr_str(instr[2]),self.expr_str(instr[3])),
398 if instr[4]: print "STEP %s" % (self.expr_str(instr[4])),
399 print
398 _out = "%s FOR %s = %s TO %s" % (line,instr[1],self.expr_str(instr[2]),self.expr_str(instr[3]))
399 if instr[4]: _out += " STEP %s" % (self.expr_str(instr[4]))
400 print(_out)
400 elif op == 'NEXT':
401 elif op == 'NEXT':
401 print line,"NEXT", instr[1]
402 print("%s NEXT %s" % (line, instr[1]))
402 elif op == 'FUNC':
403 elif op == 'FUNC':
403 print line,"DEF %s(%s) = %s" % (instr[1],instr[2],self.expr_str(instr[3]))
404 print("%s DEF %s(%s) = %s" % (line,instr[1],instr[2],self.expr_str(instr[3])))
404 elif op == 'DIM':
405 elif op == 'DIM':
405 print line,"DIM",
406 _out = "%s DIM " % line
406 first = 1
407 for vname,x,y in instr[1]:
407 first = 1
408 for vname,x,y in instr[1]:
408 if not first: print ",",
409 if not first: _out += ","
409 first = 0
410 if y == 0:
410 first = 0
411 if y == 0:
411 print "%s(%d)" % (vname,x),
412 _out += "%s(%d)" % (vname,x)
412 else:
413 else:
413 print "%s(%d,%d)" % (vname,x,y),
414
415 print
414 _out += "%s(%d,%d)" % (vname,x,y)
415
416 print(_out)
416 elif op == 'DATA':
417 elif op == 'DATA':
417 print line,"DATA",
418 _out = "%s DATA " % line
418 first = 1
419 for v in instr[1]:
419 first = 1
420 for v in instr[1]:
420 if not first: print ",",
421 if not first: _out += ","
421 first = 0
422 first = 0
422 print v,
423 print
423 _out += v
424 print(_out)
424
425 # Erase the current program
426 def new(self):
427 self.prog = {}
425
426 # Erase the current program
427 def new(self):
428 self.prog = {}
428
429
429 # Insert statements
430 def add_statements(self,prog):
431 for line,stat in prog.items():
432 self.prog[line] = stat
433
434 # Delete a statement
435 def del_line(self,lineno):
436 try:
437 del self.prog[lineno]
438 except KeyError:
439 pass
440
430 # Insert statements
431 def add_statements(self,prog):
432 for line,stat in prog.items():
433 self.prog[line] = stat
434
435 # Delete a statement
436 def del_line(self,lineno):
437 try:
438 del self.prog[lineno]
439 except KeyError:
440 pass
441