ANN: unlambda.py
Ben Wolfson
rumjuggler at cryptarchy.org
Fri Aug 18 16:08:47 EDT 2000
unlambda.py is an interpreter for the Unlambda language and is the product
of work since all of yesterday, so I'm not sure that everything works
right.
The language is described at
<http://www.eleves.ens.fr:8080/home/madore/programs/unlambda/>
'''
An interpreter for the Unlambda programming language. I believe everything
is here and
working correctly, but not all of the functions are tested. The d and c
primitives should
work correctly, and the script will correctly interpret
<ftp://quatramaran.ens.fr/pub/madore/unlambda/CUAN/quine/quine00.unl>, an
Unlambda quine, so I'm
reasonably confident that it should work. getChurchNum() and
naiveAbsElim() could be used to
aid actually writing Unlambda programs, if for some reason you wanted to do
that, but currently
aren't integrated with the rest of the script.
There are probably some memory issues.
#---unlambda.py
'''
__version__ = '0.1'
import sys
def getChurchNum(num_or_numstring):
num = int(num_or_numstring)
result = '^f^x'
for i in range(num):
result = result + '`$f'
return naiveAbsElim(result + '$x')
def naiveAbsElim(expr):
#all valid expressions are at least 3 chrs
print 'Recieved: ', expr
myRepl = expr[1]
result = ''
expr = expr[2:]
if expr[0] == '^':
expr = naiveAbsElim(expr)
i = 0
while i < len(expr):
ch = expr[i]
if ch == '`':
result = result + '``s'
elif ch == '$':
i = i + 1
ch = expr[i]
if ch == myRepl:
result = result + 'i'
else:
result = result + '`k$%c' % ch
else:
result = result + '`k%c' % ch
i = i + 1
return result
def v_func(arg):
return v_func
def dot_func(ch):
def _dot_func(arg, ch=ch):
sys.stdout.write(ch)
return arg
return _dot_func
def r_func(arg):
sys.stdout.write('\n')
return arg
def pipe_fun(arg):
global curchar
if curchar:
return arg( dot_func(curchar) )
return arg(v_func)
def i_func(arg):
return arg
def s_func(firstarg):
def s1_func(secondarg, firstarg=firstarg):
def s2_func(thirdarg, secondarg=secondarg, firstarg=firstarg):
result1 = firstarg(thirdarg)
result2 = secondarg(thirdarg)
return result1(result2)
return s2_func
return s1_func
def k_func(firstarg):
def k1_func(secondarg, firstarg=firstarg):
return firstarg
return k1_func
class btick:
def __call__(self, i=0):
global theprogram
# print 'in index %d,' % i,
if isinstance(theprogram[i+1], btick):
# print 'ascend to %d' % (i + 1)
theprogram[i+1](i+1)
return
# else: print "don't go up one,",
if isinstance(theprogram[i+2], btick) and not
isinstance(theprogram[i+1], continuation) and not
isinstance(theprogram[i+1],delay):
# print 'ascend to %d' % (i + 2)
theprogram[i+2](i+2)
return
# else: print "don't go up two,",
# print 'apply %s to %s with index %d' % (ptp1(theprogram[i+1]),
ptp1(theprogram[i+2]), i) ###
result = theprogram[i+1](theprogram[i+2])
if type(result) == type(()):
result, i = result
if result: #necessary in case theprogram[i+1] was a continuation
theprogram = theprogram[:i] + [result] + theprogram[i+3:]
# print 'the program now:\n\t %s' % ptp() ###
if not i: return
for dex in range(i - 1, -1, -1):
if isinstance(theprogram[dex], btick):
call = theprogram[dex]
# print 'drop back down to %i, where we' % dex,
index = dex
break
else:
return
call(index)
class delay:
def __call__(self, arg):
global theprogram
self.startofpromise = theprogram.index(self) + 1
self.endofpromise = getendofexpression(self.startofpromise)
self.promiseprogram =
theprogram[self.startofpromise:self.endofpromise]
#Keep one from the promise section to make it look as if something
were actually evaluated
del theprogram[self.startofpromise + 1:self.endofpromise]
return self.promise
def promise(self, arg):
#restore promise
global theprogram
index = theprogram.index(self.promise)
theprogram = theprogram[:index] + self.promiseprogram +
theprogram[index+1:]
return (None, index-1)
def getendofexpression(start):
global theprogram
ticks = 1
while ticks != 0:
if isinstance(theprogram[start], btick):
ticks = ticks + 1
else:
ticks = ticks - 1
start = start + 1
return start
class continuation:
def __call__(self, arg):
index = theprogram.index(self)
self.applicationpoint = getendofexpression(index + 1)
global theprogram
self.preservedprogram = theprogram[:]
for i in range(index - 1, -1, -1):
if isinstance(theprogram[i], btick):
self.indexofcaller = i
break
del theprogram[index]
theprogram.insert(self.applicationpoint - 1, self.timetravel)
theprogram[index-1]() #apply the backtick
def timetravel(self, arg):
#get here only if there had been a `cEXPRESSION, and in the
resulting `EXPRESSION<cont>
#something was applied to the continuation
# print 'traveled through time' ###
global theprogram
#restore the program
theprogram = self.preservedprogram[:]
# print 'restored program: \n\t%s' % ptp()
# print 'restored index of caller: %d' % self.indexofcaller
# print 'returning %s' % ptp1(arg)
#return whatever it was the continuation was called on
#plus info to properly restore state of caller
return (arg, self.indexofcaller)
def e_func(arg):
if arg == v_func:
sys.exit(1)
sys.exit(0)
def at_func():
def _at_func(arg):
global curchar
try:
curchar = sys.stdin.read(1)
except EOFError:
return result_func(v_func)
else:
return result_func(i_func)
return _at_func
def qmark_func(cmpchar):
def _qmark_func(arg,cmpchar=cmpchar):
if cmpchar == curchar:
return arg(i_func)
return arg(v_func)
return _qmark_func
def genlist(program):
result = []
i = 0
while i < len(program):
char = program[i]
if char == '#':
i = i + 1
while program[i] != '\n' and i < len(program):
i = i + 1
continue
elif char in (' ','\t','\n'):
pass
elif simplechartofunc.has_key(char):
result.append(simplechartofunc[char])
elif constructorchartofunc.has_key(char):
result.append( constructorchartofunc[char]() )
elif takenextchar.has_key(char):
i = i + 1
nextchar = program[i]
result.append( takenextchar[char](nextchar) )
i = i + 1
if not checkticks(result):
raise Exception, "Apparently incorrect tick count in: \n\n\t%s" %
program
global theprogram
theprogram = result
# print ptp()
def checkticks(oplist):
if not (isinstance(oplist[0], btick)): return None
ticks = 1
for node in oplist:
if isinstance(node, btick):
ticks = ticks + 1
else:
ticks = ticks - 1
return not ticks
def ptp():
global theprogram, reversemap
result = ''
for i in theprogram:
result = result + ptp1(i)
return result
def ptp1(a):
global reversemap
name = getattr(a, 'func_name', 0)
if not name:
name = getattr(a, '__name__',0)
if not name:
name = a.__class__.__name__
return reversemap[name]
simplechartofunc = {
'e':e_func,
'v':v_func,
'i':i_func,
'k':k_func,
's':s_func,
'r':r_func,
'@':at_func,
'|':pipe_func
}
takenextchar = {
'.':dot_func,
'?':qmark_func
}
constructorchartofunc = {
'c':continuation,
'`':btick,
'd':delay
}
curchar = None
reversemap = {}
for key, value in simplechartofunc.items() + takenextchar.items():
reversemap[value.func_name] = key
reversemap[k_func(1).func_name] = 'k1'
reversemap[s_func(1).func_name] = 's1'
reversemap[dot_func(1).func_name] = '.1'
reversemap[s_func(1)(1).func_name] = 's2'
reversemap[continuation.timetravel.__name__] = 'C'
reversemap[btick.__name__] = '`'
reversemap[continuation.__name__] = 'c'
reversemap[delay.__name__] = 'd'
def main():
if len(sys.argv) == 2:
genlist(open(sys.argv[1], 'r').read())
else:
genlist(sys.stdin.read())
theprogram[0]()
if __name__ == '__main__':
main()
#---End unlambda.py
More information about the Python-list
mailing list