User:Marinus/Unreadable Interpreter
Jump to navigation
Jump to search
This is an Unreadable interpreter. It has been approved. See Unreadable talk page.
#!/usr/bin/env python # Unreadable interpreter import sys, re # if your programs are so huge you run into the recursion limit, # you might try increasing this number. # be warned though that increasing it much further might cause python to crash sys.setrecursionlimit(13500) class Parser: class ParseError(Exception): pass class Expression: # commands and their numbers PRINT,INC,ONE,DO,WHILE,SET,GET,DEC,IF,IN =\ 1 , 2 , 3 , 4, 5 , 6 , 7 , 8 , 9,10 # amount of arguments ARGNS = {PRINT: 1, INC: 1, ONE: 0, DO: 2, WHILE: 2, SET: 2, GET: 1, DEC: 1, IF: 3, IN: 0} def __init__(self, command, args): self.command = command self.args = args # get a semi-human-readable representation of the Unreadable program def __str__(self): strreps = ['', 'print ', 'inc ', '1', 'do ', 'while ', 'set ', 'get ', 'dec ', 'if ', 'in'] return '(' + strreps[self.command] + \ ', '.join(map(str, self.args)) + ')' # parse an expression, return (Expr, rest) # assumes all non-('") chars have already been removed # from input string @staticmethod def parse(pgm): # empty program if pgm == "": raise Parser.ParseError("empty program or too few arguments") # program must begin with "'" if pgm[0] != "'": raise Parser.ParseError("invalid expression: %s" % pgm) # find end-of-string or next "'" command = 0 index = 1 while index < len(pgm) and pgm[index] != "'": if pgm[index] == '"': command += 1 index += 1 if not 1 <= command <= 10: raise Parser.ParseError("invalid command (%d): %s" % (command, pgm[:index])) # parse arguments rest = pgm[index:] args = [] argn = Parser.Expression.ARGNS[command] for i in range(argn): arg, rest = Parser.parse(rest) args.append(arg) # return expression + leftovers return Parser.Expression(command, args), rest # turn a program into a list of parsed expressions @staticmethod def parseexprs(pgm): # remove all unused characters pgm = re.sub("[^\"']", "", pgm) rest = pgm exprs = [] while rest: exp, rest = Parser.parse(rest) exprs.append(exp) return exprs # array that grows automatically class Array: def __init__(self, list = None, defaultval = 0): self.list = list[:] if list else [] self.defaultval = defaultval def __getitem__(self, x): if x < len(self.list): return self.list[x] else: return self.defaultval def __setitem__(self, x, val): if x >= len(self.list): self.list += [self.defaultval] * (x - len(self.list) + 1) self.list[x] = val # interpreter class Interpreter: # commands def print_(self, x): x = self.eval(x) # try unicode character, if it fails, truncate to normal char. try: sys.stdout.write(unichr(x)) except UnicodeEncodeError: sys.stdout.write(chr(x % 256)) return x def inc(self, x): return self.eval(x) + 1 def one(self): return 1 def do(self, x, y): self.eval(x) return self.eval(y) def while_(self, x, y): while self.eval(x): result = self.eval(y) return result def set(self, x, y): r = self.eval(y) self.vars[self.eval(x)] = r return r def get(self, x): return self.vars[self.eval(x)] def dec(self, x): return self.eval(x) - 1 def if_(self, x, y, z): if self.eval(x): return self.eval(y) else: return self.eval(z) def in_(self): ch = sys.stdin.read(1) return ord(ch) if ch else -1 # evaluate an expression def eval(self, expr): # easy, isn't it? return self.cmds[expr.command](*expr.args) # run the program (just evaluate all root exprs in order) def run(self): map(self.eval, self.pgm) def __init__(self, pgm): self.pgm = pgm self.vars = Array() e = Parser.Expression self.cmds = { e.PRINT: self.print_, e.INC: self.inc, e.ONE: self.one, e.DO: self.do, e.WHILE: self.while_, e.SET: self.set, e.GET: self.get, e.DEC: self.dec, e.IF: self.if_, e.IN: self.in_ } def main(argv): if not len(argv) in (2, 3) or len(argv)==3 and argv[1] != '-show': print "Usage: %s [-show] program" % argv[0] print "\tIf the -show flag is given, the program is parsed and" print "\toutput. If not, the program is executed." sys.exit() try: pgm = file(argv[-1]).read() except Exception, e: print "error: cannot read file %s: %s" % (argv[-1], e) sys.exit() try: exprs = Parser.parseexprs(pgm) except Exception, e: print "error: parser failed: %s" % e sys.exit() if argv[1]=='-show': for expr in exprs: print expr else: try: Interpreter(exprs).run() except Exception, e: print "runtime error: %s" % e if __name__ == '__main__': main(sys.argv)