{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module What4.Protocol.SExp
( SExp(..)
, parseSExp
, stringToSExp
, parseNextWord
, asAtomList
, asNegAtomList
, skipSpaceOrNewline
) where
#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail( MonadFail )
#endif
import Control.Applicative
import Control.Monad (msum)
import Data.Attoparsec.Text
import Data.Char
import Data.Monoid
import Data.String
import Data.Text (Text)
import qualified Data.Text as Text
import Prelude hiding (takeWhile)
skipSpaceOrNewline :: Parser ()
skipSpaceOrNewline = skipWhile f
where f c = isSpace c || c == '\r' || c == '\n'
parseNextWord :: Parser Text
parseNextWord = do
skipSpaceOrNewline
mappend (takeWhile1 isAlphaNum) (fail "Unexpected end of stream.")
data SExp = SAtom Text
| SString Text
| SApp [SExp]
deriving (Eq, Ord, Show)
instance IsString SExp where
fromString = SAtom . Text.pack
isTokenChar :: Char -> Bool
isTokenChar '(' = False
isTokenChar ')' = False
isTokenChar '"' = False
isTokenChar c = not (isSpace c)
readToken :: Parser Text
readToken = takeWhile1 isTokenChar
parseSExp ::
Parser Text ->
Parser SExp
parseSExp readString = do
skipSpaceOrNewline
msum [ char '(' *> skipSpaceOrNewline *> (SApp <$> many (parseSExp readString)) <* skipSpaceOrNewline <* char ')'
, SString <$> readString
, SAtom <$> readToken
]
stringToSExp :: MonadFail m =>
Parser Text ->
String ->
m [SExp]
stringToSExp readString s = do
let parseSExpList = many (parseSExp readString) <* skipSpace <* endOfInput
case parseOnly parseSExpList (Text.pack s) of
Left e -> fail $ "stringToSExpr error: " ++ e
Right v -> return v
asNegAtomList :: SExp -> Maybe [(Bool,Text)]
asNegAtomList (SApp xs) = go xs
where
go [] = Just []
go (SAtom a : ys) = ((True,a):) <$> go ys
go (SApp [SAtom "not", SAtom a] : ys) = ((False,a):) <$> go ys
go _ = Nothing
asNegAtomList _ = Nothing
asAtomList :: SExp -> Maybe [Text]
asAtomList (SApp xs) = go xs
where
go [] = Just []
go (SAtom a:ys) = (a:) <$> go ys
go _ = Nothing
asAtomList _ = Nothing