packages feed

caledon-0.0.0.0: Parser.hs

{-# LANGUAGE RecordWildCards, TupleSections #-}
module Parser where

import AST

import Data.Foldable as F (msum, forM_)
import Data.Functor
import Text.Parsec.String
import Text.Parsec
import Data.Monoid
import Control.Monad (unless)
import Text.Parsec.Language (haskellDef)
import Text.Parsec.Expr
import Data.Maybe
import qualified Text.Parsec.Token as P
import qualified Data.Set as S

-----------------------------------------------------------------------
-------------------------- PARSER -------------------------------------
-----------------------------------------------------------------------

data ParseState = ParseState { currentVar :: Integer, currentSet :: S.Set Name }

modifySet f s = s { currentSet = f $ currentSet s }
modifyVar f s = s { currentVar = f $ currentVar s }

getNextVar = do
  v <- currentVar <$> getState
  modifyState $ modifyVar (+1)
  return $ show v++"@?_?"

decls = do
  whiteSpace
  lst <- many (query <|> defn <?> "declaration")
  eof
  return lst

query = do 
  reserved "query" 
  (nm,ty) <- named dec_pred
  optional semi
  return $ Query nm ty

defn =  do
  reserved "defn" 
  (nm,ty) <- named dec_tipe
  let more =  do reserved "as"
                 lst <- flip sepBy1 (reservedOp "|") $ named dec_pred
                 optional semi
                 return $ Predicate nm ty lst
      none = do optional semi
                return $ Predicate nm ty []
  more <|> none <?> "definition"

pAtom =  do reserved "_"
            nm <- getNextVar
            return $ var nm
     <|> do r <- id_var
            return $ var r
     <|> do r <- identifier
            mp <- currentSet <$> getState 
            return $ (if S.member r mp then var else cons) r

     <|> (tpToTm <$> parens tipe)
     <?> "atom"
  
trm =  parens trm 
   <|> do reservedOp "λ" <|> reservedOp "\\"
          (nm,tp) <- parens anonNamed <|> anonNamed
          reservedOp "."
          tp' <- tmpState nm trm
          return $ AbsImp nm tp tp'
   <|> do reservedOp "?λ" <|> reservedOp "?\\"
          (nm,tp) <- parens anonNamed <|> anonNamed
          reservedOp "."
          tp' <- tmpState nm trm
          return $ Abs nm tp tp'          
   <|> do t <- pAtom
          tps <- many $ (parens tipe) <|> (Atom <$> pAtom)
          return $ rebuildSpine t tps
   <?> "term"

fall = Forall ""
fallImp = ForallImp ""

table = [ [ binary (reservedOp "->" <|> reservedOp "→") fall AssocRight
          , binary (reservedOp "=>" <|> reservedOp "⇒") fallImp AssocRight
          ] 
        , [ binary (reservedOp "<-" <|> reservedOp "←") (flip fall) AssocLeft
          , binary (reservedOp "<=" <|> reservedOp "⇐") (flip fallImp) AssocLeft ]
        ]
  where  binary  name fun assoc = Infix (name >> return fun) assoc
         
 
dec_tipe = (getId lower, ":")
dec_pred = (getId lower, "=")
id_var = getId $ upper <|> char '\''
dec_anon = (getId $ letter <|> char '\'' , ":")

named (ident, sep) = do
  nm <- ident
  reservedOp sep
  ty <- tipe
  return (nm, ty)

anonNamed = do
  let (ident,sep) = dec_anon 
  nm <- ident
  ty <- optionMaybe $ reservedOp sep >> tipe
  nm' <- getNextVar
  return (nm,fromMaybe (Atom $ var nm') ty)

tmpState nm m = do
  s <- currentSet <$> getState
  let b = S.member nm s
  modifyState $ modifySet (S.insert nm)
  r <- m
  unless b $ modifyState $ modifySet $ S.delete nm
  return r

tipe = buildExpressionParser table ( 
        parens tipe
    <|> (Atom <$> trm)
    <|> do (nm,tp) <- brackets anonNamed 
           tp' <- tmpState nm tipe
           return $ Forall nm tp tp'
    <|> do (nm,tp) <- braces anonNamed 
           tp' <- tmpState nm tipe
           return $ ForallImp nm tp tp'           
    <|> do reservedOp "∀" <|> reserved "forall"
           (nm,tp) <- parens anonNamed <|> anonNamed
           reservedOp "."
           tp' <- tmpState nm tipe
           return $ Forall nm tp tp'
    <|> do reservedOp "?∀" <|> reserved "?forall"
           (nm,tp) <- parens anonNamed <|> anonNamed
           reservedOp "."
           tp' <- tmpState nm tipe
           return $ ForallImp nm tp tp'           
    <?> "type")
  
           
P.TokenParser{..} = P.makeTokenParser $ mydef
mydef = haskellDef 
 { P.identStart = lower
 , P.identLetter = alphaNum <|> oneOf "_'-/"
 , P.reservedNames = ["defn", "as", "query", "forall", "?forall", "_"]
 , P.caseSensitive = True
 , P.reservedOpNames = ["->", "=>", "<=", "⇐", "⇒", "→", "<-", "←", ":", "|", "\\","?\\", "λ","?λ","∀","?∀", "."]
 }
getId start = P.identifier $ P.makeTokenParser $ mydef { P.identStart = start }