# $Id: unl.pir 18088 2007-04-09 22:03:10Z paultcochrane $
=head1 DESCRIPTION
This is an unlambda interpreter.
unlambda is a pure functional programming language with mostly eager
evaluation following the SKI calculus (+ a few extensions).
The parrot implementation uses closures, continuations, and tailcalls.
=head1 AUTHOR
leo
=head1 SEE ALSO
L
=cut
.sub _main :main
.param pmc argv
.local int argc
.local pmc in, cchar
argc = argv
if argc > 1 goto open_file
in = getstdin
goto run
open_file:
$S0 = argv[1]
in = open $S0, "<"
$I0 = defined in
if $I0 goto run
printerr "can't open '"
printerr $S0
printerr "' for reading."
exit 1
run:
.local pmc prog
$P0 = getinterp
$P0."recursion_limit"(50000)
prog = parse(in)
cchar = new .String
global "cchar" = cchar
# _dumper( prog, "prog" )
ev(prog)
.end
#
# create a parse tree of the program
#
.sub parse
.param pmc io
.local string ch
.local pmc op, arg, pair
.const .Sub i = "i"
.const .Sub k = "k"
.const .Sub s = "s"
.const .Sub c = "c"
.const .Sub d = "d"
.const .Sub v = "v"
.const .Sub e = "e"
.const .Sub rd = "rd"
.const .Sub pc = "pc"
loop:
ch = read io, 1
unless ch == '`' goto not_bq
op = parse(io)
arg = parse(io)
pair = new FixedPMCArray
pair = 2
pair[0] = op
pair[1] = arg
.return (pair)
not_bq:
unless ch == '.' goto not_dot
$S0 = read io, 1
arg = new .String
arg = $S0
.return clos_pr(arg)
not_dot:
unless ch == '@' goto not_rd
.return (rd)
not_rd:
unless ch == '|' goto not_pc
.return (pc)
not_pc:
unless ch == '?' goto not_rc
$S0 = read io, 1
arg = new .String
arg = $S0
.return clos_rc(arg)
not_rc:
unless ch == 'r' goto not_r
arg = new .String
arg = "\n"
.return clos_pr(arg)
not_r:
unless ch == 'i' goto not_i
.return (i)
not_i:
unless ch == 'k' goto not_k
.return (k)
not_k:
unless ch == 's' goto not_s
.return (s)
not_s:
unless ch == 'v' goto not_v
.return (v)
not_v:
unless ch == 'c' goto not_c
.return (c)
not_c:
unless ch == 'd' goto not_d
.return (d)
not_d:
unless ch == 'e' goto not_e
.return (e)
not_e:
unless ch == '#' goto not_comment
swallow:
ch = read io, 1
if ch != "\n" goto swallow
goto loop
not_comment:
if ch == ' ' goto loop
if ch == "\t" goto loop
if ch == "\n" goto loop
if ch == "\r" goto loop
printerr "unrecogniced char in program '"
printerr ch
printerr "'\n"
exit 1
.end
# debugging helper
.sub unparse
.param pmc exp
$I0 = typeof exp
unless $I0 == .FixedPMCArray goto no_ar
$I1 = elements exp
if $I1 != 2 goto no_ar
.local pmc car, cdr
print "`"
car = exp[0]
cdr = exp[1]
unparse(car)
unparse(cdr)
.return()
no_ar:
$S0 = exp
print $S0
.end
# debugging helper
.sub unparse_all
.param pmc exp
unparse(exp)
print "\n"
.end
#
# evaluate an expression
#
.sub ev
.param pmc exp
## unparse_all(exp)
$I0 = typeof exp
unless $I0 == .FixedPMCArray goto no_ar
$I1 = elements exp
if $I1 != 2 goto no_pair
.local pmc car, cdr, op, arg
.const .Sub d = "d"
car = exp[0]
cdr = exp[1]
# this is tricky - we have to apply car
# but discard it if it's delayed
# else this doesn't play together with call/cc
op = ev(car)
if car != d goto not_d
.return clos_d1(cdr)
not_d:
arg = ev(cdr)
.return op(arg)
no_ar:
.return (exp)
no_pair:
printerr "no pair\n"
exit 1
.end
#
# create a closure for pr
#
.sub clos_pr
.param pmc arg
.local pmc cl
.lex 'x', arg
.const .Sub pr = "pr"
cl = newclosure pr
.return (cl)
.end
# .x print
# r print newline
.sub pr :outer(clos_pr)
.param pmc arg
.local pmc x
x = find_lex "x"
print x
.return (arg)
.end
# i identy
.sub i
.param pmc arg
.return (arg)
.end
# k constant generator
.sub k
.param pmc arg
.const .Sub k1 = "k1"
.return clos_k1(arg)
.end
.sub clos_k1
.param pmc arg
.local pmc cl
.lex 'x', arg
.const .Sub k1 = "k1"
cl = newclosure k1
.return (cl)
.end
# `kX contant function
.sub k1 :outer(clos_k1)
.param pmc arg
.local pmc x
x = find_lex "x"
.return (x)
.end
# s substitution
.sub s
.param pmc arg
.return clos_s1("x", arg)
.end
.sub clos_s1
.param pmc arg
.local pmc cl
.lex 'x', arg
.const .Sub s1 = "s1"
cl = newclosure s1
.return (cl)
.end
# `sX substitution first partial
.sub s1 :outer(clos_s1)
.param pmc arg
.local pmc x
x = find_lex 'x'
.return clos_s2(x, arg)
.end
#
# create a closure for s2 with 2 args
#
.sub clos_s2
.param pmc arg
.param pmc arg2
.local pmc cl
.lex 'x', arg
.lex 'y', arg2
.const .Sub s2 = "s2"
cl = newclosure s2
.return (cl)
.end
# ``sXY substitution application
.sub s2 :outer(clos_s2)
.param pmc z
.local pmc x, y, f1, f2
x = find_lex 'x'
y = find_lex 'y'
f1 = x(z)
f2 = y(z)
.return f1(f2)
.end
.include "interpinfo.pasm"
# v void
.sub v
.param pmc arg
.param pmc self
self = interpinfo .INTERPINFO_CURRENT_SUB
.return (self)
.end
# c call/cc
.sub c
.param pmc x
.local pmc cc, c1
cc = interpinfo .INTERPINFO_CURRENT_CONT
.const .Sub c1 = "c1"
cc = clos_c1(cc)
.return x(cc)
.end
.sub clos_c1
.param pmc arg
.local pmc cl
.lex 'cc', arg
.const .Sub c1 = "c1"
cl = newclosure c1
.return (cl)
.end
#
.sub c1 :outer(clos_c1)
.param pmc x
.local pmc cc
cc = find_lex 'cc'
cc(x)
printerr "not reached\n"
exit 1
.end
# d delay
.sub d
printerr "not reached\n"
exit 1
.end
.sub clos_d1
.param pmc arg
.local pmc cl
.lex 'F', arg
.const .Sub d1 = "d1"
cl = newclosure d1
.return (cl)
.end
# `dF promise
.sub d1 :outer(clos_d1)
.param pmc y
.local pmc x, f
f = find_lex 'F'
x = ev(f)
.return x(y)
.end
# e exit
.sub e
.param pmc x
$I0 = x
exit $I0
.end
# @ read
.sub rd
.param pmc x
.local pmc cchar, i, v, io
.local string ch
io = getstdin
ch = ''
unless io goto void
ch = read io, 1
cchar = global "cchar"
cchar = ch
if ch == '' goto void
.const .Sub i = "i"
.return x(i)
void:
.const .Sub v = "v"
.return x(v)
.end
.sub clos_rc
.param pmc arg
.local pmc cl
.lex 'ch', arg
.const .Sub rc = "rc"
cl = newclosure rc
.return (cl)
.end
# ?x compare character read
.sub rc :outer(clos_rc)
.param pmc x
.local pmc cchar, i, v
.local string ch
cchar = global "cchar"
ch = cchar
if ch == '' goto void
.const .Sub i = "i"
$P0 = find_lex "ch"
$S0 = $P0
if $S0 != ch goto void
.return x(i)
void:
.const .Sub v = "v"
.return x(v)
.end
# | reprint character read
.sub pc
.param pmc x
.local pmc cchar, i, v, pr, p, s
.local string ch
cchar = global "cchar"
ch = cchar
if ch == '' goto void
s = clone cchar
p = clos_pr(s)
.return x(p)
void:
.const .Sub v = "v"
.return x(v)
.end
.include "library/dumper.pir"
# Local Variables:
# mode: pir
# fill-column: 100
# End:
# vim: expandtab shiftwidth=4: