~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to third_party/ply/example/BASIC/basinterp.py

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-05-02 13:11:51 UTC
  • Revision ID: package-import@ubuntu.com-20130502131151-q8dvteqr1ef2x7xz
Tags: upstream-1.4.1~20130504~adb56cb
ImportĀ upstreamĀ versionĀ 1.4.1~20130504~adb56cb

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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