{-# LANGUAGE BangPatterns, OverloadedStrings #-} {-# OPTIONS_GHC -Wall #-} ----------------------------------------------------------------------------- -- | -- Module : Data.PseudoBoolean.Attoparsec -- Copyright : (c) Masahiro Sakai 2011-2015 -- License : BSD-style -- -- Maintainer : masahiro.sakai@gmail.com -- Portability : non-portable (BangPatterns, OverloadedStrings) -- -- A parser library for OPB\/WBO files used in pseudo boolean competition. -- -- References: -- -- * Input/Output Format and Solver Requirements for the Competitions of -- Pseudo-Boolean Solvers -- -- ----------------------------------------------------------------------------- module Data.PseudoBoolean.Attoparsec ( -- * Parsing OPB files opbParser , parseOPBByteString , parseOPBFile -- * Parsing WBO files , wboParser , parseWBOByteString , parseWBOFile ) where import Prelude hiding (sum) import Control.Applicative ((<|>), (<*)) import Control.Monad import Data.Attoparsec.ByteString.Char8 hiding (isDigit) import qualified Data.Attoparsec.ByteString.Lazy as L import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy as BSLazy import Data.Char import Data.Maybe import Data.PseudoBoolean.Types import Data.PseudoBoolean.Internal.TextUtil -- | Parser for OPB files opbParser :: Parser Formula opbParser = formula -- | Parser for WBO files wboParser :: Parser SoftFormula wboParser = softformula -- ::= [] formula :: Parser Formula formula = do h <- optionMaybe hint sequence_of_comments obj <- optionMaybe objective cs <- sequence_of_comments_or_constraints return $ Formula { pbObjectiveFunction = obj , pbConstraints = cs , pbNumVars = fromMaybe (pbComputeNumVars obj cs) (fmap fst h) , pbNumConstraints = fromMaybe (length cs) (fmap snd h) } hint :: Parser (Int,Int) hint = try $ do _ <- char '*' zeroOrMoreSpace _ <- string "#variable=" zeroOrMoreSpace nv <- unsigned_integer oneOrMoreSpace _ <- string "#constraint=" zeroOrMoreSpace nc <- unsigned_integer _ <- manyTill anyChar eol return (fromIntegral nv, fromIntegral nc) -- ::= [] sequence_of_comments :: Parser () sequence_of_comments = skipMany comment -- XXX: we allow empty sequence -- ::= "*" comment :: Parser () comment = do _ <- char '*' _ <- manyTill anyChar eol return () -- ::= [] sequence_of_comments_or_constraints :: Parser [Constraint] sequence_of_comments_or_constraints = do xs <- many1 comment_or_constraint return $ catMaybes xs -- ::= | comment_or_constraint :: Parser (Maybe Constraint) comment_or_constraint = (comment >> return Nothing) <|> (liftM Just constraint) -- ::= "min:" ";" objective :: Parser Sum objective = do _ <- string "min:" zeroOrMoreSpace obj <- sum _ <- char ';' eol return obj -- ::= ";" constraint :: Parser Constraint constraint = do lhs <- sum op <- relational_operator zeroOrMoreSpace rhs <- integer zeroOrMoreSpace semi return (lhs, op, rhs) -- ::= | sum :: Parser Sum sum = many1 weightedterm -- ::= weightedterm :: Parser WeightedTerm weightedterm = do w <- integer oneOrMoreSpace t <- term oneOrMoreSpace return (w,t) -- ::= | "+" | "-" integer :: Parser Integer integer = msum [ unsigned_integer , char '+' >> unsigned_integer , char '-' >> liftM negate unsigned_integer ] -- ::= | unsigned_integer :: Parser Integer unsigned_integer = do ds <- takeWhile1 isDigit return $! readUnsignedInteger $ BS.unpack ds -- ::= ">=" | "=" relational_operator :: Parser Op relational_operator = (string ">=" >> return Ge) <|> (string "=" >> return Eq) -- ::= "x" variablename :: Parser Var variablename = do _ <- char 'x' i <- unsigned_integer return $! fromIntegral i -- ::= " " [] oneOrMoreSpace :: Parser () oneOrMoreSpace = skipMany1 (char ' ') -- ::= [" " ] zeroOrMoreSpace :: Parser () zeroOrMoreSpace = skipMany (char ' ') eol :: Parser () eol = char '\n' >> return () semi :: Parser () semi = char ';' >> eol {- For linear pseudo-Boolean instances, is defined as ::= For non-linear instances, is defined as ::= -} term :: Parser Term term = oneOrMoreLiterals -- ::= | oneOrMoreLiterals :: Parser [Lit] oneOrMoreLiterals = do l <- literal mplus (try $ oneOrMoreSpace >> liftM (l:) (oneOrMoreLiterals)) (return [l]) -- Note that we cannot use sepBy1. -- In "p `sepBy1` q", p should success whenever q success. -- But it's not the case here. -- ::= | "~" literal :: Parser Lit literal = variablename <|> (char '~' >> liftM negate variablename) -- | Parse a OPB format string containing pseudo boolean problem. parseOPBByteString :: BSLazy.ByteString -> Either String Formula parseOPBByteString s = L.eitherResult $ L.parse (formula <* endOfInput) s -- | Parse a OPB file containing pseudo boolean problem. parseOPBFile :: FilePath -> IO (Either String Formula) parseOPBFile fname = do s <- BSLazy.readFile fname return $ parseOPBByteString s -- ::= softformula :: Parser SoftFormula softformula = do h <- optionMaybe hint sequence_of_comments top <- softheader cs <- wbo_sequence_of_comments_or_constraints return $ SoftFormula { wboTopCost = top , wboConstraints = cs , wboNumVars = fromMaybe (wboComputeNumVars cs) (fmap fst h) , wboNumConstraints = fromMaybe (length cs) (fmap snd h) } -- ::= "soft:" [] ";" softheader :: Parser (Maybe Integer) softheader = do _ <- string "soft:" zeroOrMoreSpace -- XXX top <- optionMaybe unsigned_integer zeroOrMoreSpace -- XXX semi return top -- ::= [] wbo_sequence_of_comments_or_constraints :: Parser [SoftConstraint] wbo_sequence_of_comments_or_constraints = do xs <- many1 wbo_comment_or_constraint return $ catMaybes xs -- ::= || wbo_comment_or_constraint :: Parser (Maybe SoftConstraint) wbo_comment_or_constraint = (comment >> return Nothing) <|> m where m = liftM Just $ (constraint >>= \c -> return (Nothing, c)) <|> softconstraint -- ::= "[" "]" softconstraint :: Parser SoftConstraint softconstraint = do _ <- char '[' zeroOrMoreSpace cost <- unsigned_integer zeroOrMoreSpace _ <- char ']' zeroOrMoreSpace -- XXX c <- constraint return (Just cost, c) -- | Parse a WBO format string containing weighted boolean optimization problem. parseWBOByteString :: BSLazy.ByteString -> Either String SoftFormula parseWBOByteString s = L.eitherResult $ L.parse (softformula <* endOfInput) s -- | Parse a WBO file containing weighted boolean optimization problem. parseWBOFile :: FilePath -> IO (Either String SoftFormula) parseWBOFile fname = do s <- BSLazy.readFile fname return $ parseWBOByteString s optionMaybe :: Parser a -> Parser (Maybe a) optionMaybe p = option Nothing (liftM Just p)