{-# LANGUAGE UnicodeSyntax #-}
module Term where
import Prelude.Unicode
import Text.Parsec
import Text.Parsec.Token (makeTokenParser, GenLanguageDef(..))
import Text.Parsec.IndentParsec
import Control.Monad.Identity
data Λ = A Λ Λ -- ^ application
| Λ String Λ -- ^ abstraction
| V String -- ^ variable
| L [(String,Λ)] Λ -- ^ let binding
deriving (Show,Eq,Ord)
parseFile ∷ FilePath → IO Λ
parseFile f = do
file ← readFile f
let parseErrorOrExpr = runIdentity $ runGIPT parser () f file
return $ either (error ∘ show) id parseErrorOrExpr
type IndentCharParser a = IndentParsec String () a
langDef = LanguageDef { commentStart = "{-"
, commentEnd = "-}"
, commentLine = "--"
, identStart = letter <|> char '_'
, identLetter = alphaNum <|> char '_'
, opStart = oneOf "-+/*=<>"
, opLetter = oneOf "-+/*=<>"
, reservedNames = ["let", "in", "case", "of"]
, reservedOpNames = ["=", "->", "→", "."]
, caseSensitive = False
, nestedComments = True
}
tokP :: IndentTokenParser String () Identity
tokP = makeTokenParser langDef
parser ∷ IndentCharParser Λ
parser = expression where
expression = flip label "expression" $ application <|> letBinding
application = liftM (foldl1 A) $ many1 $ choice [parenthetic, abstraction, variable]
parenthetic = parens tokP expression
abstraction = flip label "abstraction" $ do
_ ← sym "λ" <|> sym "\\"
vars ← many1 ident
_ ← sym "." <|> sym "→" <|> sym "->"
body ← expression
return $ foldr Λ body vars
variable = liftM V $ ident <|> operator tokP <|> liftM (either show show) (naturalOrFloat tokP)
-- Ugly, but works. Keyword "in" terminates binding blocks and bindings. Allows empty lets
letBinding = flip label "let binding" $ do
keyword "let"
let parseBindings = do
e ← optionMaybe $ keyword "in" >> expression
case e of
Just je → return ([], Just je)
Nothing → do
(b,e) ← foldedLinesOf $ do
b ← binding
e ← optionMaybe $ keyword "in" >> expression
return (b,e)
case e of
Nothing → do
rec ← optionMaybe parseBindings
case rec of
Nothing → return ([b], Nothing)
Just (bs, me) → return (b:bs, me)
Just je → return ([b], Just je)
(binds, e) ← blockOf parseBindings
case e of
Nothing → liftM (L binds) (keyword "in" >> expression)
Just je → return $ L binds je
binding ∷ IndentCharParser (String,Λ)
binding = flip label "binding" $ do
funct ← ident
params ← many ident
body ← sym "=" >> expression
return (funct, foldr Λ body params)
keyword = reserved tokP
ident = identifier tokP
sym = symbol tokP