module Main where import Text.ParserCombinators.Parsec (many1, sepBy1, string, noneOf, char, digit, spaces, sepEndBy, Parser, (<|>), parse, letter, oneOf) --import Relude hiding ((<|>)) import Prelude import Data.Foldable (foldl1) import qualified Data.Set as Set import Control.Monad (forever, void) import System.IO (hFlush, stdout) data Term = Var String | App Term Term | Lam String Term deriving (Eq) instance Show Term where show (Var x) = x show (App x y) = show x ++ show y show (Lam x y) = "(λ"++x++". "++show y++")" parens p = char '(' *> spaces *> p <* spaces <* char ')' var = Var <$> many1 letter lam = do void $ char '\\' spaces vars <- many1 $ (many1 letter) <* spaces void $ string "." spaces body <- term return (foldr Lam body vars) app = foldl1 App <$> sepBy1 (var <|> parens term) spaces term = lam <|> app <|> var fv :: Term -> Set.Set String fv (Var x) = Set.singleton x fv (Lam x m) = x `Set.delete` (fv m) fv (App m1 m2) = (fv m1) `Set.union` (fv m2) pickFresh :: Set.Set String -> String -> String pickFresh s n | n `Set.notMember` s = n pickFresh s n = pickFresh s $ n <> "'" subst n x (Var e) | x == e = n | otherwise = Var e subst n x (App m1 m2) = App (subst n x m1) (subst n x m2) subst n x l@(Lam y m) | x == y = l | y `Set.notMember` (fv n) = Lam y (subst n x m) | otherwise = Lam y' (subst n x (subst (Var y') y m)) where y' = pickFresh ((fv n) `Set.union` (fv m)) y normalstep (App (Lam x m) n) = Just (subst n x m) normalstep (Lam x m) = normalstep m >>= return . Lam x normalstep (App m n) | normalstep m == Nothing = normalstep n >>= return . App m normalstep (App m n) = normalstep m >>= return . (`App` n) normalstep _ = Nothing repeatedly f x = maybe x (repeatedly f) (f x) eval = (repeatedly normalstep <$>) . parse term "" main = forever $ do putStr "Untyped> " hFlush stdout contents <- getLine putStrLn $ (either show show . eval) contents