hgom-0.5.1: Gom/Parser.hs
-------------------------------------------------------------------
-- |
-- Module : Gom.Parser
-- Copyright : (c) Paul Brauner 2009
-- (c) Emilie Balland 2009
-- (c) INRIA 2009
-- Licence : GPL (see COPYING)
--
-- Maintainer : paul.brauner@inria.fr
-- Stability : provisional
-- Portability : non-portable (requires generalized newtype deriving)
--
-- Parser for gom modules.
--------------------------------------------------------------------
module Gom.Parser (parseModule) where
import Text.Parsec
import Text.Parsec.String(Parser)
import Text.Parsec.Language
import qualified Text.Parsec.Token as P
import Control.Applicative((<$>),(<*),(*>),(<*>))
import Gom.Sig
import Control.Monad.Identity(Identity)
import Data.List
defs :: ParsecT String u Identity Char-> LanguageDef u
defs start = javaStyle {
P.reservedOpNames = ["=","|",":"],
P.reservedNames = ["module","abstract","syntax","imports","implemented by"],
P.identStart = start
}
lexer, ulexer :: P.TokenParser a
lexer = P.makeTokenParser $ defs letter
ulexer = P.makeTokenParser $ defs upper
white :: Parser ()
parens :: Parser a -> Parser a
angles :: Parser a -> Parser a
ident :: Parser String
uident :: Parser String
res :: String -> Parser ()
resOp :: String -> Parser ()
comma :: Parser String
white = P.whiteSpace lexer
parens = P.parens lexer
angles = P.angles lexer
ident = P.identifier lexer
uident = P.identifier ulexer
res = P.reserved lexer
resOp = P.reservedOp lexer
comma = P.comma lexer
-- sorts defined by the user start with a capital letter
lhsSortidP :: Parser SortId
lhsSortidP = makeSortId <$> uident
<?> "sort name"
-- ... but not necessarily the imported ones
rhsSortidP :: Parser SortId
rhsSortidP = makeSortId <$> ident
<?> "sort name"
-- The code of this function is convoluted because
-- it reconstructs the string it parses instead of building
-- a datastructure. They may be a better way to do it with parsec.
classidP :: Parser ClassId
classidP = uncurry makeClassId <$> classidP' <?> "class name"
where classidP' = do
qname <- intercalate "." <$> ident `sepBy1` resOp "."
params <- option "" paramsP
return (qname,params)
paramsP = do
ps <- angles (classidP' `sepBy` comma)
return $ "<" ++ intercalate "," (map glue ps) ++ ">"
where glue = uncurry (++)
fieldidP :: Parser FieldId
fieldidP = makeFieldId <$> ident
<?> "field name"
ctoridP :: Parser CtorId
ctoridP = makeCtorId <$> ident
<?> "constructor name"
sigP :: Parser Module
sigP = Module <$> (res "module" *> ident)
<*> option [] (res "imports" *> rhsSortidP `sepBy` spaces)
<* res "abstract"
<* res "syntax"
<*> sortP `sepBy` spaces
<* eof
<?> "module definition"
sortP :: Parser SortDef
sortP = do n <- lhsSortidP
cn <- option Nothing $ Just <$> (res "implemented by" *> classidP)
resOp "="
c <- ctorP `sepBy` resOp "|"
return $ SortDef n cn c
<?> "sort definition"
ctorP :: Parser Ctor
ctorP = try variadicP <|> simpleP
<?> "constructor declaration"
variadicP :: Parser Ctor
variadicP = Variadic <$> ctoridP <*> parens (rhsSortidP <* resOp "*")
<?> "variadic constructor"
simpleP :: Parser Ctor
simpleP = Simple <$> ctoridP <*> parens (fieldP `sepBy` comma)
<?> "non-variadic constructor"
fieldP :: Parser (FieldId, SortId)
fieldP = do x <- fieldidP
resOp ":"
ty <- rhsSortidP
return (x,ty)
<?> "field declaration"
run :: Parser a -> String -> Either ParseError a
run p = parse (white *> p) ""
-- | Parses a gom module.
parseModule :: String -> Either ParseError Module
parseModule = run sigP