3e8a7774a277314a7f397743980dafaf9c40d809
[platform/framework/web/lwnode.git] /
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