{-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE RecordWildCards, ScopedTypeVariables, TemplateHaskell #-}
module Lambda where
import Control.Applicative
import Data.Char (isAlphaNum, isLetter)
import Data.Map (Map, insert, (!))
import Data.Monoid ((<>))
import Text.Parser.Token (symbol, whiteSpace)
import qualified Rank2
import qualified Rank2.TH
import Text.Grampa
import Text.Grampa.ContextFree.LeftRecursive (Parser)
class LambdaDomain e where
apply :: e -> e -> e
lambda :: String -> e -> e
var :: String -> e
data LambdaInitial = ApplyI LambdaInitial LambdaInitial
| LambdaI String LambdaInitial
| VarI String
deriving Show
data LambdaDeBruin = ApplyB LambdaDeBruin LambdaDeBruin
| LambdaB LambdaDeBruin
| VarB Int
deriving Show
data LambdaPHOAS a = ApplyP (LambdaPHOAS a) (LambdaPHOAS a)
| LambdaP (a -> LambdaPHOAS a)
| VarP a
instance LambdaDomain (Map String a -> [a] -> a) where
apply f arg env stack = f env (arg env [] : stack)
lambda v body env (arg:stack) = body (insert v arg env) stack
var v env _stack = env ! v
reduceNormallyI :: Map String a -> [a] -> LambdaInitial -> a
reduceNormallyI env stack (ApplyI f arg) = reduceNormallyI env (reduceNormallyI env stack arg : stack) f
reduceNormallyI env (arg:stack) (LambdaI v body) = reduceNormallyI (insert v arg env) stack body
reduceNormallyI env _stack (VarI v) = env ! v
reduceNormallyP :: (a -> LambdaPHOAS a) -> LambdaPHOAS a -> LambdaPHOAS a
reduceNormallyP use (ApplyP f arg) = case reduceNormallyP use f
of LambdaP f' -> reduceNormallyP use (reduceNormallyP f' arg)
x -> ApplyP x arg
reduceNormallyP use (VarP x) = use x
reduceNormallyP _ x@LambdaP{} = x
instance LambdaDomain LambdaInitial where
apply = ApplyI
lambda = LambdaI
var = VarI
instance LambdaDomain (Map String a -> LambdaPHOAS a) where
apply f arg env = ApplyP (f env) (arg env)
lambda v body env = LambdaP (\x-> body $ insert v x env)
var v env = VarP (env ! v)
instance LambdaDomain (Map String Int -> Int -> LambdaDeBruin) where
apply f arg env depth = ApplyB (f env depth) (arg env depth)
lambda v body env depth = LambdaB (body (insert v (succ depth) env) (succ depth))
var v env depth = VarB (depth - env ! v)
{-
instance LambdaDomain (Map String e -> [e] -> e) where
apply f arg env stack = f env (arg env stack : stack)
lambda v body env (arg : stack) = body (insert v arg env) stack
var v map = (map ! v) map
-}
instance LambdaDomain String where
apply f arg = f ++ " " ++ arg
lambda v body = "(\\" ++ v ++ ". " ++ body ++ ")"
var v = v
data Lambda e f =
Lambda{
expr :: f e,
abstraction :: f e,
application :: f e,
argument :: f e,
primary :: f e,
varName :: f String}
instance (Show (f e), Show (f String)) => Show (Lambda e f) where
showsPrec prec g rest = "Lambda{expr=" ++ showsPrec prec (expr g)
(", abstraction=" ++ showsPrec prec (abstraction g)
(", application=" ++ showsPrec prec (application g)
(", argument=" ++ showsPrec prec (application g)
(", primary=" ++ showsPrec prec (primary g)
(", varName=" ++ showsPrec prec (varName g) ("}" ++ rest))))))
$(Rank2.TH.deriveAll ''Lambda)
lambdaCalculus :: (Lexical g, LexicalConstraint Parser g String, LambdaDomain e)
=> GrammarBuilder (Lambda e) g Parser String
lambdaCalculus Lambda{..} = Lambda{
expr= abstraction,
abstraction= lambda <$> (symbol "\\" *> varName <* symbol "->") <*> abstraction
<|> application,
application= apply <$> application <*> argument
<|> argument,
argument= symbol "(" *> expr <* symbol ")"
<|> primary,
primary= var <$> varName,
varName= whiteSpace *> takeCharsWhile1 isLetter <> takeCharsWhile isAlphaNum}