Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/third_party/ply/example/BASIC/basinterp.py
7087 views
1
# This file provides the runtime support for running a basic program
2
# Assumes the program has been parsed using basparse.py
3
4
import sys
5
import math
6
import random
7
8
class BasicInterpreter:
9
10
# Initialize the interpreter. prog is a dictionary
11
# containing (line,statement) mappings
12
def __init__(self,prog):
13
self.prog = prog
14
15
self.functions = { # Built-in function table
16
'SIN' : lambda z: math.sin(self.eval(z)),
17
'COS' : lambda z: math.cos(self.eval(z)),
18
'TAN' : lambda z: math.tan(self.eval(z)),
19
'ATN' : lambda z: math.atan(self.eval(z)),
20
'EXP' : lambda z: math.exp(self.eval(z)),
21
'ABS' : lambda z: abs(self.eval(z)),
22
'LOG' : lambda z: math.log(self.eval(z)),
23
'SQR' : lambda z: math.sqrt(self.eval(z)),
24
'INT' : lambda z: int(self.eval(z)),
25
'RND' : lambda z: random.random()
26
}
27
28
# Collect all data statements
29
def collect_data(self):
30
self.data = []
31
for lineno in self.stat:
32
if self.prog[lineno][0] == 'DATA':
33
self.data = self.data + self.prog[lineno][1]
34
self.dc = 0 # Initialize the data counter
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")
44
self.error = 1
45
return
46
if has_end != lineno:
47
print("END IS NOT LAST")
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:
64
print("FOR WITHOUT NEXT AT LINE %s" % self.stat[pc])
65
self.error = 1
66
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:
83
if var in self.vars:
84
return self.vars[var]
85
else:
86
print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))
87
raise RuntimeError
88
# May be a list lookup or a function evaluation
89
if dim1 and not dim2:
90
if var in self.functions:
91
# A function
92
return self.functions[var](dim1)
93
else:
94
# A list evaluation
95
if var in self.lists:
96
dim1val = self.eval(dim1)
97
if dim1val < 1 or dim1val > len(self.lists[var]):
98
print("LIST INDEX OUT OF BOUNDS AT LINE %s" % self.stat[self.pc])
99
raise RuntimeError
100
return self.lists[var][dim1val-1]
101
if dim1 and dim2:
102
if var in self.tables:
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]):
106
print("TABLE INDEX OUT OUT BOUNDS AT LINE %s" % self.stat[self.pc])
107
raise RuntimeError
108
return self.tables[var][dim1val-1][dim2val-1]
109
print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))
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 == '<':
118
if lhs < rhs: return 1
119
else: return 0
120
121
elif etype == '<=':
122
if lhs <= rhs: return 1
123
else: return 0
124
125
elif etype == '>':
126
if lhs > rhs: return 1
127
else: return 0
128
129
elif etype == '>=':
130
if lhs >= rhs: return 1
131
else: return 0
132
133
elif etype == '=':
134
if lhs == rhs: return 1
135
else: return 0
136
137
elif etype == '<>':
138
if lhs != rhs: return 1
139
else: return 0
140
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)
149
if not var in self.lists:
150
self.lists[var] = [0]*10
151
152
if dim1val > len(self.lists[var]):
153
print ("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
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)
159
if not var in self.tables:
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]):
166
print("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
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):
172
if not linenum in self.prog:
173
print("UNDEFINED LINE NUMBER %d AT LINE %d" % (linenum, self.stat[self.pc]))
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
187
self.stat = list(self.prog) # Ordered list of all line numbers
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]
202
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':
211
newline = instr[1]
212
self.goto(newline)
213
continue
214
215
# PRINT statement
216
elif op == 'PRINT':
217
plist = instr[1]
218
out = ""
219
for label,val in plist:
220
if out:
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]
229
if not (end == ',' or end == ';'):
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)))
233
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':
242
for target in instr[1]:
243
if self.dc < len(self.data):
244
value = ('NUM',self.data[self.dc])
245
self.assign(target,value)
246
self.dc += 1
247
else:
248
# No more data. Program ends
249
return
250
elif op == 'IF':
251
relop = instr[1]
252
newline = instr[2]
253
if (self.releval(relop)):
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]
262
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))
271
else:
272
# It's a repeat of the previous loop
273
# Update the value of the loop variable according to the step
274
stepval = ('NUM',self.loops[-1][1])
275
newvalue = ('BINOP','+',('VAR',(loopvar,None,None)),stepval)
276
277
if self.loops[-1][1] < 0: relop = '>='
278
else: relop = '<='
279
if not self.releval(('RELOP',relop,newvalue,finval)):
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:
288
print("NEXT WITHOUT FOR AT LINE %s" % line)
289
return
290
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:
296
print("NEXT DOESN'T MATCH FOR AT LINE %s" % line)
297
return
298
continue
299
elif op == 'GOSUB':
300
newline = instr[1]
301
if self.gosub:
302
print("ALREADY IN A SUBROUTINE AT LINE %s" % line)
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:
310
print("RETURN WITHOUT A GOSUB AT LINE %s" % line)
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]
319
def eval_func(pvalue,name=pname,self=self,expr=expr):
320
self.assign((pname,None,None),pvalue)
321
return self.eval(expr)
322
self.functions[fname] = eval_func
323
324
elif op == 'DIM':
325
for vname,x,y in instr[1]:
326
if y == 0:
327
# Single dimension variable
328
self.lists[vname] = [0]*x
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
337
self.pc += 1
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])
346
elif etype == 'BINOP':
347
return "%s %s %s" % (self.expr_str(expr[2]),expr[1],self.expr_str(expr[3]))
348
elif etype == 'VAR':
349
return self.var_str(expr[1])
350
351
def relexpr_str(self,expr):
352
return "%s %s %s" % (self.expr_str(expr[2]),expr[1],self.expr_str(expr[3]))
353
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):
362
stat = list(self.prog) # Ordered list of all line numbers
363
stat.sort()
364
for line in stat:
365
instr = self.prog[line]
366
op = instr[0]
367
if op in ['END','STOP','RETURN']:
368
print("%s %s" % (line, op))
369
continue
370
elif op == 'REM':
371
print("%s %s" % (line, instr[1]))
372
elif op == 'PRINT':
373
_out = "%s %s " % (line, op)
374
first = 1
375
for p in instr[1]:
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],)
380
first = 0
381
if instr[2]: _out += instr[2]
382
print(_out)
383
elif op == 'LET':
384
print("%s LET %s = %s" % (line,self.var_str(instr[1]),self.expr_str(instr[2])))
385
elif op == 'READ':
386
_out = "%s READ " % line
387
first = 1
388
for r in instr[1]:
389
if not first: _out += ","
390
_out += self.var_str(r)
391
first = 0
392
print(_out)
393
elif op == 'IF':
394
print("%s IF %s THEN %d" % (line,self.relexpr_str(instr[1]),instr[2]))
395
elif op == 'GOTO' or op == 'GOSUB':
396
print("%s %s %s" % (line, op, instr[1]))
397
elif op == 'FOR':
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)
401
elif op == 'NEXT':
402
print("%s NEXT %s" % (line, instr[1]))
403
elif op == 'FUNC':
404
print("%s DEF %s(%s) = %s" % (line,instr[1],instr[2],self.expr_str(instr[3])))
405
elif op == 'DIM':
406
_out = "%s DIM " % line
407
first = 1
408
for vname,x,y in instr[1]:
409
if not first: _out += ","
410
first = 0
411
if y == 0:
412
_out += "%s(%d)" % (vname,x)
413
else:
414
_out += "%s(%d,%d)" % (vname,x,y)
415
416
print(_out)
417
elif op == 'DATA':
418
_out = "%s DATA " % line
419
first = 1
420
for v in instr[1]:
421
if not first: _out += ","
422
first = 0
423
_out += v
424
print(_out)
425
426
# Erase the current program
427
def new(self):
428
self.prog = {}
429
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
442
443