lambdabot-4.0: scripts/Unlambda.hs
{-# OPTIONS -fglasgow-exts #-}
--
-- Uses pattern guards
--
-- This is an interpreter of the Unlambda language, written in
-- the pure, lazy, functional language Haskell.
--
-- Copyright (C) 2001 by Ørjan Johansen <oerjan@nvg.ntnu.no>
-- Copyright (C) 2006 by Don Stewart - http://www.cse.unsw.edu.au/~dons
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
--
--
-- A time- and output-limited unlambda
--
import Data.Char
import System.IO
import System.Posix.Resource
rlimit = ResourceLimit 3
main = setResourceLimit ResourceCPUTime (ResourceLimits rlimit rlimit) >> run
run = do
exp <- parse stdin
let (Eval cp) = eval exp
cp (Nothing, 2048) (const return)
------------------------------------------------------------------------
-- Abstract syntax
data Exp
= App Exp Exp
| K
| K1 Exp
| S
| S1 Exp
| S2 Exp Exp
| I
| V
| C
| Cont (Cont Exp)
| D
| D1 Exp
| Dot Char
| E
| At
| Ques Char
| Pipe
------------------------------------------------------------------------
-- Parsing of the Unlambda program directly from handle
--
parse :: Handle -> IO Exp
parse h = do
c <- catch (hGetChar h) (\_ -> error "Parse error at end of file")
case toLower c of
c | c `elem` " \t\n" -> parse h
'`' -> do e1 <- parse h
e2 <- parse h
return (App e1 e2)
'#' -> hGetLine h >> parse h
'.' -> hGetChar h >>= return . Dot
'?' -> hGetChar h >>= return . Ques
_ | Just fn <- lookup c table -> return fn
| otherwise -> error $ "Unknown operator " ++ show c
where table = zip "ksivcdre@|" [K,S,I,V,C,D,Dot '\n',E,At,Pipe]
------------------------------------------------------------------------
-- Printing
instance Show Exp where showsPrec _ e = sh e
sh (App x y) = showChar '`' . sh x . sh y
sh K = showChar 'k'
sh (K1 x) = showString "`k" . sh x
sh S = showChar 's'
sh (S1 x) = showString "`s" . sh x
sh (S2 x y) = showString "``s" . sh x . sh y
sh I = showChar 'i'
sh V = showChar 'v'
sh C = showChar 'c'
sh (Cont _) = showString "<cont>"
sh D = showChar 'd'
sh (D1 x) = showString "`d" . sh x
sh (Dot '\n') = showChar 'r'
sh (Dot c) = showChar '.' . showChar c
sh E = showChar 'e'
sh At = showChar '@'
sh (Ques c) = showChar '?' . showChar c
sh Pipe = showChar '|'
------------------------------------------------------------------------
-- Eval monad
newtype Eval a = Eval ((Maybe Char, Int) -> Cont a -> IO Exp)
type Cont a = (Maybe Char, Int) -> a -> IO Exp
instance Monad Eval where
(Eval cp1) >>= f = Eval $ \dat1 cont2 ->
cp1 dat1 $ \dat2 a ->
let (Eval cp2) = f a in cp2 dat2 cont2
return a = Eval $ \dat cont -> cont dat a
------------------------------------------------------------------------
-- Basics
currentChar = Eval (\dat@(c,_) cont -> cont dat c)
setCurrentChar c = Eval (\(_,i) cont -> cont (c,i) ())
io iocp = Eval (\dat cont -> iocp >>= cont dat)
throw c x = Eval (\dat cont -> c dat x)
exit e = Eval (\_ _ -> return e)
callCC f = Eval $ \dat cont -> let Eval cp2 = f cont in cp2 dat cont
step = Eval (\(c,i) cont -> if i<1 then return E else cont (c,i-1) ())
------------------------------------------------------------------------
-- Interpretation in the Eval monad
eval (App e1 e2) = do
f <- eval e1
case f of
D -> return (D1 e2)
_ -> eval e2 >>= apply f
eval e = return e
apply K x = return (K1 x)
apply (K1 x) y = return x
apply S x = return (S1 x)
apply (S1 x) y = return (S2 x y)
apply (S2 x y) z = eval (App (App x z) (App y z))
apply I x = return x
apply V x = return V
apply C x = callCC (\c -> apply x (Cont c))
apply (Cont c) x = throw c x
apply D x = return x
apply (D1 e) x = do f <- eval e; apply f x
apply (Dot c) x = step >> io (putChar c) >> return x
apply E x = exit x
apply At f = do
dat <- io $ catch (getChar >>= return . Just) (const $ return Nothing)
setCurrentChar dat
apply f (case dat of Nothing -> V ; Just _ -> I)
apply (Ques c) f = do
cur <- currentChar
apply f (if cur == Just c then I else V)
apply Pipe f = do
cur <- currentChar
apply f (case cur of Nothing -> V ; Just c -> (Dot c))