packages feed

inch-0.1.0: src/Language/Inch/Parser.lhs

> {-# OPTIONS_GHC -fno-warn-missing-signatures #-}

> module Language.Inch.Parser (parseModule, parseInterface) where

> import Control.Applicative
> import Control.Monad
> import Data.Char
> import Data.Maybe
> import Data.List

> import Text.ParserCombinators.Parsec hiding (parse, optional, many, (<|>))
> import Text.ParserCombinators.Parsec.Expr
> import Text.ParserCombinators.Parsec.Language
> import qualified Text.ParserCombinators.Parsec.Token as T
> import qualified Text.ParserCombinators.Parsec.IndentParser as I
> import qualified Text.ParserCombinators.Parsec.IndentParser.Token as IT


> import Language.Inch.Type
> import Language.Inch.Syntax
> import Language.Inch.ModuleSyntax
> import Language.Inch.Kit
> import Language.Inch.Kind hiding (kind)

> parseModule = I.parse module_

> parseInterface = I.parse interface

> def = haskellDef { identStart = identStart haskellDef <|> char '_'
>                  , reservedNames = "_" : reservedNames haskellDef }

> lexer       = T.makeTokenParser def    
      
> identifier     = IT.identifier lexer
> reserved       = IT.reserved lexer
> operator       = IT.operator lexer
> reservedOp     = IT.reservedOp lexer
> charLiteral    = IT.charLiteral lexer
> stringLiteral  = IT.stringLiteral lexer
> natural        = IT.natural lexer
> integer        = IT.integer lexer
> symbol         = IT.symbol lexer
> whiteSpace     = IT.whiteSpace lexer
> parens         = IT.parens lexer
> braces         = IT.braces lexer
> brackets       = IT.brackets lexer
> dot            = IT.dot lexer
> commaSep       = IT.commaSep lexer
> commaSep1      = IT.commaSep1 lexer

< lexeme         = IT.lexeme lexer
< angles         = IT.angles lexer
< semi           = IT.semi lexer
< comma          = IT.comma lexer
< colon          = IT.colon lexer
< semiSep        = IT.semiSep lexer
< semiSep1       = IT.semiSep1 lexer

> backticks p = reservedOp "`" *> p <* reservedOp "`"

> specialOp s = try $
>     string s >> notFollowedBy (opLetter def) >> whiteSpace

> optionalList p = maybe [] id <$> optional p

> doubleColon = reservedOp "::"

> underscore = reserved "_"

> wrapParens p = (\ s -> "(" ++ s ++ ")") <$> p

> single p = (\ x -> [x]) <$> p
> manymany p = join <$> many p 

> isVar :: String -> Bool
> isVar ('_':_:_)  = True
> isVar (x:_)      = isLower x
> isVar []         = error "isVar: empty"

> isVarOp :: String -> Bool
> isVarOp (':':_)  = False
> isVarOp _        = True

> identLike v desc = try $ do
>     s <- identifier <?> desc
>     when (v /= isVar s) $ fail $ "expected " ++ desc
>     return s

> opLike v desc = try $ do
>     s <- operator <?> desc
>     when (v /= isVarOp s) $ fail $ "expected " ++ desc
>     return s


> varid   = identLike True "variable"
> conid   = identLike False "constructor"
> varsym  = wrapParens (opLike True "variable symbol")
> consym  = wrapParens (opLike False "constructor symbol")

> var     = varid <|> try (parens varsym)
> con     = conid <|> try (parens consym)
> varop   = varsym <|> backticks varid
> conop   = consym <|> backticks conid

< op      = varop <|> conop

> gcon    =    reservedOp "()" *> return "()"
>         <|>  reservedOp "[]" *> return "[]"
>         <|>  reservedOp "(,)" *> return "(,)"
>         <|>  con

> gtycon =     reservedOp "()" *> return "()"
>         <|>  reservedOp "[]" *> return "[]"
>         <|>  reservedOp "(,)" *> return "(,)"
>         <|>  con



Kinds

> kind       = kindBit `chainr1` kindArrow
> kindBit    = setKind <|> try numKind <|> natKind <|> constraintKind <|> parens kind
> setKind    = symbol "*" >> return SKSet
> numKind    = (symbol "Integer" <|> symbol "Num") >> return SKNum
> natKind    = symbol "Nat" >> return SKNat
> constraintKind = symbol "Constraint" >> return SKConstraint
> kindArrow  = reservedOp "->" >> return (:-->)



Types

> tyVarName  = identLike True "type variable"
> tyConName  = identLike False "type constructor"
>              <|> try (reservedOp "()" >> return unitTypeName)
> numVarName = identLike True "numeric type variable"
> tyVar      = STyVar <$> tyVarName
> tyCon      = STyCon <$> gtycon
> tyExp      = tyAll <|> tyPi <|> tyQual <|> tyExpArr
> tyAll      = tyQuant "forall" (SBind All)
> tyPi       = tyQuant "pi" (SBind Pi)
> tyExpArr   = tyBit `chainr1` tyArrow
> tyArrow    = reservedOp "->" *> return (--->)
>            <|> reservedOp "=>" *> return SQual

> tyBit = buildExpressionParser
>     [  [prefix "-" negate]
>     ,  [binary "^" (sbinOp Pow) AssocLeft]
>     ,  [binary "*" (*) AssocLeft]
>     ,  [binary "+" (+) AssocLeft, sbinary "-" (-) AssocLeft]
>     ,  [  binary "<"  (styPred LS) AssocNone
>        ,  binary "<=" (styPred LE) AssocNone
>        ,  binary ">"  (styPred GR) AssocNone
>        ,  binary ">=" (styPred GE) AssocNone
>        ,  binary "~"  (styPred EL) AssocNone
>        ] 
>     ]
>     (tyAtom `chainl1` pure STyApp)

> tyAtom     =    STyInt <$> try natural
>            <|>  SBinOp <$> prefixBinOp
>            <|>  SUnOp <$> prefixUnOp
>            <|>  STyComp <$> prefixComparator
>            <|>  tyVar
>            <|>  tyCon
>            <|>  parens ((reservedOp "->" *> pure SArr) <|> fmap (foldr1 (STyApp . STyApp (STyCon tupleTypeName))) (commaSep1 tyExp))
>            <|>  brackets (STyApp (STyCon listTypeName) <$> tyExp)

> prefixBinOp  =    reserved "min" *> pure Min
>              <|>  reserved "max" *> pure Max
>              <|>  try (parens ((specialOp "-" *> pure Minus)
>                                <|> (reservedOp "*" *> pure Times)
>                                <|> (reservedOp "+" *> pure Plus)
>                                <|> (reservedOp "^" *> pure Pow)))

> prefixUnOp   =    reserved "abs" *> pure Abs
>              <|>  reserved "signum" *> pure Signum

> prefixComparator  =    reservedOp "(~)" *> pure EL
>                   <|>  reservedOp "(>=)" *> pure GE
>                   <|>  reservedOp "(>)"  *> pure GR
>                   <|>  reservedOp "(<=)" *> pure LE
>                   <|>  reservedOp "(<)"  *> pure LS

> binary   name fun assoc = Infix (do{ reservedOp name; return fun }) assoc
> sbinary  name fun assoc = Infix (do{ specialOp name; return fun }) assoc
> prefix   name fun       = Prefix (do{ reservedOp name; return fun })

< postfix  name fun       = Postfix (do{ reservedOp name; return fun })


> tyQuant q f = do
>     reserved q
>     aks <- many1 $ foo <$> quantifiedVar
>     reservedOp "."
>     t <- tyExp
>     return $ foldr (\ (a, k) ty -> f a k ty) t $ join aks
>   where
>     foo :: ([as], k) -> [(as, k)]
>     foo (as, k) = map (\ a -> (a, k)) as

> quantifiedVar  =    parens ((,) <$> many1 tyVarName <* doubleColon <*> kind)
>                <|>  (\ a -> ([a] , SKSet)) <$> tyVarName

> tyQual = do
>     ps <- try ((parens constraints <|> (pure <$> tyBit)) <* reservedOp "=>")
>     t <- tyExp
>     return $ foldr SQual t ps

> constraints = commaSep1 constraint
> constraint = tyBit

> predicates = commaSep1 predicate

> predicate = do
>     c <- constraint
>     case sConstraintToPred c of
>         Just p   -> return p
>         Nothing  -> fail "expected testable predicate"




Terms

> expr = do
>     t    <- lexp
>     mty  <- optionMaybe (doubleColon >> tyExp)
>     case mty of
>         Just ty -> return $ t :? ty
>         Nothing -> return t

> lexp  =    lambda
>       <|>  letExpr
>       <|>  caseExpr
>       <|>  fexp


> letExpr = do
>     reserved "let"
>     ds <- I.block decls
>     reserved "in"
>     t <- expr
>     return $ Let ds t

> caseExpr = do
>     reserved "case"
>     t <- expr
>     reserved "of"
>     as <- I.block $ many caseAlternative
>     return $ Case t as

> caseAlternative = I.lineFold (CaseAlt <$> pat <*> altRest (reservedOp "->")
>     <?> "case alternative")

> fexp = buildExpressionParser
>     [
>         [prefix "-" (tmBinOp Minus (TmInt 0))],
>         [binary "^" (tmBinOp Pow) AssocLeft],
>         [binary "*" (tmBinOp Times) AssocLeft],    
>         [binary "+" (tmBinOp Plus) AssocLeft, sbinary "-" (tmBinOp Minus) AssocLeft],
>         [binary ":" (TmApp . TmApp (TmCon listConsName)) AssocRight]
>     ]
>     (aexp `chainl1` pure TmApp)

> aexp :: I.IndentCharParser st (STerm ())
> aexp  =    TmInt <$> try natural
>       <|>  CharLit <$> charLiteral
>       <|>  StrLit  <$> stringLiteral
>       <|>  TmVar <$> var
>       <|>  TmCon <$> gcon
>       <|>  parens (fmap (foldr1 (TmApp . TmApp (TmCon tupleConsName))) (commaSep1 expr))
>       <|>  braces (TmBrace <$> tyBit) 
>       <|>  listy

> listy = foldr (TmApp . TmApp (TmCon listConsName)) (TmCon listNilName) <$> brackets (commaSep fexp)

> lambda = do
>     reservedOp "\\"
>     ss <- many1 $ (Left <$> var) <|> (Right <$> braces numVarName)
>     reservedOp "->"
>     t <- expr
>     return $ wrapLam ss t
>   where
>     wrapLam []              t = t
>     wrapLam (Left s : ss)   t = Lam s $ wrapLam ss t
>     wrapLam (Right s : ss)  t = NumLam s $ rawCoerce $ wrapLam ss t


Interface files

> interface = manymany (   single dataDecl
>                      <|> single typeDecl
>                      <|> single classDecl
>                      <|> single instHeader
>                      <|> map Decl <$> sigDecls
>                      ) <* eof


Modules

> module_ = do
>     whiteSpace
>     _ <- optional (reserved "#line" >> integer >> stringLiteral)
>     mh <- optional (reserved "module" *>
>                        ((,) <$> moduleName
>                            <*> optionalList (parens (commaSep identifier)))
>                     <* reserved "where")
>     is <- many importStmt
>     ds <- topdecls
>     eof
>     return $ Mod mh is ds

> importStmt = do
>     reserved "import"
>     q   <- isJust <$> optional (reserved "qualified")
>     n   <- moduleName
>     as  <- optional (reserved "as" *> moduleName)
>     im  <- importSpec
>     return $ Import q n as im

> importSpec =    Imp <$> parens (commaSep identifier)
>            <|>  ImpHiding <$> (reserved "hiding" *> parens (commaSep (var <|> con)))
>            <|>  pure ImpAll

> moduleName = join . intersperse "." <$> identLike False "module name" `sepBy` dot


> topdecls  = associateTop <$> manymany (   single dataDecl
>                                       <|> single typeDecl
>                                       <|> single classDecl
>                                       <|> single instDecl
>                                       <|> map Decl <$> (sigDecls <|> single funDecl)
>                                       )
>  where
>     associateTop :: [STopDeclaration] -> [STopDeclaration]
>     associateTop = map joinFun . groupBy sameFun
>
>     sameFun (Decl (FunDecl x _)) (Decl (FunDecl y _))  = x == y
>     sameFun _             _                            = False
> 
>     joinFun :: [STopDeclaration] -> STopDeclaration
>     joinFun [d] = d
>     joinFun fs@(Decl (FunDecl x _) : _) = Decl (FunDecl x (join (map altsOf fs)))
>     joinFun _ = error "joinFun: impossible"
>
>     altsOf (Decl (FunDecl _ as)) = as
>     altsOf _ = error "altsOf: impossible"

> decls     = associate <$> manymany (sigDecls <|> single funDecl)
>   where
>     associate :: [SDeclaration ()] -> [SDeclaration ()]
>     associate = map joinFun . groupBy sameFun
>
>     sameFun (FunDecl x _) (FunDecl y _)  = x == y
>     sameFun _             _              = False
> 
>     joinFun :: [SDeclaration ()] -> SDeclaration ()
>     joinFun [d] = d
>     joinFun fs@(FunDecl x _ : _) = FunDecl x (join (map altsOf fs))
>     joinFun _ = error "joinFun: impossible"
>
>     altsOf (FunDecl _ as) = as
>     altsOf _ = error "altsOf: impossible"



> dataDecl = I.lineFold $ do
>     try (reserved "data")
>     s <- tyConName
>     k <- (doubleColon >> kind) <|> return SKSet
>     reserved "where"
>     cs <- many $ I.lineFold constructor
>     ds <- maybe [] id <$> optional (reserved "deriving" >>
>               parens (commaSep className)
>               <|> fmap pure className)
>     return $ DataDecl s k cs ds
>     


> typeDecl = I.lineFold $ do
>     reserved "type"
>     x <- tyConName
>     t <- tySyn
>     return $ TypeDecl x t
>   where
>     tySyn = SSynTy <$> (reservedOp "=" *> tyExp)
>           <|> SSynAll <$> tyVarName <*> pure SKSet <*> tySyn
>           <|> (do
>                 (x, k)  <- kindParens
>                 t       <- tySyn
>                 return $ SSynAll x k t
>               )


> kindParens = parens ((,) <$> tyVarName <* doubleColon <*> kind)


> className = identLike False "type class name"

> classDecl = I.lineFold $ do
>     reserved "class"
>     ss  <- optionalList $ parens (commaSep tyExp) <* reservedOp "=>"
>     s   <- className
>     vs  <- many classVar
>     ms  <- optionalList (reserved "where" *> manymany tmtypes)
>     return $ CDecl s (ClassDecl vs ss ms) 
>   where
>     classVar = ( ((\ v -> VK v SKSet) <$> var)
>              <|> parens (VK <$> var <*> (doubleColon *> kind)))

> instDecl = I.lineFold $ do
>     reserved "instance"
>     t <- tyExp
>     (cs, s, ts) <- implyBits t
>     zs <- optionalList (reserved "where" *> many funline)
>     return $ IDecl s (InstDecl ts cs zs)


> implyBits :: Monad m => SType -> m ([SType], String, [SType])
> implyBits (SQual q t) = do
>     let qs = uncomma q
>     (cs, s, ts) <- implyBits t
>     return (qs ++ cs, s, ts)
>   where
>     uncomma (STyCon c `STyApp` x `STyApp` y)
>         | c == tupleConsName = uncomma x ++ uncomma y
>     uncomma x = [x]
> implyBits (STyApp f t) = do
>     ([], s, ts) <- implyBits f
>     return ([], s, ts ++ [t])
> implyBits (STyCon c) = return ([], c, [])
> implyBits _ = fail "ook"



> instHeader = instDecl


> constructor = do
>     s <- con
>     doubleColon
>     t <- tyExp
>     return $ s ::: t


> tmtypes = I.lineFold $ do
>     ss  <- try $ commaSep var <* doubleColon
>     ty  <- tyExp
>     return $ map (\ s -> s ::: ty) ss

> sigDecls = map (\ (s ::: ty) -> SigDecl s ty) <$> tmtypes

> funline = I.lineFold $ do
>     (v, ps)  <- funlhs
>     gt       <- rhs
>     return (v, [Alt (foldr (:!) P0 ps) gt])

> funDecl = uncurry FunDecl <$> funline

> altRest p  =    Unguarded <$> (p *> expr) <*> whereClause
>            <|>  Guarded <$> (many1 (reservedOp "|" *> ((:*:) <$> guarded <* p <*> expr)))
>                     <*> whereClause

> guarded  =    NumGuard <$> braces predicates
>          <|>  ExpGuard <$> commaSep expr

> whereClause = maybe [] id <$> optional (reserved "where" >> I.block decls)





> funlhs  =    (,) <$> var <*> many apat
>         <|>  (\ x o y -> (o, [x, y])) <$> pat <*> varop <*> pat
>         <|>  (\ (o, ps) qs -> (o, ps ++ qs)) <$> parens funlhs <*> many apat

> rhs = (Unguarded <$> (reservedOp "=" *> expr)
>     <|> Guarded <$> (many1 (reservedOp "|" *> ((:*:) <$> guarded <* reservedOp "=" <*> expr))))
>     <*> whereClause


> rtc p  =    (:!) <$> p <*> rtc p
>        <|>  pure P0

> patList = rtc apat

> pat = do
>     l   <- lpat
>     mr  <- optional ((,) <$> conop <*> pat)
>     case mr of
>         Nothing      -> return l
>         Just (o, r)  -> return $ PatCon o (l :! r :! P0)

> lpat  =    PatCon <$> gcon <*> patList
>       <|>  apat

> apat =        nplusk
>          <|>  PatCon <$> gcon <*> pure P0
>          <|>  PatIntLit <$> try integer
>          <|>  PatStrLit <$> stringLiteral
>          <|>  PatCharLit <$> charLiteral
>          <|>  underscore *> pure PatIgnore
>          <|>  parens (foldr1 tupleConsPat <$> commaSep1 pat)
>          <|>  brackets (foldr listConsPat listNilPat <$> commaSep pat)
>          <|>  braces patBrace
>   where
>     tupleConsPat x y  = PatCon tupleConsName (x :! y :! P0)
>     listConsPat x y   = PatCon listConsName (x :! y :! P0)
>     listNilPat        = PatCon listNilName P0

> nplusk = do
>     v <- var
>     mk <- optional (reservedOp "+" *> integer)
>     return $ case mk of
>        Nothing  -> PatVar v
>        Just k   -> PatNPlusK v k


> patBrace = do
>     ma  <- optional var
>     k   <- option 0 $ case ma of
>                           Just _   -> reservedOp "+" *> integer
>                           Nothing  -> integer
>     return $ case ma of
>         Just a   -> rawCoerce2 $ PatBrace a k
>         Nothing  -> PatBraceK k