module Interfaces.FZSolutionParser (
MValue(..), Solution,
valueM,
intM, boolM, floatM, stringM, setM,
setRange, arrayM,
varName, simpleVarName, quotedVarName,
comment, comments,
defaultNameValuePair,
defaultUnsat, defaultSolution,
trySolutionsDefault,
getAllSolutionsDefault, getDefaultSolutionsFromFile,
getAllSolutions, trySolutions,
nameValuePair,
allSolutions, takeSolutionsWithParser
) where
import Data.Char
import Control.Applicative
import Data.Set (Set, fromDistinctAscList)
import qualified Text.Parsec as P
import qualified Text.Parsec.Char as C
import Text.Parsec.String (Parser)
type Solution = [(String, MValue)]
data MValue = MError String
| MInt Int
| MFloat Float
| MBool Bool
| MString String
| MArray [MValue]
| MSet (Set MValue)
deriving Show
getDefaultSolutionsFromFile :: FilePath -> Int -> IO (Either P.ParseError [Solution])
getDefaultSolutionsFromFile path n = do
output <- readFile path
return $ getAllSolutionsDefault output
getAllSolutions :: Parser [Solution] -> String -> Either P.ParseError [Solution]
getAllSolutions = runParser
getAllSolutionsDefault :: String -> Either P.ParseError [Solution]
getAllSolutionsDefault = getAllSolutions trySolutionsDefault
takeSolutionsWithParser :: (Int -> Parser [Solution]) -> Int -> String -> Either P.ParseError [Solution]
takeSolutionsWithParser p n = runParser (p n)
allSolutions' :: Parser [Solution] -> String -> Either P.ParseError [Solution]
allSolutions' = runParser
digit :: Parser Char
digit = C.digit
anyChar :: Parser Char
anyChar = C.anyChar
char :: Char -> Parser Char
char = C.char
sepBy :: Parser a -> Parser b -> Parser [a]
sepBy = P.sepBy
between :: Parser a -> Parser b -> Parser c -> Parser c
between = P.between
manyTill :: Parser a -> Parser b -> Parser [a]
manyTill = P.manyTill
many1 :: Parser a -> Parser [a]
many1 = P.many1
skipMany :: Parser a -> Parser ()
skipMany = P.skipMany
anyToken = P.anyToken
eof :: Parser ()
eof = P.eof
endOfLine :: Parser Char
endOfLine = C.endOfLine
string :: String -> Parser String
string = C.string
spaces :: Parser ()
spaces = C.spaces
parseAll :: Parser a -> P.SourceName -> String -> Either P.ParseError a
parseAll = P.parse
count :: Int -> Parser a -> Parser [a]
count = P.count
try :: Parser a -> Parser a
try = P.try
unsatMSG = "=====UNSATISFIABLE====="
eoSMSG = "=========="
eosMSG = "----------"
runParser :: Parser a -> String -> Either P.ParseError a
runParser p = parseAll (p <* eof) ""
trySolutionsDefault :: Parser [Solution]
trySolutionsDefault = trySolutions allSolutionsDefault defaultUnsat
trySolutions :: Parser [Solution]
-> Parser String
-> Parser [Solution]
trySolutions p u = try $ p <|> (u >> return [[]])
defaultUnsat :: Parser String
defaultUnsat = skipMany comment *> (string unsatMSG) <* endOfLine <* many comment
takeSolutions :: Parser Solution -> Int -> Parser [Solution]
takeSolutions p n = case (n > 0) of
True -> count n p
_ -> allSolutions p
takeSolutionsDefault :: Int -> Parser [Solution]
takeSolutionsDefault = takeSolutions defaultSolution
allSolutions :: Parser Solution -> Parser [Solution]
allSolutions p = manyTill p (optional (string eoSMSG *> endOfLine) *> eof)
allSolutionsDefault :: Parser [Solution]
allSolutionsDefault = allSolutions defaultSolution
defaultSolution :: Parser Solution
defaultSolution = P.many (comments *> defaultNameValuePair)
<* string eosMSG <* endOfLine
comment :: Parser String
comment = char '%' *> spaces *> (manyTill anyToken endOfLine)
comments :: Parser String
comments = unlines <$> P.many comment
defaultNameValuePair :: Parser (String, MValue)
defaultNameValuePair = nameValuePair (spaces *> (string "=") <* spaces)
<* ((: []) <$> (char ';' *> endOfLine))
nameValuePair :: Parser String
-> Parser (String, MValue)
nameValuePair p1 = do
name <- varName
p1
value <- valueM
return (name, value)
simpleVarName :: Parser String
simpleVarName = do
first <- C.letter
rest <- P.many (C.alphaNum <|> char '_')
return (first : rest)
quotedVarName :: Parser String
quotedVarName = do
lq <- char '\''
name <- manyTill anyChar (char '\'')
return (lq : (name ++ "\'"))
varName :: Parser String
varName = simpleVarName <|> quotedVarName
valueM :: Parser MValue
valueM = try floatM <|> intM <|> boolM <|> (setM scalar) <|> (arrayM scalar) <|> stringM
intM :: Parser MValue
intM = MInt <$> int
boolM :: Parser MValue
boolM = MBool <$> bool
floatM :: Parser MValue
floatM = MFloat <$> float
stringM :: Parser MValue
stringM = MString <$> (string "\"" *> manyTill anyChar (string "\""))
setM :: Parser MValue -> Parser MValue
setM p = (MSet <$> fromDistinctAscList <$> (set p)) <|> setRange
int :: Parser Int
int = (char '-' >> opposite ) <|> natural
bool :: Parser Bool
bool = string "true" >> return True <|> (string "false" >> return False)
float :: Parser Float
float = do
ipart <- many1 digit
char '.'
dpart <- many1 digit
let a = read (ipart ++ "." ++ dpart) :: Float in
return a
set :: Parser a -> Parser [a]
set p = between (char '{') (char '}') (sepBy p (string "," >> spaces))
setRange :: Parser MValue
setRange = MSet <$> fromDistinctAscList <$> do
v1 <- int
string ".."
v2 <- int
return (map MInt (take (v2 v1 + 1) (iterate ((+) 1) v1)))
arrayM :: Parser MValue -> Parser MValue
arrayM p = do
string "array"
manyTill anyChar (char '(')
ls <- arraySizes
es <- extract p
string ")"
return (fixDims ls es)
natural :: Parser Int
natural = P.chainl1 digitValue ascendDecimal
opposite :: Parser Int
opposite = (0 ) <$> natural
digitValue :: Parser Int
digitValue = do
d <- digit
return $ ord(d) ord('0')
ascendDecimal :: Parser (Int -> Int -> Int)
ascendDecimal = do
return $ \x y -> x*10 + y
indexRange :: Parser Int
indexRange = do
a <- int
string ".."
b <- int
return (b a + 1)
arraySizes :: Parser [Int]
arraySizes = P.sepEndBy1 indexRange (string "," >> spaces)
extract :: Parser MValue -> Parser [MValue]
extract p = between (char '[') (char ']') (sepBy p (string "," >> spaces))
fixDims :: [Int] -> [MValue] -> MValue
fixDims [] _ = MError "Array dimensions error: fixDims applied on empty list"
fixDims [d] ms = MArray $ ms
fixDims ds ms = fixDims (init ds) (fix1Dim (last ds) ms)
fix1Dim :: Int -> [MValue] -> [MValue]
fix1Dim _ [] = []
fix1Dim d ms = MArray (take d ms) : (fix1Dim d (drop d ms))
scalar :: Parser MValue
scalar = try floatM <|> intM <|> boolM <|> stringM
parseWithLeftOver :: Parser a -> String -> Either P.ParseError (a,String)
parseWithLeftOver p = parseAll ((,) <$> p <*> leftOver) ""
where leftOver = manyTill anyToken eof