module Interfaces.FZSolutionParser (
MValue(..), Solution,
valueM,
intM, boolM, floatM, stringM, setM,
setRange, arrayM,
varName, simpleVarName, quotedVarName,
comment, comments,
defaultNameValuePair,
defaultUnsat, defaultSolution, defaultSolutions,
tryDefaultSolutions,
getDefaultSolutions, getDefaultSolutionsFromFile,
nameValuePair,
trySolutions, getSolutions
) 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 $ runParser (tryDefaultSolutions n) output
getDefaultSolutions :: Int -> String -> Either P.ParseError [Solution]
getDefaultSolutions = getSolutions tryDefaultSolutions
getSolutions :: (Int -> Parser [Solution]) -> Int -> String -> Either P.ParseError [Solution]
getSolutions p n = runParser (p n)
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
parse :: Parser a -> P.SourceName -> String -> Either P.ParseError a
parse = P.parse
try :: Parser a -> Parser a
try = P.try
unsatMSG = "=====UNSATISFIABLE====="
eoSMSG = "=========="
eosMSG = "----------"
runParser :: Parser a -> String -> Either P.ParseError a
runParser p = parse (p <* eof) ""
tryDefaultSolutions :: Int -> Parser [Solution]
tryDefaultSolutions = trySolutions takeSolutions defaultUnsat
trySolutions :: (Int -> Parser [Solution])
-> Parser String
-> Int
-> Parser [Solution]
trySolutions p u n = try $ (p n) <|> (u >> return [[]])
defaultUnsat :: Parser String
defaultUnsat = skipMany comment *> (string unsatMSG) <* endOfLine <* many comment
takeSolutions :: Int -> Parser [Solution]
takeSolutions n = take n <$> defaultSolutions
defaultSolutions :: Parser [Solution]
defaultSolutions = manyTill defaultSolution (string eoSMSG *> endOfLine)
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 = parse ((,) <$> p <*> leftOver) ""
where leftOver = manyTill anyToken eof