User:Marinus/Oozlybub and Murphy interpreter

From Esolang
Jump to navigation Jump to search

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.


#!/usr/bin/env python3

#################
## oozly.py
## Oozlybub & Murphy interpreter
##
## written by Marinus Oosters
##
#################
##
## For parsing, I use the LEPL library:
##    * http://www.acooke.org/lepl/
##
## For the regexp equivalency tests, I am using Sam Hughes's 'greenery' library:
##    * https://github.com/ferno/greenery
##    * 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()

# check for Python 3
import sys, re, math, operator, random, argparse

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

# check if the greenery library is installed
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")

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

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

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

      
################
# Code error
class CodeError(Exception): pass

# Generate ordinal suffix
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'

# Primes
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

# This class holds variable functions
class BaseVar(object):

   _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

# Parse Streams

# A ring of parse streams (the objects it contains must be 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)

   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.")

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

          
       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()  
    
            
## parser for the actual code 
class CodeParser(object):
   # types
   class OToken(object): 
      def __init__(self, obj):
         self.contents = obj
      def __repr__(self):
         return self.__class__.__name__ + '<' + str(self.contents) + '>'

   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

          
# base class for var holder
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")

       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())
       
    
# holds a dynast + associated vars
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)

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

# the environment, also holds global vars
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))

       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)
    

# interpreter
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()

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()