#!/usr/bin/python # -*- coding: iso-8859-2 -*- ########################################################################### # Interpreter for a Simple Programming Language # # Copyright (C) 2006 Gergõ ÉRDI # http://cactus.rulez.org/ # # Published under the terms of the GNU General Public License 2.0 # $Id: sp-interpreter.py 428 2008-03-03 22:04:56Z cactus $ ########################################################################### # # SP is a toy language allowing the following simple statements: # # READ var Read an int from the user, and store it in 'var' # WRITE expr Print the value of 'expr' # GOTO lbl [IF expr] Jump to the specified line. If 'expr' is present, # jump only if the result of 'expr' >= 0 # LET var = expr Set value of 'var' to 'expr' # # There are 33 variables, named X, Y, Z, X0, ..., X9, Y0, ... # The type of variables and expressions are all integers # ########################################################################### class Interpreter: def __init__ (self): self.vars = {} self.stmts = [] self.lbl_map = {} def get_variable (self, var): if not var in self.vars: return 0 return self.vars[var] def set_variable (self, var, val): self.vars[var] = val def load (self, program): for (lbl, stmt) in program: self.stmts.append(stmt) pos = len(self.stmts) - 1 if lbl: self.lbl_map[lbl] = pos def run(self): self.pc = 0 end = len(self.stmts) while self.pc < end: self.step() def step(self): stmt = self.stmts[self.pc] self.pc += 1 stmt.evaluate (self) def jump(self, lbl): self.pc = self.lbl_map[lbl] def write(self, val): print val def read(self): try: s = raw_input("> ") return int(s) except EOFError: print return self.read() except ValueError: print "'%s': Invalid number" % s return self.read() class Stmt: class Read: def __init__(self, var): self.var = var def evaluate(self, interpreter): interpreter.set_variable(self.var, interpreter.read ()) class Write: def __init__(self, expr): self.expr = expr def evaluate(self, interpreter): interpreter.write(self.expr.evaluate(interpreter)) class Assign: def __init__(self, var, expr): self.var = var self.expr = expr def evaluate(self, interpreter): interpreter.set_variable(self.var, self.expr.evaluate(interpreter)) class Goto: def __init__(self, lbl, cond = None): self.lbl = lbl self.cond = cond def evaluate(self, interpreter): if (self.cond is None) or (self.cond.evaluate(interpreter) >= 0): interpreter.jump (self.lbl) class Expr: class NumLit: def __init__(self, num): self.num = num def evaluate(self, interpreter): return self.num class VariableVal: def __init__(self, var): self.var = var def evaluate(self, interpreter): return interpreter.get_variable (self.var) class Function: def __init__(self, fun, left, right): self.left = left self.right = right self.fun = fun def evaluate(self, interpreter): return self.fun (self.left.evaluate(interpreter), self.right.evaluate(interpreter)) ######################## # Create lexer ######################## import yappy.parser def token(name, fun = lambda x: x): return lambda x: (name, fun(x)) lexer = [("\s+", ""), ("[+\-*/:;()=]", lambda x: (x, x)), ("(?i)GOTO", token("GOTO")), ("(?i)IF", token("IF")), ("(?i)READ", token("READ")), ("(?i)WRITE", token("WRITE")), ("(?i)LET", token("LET")), ("(?i)[a-z]", token("LETTER", lambda x: x.lower())), ("(?i)[0-9]", token("DIGIT", lambda x: int(x)))] ######################## # Create parser ######################## grammar = [("program", ["progline"], lambda (l,),c: [l]), ("program", ["program", "progline"], lambda (prog, l),c: prog + [l]), ("progline", ["number", ":", "stmt", ";"], lambda (lbl, col, stmt, scol), c: (lbl, stmt)), ("progline", ["stmt", ";"], lambda (stmt, scol), c: (None, stmt)), ("stmt", ["READ", "var_id"], lambda (read, id), c: Stmt.Read(id)), ("stmt", ["WRITE", "expr"], lambda (write, expr), c: Stmt.Write(expr)), ("stmt", ["LET", "var_id", "=", "expr"], lambda (let, id, eq, expr), c: Stmt.Assign(id, expr)), ("stmt", ["GOTO", "number"], lambda (goto, lbl), c: Stmt.Goto(lbl)), ("stmt", ["GOTO", "number", "IF", "expr"], lambda (goto, lbl, if_, cond), c: Stmt.Goto(lbl, cond)), ("expr", ["term"], lambda (t,),c: t), ("expr", ["term", "+", "term"], lambda (a, op, b), c: Expr.Function(lambda x, y: x + y, a, b)), ("expr", ["term", "-", "term"], lambda (a, op, b), c: Expr.Function(lambda x, y: x - y, a, b)), ("term", ["factor"], lambda (f,),c: f), ("term", ["factor", "*", "factor"], lambda (a, op, b), c: Expr.Function(lambda x, y: x * y, a, b)), ("term", ["factor", "/", "factor"], lambda (a, op, b), c: Expr.Function(lambda x, y: x / y, a, b)), ("factor", ["(", "expr", ")"], lambda (lp, e, rp),c: e), ("factor", ["number"], lambda (num,),c: Expr.NumLit(num)), ("factor", ["var_id"], lambda (id,),c: Expr.VariableVal(id)), ("var_id", ["LETTER"], lambda (l,), c: l), ("var_id", ["LETTER", "DIGIT"], lambda (a, b), c: a + str(b)), ("number", ["DIGIT"], lambda (d,),c: d), ("number", ["number", "DIGIT"], lambda (num, d),c: num * 10 + d)] parser = yappy.parser.Yappy (lexer, grammar) ######################## # Run programs ######################## def load_sp (filename): f = open (filename, "r") return parser.input (f.read ()) def run_sp (filename): interpreter = Interpreter () interpreter.load(load_sp (filename)) interpreter.run() import sys for filename in sys.argv[1:]: run_sp (filename)