Path: blob/main/third_party/ply/example/BASIC/basinterp.py
7087 views
# This file provides the runtime support for running a basic program1# Assumes the program has been parsed using basparse.py23import sys4import math5import random67class BasicInterpreter:89# Initialize the interpreter. prog is a dictionary10# containing (line,statement) mappings11def __init__(self,prog):12self.prog = prog1314self.functions = { # Built-in function table15'SIN' : lambda z: math.sin(self.eval(z)),16'COS' : lambda z: math.cos(self.eval(z)),17'TAN' : lambda z: math.tan(self.eval(z)),18'ATN' : lambda z: math.atan(self.eval(z)),19'EXP' : lambda z: math.exp(self.eval(z)),20'ABS' : lambda z: abs(self.eval(z)),21'LOG' : lambda z: math.log(self.eval(z)),22'SQR' : lambda z: math.sqrt(self.eval(z)),23'INT' : lambda z: int(self.eval(z)),24'RND' : lambda z: random.random()25}2627# Collect all data statements28def collect_data(self):29self.data = []30for lineno in self.stat:31if self.prog[lineno][0] == 'DATA':32self.data = self.data + self.prog[lineno][1]33self.dc = 0 # Initialize the data counter3435# Check for end statements36def check_end(self):37has_end = 038for lineno in self.stat:39if self.prog[lineno][0] == 'END' and not has_end:40has_end = lineno41if not has_end:42print("NO END INSTRUCTION")43self.error = 144return45if has_end != lineno:46print("END IS NOT LAST")47self.error = 14849# Check loops50def check_loops(self):51for pc in range(len(self.stat)):52lineno = self.stat[pc]53if self.prog[lineno][0] == 'FOR':54forinst = self.prog[lineno]55loopvar = forinst[1]56for i in range(pc+1,len(self.stat)):57if self.prog[self.stat[i]][0] == 'NEXT':58nextvar = self.prog[self.stat[i]][1]59if nextvar != loopvar: continue60self.loopend[pc] = i61break62else:63print("FOR WITHOUT NEXT AT LINE %s" % self.stat[pc])64self.error = 16566# Evaluate an expression67def eval(self,expr):68etype = expr[0]69if etype == 'NUM': return expr[1]70elif etype == 'GROUP': return self.eval(expr[1])71elif etype == 'UNARY':72if expr[1] == '-': return -self.eval(expr[2])73elif etype == 'BINOP':74if expr[1] == '+': return self.eval(expr[2])+self.eval(expr[3])75elif expr[1] == '-': return self.eval(expr[2])-self.eval(expr[3])76elif expr[1] == '*': return self.eval(expr[2])*self.eval(expr[3])77elif expr[1] == '/': return float(self.eval(expr[2]))/self.eval(expr[3])78elif expr[1] == '^': return abs(self.eval(expr[2]))**self.eval(expr[3])79elif etype == 'VAR':80var,dim1,dim2 = expr[1]81if not dim1 and not dim2:82if var in self.vars:83return self.vars[var]84else:85print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))86raise RuntimeError87# May be a list lookup or a function evaluation88if dim1 and not dim2:89if var in self.functions:90# A function91return self.functions[var](dim1)92else:93# A list evaluation94if var in self.lists:95dim1val = self.eval(dim1)96if dim1val < 1 or dim1val > len(self.lists[var]):97print("LIST INDEX OUT OF BOUNDS AT LINE %s" % self.stat[self.pc])98raise RuntimeError99return self.lists[var][dim1val-1]100if dim1 and dim2:101if var in self.tables:102dim1val = self.eval(dim1)103dim2val = self.eval(dim2)104if dim1val < 1 or dim1val > len(self.tables[var]) or dim2val < 1 or dim2val > len(self.tables[var][0]):105print("TABLE INDEX OUT OUT BOUNDS AT LINE %s" % self.stat[self.pc])106raise RuntimeError107return self.tables[var][dim1val-1][dim2val-1]108print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))109raise RuntimeError110111# Evaluate a relational expression112def releval(self,expr):113etype = expr[1]114lhs = self.eval(expr[2])115rhs = self.eval(expr[3])116if etype == '<':117if lhs < rhs: return 1118else: return 0119120elif etype == '<=':121if lhs <= rhs: return 1122else: return 0123124elif etype == '>':125if lhs > rhs: return 1126else: return 0127128elif etype == '>=':129if lhs >= rhs: return 1130else: return 0131132elif etype == '=':133if lhs == rhs: return 1134else: return 0135136elif etype == '<>':137if lhs != rhs: return 1138else: return 0139140# Assignment141def assign(self,target,value):142var, dim1, dim2 = target143if not dim1 and not dim2:144self.vars[var] = self.eval(value)145elif dim1 and not dim2:146# List assignment147dim1val = self.eval(dim1)148if not var in self.lists:149self.lists[var] = [0]*10150151if dim1val > len(self.lists[var]):152print ("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])153raise RuntimeError154self.lists[var][dim1val-1] = self.eval(value)155elif dim1 and dim2:156dim1val = self.eval(dim1)157dim2val = self.eval(dim2)158if not var in self.tables:159temp = [0]*10160v = []161for i in range(10): v.append(temp[:])162self.tables[var] = v163# Variable already exists164if dim1val > len(self.tables[var]) or dim2val > len(self.tables[var][0]):165print("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])166raise RuntimeError167self.tables[var][dim1val-1][dim2val-1] = self.eval(value)168169# Change the current line number170def goto(self,linenum):171if not linenum in self.prog:172print("UNDEFINED LINE NUMBER %d AT LINE %d" % (linenum, self.stat[self.pc]))173raise RuntimeError174self.pc = self.stat.index(linenum)175176# Run it177def run(self):178self.vars = { } # All variables179self.lists = { } # List variables180self.tables = { } # Tables181self.loops = [ ] # Currently active loops182self.loopend= { } # Mapping saying where loops end183self.gosub = None # Gosub return point (if any)184self.error = 0 # Indicates program error185186self.stat = list(self.prog) # Ordered list of all line numbers187self.stat.sort()188self.pc = 0 # Current program counter189190# Processing prior to running191192self.collect_data() # Collect all of the data statements193self.check_end()194self.check_loops()195196if self.error: raise RuntimeError197198while 1:199line = self.stat[self.pc]200instr = self.prog[line]201202op = instr[0]203204# END and STOP statements205if op == 'END' or op == 'STOP':206break # We're done207208# GOTO statement209elif op == 'GOTO':210newline = instr[1]211self.goto(newline)212continue213214# PRINT statement215elif op == 'PRINT':216plist = instr[1]217out = ""218for label,val in plist:219if out:220out += ' '*(15 - (len(out) % 15))221out += label222if val:223if label: out += " "224eval = self.eval(val)225out += str(eval)226sys.stdout.write(out)227end = instr[2]228if not (end == ',' or end == ';'):229sys.stdout.write("\n")230if end == ',': sys.stdout.write(" "*(15-(len(out) % 15)))231if end == ';': sys.stdout.write(" "*(3-(len(out) % 3)))232233# LET statement234elif op == 'LET':235target = instr[1]236value = instr[2]237self.assign(target,value)238239# READ statement240elif op == 'READ':241for target in instr[1]:242if self.dc < len(self.data):243value = ('NUM',self.data[self.dc])244self.assign(target,value)245self.dc += 1246else:247# No more data. Program ends248return249elif op == 'IF':250relop = instr[1]251newline = instr[2]252if (self.releval(relop)):253self.goto(newline)254continue255256elif op == 'FOR':257loopvar = instr[1]258initval = instr[2]259finval = instr[3]260stepval = instr[4]261262# Check to see if this is a new loop263if not self.loops or self.loops[-1][0] != self.pc:264# Looks like a new loop. Make the initial assignment265newvalue = initval266self.assign((loopvar,None,None),initval)267if not stepval: stepval = ('NUM',1)268stepval = self.eval(stepval) # Evaluate step here269self.loops.append((self.pc,stepval))270else:271# It's a repeat of the previous loop272# Update the value of the loop variable according to the step273stepval = ('NUM',self.loops[-1][1])274newvalue = ('BINOP','+',('VAR',(loopvar,None,None)),stepval)275276if self.loops[-1][1] < 0: relop = '>='277else: relop = '<='278if not self.releval(('RELOP',relop,newvalue,finval)):279# Loop is done. Jump to the NEXT280self.pc = self.loopend[self.pc]281self.loops.pop()282else:283self.assign((loopvar,None,None),newvalue)284285elif op == 'NEXT':286if not self.loops:287print("NEXT WITHOUT FOR AT LINE %s" % line)288return289290nextvar = instr[1]291self.pc = self.loops[-1][0]292loopinst = self.prog[self.stat[self.pc]]293forvar = loopinst[1]294if nextvar != forvar:295print("NEXT DOESN'T MATCH FOR AT LINE %s" % line)296return297continue298elif op == 'GOSUB':299newline = instr[1]300if self.gosub:301print("ALREADY IN A SUBROUTINE AT LINE %s" % line)302return303self.gosub = self.stat[self.pc]304self.goto(newline)305continue306307elif op == 'RETURN':308if not self.gosub:309print("RETURN WITHOUT A GOSUB AT LINE %s" % line)310return311self.goto(self.gosub)312self.gosub = None313314elif op == 'FUNC':315fname = instr[1]316pname = instr[2]317expr = instr[3]318def eval_func(pvalue,name=pname,self=self,expr=expr):319self.assign((pname,None,None),pvalue)320return self.eval(expr)321self.functions[fname] = eval_func322323elif op == 'DIM':324for vname,x,y in instr[1]:325if y == 0:326# Single dimension variable327self.lists[vname] = [0]*x328else:329# Double dimension variable330temp = [0]*y331v = []332for i in range(x):333v.append(temp[:])334self.tables[vname] = v335336self.pc += 1337338# Utility functions for program listing339def expr_str(self,expr):340etype = expr[0]341if etype == 'NUM': return str(expr[1])342elif etype == 'GROUP': return "(%s)" % self.expr_str(expr[1])343elif etype == 'UNARY':344if expr[1] == '-': return "-"+str(expr[2])345elif etype == 'BINOP':346return "%s %s %s" % (self.expr_str(expr[2]),expr[1],self.expr_str(expr[3]))347elif etype == 'VAR':348return self.var_str(expr[1])349350def relexpr_str(self,expr):351return "%s %s %s" % (self.expr_str(expr[2]),expr[1],self.expr_str(expr[3]))352353def var_str(self,var):354varname,dim1,dim2 = var355if not dim1 and not dim2: return varname356if dim1 and not dim2: return "%s(%s)" % (varname, self.expr_str(dim1))357return "%s(%s,%s)" % (varname, self.expr_str(dim1),self.expr_str(dim2))358359# Create a program listing360def list(self):361stat = list(self.prog) # Ordered list of all line numbers362stat.sort()363for line in stat:364instr = self.prog[line]365op = instr[0]366if op in ['END','STOP','RETURN']:367print("%s %s" % (line, op))368continue369elif op == 'REM':370print("%s %s" % (line, instr[1]))371elif op == 'PRINT':372_out = "%s %s " % (line, op)373first = 1374for p in instr[1]:375if not first: _out += ", "376if p[0] and p[1]: _out += '"%s"%s' % (p[0],self.expr_str(p[1]))377elif p[1]: _out += self.expr_str(p[1])378else: _out += '"%s"' % (p[0],)379first = 0380if instr[2]: _out += instr[2]381print(_out)382elif op == 'LET':383print("%s LET %s = %s" % (line,self.var_str(instr[1]),self.expr_str(instr[2])))384elif op == 'READ':385_out = "%s READ " % line386first = 1387for r in instr[1]:388if not first: _out += ","389_out += self.var_str(r)390first = 0391print(_out)392elif op == 'IF':393print("%s IF %s THEN %d" % (line,self.relexpr_str(instr[1]),instr[2]))394elif op == 'GOTO' or op == 'GOSUB':395print("%s %s %s" % (line, op, instr[1]))396elif op == 'FOR':397_out = "%s FOR %s = %s TO %s" % (line,instr[1],self.expr_str(instr[2]),self.expr_str(instr[3]))398if instr[4]: _out += " STEP %s" % (self.expr_str(instr[4]))399print(_out)400elif op == 'NEXT':401print("%s NEXT %s" % (line, instr[1]))402elif op == 'FUNC':403print("%s DEF %s(%s) = %s" % (line,instr[1],instr[2],self.expr_str(instr[3])))404elif op == 'DIM':405_out = "%s DIM " % line406first = 1407for vname,x,y in instr[1]:408if not first: _out += ","409first = 0410if y == 0:411_out += "%s(%d)" % (vname,x)412else:413_out += "%s(%d,%d)" % (vname,x,y)414415print(_out)416elif op == 'DATA':417_out = "%s DATA " % line418first = 1419for v in instr[1]:420if not first: _out += ","421first = 0422_out += v423print(_out)424425# Erase the current program426def new(self):427self.prog = {}428429# Insert statements430def add_statements(self,prog):431for line,stat in prog.items():432self.prog[line] = stat433434# Delete a statement435def del_line(self,lineno):436try:437del self.prog[lineno]438except KeyError:439pass440441442443