module Flite.Interp (interp, frontend) where
import Flite.Syntax hiding (Lam)
import Data.Array
import Flite.InterpFrontend
import Flite.Inline
import System.IO.Unsafe(unsafePerformIO)
infixl :@
data Val =
Error
| C Id Int Int [Val]
| F Id
| V Id
| N Int
| Lut (Array Int Val)
| Val :@ Val
| Lambda Id Val
| Lam (Val -> Val)
instance Show Val where
show (Lam f) = "lambda!"
show (C n _ _ vs) = "(" ++ unwords (n:map show vs) ++ ")"
show (N i) = show i
lut :: [Val] -> Val
lut vs = Lut (listArray (0, length vs) vs)
app :: [Val] -> Val
app xs = foldl1 (:@) xs
val :: Exp -> Val
val (App e xs) = app (val e : map val xs)
val (Var v) = V v
val (Alts as _) = lut (map F as)
val (Ctr s arity i) = C s arity i []
val (Fun f) = F f
val (Int n) = N n
val Bottom = Error
val (Let bs e) = elimLet vs (map val es) (val e)
where (vs, es) = unzip bs
compile :: Prog -> [(Id, Val)]
compile p = [(f, comp $ lambdify args $ val e) | Func f args e <- p]
where lambdify args e = foldr (\(Var v) -> Lambda v) e args
comp :: Val -> Val
comp (Lambda v x) = abstr v (comp x)
comp (e1 :@ e2) = comp e1 :@ comp e2
comp e = e
abstr :: Id -> Val -> Val
abstr v (e1 :@ e2) = opt (F "S" :@ abstr v e1 :@ abstr v e2)
abstr v (V w)
| v == w = F "I"
| otherwise = F "K" :@ V w
abstr v e = F "K" :@ e
opt :: Val -> Val
opt (F "S" :@ (F "K":@p) :@ (F "K" :@ q)) = F "K" :@ (p :@ q)
opt (F "S" :@ (F "K":@p) :@ F "I") = p
opt (F "S" :@ (F "K":@p) :@ (F "B" :@ q :@ r)) = F "B*" :@ p :@ q :@ r
opt (F "S" :@ (F "K":@p) :@ q) = F "B" :@ p :@ q
opt (F "S" :@ (F "B":@p:@q) :@ (F "K" :@ r)) = F "C'" :@ p :@ q :@ r
opt (F "S" :@ p :@ (F "K":@q)) = F "C" :@ p :@ q
opt (F "S" :@ (F "B":@p:@q) :@ r) = F "S'" :@ p :@ q :@ r
opt e = e
interp :: InlineFlag -> Prog -> Val
interp i p = case lookup "main" bs of
Nothing -> error "No 'main' function defined"
Just e -> e
where bs = prims ++ map (\(f, e) -> (f, link bs e)) (compile p')
p' = frontend i p
link :: [(Id, Val)] -> Val -> Val
link bs (f :@ a) = link bs f @@ link bs a
link bs (Lut a) = Lut (fmap (link bs) a)
link bs (F f) = case lookup f bs of
Nothing -> error ("Function '" ++ f ++ "' not defined")
Just e -> e
link bs Error = error "_|_"
link bs (V v) = error ("Unknown identifier '" ++ v ++ "'")
link bs e = e
infixl 0 @@
(@@) :: Val -> Val -> Val
(Lam f) @@ x = f x
(C s 0 i args) @@ (Lut alts) = run (alts ! i) args @@ Lut alts
(C s arity i args) @@ x = C s (arity1) i (x:args)
run :: Val -> [Val] -> Val
run e [] = e
run e (x:xs) = run e xs @@ x
prims :: [(Id, Val)]
prims = let (-->) = (,) in
[ "I" --> (Lam $ \x -> x)
, "K" --> (Lam $ \x -> Lam $ \y -> x)
, "S" --> (Lam $ \f -> Lam $ \g -> Lam $ \x -> f@@x@@(g@@x))
, "B" --> (Lam $ \f -> Lam $ \g -> Lam $ \x -> f@@(g@@x))
, "C" --> (Lam $ \f -> Lam $ \g -> Lam $ \x -> f@@x@@g)
, "S'" --> (Lam $ \c -> Lam $ \f -> Lam $ \g -> Lam $ \x -> c@@(f@@x)@@(g@@x))
, "B*" --> (Lam $ \c -> Lam $ \f -> Lam $ \g -> Lam $ \x -> c@@(f@@(g@@x)))
, "C'" --> (Lam $ \c -> Lam $ \f -> Lam $ \g -> Lam $ \x -> c@@(f@@x)@@g)
, "Y" --> (Lam $ \f -> fix f)
, "(+)" --> arith2 (+)
, "(-)" --> arith2 ()
, "(==)" --> logical2 (==)
, "(/=)" --> logical2 (/=)
, "(<=)" --> logical2 (<=)
, "emit" --> (Lam $ \(N a) -> Lam $ \k -> emitStr [toEnum a] k)
, "emitInt" --> (Lam $ \(N a) -> Lam $ \k -> emitStr (show a) k)
]
fix :: Val -> Val
fix f = let a = f @@ a in a
arith2 :: (Int -> Int -> Int) -> Val
arith2 op = Lam $ \(N a) -> Lam $ \(N b) -> N (op a b)
logical2 :: (Int -> Int -> Bool) -> Val
logical2 op =
Lam $ \(N a) -> Lam $ \(N b) -> if op a b then true else false
false :: Val
false = C "False" 0 0 []
true :: Val
true = C "True" 0 1 []
emitStr :: String -> a -> a
emitStr s k = unsafePerformIO (putStr s >> return k)
elimLet :: [Id] -> [Val] -> Val -> Val
elimLet vs es e = (Lambda "#" $ sub e) :@ (F "Y" :@ Lambda "#" t)
where
t = app (tuple (length vs):map sub es)
sels = [V "#" :@ select (length vs) i | i <- [0..]]
sub e = subst (zip vs sels) e
tuple :: Int -> Val
tuple n = foldr Lambda (app $ map (V . var) (n:[0..n1])) (map var [0..n])
where var i = 'v':show i
select :: Int -> Int -> Val
select n i = foldr Lambda (V (var i)) (map var [0..n1])
where var i = 'v':show i
subst :: [(Id, Val)] -> Val -> Val
subst s (e1 :@ e2) = subst s e1 :@ subst s e2
subst s (V v) = case lookup v s of
Nothing -> V v
Just x -> x
subst s e = e