{-# LANGUAGE UnicodeSyntax #-}
module Term where
import Prelude.Unicode
import Text.Parsec
import Control.Monad.Identity
import Text.Parsec.Token (makeTokenParser, GenLanguageDef(..))
import Text.Parsec.IndentParsec
-- | The AST of a lambda expression
data Λ = A Λ Λ -- ^ application
| Λ String Λ -- ^ abstraction
| L [(String,Λ)] Λ -- ^ let binding
| Case Λ [(Pattern,Λ)] -- ^ case expression
| C String [Λ] -- ^ constructor
deriving (Show,Eq,Ord)
-- | The LHS of a case expression. Numbers are parsed as strings.
data Pattern = Pat {constr ∷ String, vars ∷ [String]} deriving (Show, Eq, Ord)
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
}
parseFile ∷ FilePath → IO Λ
parseFile f = do
file ← readFile f
let parseErrorOrExpr = runIdentity $ runGIPT expression () f file
return $ either (error ∘ show) id parseErrorOrExpr
expression ∷ IndentCharParser Λ
expression = flip label "expression" $ letBinding <|> caseExpr <|> application
application ∷ IndentCharParser Λ
application = foldl1 A <$> many1 (parenthetic <|> abstraction <|> variable <|> numeral)
variable ∷ IndentCharParser Λ
variable = C <$> (ident <|> operator tokP) <*> pure []
numeral ∷ IndentCharParser Λ
numeral = C <$> numeric <*> pure []
numeric ∷ IndentCharParser String
numeric = either show show <$> naturalOrFloat tokP
parenthetic ∷ IndentCharParser Λ
parenthetic = parens tokP expression
tokP :: IndentTokenParser String () Identity
tokP = makeTokenParser langDef
caseExpr ∷ IndentCharParser Λ
caseExpr = flip label "case expression" $ do
keyword "case"
Case <$> expression <* keyword "of" <*> blockOf (many1 $ foldedLinesOf pattern)
arrow ∷ IndentCharParser ()
arrow = reservedOp tokP "→" <|> reservedOp tokP "->"
point ∷ IndentCharParser ()
point = reservedOp tokP "."
pattern ∷ IndentCharParser (Pattern, Λ)
pattern = (,) <$> lhs <*> expression where
lhs = Pat <$> (ident <|> numeric) <*> manyTill (ident <|> numeric) arrow
lambda ∷ IndentCharParser String
lambda = sym "λ" <|> sym "\\"
abstraction ∷ IndentCharParser Λ
abstraction = flip label "abstraction" $
flip (foldr Λ) <$> (lambda *> many1 ident) <*> ((arrow <|> point) *> expression)
letBinding ∷ IndentCharParser Λ
letBinding = flip label "let binding" $ do
keyword "let"
try oneLiner <|> multipleBindings where
oneLiner = do
b ← binding
L [b] <$> (keyword "in" *> expression)
multipleBindings = L <$> blockOf (many1 $ foldedLinesOf binding) <*> (keyword "in" *> expression)
binding ∷ IndentCharParser (String,Λ)
binding = flip label "binding" $ do
funct ← ident
rhs ← flip (foldr Λ) <$> manyTill ident equals <*> expression
return (funct, rhs)
keyword ∷ String → IndentCharParser ()
keyword = reserved tokP
ident ∷ IndentCharParser String
ident = identifier tokP
sym ∷ String → IndentCharParser String
sym = symbol tokP
equals ∷ IndentCharParser ()
equals = reservedOp tokP "="