haskell-exp-parser-0.1: src/Language/Haskell/ParseExp.hs
-- | Simple parser for a subset of Haskell expressions and patterns to the
-- TemplateHaskell AST
--
-- The following expressions are currently supported:
--
-- * Variables
-- * Integer and string literals
-- * Prefix function application
-- * Lists and tuples
--
-- The following patterns are currently supported:
--
-- * Variables
module Language.Haskell.ParseExp
( parseExp
, parsePat
) where
import Control.Monad
import Data.Char
import Language.Haskell.TH
import Text.ParserCombinators.ReadP
-- | Skip any amount of whitespace
skipSpace :: ReadP ()
skipSpace = void $ munch isSpace
-- | Check if a character is a valid non-initial character in a name (variable
-- or constructor)
nameChar :: Char -> Bool
nameChar c = isAlphaNum c || elem c ['\'','_']
-- | Parse a Haskell variable name
name :: ReadP Name
name = do
skipSpace
h <- get
guard ('a' <= h && h <= 'z')
rest <- munch nameChar
return $ mkName (h:rest)
-- | Parse a Haskell variable
variable :: ReadP Exp
variable = fmap VarE name
-- | Parse a Haskell Constructor
constructor :: ReadP Exp
constructor = do
skipSpace
h <- get
guard ('A' <= h && h <= 'Z')
rest <- munch nameChar
return $ ConE $ mkName (h:rest)
-- | Parse an integer
integer :: Bool -> ReadP Integer
integer first = do
c:_ <- look
guard (first || isNumber c)
readS_to_P reads
-- | Parse a Haskell literal
literal :: Bool -> ReadP Exp
literal first
= fmap (LitE . IntegerL) (integer first)
<++ fmap (LitE . CharL) (readS_to_P reads)
<++ fmap (LitE . StringL) (readS_to_P reads)
-- | Parse a comma-separated list of expressions
expressionList :: ReadP [Exp]
expressionList = expression `sepBy` char ','
-- | Parse a list expression
list :: ReadP Exp
list = fmap ListE $ between (char '[') (char ']') expressionList
-- | Parse a tuple expression
--
-- This also handles empty tuples ('()') and parenthesized expressions
tuple :: ReadP Exp
tuple = do
es <- between (char '(') (char ')') (skipSpace >> expressionList)
-- skipSpace needed to parse empty tuples with space inside
case es of
[] -> return $ ConE $ mkName "()"
[e] -> return e
_ -> return $ TupE es
-- | Parse an expression that is not an application
expPart :: Bool -> ReadP Exp
expPart first = do
skipSpace
pfail <++ variable
<++ constructor
<++ list
<++ literal first
<++ tuple
-- Must handle lists before literals, because the "['a']" is accepted as a
-- String literal
-- | Expression parser
expression :: ReadP Exp
expression = do
skipSpace
f <- expPart True
args <- many (expPart False)
let expr = foldl AppE f args
skipSpace
return expr
-- | Parse a Haskell expression (the supported subset is given above)
parseExp :: String -> Either String Exp
parseExp str = case [expr | (expr,"") <- readP_to_S expression str] of
[expr] -> return expr
_ -> fail $ "parseExp: cannot parse '" ++ str ++ "'"
++ " (parseExp only supports a limited subset of Haskell)"
-- | Parse a Haskell pattern (the supported subset is given above)
parsePat :: String -> Either String Pat
parsePat str = case [pat | (pat,"") <- readP_to_S name str] of
[pat] -> return (VarP pat)
_ -> fail $ "parsePat: cannot parse '" ++ str ++ "'"
++ " (parsePat only supports a limited subset of Haskell)"