diff ply-3.8/example/BASIC/basinterp.py @ 7267:343ff337a19b

<ais523> ` tar -xf ply-3.8.tar.gz
author HackBot
date Wed, 23 Mar 2016 02:40:16 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ply-3.8/example/BASIC/basinterp.py	Wed Mar 23 02:40:16 2016 +0000
@@ -0,0 +1,441 @@
+# This file provides the runtime support for running a basic program
+# Assumes the program has been parsed using basparse.py
+
+import sys
+import math
+import random
+
+class BasicInterpreter:
+
+    # Initialize the interpreter. prog is a dictionary
+    # containing (line,statement) mappings
+    def __init__(self,prog):
+         self.prog = prog
+
+         self.functions = {           # Built-in function table
+             'SIN' : lambda z: math.sin(self.eval(z)),
+             'COS' : lambda z: math.cos(self.eval(z)),
+             'TAN' : lambda z: math.tan(self.eval(z)),
+             'ATN' : lambda z: math.atan(self.eval(z)),
+             'EXP' : lambda z: math.exp(self.eval(z)),
+             'ABS' : lambda z: abs(self.eval(z)),
+             'LOG' : lambda z: math.log(self.eval(z)),
+             'SQR' : lambda z: math.sqrt(self.eval(z)),
+             'INT' : lambda z: int(self.eval(z)),
+             'RND' : lambda z: random.random()
+         }
+
+    # Collect all data statements
+    def collect_data(self):
+         self.data = []
+         for lineno in self.stat:
+              if self.prog[lineno][0] == 'DATA':
+                  self.data = self.data + self.prog[lineno][1]
+         self.dc = 0                  # Initialize the data counter
+
+    # Check for end statements
+    def check_end(self):
+         has_end = 0
+         for lineno in self.stat:
+             if self.prog[lineno][0] == 'END' and not has_end:
+                  has_end = lineno
+         if not has_end:
+             print("NO END INSTRUCTION")
+             self.error = 1
+             return
+         if has_end != lineno:
+             print("END IS NOT LAST")
+             self.error = 1
+
+    # Check loops
+    def check_loops(self):
+         for pc in range(len(self.stat)):
+             lineno = self.stat[pc]
+             if self.prog[lineno][0] == 'FOR':
+                  forinst = self.prog[lineno]
+                  loopvar = forinst[1]
+                  for i in range(pc+1,len(self.stat)):
+                       if self.prog[self.stat[i]][0] == 'NEXT':
+                            nextvar = self.prog[self.stat[i]][1]
+                            if nextvar != loopvar: continue
+                            self.loopend[pc] = i
+                            break
+                  else:
+                       print("FOR WITHOUT NEXT AT LINE %s" % self.stat[pc])
+                       self.error = 1
+                  
+    # Evaluate an expression
+    def eval(self,expr):
+        etype = expr[0]
+        if etype == 'NUM': return expr[1]
+        elif etype == 'GROUP': return self.eval(expr[1])
+        elif etype == 'UNARY':
+             if expr[1] == '-': return -self.eval(expr[2])
+        elif etype == 'BINOP':
+             if expr[1] == '+': return self.eval(expr[2])+self.eval(expr[3])
+             elif expr[1] == '-': return self.eval(expr[2])-self.eval(expr[3])
+             elif expr[1] == '*': return self.eval(expr[2])*self.eval(expr[3])
+             elif expr[1] == '/': return float(self.eval(expr[2]))/self.eval(expr[3])
+             elif expr[1] == '^': return abs(self.eval(expr[2]))**self.eval(expr[3])
+        elif etype == 'VAR':
+             var,dim1,dim2 = expr[1]
+             if not dim1 and not dim2:
+                  if var in self.vars:
+                       return self.vars[var]
+                  else:
+                       print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))
+                       raise RuntimeError
+             # May be a list lookup or a function evaluation
+             if dim1 and not dim2:
+                if var in self.functions:
+                      # A function
+                      return self.functions[var](dim1)
+                else:
+                      # A list evaluation
+                      if var in self.lists:
+                            dim1val = self.eval(dim1)
+                            if dim1val < 1 or dim1val > len(self.lists[var]):
+                                 print("LIST INDEX OUT OF BOUNDS AT LINE %s" % self.stat[self.pc])
+                                 raise RuntimeError
+                            return self.lists[var][dim1val-1]
+             if dim1 and dim2:
+                 if var in self.tables:
+                      dim1val = self.eval(dim1)
+                      dim2val = self.eval(dim2)
+                      if dim1val < 1 or dim1val > len(self.tables[var]) or dim2val < 1 or dim2val > len(self.tables[var][0]):
+                           print("TABLE INDEX OUT OUT BOUNDS AT LINE %s" % self.stat[self.pc])
+                           raise RuntimeError
+                      return self.tables[var][dim1val-1][dim2val-1]
+             print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc]))
+             raise RuntimeError
+
+    # Evaluate a relational expression
+    def releval(self,expr):
+         etype = expr[1]
+         lhs   = self.eval(expr[2])
+         rhs   = self.eval(expr[3])
+         if etype == '<':
+             if lhs < rhs: return 1
+             else: return 0
+
+         elif etype == '<=':
+             if lhs <= rhs: return 1
+             else: return 0
+
+         elif etype == '>':
+             if lhs > rhs: return 1
+             else: return 0
+
+         elif etype == '>=':
+             if lhs >= rhs: return 1
+             else: return 0
+
+         elif etype == '=':
+             if lhs == rhs: return 1
+             else: return 0
+
+         elif etype == '<>':
+             if lhs != rhs: return 1
+             else: return 0
+
+    # Assignment
+    def assign(self,target,value):
+        var, dim1, dim2 = target
+        if not dim1 and not dim2:
+            self.vars[var] = self.eval(value)
+        elif dim1 and not dim2:
+            # List assignment
+            dim1val = self.eval(dim1)
+            if not var in self.lists:
+                 self.lists[var] = [0]*10
+
+            if dim1val > len(self.lists[var]):
+                 print ("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
+                 raise RuntimeError
+            self.lists[var][dim1val-1] = self.eval(value)
+        elif dim1 and dim2:
+            dim1val = self.eval(dim1)
+            dim2val = self.eval(dim2)
+            if not var in self.tables:
+                 temp = [0]*10
+                 v = []
+                 for i in range(10): v.append(temp[:])
+                 self.tables[var] = v
+            # Variable already exists
+            if dim1val > len(self.tables[var]) or dim2val > len(self.tables[var][0]):
+                 print("DIMENSION TOO LARGE AT LINE %s" % self.stat[self.pc])
+                 raise RuntimeError
+            self.tables[var][dim1val-1][dim2val-1] = self.eval(value)
+
+    # Change the current line number
+    def goto(self,linenum):
+         if not linenum in self.prog:
+              print("UNDEFINED LINE NUMBER %d AT LINE %d" % (linenum, self.stat[self.pc]))
+              raise RuntimeError
+         self.pc = self.stat.index(linenum)
+
+    # Run it
+    def run(self):
+        self.vars   = { }            # All variables
+        self.lists  = { }            # List variables
+        self.tables = { }            # Tables
+        self.loops  = [ ]            # Currently active loops
+        self.loopend= { }            # Mapping saying where loops end
+        self.gosub  = None           # Gosub return point (if any)
+        self.error  = 0              # Indicates program error
+
+        self.stat = list(self.prog)  # Ordered list of all line numbers
+        self.stat.sort()
+        self.pc = 0                  # Current program counter
+
+        # Processing prior to running
+
+        self.collect_data()          # Collect all of the data statements
+        self.check_end()
+        self.check_loops()
+
+        if self.error: raise RuntimeError
+
+        while 1:
+            line  = self.stat[self.pc]
+            instr = self.prog[line]
+            
+            op = instr[0]
+
+            # END and STOP statements
+            if op == 'END' or op == 'STOP':
+                 break           # We're done
+
+            # GOTO statement
+            elif op == 'GOTO':
+                 newline = instr[1]
+                 self.goto(newline)
+                 continue
+
+            # PRINT statement
+            elif op == 'PRINT':
+                 plist = instr[1]
+                 out = ""
+                 for label,val in plist:
+                     if out:
+                          out += ' '*(15 - (len(out) % 15))
+                     out += label
+                     if val:
+                          if label: out += " "
+                          eval = self.eval(val)
+                          out += str(eval)
+                 sys.stdout.write(out)
+                 end = instr[2]
+                 if not (end == ',' or end == ';'): 
+                     sys.stdout.write("\n")
+                 if end == ',': sys.stdout.write(" "*(15-(len(out) % 15)))
+                 if end == ';': sys.stdout.write(" "*(3-(len(out) % 3)))
+                     
+            # LET statement
+            elif op == 'LET':
+                 target = instr[1]
+                 value  = instr[2]
+                 self.assign(target,value)
+
+            # READ statement
+            elif op == 'READ':
+                 for target in instr[1]:
+                      if self.dc < len(self.data):
+                          value = ('NUM',self.data[self.dc])
+                          self.assign(target,value)
+                          self.dc += 1
+                      else:
+                          # No more data.  Program ends
+                          return
+            elif op == 'IF':
+                 relop = instr[1]
+                 newline = instr[2]
+                 if (self.releval(relop)):
+                     self.goto(newline)
+                     continue
+
+            elif op == 'FOR':
+                 loopvar = instr[1]
+                 initval = instr[2]
+                 finval  = instr[3]
+                 stepval = instr[4]
+              
+                 # Check to see if this is a new loop
+                 if not self.loops or self.loops[-1][0] != self.pc:
+                        # Looks like a new loop. Make the initial assignment
+                        newvalue = initval
+                        self.assign((loopvar,None,None),initval)
+                        if not stepval: stepval = ('NUM',1)
+                        stepval = self.eval(stepval)    # Evaluate step here
+                        self.loops.append((self.pc,stepval))
+                 else:
+                        # It's a repeat of the previous loop
+                        # Update the value of the loop variable according to the step
+                        stepval = ('NUM',self.loops[-1][1])
+                        newvalue = ('BINOP','+',('VAR',(loopvar,None,None)),stepval)
+
+                 if self.loops[-1][1] < 0: relop = '>='
+                 else: relop = '<='
+                 if not self.releval(('RELOP',relop,newvalue,finval)):
+                      # Loop is done. Jump to the NEXT
+                      self.pc = self.loopend[self.pc]
+                      self.loops.pop()
+                 else:
+                      self.assign((loopvar,None,None),newvalue)
+
+            elif op == 'NEXT':
+                 if not self.loops:
+                       print("NEXT WITHOUT FOR AT LINE %s" % line)
+                       return
+ 
+                 nextvar = instr[1]
+                 self.pc = self.loops[-1][0]
+                 loopinst = self.prog[self.stat[self.pc]]
+                 forvar = loopinst[1]
+                 if nextvar != forvar:
+                       print("NEXT DOESN'T MATCH FOR AT LINE %s" % line)
+                       return
+                 continue
+            elif op == 'GOSUB':
+                 newline = instr[1]
+                 if self.gosub:
+                       print("ALREADY IN A SUBROUTINE AT LINE %s" % line)
+                       return
+                 self.gosub = self.stat[self.pc]
+                 self.goto(newline)
+                 continue
+
+            elif op == 'RETURN':
+                 if not self.gosub:
+                      print("RETURN WITHOUT A GOSUB AT LINE %s" % line)
+                      return
+                 self.goto(self.gosub)
+                 self.gosub = None
+
+            elif op == 'FUNC':
+                 fname = instr[1]
+                 pname = instr[2]
+                 expr  = instr[3]
+                 def eval_func(pvalue,name=pname,self=self,expr=expr):
+                      self.assign((pname,None,None),pvalue)
+                      return self.eval(expr)
+                 self.functions[fname] = eval_func
+
+            elif op == 'DIM':
+                 for vname,x,y in instr[1]:
+                     if y == 0:
+                          # Single dimension variable
+                          self.lists[vname] = [0]*x
+                     else:
+                          # Double dimension variable
+                          temp = [0]*y
+                          v = []
+                          for i in range(x):
+                              v.append(temp[:])
+                          self.tables[vname] = v
+
+            self.pc += 1         
+
+    # Utility functions for program listing
+    def expr_str(self,expr):
+        etype = expr[0]
+        if etype == 'NUM': return str(expr[1])
+        elif etype == 'GROUP': return "(%s)" % self.expr_str(expr[1])
+        elif etype == 'UNARY':
+             if expr[1] == '-': return "-"+str(expr[2])
+        elif etype == 'BINOP':
+             return "%s %s %s" % (self.expr_str(expr[2]),expr[1],self.expr_str(expr[3]))
+        elif etype == 'VAR':
+              return self.var_str(expr[1])
+
+    def relexpr_str(self,expr):
+         return "%s %s %s" % (self.expr_str(expr[2]),expr[1],self.expr_str(expr[3]))
+
+    def var_str(self,var):
+         varname,dim1,dim2 = var
+         if not dim1 and not dim2: return varname
+         if dim1 and not dim2: return "%s(%s)" % (varname, self.expr_str(dim1))
+         return "%s(%s,%s)" % (varname, self.expr_str(dim1),self.expr_str(dim2))
+
+    # Create a program listing
+    def list(self):
+         stat = list(self.prog)      # Ordered list of all line numbers
+         stat.sort()
+         for line in stat:
+             instr = self.prog[line]
+             op = instr[0]
+             if op in ['END','STOP','RETURN']:
+                   print("%s %s" % (line, op))
+                   continue
+             elif op == 'REM':
+                   print("%s %s" % (line, instr[1]))
+             elif op == 'PRINT':
+                   _out = "%s %s " % (line, op)
+                   first = 1
+                   for p in instr[1]:
+                         if not first: _out += ", "
+                         if p[0] and p[1]: _out += '"%s"%s' % (p[0],self.expr_str(p[1]))
+                         elif p[1]: _out += self.expr_str(p[1])
+                         else: _out += '"%s"' % (p[0],)
+                         first = 0
+                   if instr[2]: _out += instr[2]
+                   print(_out)
+             elif op == 'LET':
+                   print("%s LET %s = %s" % (line,self.var_str(instr[1]),self.expr_str(instr[2])))
+             elif op == 'READ':
+                   _out = "%s READ " % line
+                   first = 1
+                   for r in instr[1]:
+                         if not first: _out += ","
+                         _out += self.var_str(r)
+                         first = 0
+                   print(_out)
+             elif op == 'IF':
+                   print("%s IF %s THEN %d" % (line,self.relexpr_str(instr[1]),instr[2]))
+             elif op == 'GOTO' or op == 'GOSUB':
+                   print("%s %s %s" % (line, op, instr[1]))
+             elif op == 'FOR':
+                   _out = "%s FOR %s = %s TO %s" % (line,instr[1],self.expr_str(instr[2]),self.expr_str(instr[3]))
+                   if instr[4]: _out += " STEP %s" % (self.expr_str(instr[4]))
+                   print(_out)
+             elif op == 'NEXT':
+                   print("%s NEXT %s" % (line, instr[1]))
+             elif op == 'FUNC':
+                   print("%s DEF %s(%s) = %s" % (line,instr[1],instr[2],self.expr_str(instr[3])))
+             elif op == 'DIM':
+                   _out = "%s DIM " % line
+                   first = 1
+                   for vname,x,y in instr[1]:
+                         if not first: _out += ","
+                         first = 0
+                         if y == 0:
+                               _out += "%s(%d)" % (vname,x)
+                         else:
+                               _out += "%s(%d,%d)" % (vname,x,y)
+                         
+                   print(_out)
+             elif op == 'DATA':
+                   _out = "%s DATA " % line
+                   first = 1
+                   for v in instr[1]:
+                        if not first: _out += ","
+                        first = 0
+                        _out += v
+                   print(_out)
+
+    # Erase the current program
+    def new(self):
+         self.prog = {}
+ 
+    # Insert statements
+    def add_statements(self,prog):
+         for line,stat in prog.items():
+              self.prog[line] = stat
+
+    # Delete a statement
+    def del_line(self,lineno):
+         try:
+             del self.prog[lineno]
+         except KeyError:
+             pass
+