module Data.TreeDiff.Parser (
exprParser
) where
import Control.Applicative (optional, (<|>))
import Data.Char (chr, isAlphaNum, isPunctuation, isSymbol)
import Prelude ()
import Prelude.Compat
import Text.Parser.Char
import Text.Parser.Combinators
import Text.Parser.Token
import Text.Parser.Token.Highlight
import Data.TreeDiff.Expr
import qualified Data.Map as Map
exprParser :: (Monad m, TokenParsing m) => m Expr
exprParser = apprecP <|> lstP
lstP :: forall m. (Monad m, TokenParsing m) => m Expr
lstP = Lst <$> brackets (commaSep exprParser)
<?> "list"
apprecP :: forall m. (Monad m, TokenParsing m) => m Expr
apprecP = do
r <- recP
case r of
Right e -> return e
Left n -> App n <$> many litP'
fieldP :: forall m. (Monad m, TokenParsing m) => m (FieldName, Expr)
fieldP = (,) <$> litP <* symbolic '=' <*> exprParser
litP :: forall m. (Monad m, TokenParsing m) => m String
litP = atomP <|> identP <|> stringP
recP :: forall m. (Monad m, TokenParsing m) => m (Either String Expr)
recP = mk <$> litP <*> optional (braces (commaSep fieldP)) where
mk n Nothing = Left n
mk n (Just fs) = Right (Rec n (Map.fromList fs))
litP' :: forall m. (Monad m, TokenParsing m) => m Expr
litP' = mk <$> recP <|> parens exprParser <|> lstP
where
mk (Left n) = App n []
mk (Right e) = e
identP :: forall m. (Monad m, TokenParsing m) => m String
identP = token (highlight Identifier lit) where
lit :: m [Char]
lit = (:) <$> firstLetter <*> many restLetter
<?> "identifier"
firstLetter :: m Char
firstLetter = satisfy (\c -> valid' c && c /= '-' && c /= '+')
restLetter :: m Char
restLetter = satisfy valid'
stringP :: forall m. (Monad m, TokenParsing m) => m String
stringP = token (highlight StringLiteral lit) where
lit :: m [Char]
lit = mk <$> between (char '"') (char '"' <?> "end of string") (many stringChar)
<?> "atom"
mk :: [[Char]] -> String
mk ss = "\"" ++ concat ss ++ "\""
stringChar :: m [Char]
stringChar = stringLetter <|> stringEscape
<?> "string character"
stringEscape :: m [Char]
stringEscape = (\x y -> [x,y]) <$> char '\\' <*> anyChar
stringLetter :: m [Char]
stringLetter = return <$> satisfy (\c -> c /= '\\' && c /= '"')
atomP :: forall m. (Monad m, TokenParsing m) => m String
atomP = token (highlight Symbol lit) where
lit :: m [Char]
lit = between (char '`') (char '`' <?> "end of atom") (many atomChar)
<?> "atom"
atomChar :: m Char
atomChar = atomLetter <|> atomEscape <|> char ' '
<?> "atom character"
atomEscape :: m Char
atomEscape = char '\\' *> (char '\\' <|> char '`' <|> escapedHex)
escapedHex :: m Char
escapedHex = chr . fromInteger <$> hexadecimal <* char ';'
atomLetter :: m Char
atomLetter = satisfy (\c -> c /= '\\' && c /= '`' && valid c)
valid :: Char -> Bool
valid c = isAlphaNum c || isSymbol c || isPunctuation c
valid' :: Char -> Bool
valid' c = valid c && c `notElem` "[](){}`\","