diff options
Diffstat (limited to 'ext/ply/example/BASIC')
-rw-r--r-- | ext/ply/example/BASIC/basic.py | 9 | ||||
-rw-r--r-- | ext/ply/example/BASIC/basiclex.py | 16 | ||||
-rw-r--r-- | ext/ply/example/BASIC/basiclog.py | 79 | ||||
-rw-r--r-- | ext/ply/example/BASIC/basinterp.py | 125 | ||||
-rw-r--r-- | ext/ply/example/BASIC/basparse.py | 26 |
5 files changed, 169 insertions, 86 deletions
diff --git a/ext/ply/example/BASIC/basic.py b/ext/ply/example/BASIC/basic.py index 3a07acdbf..b14483d2d 100644 --- a/ext/ply/example/BASIC/basic.py +++ b/ext/ply/example/BASIC/basic.py @@ -4,6 +4,9 @@ import sys sys.path.insert(0,"../..") +if sys.version_info[0] >= 3: + raw_input = input + import basiclex import basparse import basinterp @@ -41,7 +44,7 @@ while 1: prog = basparse.parse(line) if not prog: continue - keys = prog.keys() + keys = list(prog) if keys[0] > 0: b.add_statements(prog) else: @@ -58,8 +61,8 @@ while 1: elif stat[0] == 'NEW': b.new() - - + + diff --git a/ext/ply/example/BASIC/basiclex.py b/ext/ply/example/BASIC/basiclex.py index 727383f2b..3d27cdeeb 100644 --- a/ext/ply/example/BASIC/basiclex.py +++ b/ext/ply/example/BASIC/basiclex.py @@ -25,7 +25,7 @@ def t_ID(t): if t.value in keywords: t.type = t.value return t - + t_EQUALS = r'=' t_PLUS = r'\+' t_MINUS = r'-' @@ -41,7 +41,7 @@ t_GE = r'>=' t_NE = r'<>' t_COMMA = r'\,' t_SEMI = r';' -t_INTEGER = r'\d+' +t_INTEGER = r'\d+' t_FLOAT = r'((\d*\.\d+)(E[\+-]?\d+)?|([1-9]\d*E[\+-]?\d+))' t_STRING = r'\".*?\"' @@ -51,14 +51,10 @@ def t_NEWLINE(t): return t def t_error(t): - print "Illegal character", t.value[0] + print("Illegal character %s" % t.value[0]) t.lexer.skip(1) -lex.lex() - - - - +lex.lex(debug=0) @@ -66,6 +62,10 @@ lex.lex() + + + + diff --git a/ext/ply/example/BASIC/basiclog.py b/ext/ply/example/BASIC/basiclog.py new file mode 100644 index 000000000..ccfd7b967 --- /dev/null +++ b/ext/ply/example/BASIC/basiclog.py @@ -0,0 +1,79 @@ +# An implementation of Dartmouth BASIC (1964) +# + +import sys +sys.path.insert(0,"../..") + +if sys.version_info[0] >= 3: + raw_input = input + +import logging +logging.basicConfig( + level = logging.INFO, + filename = "parselog.txt", + filemode = "w" +) +log = logging.getLogger() + +import basiclex +import basparse +import basinterp + +# If a filename has been specified, we try to run it. +# If a runtime error occurs, we bail out and enter +# interactive mode below +if len(sys.argv) == 2: + data = open(sys.argv[1]).read() + prog = basparse.parse(data,debug=log) + if not prog: raise SystemExit + b = basinterp.BasicInterpreter(prog) + try: + b.run() + raise SystemExit + except RuntimeError: + pass + +else: + b = basinterp.BasicInterpreter({}) + +# Interactive mode. This incrementally adds/deletes statements +# from the program stored in the BasicInterpreter object. In +# addition, special commands 'NEW','LIST',and 'RUN' are added. +# Specifying a line number with no code deletes that line from +# the program. + +while 1: + try: + line = raw_input("[BASIC] ") + except EOFError: + raise SystemExit + if not line: continue + line += "\n" + prog = basparse.parse(line,debug=log) + if not prog: continue + + keys = list(prog) + if keys[0] > 0: + b.add_statements(prog) + else: + stat = prog[keys[0]] + if stat[0] == 'RUN': + try: + b.run() + except RuntimeError: + pass + elif stat[0] == 'LIST': + b.list() + elif stat[0] == 'BLANK': + b.del_line(stat[1]) + elif stat[0] == 'NEW': + b.new() + + + + + + + + + diff --git a/ext/ply/example/BASIC/basinterp.py b/ext/ply/example/BASIC/basinterp.py index 5850457cb..3e8a7774a 100644 --- a/ext/ply/example/BASIC/basinterp.py +++ b/ext/ply/example/BASIC/basinterp.py @@ -40,10 +40,11 @@ class BasicInterpreter: if self.prog[lineno][0] == 'END' and not has_end: has_end = lineno if not has_end: - print "NO END INSTRUCTION" + print("NO END INSTRUCTION") self.error = 1 + return if has_end != lineno: - print "END IS NOT LAST" + print("END IS NOT LAST") self.error = 1 # Check loops @@ -60,9 +61,9 @@ class BasicInterpreter: self.loopend[pc] = i break else: - print "FOR WITHOUT NEXT AT LINE" % self.stat[pc] + print("FOR WITHOUT NEXT AT LINE %s" % self.stat[pc]) self.error = 1 - + # Evaluate an expression def eval(self,expr): etype = expr[0] @@ -79,33 +80,33 @@ class BasicInterpreter: elif etype == 'VAR': var,dim1,dim2 = expr[1] if not dim1 and not dim2: - if self.vars.has_key(var): + if var in self.vars: return self.vars[var] else: - print "UNDEFINED VARIABLE", var, "AT LINE", self.stat[self.pc] + 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 self.functions.has_key(var): + if var in self.functions: # A function return self.functions[var](dim1) else: # A list evaluation - if self.lists.has_key(var): + 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", self.stat[self.pc] + 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 self.tables.has_key(var): + 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", self.stat[self.pc] + 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", var, "AT LINE", self.stat[self.pc] + print("UNDEFINED VARIABLE %s AT LINE %s" % (var, self.stat[self.pc])) raise RuntimeError # Evaluate a relational expression @@ -145,31 +146,31 @@ class BasicInterpreter: elif dim1 and not dim2: # List assignment dim1val = self.eval(dim1) - if not self.lists.has_key(var): + if not var in self.lists: self.lists[var] = [0]*10 if dim1val > len(self.lists[var]): - print "DIMENSION TOO LARGE AT LINE", self.stat[self.pc] + 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 self.tables.has_key(var): + 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", self.stat[self.pc] + 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 self.prog.has_key(linenum): - print "UNDEFINED LINE NUMBER %d AT LINE %d" % (linenum, self.stat[self.pc]) + 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) @@ -183,7 +184,7 @@ class BasicInterpreter: self.gosub = None # Gosub return point (if any) self.error = 0 # Indicates program error - self.stat = self.prog.keys() # Ordered list of all line numbers + self.stat = list(self.prog) # Ordered list of all line numbers self.stat.sort() self.pc = 0 # Current program counter @@ -198,7 +199,7 @@ class BasicInterpreter: while 1: line = self.stat[self.pc] instr = self.prog[line] - + op = instr[0] # END and STOP statements @@ -225,11 +226,11 @@ class BasicInterpreter: out += str(eval) sys.stdout.write(out) end = instr[2] - if not (end == ',' or end == ';'): + 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] @@ -258,7 +259,7 @@ class BasicInterpreter: 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 @@ -284,21 +285,21 @@ class BasicInterpreter: elif op == 'NEXT': if not self.loops: - print "NEXT WITHOUT FOR AT LINE",line + 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", line + 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", line + print("ALREADY IN A SUBROUTINE AT LINE %s" % line) return self.gosub = self.stat[self.pc] self.goto(newline) @@ -306,7 +307,7 @@ class BasicInterpreter: elif op == 'RETURN': if not self.gosub: - print "RETURN WITHOUT A GOSUB AT LINE",line + print("RETURN WITHOUT A GOSUB AT LINE %s" % line) return self.goto(self.gosub) self.gosub = None @@ -333,7 +334,7 @@ class BasicInterpreter: v.append(temp[:]) self.tables[vname] = v - self.pc += 1 + self.pc += 1 # Utility functions for program listing def expr_str(self,expr): @@ -358,74 +359,74 @@ class BasicInterpreter: # Create a program listing def list(self): - stat = self.prog.keys() # Ordered list of all line numbers + 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 line, op + print("%s %s" % (line, op)) continue elif op == 'REM': - print line, instr[1] + print("%s %s" % (line, instr[1])) elif op == 'PRINT': - print line, op, + _out = "%s %s " % (line, op) first = 1 for p in instr[1]: - if not first: print ",", - if p[0] and p[1]: print '"%s"%s' % (p[0],self.expr_str(p[1])), - elif p[1]: print self.expr_str(p[1]), - else: print '"%s"' % (p[0],), + 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]: print instr[2] - else: print + if instr[2]: _out += instr[2] + print(_out) elif op == 'LET': - print line,"LET",self.var_str(instr[1]),"=",self.expr_str(instr[2]) + print("%s LET %s = %s" % (line,self.var_str(instr[1]),self.expr_str(instr[2]))) elif op == 'READ': - print line,"READ", + _out = "%s READ " % line first = 1 for r in instr[1]: - if not first: print ",", - print self.var_str(r), + if not first: _out += "," + _out += self.var_str(r) first = 0 - print "" + print(_out) elif op == 'IF': - print line,"IF %s THEN %d" % (self.relexpr_str(instr[1]),instr[2]) + print("%s IF %s THEN %d" % (line,self.relexpr_str(instr[1]),instr[2])) elif op == 'GOTO' or op == 'GOSUB': - print line, op, instr[1] + print("%s %s %s" % (line, op, instr[1])) elif op == 'FOR': - print line,"FOR %s = %s TO %s" % (instr[1],self.expr_str(instr[2]),self.expr_str(instr[3])), - if instr[4]: print "STEP %s" % (self.expr_str(instr[4])), - print + _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 line,"NEXT", instr[1] + print("%s NEXT %s" % (line, instr[1])) elif op == 'FUNC': - print line,"DEF %s(%s) = %s" % (instr[1],instr[2],self.expr_str(instr[3])) + print("%s DEF %s(%s) = %s" % (line,instr[1],instr[2],self.expr_str(instr[3]))) elif op == 'DIM': - print line,"DIM", + _out = "%s DIM " % line first = 1 for vname,x,y in instr[1]: - if not first: print ",", + if not first: _out += "," first = 0 if y == 0: - print "%s(%d)" % (vname,x), + _out += "%s(%d)" % (vname,x) else: - print "%s(%d,%d)" % (vname,x,y), - - print + _out += "%s(%d,%d)" % (vname,x,y) + + print(_out) elif op == 'DATA': - print line,"DATA", + _out = "%s DATA " % line first = 1 for v in instr[1]: - if not first: print ",", + if not first: _out += "," first = 0 - print v, - print + _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(): diff --git a/ext/ply/example/BASIC/basparse.py b/ext/ply/example/BASIC/basparse.py index 930af9a22..ccdeb16b6 100644 --- a/ext/ply/example/BASIC/basparse.py +++ b/ext/ply/example/BASIC/basparse.py @@ -39,12 +39,12 @@ def p_program_error(p): p[0] = None p.parser.error = 1 -#### Format of all BASIC statements. +#### Format of all BASIC statements. def p_statement(p): '''statement : INTEGER command NEWLINE''' if isinstance(p[2],str): - print p[2],"AT LINE", p[1] + print("%s %s %s" % (p[2],"AT LINE", p[1])) p[0] = None p.parser.error = 1 else: @@ -68,7 +68,7 @@ def p_statement_blank(p): def p_statement_bad(p): '''statement : INTEGER error NEWLINE''' - print "MALFORMED STATEMENT AT LINE", p[1] + print("MALFORMED STATEMENT AT LINE %s" % p[1]) p[0] = None p.parser.error = 1 @@ -121,7 +121,7 @@ def p_command_print_bad(p): #### Optional ending on PRINT. Either a comma (,) or semicolon (;) def p_optend(p): - '''optend : COMMA + '''optend : COMMA | SEMI |''' if len(p) == 2: @@ -188,7 +188,7 @@ def p_optstep(p): p[0] = None #### NEXT statement - + def p_command_next(p): '''command : NEXT ID''' @@ -392,30 +392,30 @@ def p_item_expr(p): p[0] = ("",p[1]) #### Empty - + def p_empty(p): '''empty : ''' #### Catastrophic error handler def p_error(p): if not p: - print "SYNTAX ERROR AT EOF" + print("SYNTAX ERROR AT EOF") bparser = yacc.yacc() -def parse(data): +def parse(data,debug=0): bparser.error = 0 - p = bparser.parse(data) + p = bparser.parse(data,debug=debug) if bparser.error: return None return p - - - - + + + + |