User:Marinus/Oozlybub and Murphy interpreter

This is a prototype of an Oozlybub and Murphy interpreter. All the functionality should be there, but:
 * it is slow and buggy,
 * the parser is too inefficient for practical use,
 * there's no optimalisation at all, so the canonical way to loop slows down on each iteration.

I might get round to fixing it.


 * 1) !/usr/bin/env python3


 * 1) oozly.py
 * 2) Oozlybub & Murphy interpreter
 * 3) written by Marinus Oosters
 * 4) For parsing, I use the LEPL library:
 * 5)    * http://www.acooke.org/lepl/
 * 6) For the regexp equivalency tests, I am using Sam Hughes's 'greenery' library:
 * 7)    * https://github.com/ferno/greenery
 * 8)    * http://qntm.org/greenery
 * 1) For parsing, I use the LEPL library:
 * 2)    * http://www.acooke.org/lepl/
 * 3) For the regexp equivalency tests, I am using Sam Hughes's 'greenery' library:
 * 4)    * https://github.com/ferno/greenery
 * 5)    * http://qntm.org/greenery
 * 1)    * http://qntm.org/greenery

def die(error): print("\x1b[1;40;31mError:\x1b[0m", error, file=sys.stderr) # only actually exit if the file is running as a script if __name__ == "__main__": sys.exit

import sys, re, math, operator, random, argparse
 * 1) check for Python 3

if (sys.version_info.major < 3): die("This program needs Python 3")

try: # Depending on installation, the parse function might end up as greenery.parse or greenery.lego.parse # I have no idea why, but here's a workaround import greenery if not "parse" in greenery.__dict__: from greenery import lego greenery = lego except ImportError: die("This program needs the Greenery library. See: http://qntm.org/greenery")
 * 1) check if the greenery library is installed

try: from lepl import* except ImportError: die("This program needs LEPL. See: http://www.acooke.org/lepl")
 * 1) check if the lepl libary is installed

DEBUG = True def debug(message): if DEBUG: print("\x1b[1;7m?>\x1b[0m", message, file=sys.stderr)
 * 1) debug/verbose function

VERBOSE = True def verbose(message): if VERBOSE: print("\x1b[1;7m>>\x1b[0m", message, file=sys.stderr)

class CodeError(Exception): pass
 * 1) Code error
 * 1) Code error

def ordsuffix(num): if num in (11,12,13): return 'th' if num % 10 == 1: return 'st' if num % 10 == 2: return 'nd' if num % 10 == 3: return 'rd' return 'th'
 * 1) Generate ordinal suffix

class Primes(object): def __init__(self, pregen = 1000, primality_test = None): self.primes = [] self.lastnum = 1 self.pregen = pregen if primality_test: self.__primality_test = primality_test self.__more_primes(pregen) def __more_primes(self, amount): verbose('Generating %d primes. (Have %d, last: %d)' % (amount, len(self.primes), self.lastnum)) for _ in range(amount): self.__sieve def below(self, num): # generate primes upto num if necessary while num > self.lastnum: self.__more_primes(self.pregen) # get all primes below num # don't use list comprehension because it'll go through the whole list and it's sorted primes = [] for prime in self.primes: if prime > num: break primes.append(prime) # return them in descending order (for for-each-prime) return reversed(primes) def isPrime(self, num): # if we already have it, use it      if (num <= self.lastnum): return num in self.primes else: return self.primality_test(num) @staticmethod def __primality_test(num): for n in self.primes: if num%n==0: return False for n in range(self.lastnum, math.sqrt(num)): if num%n==0: return False return True def __sieve(self): prime = False while not prime: self.lastnum += 1 prime = True for n in self.primes: if self.lastnum % n == 0: prime = False break if n>math.sqrt(self.lastnum): prime = True break self.primes.append(self.lastnum) return self.lastnum
 * 1) Primes

class BaseVar(object):
 * 1) This class holds variable functions

_regexp = None # the variable names may only contain [A-Za-z0-9 ]. allowedAlphabet = greenery.parse("[A-Za-z0-9 ]").fsm.alphabet

def _makeRegexp(self, varname): # variable names must be infinite, so there must be at least one '*' or '+' in the varname if not ('*' in varname or '+' in varname): raise ValueError("Invalid varname regexp: /%s/ does not match an infinite string." % varname) # create reduced fsm for matching try: fsm = greenery.parse(varname).fsm.reduce except Exception as e:         # throw something better than just an exception raise ValueError("Invalid varname regexp: /%s/" % e.args[0]) # see if no invalid characters are used if self.allowedAlphabet != (self.allowedAlphabet | fsm.alphabet): raise ValueError("Invalid varname regexp: /%s/ matches dissalowed characters: [%s]." % ( varname, ''.join(fsm.alphabet - self.allowedAlphabet))) # finally store the regexp self._regexp = fsm.lego def matches(self,othervar): if not self._regexp: self._makeRegexp(self.getName) return self._regexp == othervar.getRegexp def getName(self): return NotImplemented

def getRegexp(self): if not self._regexp: self._makeRegexp(self.getName) return self._regexp


 * 1) Parse Streams

class Ring(object): def __init__(self, first): self.streams = [first] self.index = 0 def add(self, stream): self.streams.insert(self.index+1, stream)
 * 1) A ring of parse streams (the objects it contains must be parse streams)

def current(self): return self.streams[self.index] def left(self): self.current.add(' ') # to separate lexemes if self.index <= 0: self.index = len(self.streams) self.index -= 1

def right(self): self.current.add(' ') # to separate lexemes self.index += 1 if self.index >= len(self.streams): self.index = 0 def delete(self): if len(self.streams) == 0: die("{@-}, but there are no more parse streams left to delete.")

curstream = self.streams[self.index] del self.streams[self.index] self.index -= 1 curstream.finalize return curstream

def flush(self): streams = [] while len(self.streams): streams.append(self.delete) return streams class ParseStream(object): def __init__(self): self.data = ""

def finalize(self): verbose("Parse stream finalized: " + self.data) def add(self, data): self.data += data

@staticmethod def parseStreams(data): try: data = data.strip verbose("Separating parse streams...") ring = Ring(ParseStream) streams = [] i = 0 while i < len(data): # check pragmas if data[i:i+4] == '{@+}': ring.add(ParseStream) i += 4 elif data[i:i+4] == '{@>}': ring.right i += 4 elif data[i:i+4] == '{@<}': ring.left i += 4 elif data[i:i+4] == '{@-}': streams.append(ring.delete) i += 4 else: ring.current.add(data[i]) i += 1 verbose("Deleting all active parse streams...") streams += ring.flush return [stream.data.strip for stream in streams] except IndexError: die("Extra data after deleting final parse stream.")

class Exprs(object): class Val(object): # holds a value + type def __init__(self, type, value): self.value = value self.type = type
 * 1) expressions

def __eq__(self, other): if other == None: return False

if self.type != other.type: if self.type in 'pi' and other.type in 'pi': # prime and int may be compared return self.value == other.value else: raise CodeError("Type mismatch, comparing %s (%s) to %s (%s)"                             % (self.value, self.type, other.value, other.type)) if self.type == 'a': # compare all inner values. allSetVals = set(self.value.keys) + set(other.value.keys) for val in allSetVals: if self[val] != other[val]: return False return True else: return self.value == other.value def copy(self): if self.type != 'a': return Exprs.Val(self.type, self.value) else: newval = Exprs.Val(self.type, {}) for setval in self.value: newval[setval] = self[setval].copy return newval def __getitem__(self, attr): if self.type != 'a': raise CodeError("Type mismatch, attempt to subscript non-array") if not attr.type in 'pi': raise CodeError("Type mismatch, array subscript must be integer") if not attr.value in self.value: return Exprs.Val('i', 0) return self.value[attr.value] def __setitem__(self, attr, val): if self.type != 'a': raise CodeError("Type mismatch, attempt to subscript non-array") if not attr.type in 'pi': raise CodeError("Type mismatch, array subscript must be integer") if not val.type in 'pi': raise CodeError("Type mismatch, array value must be integer") self.value[attr.value] = Exprs.Val('i', val.value) def __repr__(self): return "Val<%s,%s>"%(self.type,str(self.value)) class Expr(object): def eval(self,env): raise NotImplemented def vars(self): raise NotImplemented class BracedExpr(Expr): def __init__(self, expr): self.expr = expr def eval(self,env): return self.expr.eval(env) def __repr__(self): return '(.' + str(self.expr) + '.)' def vars(self): return self.expr.vars class HashFn(Expr): def __init__(self, name): self.name = name def eval(self,env): return env.hashfn(self.name) def __repr__(self): return '#'+self.name+'#' def vars(self): return [] class Number(Expr): def __init__(self, num): self.num = int(num) def eval(self,env): return Exprs.Val('i', self.num) def __repr__(self): return str(self.num) def vars(self): return [] class Var(Expr, BaseVar): def __init__(self, var): self.var = var #self._makeRegexp(var)

def eval(self,env): x = env.lookup(self) #debug("Var<%s> = %s"%(str(self), str(x)))

return x      def __repr__(self): return '/' + str(self.var) + '/' def vars(self): return [self] def getName(self): return self.var class Assign(Expr): def __init__(self, var, expr): self.var = var self.expr = expr def eval(self,env): val = self.expr.eval(env) #debug("Assign<%s := %s>"%(str(self.var),str(val))) env.assign(self.var, val) return val def __repr__(self): return str(self.var) + ' := ' + str(self.expr) def vars(self): return [self.var] + self.expr.vars class ArrLookup(Expr): def __init__(self, var, iexpr): self.var = var self.iexpr = iexpr def eval(self,env): ival = self.iexpr.eval(env) #debug("Lookup<%s[%s]>"%(str(self.var),str(ival))) return env.lookup(self.var, index = ival) def __repr__(self): return (str(self.var)                                  + '['                                   + str(self.iexpr)                                  + ']') def vars(self): return [self.var] + self.iexpr.vars class ArrAssign(Expr): def __init__(self, var, iexpr, newexpr): self.var = var self.iexpr = iexpr self.newexpr = newexpr def eval(self,env): ival = self.iexpr.eval(env) val = self.newexpr.eval(env) #debug("Assign<%s[%s] := %s>"%(str(self.var),str(ival),str(val))) env.assign(self.var, val, index = ival) return val def __repr__(self): return (str(self.var)                                 + '['                                  + str(self.iexpr)                                  + '] := '                                  + str(self.newexpr)                                  ) def vars(self): return [self.var] + self.iexpr.vars + self.newexpr.vars class ArrCheck(Expr): def __init__(self, var, iexpr): self.var = var self.iexpr = iexpr def eval(self,env): ival = self.iexpr.eval(env) arrval = env.lookup(self.var, index = ival) #debug("?<%s ? %s> (=%s)"%(str(self.var),str(ival),str(arrval))) return Exprs.Val('c', ival == arrval) def __repr__(self): return (str(self.var) + '?' + str(self.iexpr)) def vars(self): return [self.var] + self.iexpr.vars class Minus(Expr): def __init__(self, iexpr): self.iexpr = iexpr def eval(self, env): val = self.iexpr.eval(env) #debug("Minus<%s>"%str(val)) if not val.type in 'pi': raise CodeError("Type mismatch: 'minus' needs i or p, not %s"%val.type) return Exprs.Val('i', -val.value) def __repr__(self): return "minus " + str(self.iexpr) def vars(self): return self.iexpr.vars class Write(Expr): def __init__(self, iexpr): self.iexpr = iexpr def eval(self, env): val = self.iexpr.eval(env) #debug("Write<%s>"%str(val)) if not val.type in 'pi': raise CodeError("Type mismatch: 'write' needs i or p, not %s"%val.type) if 0 <= val.value < 0x110000: #print(val.value) sys.stdout.buffer.write(chr(val.value).encode('utf-8')) sys.stdout.flush else: raise CodeError("Attempt to write value (%x) outside of unicode range." % val.value) return val def __repr__(self): return "write " + str(self.iexpr) def vars(self): return self.iexpr.vars class Not(Expr): def __init__(self, zexpr): self.zexpr = zexpr def eval(self, env): val = self.zexpr.eval(env) #debug("Not<%s>"%str(val)) if not val.type == 'z': raise CodeError("Type mismatch: 'not?' needs z, not %s"%val.type) return Exprs.Val('b', not val.value) def __repr__(self): return 'not? ' + str(self.zexpr) def vars(self): return self.zexpr.vars class If(Expr): def __init__(self, bexpr): self.bexpr = bexpr def eval(self, env): val = self.bexpr.eval(env) #debug("If<%s>"%str(val)) if not val.type == 'b': raise CodeError("Type mismatch: 'if?' needs b, not %s"%val.type) return Exprs.Val('c', val.value) def __repr__(self): return 'if? ' + str(self.bexpr) def vars(self): return self.bexpr.vars class Cvt(Expr): def __init__(self, cexpr): self.cexpr = cexpr def eval(self, env): val = self.cexpr.eval(env) #debug("Cvt<%s>"%str(val)) if not val.type == 'c': raise CodeError("Type mismatch: 'cvt?' needs c, not %s"%val.type) return Exprs.Val('t', val.value) def __repr__(self): return 'cvt? ' + str(self.cexpr) def vars(self): return self.cexpr.vars class To(Expr): def __init__(self, texpr): self.texpr = texpr def eval(self, env): val = self.texpr.eval(env) #debug("To<%s>"%str(val)) if not val.type == 't': raise CodeError("Type mismatch: 'to?' needs t, not %s"%val.type) return Exprs.Val('z', val.value) def __repr__(self): return 'to? ' + str(self.texpr) def vars(self): return self.texpr.vars

class Prime(Expr): def __init__(self, iexpr, tvar): self.iexpr = iexpr self.tvar = tvar def eval(self, env): val = self.iexpr.eval(env) #debug("P?<%s>"%str(val)) if not val.type in 'pi': raise CodeError("Type mismatch: 'P?' needs i or p, not %s"%val.type) if env.isPrime(val.value): return Exprs.Val('p', val.value) else: env.assign(self.tvar, Exprs.Val('t', 0)) return Exprs.Val('p', 2) def __repr__(self): return 'P? ' + str(self.iexpr) + ' [' + str(self.tvar) + ']' def vars(self): return self.iexpr.vars + [self.tvar] class MathOp(Expr): def __init__(self, op, iexpr1, iexpr2): self.op=op self.iexpr1=iexpr1 self.iexpr2=iexpr2 def eval(self, env): ival1 = self.iexpr1.eval(env) ival2 = self.iexpr2.eval(env) #debug("Math<%s %s %s>"%(str(ival1),self.op,str(ival2))) if (not ival1.type in 'pi') or (not ival2.type in 'pi'): raise CodeError("Type mismatch: %s needs i or p, not %s/%s" % (self.op, ival1.type, ival2.type)) newval = {'+': operator.add, '*': operator.mul}[self.op](ival1.value, ival2.value) return Exprs.Val('i', newval) def __repr__(self): return str(self.iexpr1) + ' ' + self.op + ' ' + str(self.iexpr2) def vars(self): return self.iexpr1.vars + self.iexpr2.vars class ExistsDynast(Expr): def __init__(self, iexpr): self.iexpr = iexpr def eval(self, env): ival = self.iexpr.eval(env) #debug("Exists/Dynast<%s>"%str(ival)) if not ival.type in 'pi': raise CodeError("Type mismatch: exists/dynast needs i or p, not %s" % ival.type) return Exprs.Val('z', env.existsDynast(ival.value)) def __repr__(self): return 'exists/dynast ' + str(self.iexpr) def vars(self): return self.iexpr.vars class CopyDynast(Expr): def __init__(self, iexpr, pexpr1, pexpr2): self.iexpr=iexpr self.pexpr1=pexpr1 self.pexpr2=pexpr2 def eval(self,env): ival = self.iexpr.eval(env) pval1 = self.pexpr1.eval(env) pval2 = self.pexpr2.eval(env) #debug("Copy/Dynast<%s,%s,%s>"%(str(ival),str(pval1),str(pval2))) if (not ival.type in 'pi') or pval1.type!='p' or pval2.type!='p': raise CodeError("Type mismatch: copy/dynast needs i,p,p not %s,%s,%s" %                                      (ival.type, pval1.type, pval2.type)) env.copyDynast(ival.value, pval1.value + pval2.value) return Exprs.Val('i', ival.value) def __repr__(self): return 'copy/dynast ' + str(self.iexpr) + ', ' + str(self.pexpr1) + ', ' + str(self.pexpr2) def vars(self): return self.iexpr.vars + self.pexpr1.vars + self.pexpr2.vars class CreateCountablyManyDynasts(Expr): def __init__(self, iexpr1, iexpr2): self.iexpr1 = iexpr1 self.iexpr2 = iexpr2 def eval(self,env): ival1 = self.iexpr1.eval(env) ival2 = self.iexpr2.eval(env) #debug("Create/Countably/Many/Dynasts<%s,%s>"%(str(ival1),str(ival2))) if (not ival1.type in 'pi') or (not ival2.type in 'pi'): raise CodeError("Type mismatch: create/countably/many/dynasts needs i,i not %s,%s" %                                                    (ival1.type, ival2.type)) env.createCountablyManyDynasts(ival1.value, ival2.value) return ival1 def __repr__(self): return 'create/countably/many/dynasts ' + str(self.iexpr1) + ', ' + str(self.iexpr2) def vars(self): return self.iexpr1.vars + self.iexpr2.vars class And(Expr): def __init__(self, bexpr1, bexpr2): self.bexpr1 = bexpr1 self.bexpr2 = bexpr2 def eval(self, env): bval1 = self.bexpr1.eval(env) bval2 = self.bexpr2.eval(env) #debug("And<%s,%s>"%(str(bval1),str(bval2))) if bval1.type!='b' or bval2.type!='b': raise CodeError("Type mismatch: 'and' needs b,b not %s,%s"%(bval1.type,bval2.type)) return Exprs.Val('z', bval1.value and bval2.vale) def __repr__(self): return str(self.bexpr1) + ' and ' + str(self.bexpr2) def vars(self): return self.bexpr1.vars + self.bexpr2.vars class Or(Expr): def __init__(self, cexpr1, cexpr2): self.cexpr1 = cexpr1 self.cexpr2 = cexpr2 def eval(self, env): cval1 = self.cexpr1.eval(env) cval2 = self.cexpr2.eval(env) #debug("Or<%s,%s>"%(str(cval1),str(cval2))) if cval1.type!='c' or cval2.type!='c': raise CodeError("Type mismatch: 'or' needs c,c not %s,%s"%(cval1.type,cval2.type)) return Exprs.Val('t', cval1.value or cval2.value) def __repr__(self): return str(self.cexpr1) + ' or ' + str(self.cexpr2) def vars(self): return self.cexpr1.vars + self.cexpr2.vars class Do(Expr): def __init__(self, expr): self.expr = expr def eval(self, env): #debug("Do") self.expr.eval(env) return Exprs.Val('c', True) def __repr__(self): return 'do ' + str(self.expr) def vars(self): return self.expr.vars class WimpThen(Expr): def __init__(self, cexpr, expr): self.cexpr = cexpr self.expr = expr def eval(self, env): if (not env.wimpmode): raise CodeError("'then' is a wimpmode instruction but wimpmode is not on") val = self.cexpr.eval(env) #debug("WimpThen<%s>"%str(val)) if (val.type != 'c'): print("\x1b[1;7m>>\x1b[0m", "'then' takes a c-expr, and this wasn't (it was %s)."%val.type,                    "However, you are a wimp so I will let it slide.") return self.expr.eval(env) def __repr__(self): return str(self.cexpr) + ' then ' + str(self.expr) def vars(self): return self.expr.vars class Then(Expr): def __init__(self, cexpr, iexpr): self.cexpr = cexpr self.iexpr = iexpr def eval(self, env): cval = self.cexpr.eval(env) #debug("Then<%s>"%str(cval)) if cval.type != 'c': raise CodeError("',then' takes a c, not a %s" % cval.type) if cval.value: ival = self.iexpr.eval(env) if not ival.type in 'pi': raise CodeError("',then' takes an i, not a %s" % ival.type) return Exprs.Val('i', ival.value) else: return Exprs.Val('i', 1 + int(random.random * 1e6)) def __repr__(self): return str(self.cexpr) + ' ,then ' + str(self.iexpr) def vars(self): return self.cexpr.vars + self.iexpr.vars class ForEach(Expr): def __init__(self, var, iexpr1, iexpr2): self.var = var self.iexpr1 = iexpr1 self.iexpr2 = iexpr2 def eval(self, env): ival1 = self.iexpr1.eval(env) if not ival1.type in 'pi': raise CodeError("foreach: 'below' value is not i.") #debug("ForEach<%s>"%str(ival1)) primes = env.primesBelow(ival1.value) ival2 = Exprs.Val('i', 0) # returned if no primes below ival1 for prime in primes: env.assign(self.var, Exprs.Val('p', prime)) ival2 = self.iexpr2.eval(env).copy if not ival2.type in 'pi': raise CodeError("foreach: body returned non-i. (%s)" % ival2.type) return Exprs.Val('i', ival2.value) # cast to i (for if it was p)       def __repr__(self): return 'for each prime ' + str(self.var) + ' below ' + str(self.iexpr1) + ' do ' + str(self.iexpr2) def vars(self): return [self.var] + self.iexpr1.vars + self.iexpr2.vars class CodeParser(object): # types class OToken(object): def __init__(self, obj): self.contents = obj def __repr__(self): return self.__class__.__name__ + '<' + str(self.contents) + '>'
 * 1) parser for the actual code

class Var(OToken, BaseVar): def __init__(self, type, name): self.contents=name self.type=type self._makeRegexp(name) def getName(self): return self.contents def __repr__(self): return 'Var<%s,%s>'%(self.type,self.contents)

class VarDecl(OToken): pass

class Dynast(OToken): def __init__(self, id, expr): self.id=int(id); self.contents=expr def __repr__(self): return 'Dynast<%d,%s>'%(self.id,str(self.contents)) class ParseStream(OToken): def __init__(self, vars, dynast): self.vars=vars self.contents=dynast def __repr__(self): return 'Stream<%s,%s>'%(str(self.vars),str(self.dynast)) # parsers # whitespace wspace = Any(' \t\r\n')[:] owspace = Any(' \t\r\n')[1:] # variable name: /something/ varname = (~Literal('/')            & (Literal(r'\/') | (~Lookahead('/') & Any)) [1:]             & ~Literal('/') > (lambda x: ''.join(x))             ) # variable types: (i)nteger (p)rime (a)rray (b)ool (t)ruth bit(z) (c)ondition vartype = Any('ipabtzc') # variable var    = vartype & ~wspace & varname > args(Var) # variable declaration vardecl = (~Literal('VARIABLES')            & ~wspace            & ~Literal('ARE')            & ~wspace            & var[Drop(Literal(',') & wspace)]            & ~Literal('.')             ) > VarDecl ## expressions expri, expr1i, expr2i, expr3i, expr4i, expr5i, expr6i = [Delayed for _ in range(7)] exprp, expr1p, expr2p, expr3p, expr4p, expr5p, expr6p = [Delayed for _ in range(7)] exprb, expr1b, expr2b, expr3b, expr4b, expr5b, expr6b = [Delayed for _ in range(7)] exprt, expr1t, expr2t, expr3t, expr4t, expr5t, expr6t = [Delayed for _ in range(7)] exprz, expr1z, expr2z, expr3z, expr4z, expr5z, expr6z = [Delayed for _ in range(7)] exprc, expr1c, expr2c, expr3c, expr4c, expr5c, expr6c = [Delayed for _ in range(7)] expr = Delayed

# hash function hashfn = (~Literal('#')           & ((~Lookahead('#') & Any)[:]>(lambda x:''.join(x)))            & ~Literal('#')) > args(Exprs.HashFn)

# braces openbrace = Literal('(')[1:] & Literal('.')   closebrace = Literal('.') & Literal(')')[1:] bracedexp = (lambda openbrace,wspace,closebrace:                 (lambda what: (~openbrace                             & ~wspace                             & what                             & ~wspace                             & ~closebrace) > args(Exprs.BracedExpr)))(openbrace,wspace,closebrace) # constant expr constant = hashfn | ( Integer > args(Exprs.Number) ) # variable expr varexp  = varname > args(Exprs.Var)

#   minus    = (~Literal("minus") & ~wspace & expri) > args(Exprs.Minus) write   = (~Literal("write") & ~wspace & expri) > args(Exprs.Write) not_    = (~Literal("not?")  & ~wspace & exprz) > args(Exprs.Not) if_     = (~Literal("if?")   & ~wspace & exprb) > args(Exprs.If) cvt     = (~Literal("cvt?")  & ~wspace & exprc) > args(Exprs.Cvt) to      = (~Literal("to?")   & ~wspace & exprt) > args(Exprs.To) prime   = (~Literal("P?") & ~wspace              & expri & ~wspace             & ~Literal('[') & ~wspace             & varexp & ~wspace             & ~Literal(']')) > args(Exprs.Prime) exists  = (~Literal("exists/dynast") & ~wspace & expri) > args(Exprs.ExistsDynast) copy    = (~Literal("copy/dynast") & ~wspace             & expri & ~wspace & ~Literal(",") & ~wspace             & exprp & ~wspace & ~Literal(",") & ~wspace             & exprp & ~wspace) > args(Exprs.CopyDynast) create  = (~Literal("create/countably/many/dynasts") & ~wspace             & expri & ~wspace & ~Literal(",") & ~wspace             & expri & ~wspace) > args(Exprs.CreateCountablyManyDynasts) #do      = lambda what: (~Literal("do") & ~wspace & what) > args(Exprs.Do)

do      = (~Literal("do") & ~wspace & expr) > args(Exprs.Do) foreach = (               ~(Literal("for")&wspace&Literal("each")&wspace&Literal("prime")&wspace)             & varexp & ~wspace             & ~Literal("below") & ~wspace             & expri & ~wspace             & ~Literal("do") & ~wspace             & expri) > args(Exprs.ForEach) # array lookup arrlookup = (varexp            & ~wspace             & ~Literal('[')             & ~wspace             & expri             & ~wspace             & ~Literal(']')              ) > (lambda x: Exprs.ArrLookup(x[0], x[1])) #prim    = foreach|do|create|copy|exists|prime|to|cvt|if_|not_|write|minus|varexp|constant|bracedexp primp    = varexp|bracedexp(exprz)|prime primi    = arrlookup|primp|bracedexp(expri)|constant|minus|write|copy|create|foreach primb    = varexp|bracedexp(exprb)|not_ primt    = varexp|bracedexp(exprt)|cvt primz    = varexp|bracedexp(exprz)|to|exists primc    = varexp|bracedexp(exprc)|do|if_

# assignment #assign  = (lambda varexp,wspace:   #            (lambda what: (varexp    #                       & ~wspace    #                       & ~Literal(':=')    #                       & ~wspace    #                       & what) > (lambda x: Exprs.Assign(x[0], x[1]))))(varexp,wspace) assign   = (varexp & ~wspace & ~Literal(':=') & ~wspace & expr) > (lambda x: Exprs.Assign(x[0], x[1]))

# array assign arrassign = (varexp & ~wspace             & ~Literal('[') & ~wspace              & expri & ~wspace              & ~Literal(']') & ~wspace              & ~Literal(':=') & ~wspace              & expri) > (lambda x: Exprs.ArrAssign(x[0], x[1], x[2]))

#expr6 += arrassign | arrlookup | assign | prim expr6p += assign | primp expr6i += assign | arrassign | arrlookup | expr6p | primi expr6b += assign | primb expr6t += assign | primt expr6z += assign | primz expr6c += assign | primc

arrcheck = (varexp & ~wspace & ~Literal("?") & ~wspace & expr6i) > args(Exprs.ArrCheck) expr5p = expr6p expr5i = expr6i expr5b = expr6b expr5t = expr6t expr5z = expr6z expr5c += arrcheck | expr6c

multiply = (expr5i & ~wspace & ~Literal("*") & ~wspace & expr5i) > (lambda x: Exprs.MathOp("*", *x)) expr4p = expr5p expr4i += multiply | expr5i expr4b = expr5b expr4t = expr5t expr4z = expr5z expr4c = expr5c

add     = (expr4i & ~wspace & ~Literal("+") & ~wspace & expr4i) > (lambda x: Exprs.MathOp("+", *x)) expr3p = expr4p expr3i += add | expr4i expr3b = expr4b expr3t = expr4t expr3z = expr4z expr3c = expr4c

and_    = (expr3b & ~wspace & ~Literal("and") & ~wspace & expr3b) > args(Exprs.And) expr2p = expr3p expr2i = expr3i expr2b = expr3b expr2t = expr3t expr2z += and_ | expr3z expr2c = expr3c

or_     = (expr2c & ~wspace & ~Literal("or") & ~wspace & expr2c) > args(Exprs.Or) expr1p = expr2p expr1i = expr2i expr1b = expr2b expr1t += or_ | expr2t expr1z = expr2z expr1c = expr2c

wimpthen = (lambda expr1c,wspace:              (lambda what: (expr1c                             & ~wspace                             & ~Literal("then")                             & ~wspace                             & what) > args(Exprs.WimpThen)))(expr1c,wspace)

then    = (expr1c & ~wspace & ~Literal(",then") & ~wspace & expri) > args(Exprs.Then)

#wimpthen.config.left_memoize #then.config.left_memoize #expr += wimpthen | then | expr1 exprp += wimpthen(exprp) | expr1p expri += wimpthen(expri) | then | expr1i exprb += wimpthen(exprb) | expr1b exprt += wimpthen(exprt) | expr1t exprz += wimpthen(exprz) | expr1z exprc += wimpthen(exprc) | expr1c

expr += expri | exprp | exprc | exprb | exprt | exprz expr.config.left_memoize fullexpr = ~wspace & expr & ~wspace ###  dynast   = (~wspace & ~Literal("dynast")             & ~wspace & ~Literal("(") & ~wspace & Integer & ~wspace & ~Literal(")")            & ~wspace & ~Literal("<->")             & ~wspace & fullexpr) > args(Dynast) parsestream = (~wspace & Optional(vardecl & ~wspace) & Optional(dynast)) # parse a parse stream @staticmethod def parse(code): CodeParser.fibonacci_check(code) debug("Parsing: " + code) return CodeParser.parsestream.parse(code) # check fibonacci indentation @staticmethod def fibonacci_check(code): # remove the keywords with slashes in them code = (code           .  replace('exists/dynast', )            .  replace('create/countably/many/dynasts', )            .  replace('copy/dynast', '')) # remove the variables (which might contain dots) acc = [] i = 0 invar = False while i < len(code): if code[i]=='\\': i+=1; continue if code[i]=='/': invar = not invar if not invar and not code[i] == '/': acc.append(code[i]) i += 1 debug("Removed slashes: " + ''.join(acc)) # make a list of consecutive braces, and get their lengths # this should be [fibonacci, 0, reverse fibonacci] removed = ''.join(x for x in acc if x in '(.)') # remove the first dot if there's a variable declaration if 'VARIABLES' in code: removed=removed[1:] # remove the s from dynast if they're there if 'dynast' in code: removed=removed[2:] braces = removed.split('.') # split the places where braces meet brace_split = [] for x in braces: if not x: continue if ')(' in x:            splitplace = 1 + x.index(')(') brace_split.append(x[:splitplace]) brace_split.append(x[splitplace:]) else: brace_split.append(x) debug("Split braces: " + str(brace_split)) # check fibonacci fibonacci = [1,1] level = 0 num = 0 for x in brace_split: num += 1 if ')' in x: level -= 1         # generate more fibonacci numbers if needed          while level >= len(fibonacci):             fibonacci.append(sum(fibonacci[-2:]))          debug(str(level) + ' ' + str(fibonacci[level]) + ' ' + x + ' ' + str(fibonacci))          # level ok?          if len(x) != fibonacci[level]:             raise ValueError(('braces do not match fibonacci at %d%s group in dynast. found %d, should be %d'                               )% (num, ordsuffix(num), len(x), fibonacci[level]))          # next level          if '(' in x: level += 1

class VarHolder(object): class Var(BaseVar): defaultValues = {'i': 0, 'p': 2, 'a': None, 'b': 0, 't': 0, 'z': 0, 'c': 0} def __init__(self, varexp): self._regexp = varexp.getRegexp self.name = varexp.getName self.type = varexp.type if isinstance(varexp, VarHolder.Var): self.__value = varexp.__value.copy else: defaultval = self.defaultValues[self.type] if defaultval==None: defaultval={} self.__value = Exprs.Val(self.type, defaultval) def getName(self): return self.name def get(self): return self.__value def set(self, value): if (self.type != value.type) and not (self.type == 'i' and value.type == 'p'): raise CodeError("Type error: cannot assign %s value to %s variable" % (self.type, value.type)) if self.type == 'a': # arrays need to be copied self.__value = value.copy else: self.__value = value def __init__(self, varexps): self.vars = [VarHolder.Var(x) for x in varexps] self.matching = {} def __getitem__(self, attr): if not isinstance(attr, BaseVar): raise TypeError("attr must be basevar")
 * 1) base class for var holder

if id(attr) in self.matching: return self.matching[id(attr)] else: for v in self.vars: if v.matches(attr): self.matching[id(attr)]=v return v

raise KeyError("There is no variable named /%s/" % attr.getName) class Dynast(VarHolder): def __init__(self, number, vars, expr): super(Dynast, self).__init__(vars) self.number = number self.expr  = expr def copy(self, newnumber): return Dynast(newnumber, self.vars, self.expr)
 * 1) holds a dynast + associated vars

def run(self, env): self.expr.eval(env) def __repr__(self): return '<%d: %s>' % (self.number,self.expr)

class Environment(VarHolder): def __init__(self, globalvars, dynasts): super(Environment, self).__init__(globalvars) self.dynasts = {} for dynast in dynasts: debug("Dynast %d: %s" % (dynast.number, str(dynast.expr))) self.dynasts[dynast.number] = dynast debug(str(self.dynasts))
 * 1) the environment, also holds global vars

self.infiniteDynasts = None self.infiniteDynastsBegin = 0 self.primes = Primes # check for wimpmode wimpvar = CodeParser.Var("i", "am *a *wimp") self.wimpmode = False for var in globalvars: if var.type == 'i' and var.matches(wimpvar): self.wimpmode = True break verbose("Wimpmode is %s." % ["OFF", "ON"][self.wimpmode]) # activate the dynast with the lowest number self.activeDynast = self.dynasts[min(self.dynasts.keys)] def createCountablyManyDynasts(self, dynast, begin): begin += 1 + begin % 2 # first odd integer greater than 'begin' verbose("Creating countably many dynasts from dynast %d starting at %d." % (dynast, begin)) # check if there are already infinitely many dynasts if self.infiniteDynasts: verbose("Exiting: dynasts already exist on the uneven numbers.") sys.exit(0) # check if the infinite dynasts conflict with a previously existant dynast for dyn in self.dynasts: if dyn >= begin and dyn % 2: verbose("Exiting: dynast %d already exists." % dyn) sys.exit(0) # set the infinite dynast self.infiniteDynasts = self.dynasts[dynast] self.infiniteDynastsBegin = begin debug("Dynast expr: %s" % str(self.infiniteDynasts.expr))

self.dynasts[begin] = self.infiniteDynasts.copy(begin) def existsDynast(self, number): if self.infiniteDynasts and number >= self.infiniteDynastsBegin and number % 2: return True return number in self.dynasts def getDynast(self, number): debug("Retrieving dynast %d" % number) if number in self.dynasts: return self.dynasts[number] debug("Dynast not in dynast list <%d,%d,%d,%d>"%( (self.infiniteDynasts != None), number, self.infiniteDynastsBegin, number%2)) if self.infiniteDynasts and number >= self.infiniteDynastsBegin and number % 2: debug("Copying dynast %d" % number) newdynast = self.infiniteDynasts.copy(number) self.dynasts[number] = newdynast return newdynast verbose("Exiting: attempting to access nonexistant dynast %d." % number) sys.exit(0) def primesBelow(self, number): return self.primes.below(number) def copyDynast(self, old, new): verbose("Copying dynast %d to %d" % (old, new)) if not self.existsDynast(old): raise CodeError("Cannot copy dynast %d: dynast does not exist." % old) if self.existsDynast(new): verbose("Exiting: dynast %d already exists." % new) sys.exit(0) self.dynasts[new] = self.dynasts[old].copy(new) def isPrime(self, number): return self.primes.isPrime(number) def lookup(self, varname, index=None): try: var = self.activeDynast[varname] except KeyError: var = self[varname] value = var.get if index != None: return value[index] else: return value def assign(self, varname, value, index=None): try: var = self.activeDynast[varname] except KeyError: var = self[varname] if index == None: var.set(value) # set whole variable else: # set var index var.get[index] = value def hashfn(self, hashfn): if hashfn == 'myself': return Exprs.Val('i', self.activeDynast.number) elif hashfn == 'read': char = sys.stdin.read(1) if not char: num = -1 # EOF else: num = ord(char) return Exprs.Val('i', num) else: raise CodeError("No such function: #%s%#" % hashfn) def run(self): while True: debug("Current expr: " + str(self.activeDynast.expr)) self.activeDynast.run(self) self.activeDynast = self.getDynast(self.activeDynast.number + 1)

class Interpreter(object): def __init__(self, code): # parse the code try: streams = ParseStream.parseStreams(code) verbose("Parsing streams...") parsed = [CodeParser.parse(stream) for stream in streams] except RuntimeError as e:          die("Parse error: " + str(e)) verbose("Getting vars and dynasts from streams...") globalvars = [] dynasts = [] for stream in parsed: if any(isinstance(x, CodeParser.Dynast) for x in stream): # this is a dynast if len(stream) == 2: # with local vars vars = stream[0].contents num = stream[1].id               expr = stream[1].contents dynasts.append(Dynast(num, vars, expr)) elif len(stream) == 1: num = stream[0].id               expr = stream[0].contents dynasts.append(Dynast(num, [], expr)) else: raise RuntimeError("impossible situation: stream is of wrong length") else: # these are global vars vars = stream[0].contents globalvars += vars # create the environment self.environment = Environment(globalvars, dynasts) # if we're not in wimpmode, check if a variable name is reused literally if not self.environment.wimpmode: verbose("Checking for literally repeated variables...") vardefs = [] for var in globalvars: if var.getName in vardefs: die("reused variable name while not in wimpmode: /%s/" % var.getName) vardefs.append(var.getName) for dynast in dynasts: for var in dynast.expr.vars: if var.getName in vardefs: die("reused variable name while not in wimpmode: /%s/" % var.getName) vardefs.append(var.getName) def run(self): self.environment.run
 * 1) interpreter

def main: global VERBOSE, DEBUG parser = argparse.ArgumentParser(description='Oozlybub & Murphy interpreter') parser.add_argument('-v', action='store_const', const=True, default=False, help='Verbose output') parser.add_argument('-d', action='store_const', const=True, default=False, help='Show debug messages') parser.add_argument('program', type=argparse.FileType('r'), nargs=1, help='Source code file') args = parser.parse_args DEBUG=args.d   VERBOSE=args.v    try: Interpreter(args.program[0].read).run except Exception as e:      raise e       die(str(e)) if __name__ == '__main__': main