{-# LANGUAGE BangPatterns, FlexibleContexts #-} {-# OPTIONS_GHC -Wall #-} ----------------------------------------------------------------------------- -- | -- Module : Data.PseudoBoolean.Parsec -- Copyright : (c) Masahiro Sakai 2011-2015 -- License : BSD-style -- -- Maintainer : masahiro.sakai@gmail.com -- Portability : non-portable (BangPatterns, FlexibleContexts) -- -- A parser library for OPB file and WBO files used in pseudo boolean competition. -- -- References: -- -- * Input/Output Format and Solver Requirements for the Competitions of -- Pseudo-Boolean Solvers -- -- ----------------------------------------------------------------------------- module Data.PseudoBoolean.Parsec ( -- * Parsing OPB files opbParser , parseOPBString , parseOPBByteString , parseOPBFile -- * Parsing WBO files , wboParser , parseWBOString , parseWBOByteString , parseWBOFile ) where import Prelude hiding (sum) import Control.Monad import Data.ByteString.Lazy (ByteString) import Data.Maybe import Text.Parsec import qualified Text.Parsec.ByteString.Lazy as ParsecBS import Data.PseudoBoolean.Types import Data.PseudoBoolean.Internal.TextUtil -- | Parser for OPB files opbParser :: Stream s m Char => ParsecT s u m Formula opbParser = formula -- | Parser for WBO files wboParser :: Stream s m Char => ParsecT s u m SoftFormula wboParser = softformula -- ::= [] formula :: Stream s m Char => ParsecT s u m 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 :: Stream s m Char => ParsecT s u m (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 :: Stream s m Char => ParsecT s u m () sequence_of_comments = skipMany comment -- XXX: we allow empty sequence -- ::= "*" comment :: Stream s m Char => ParsecT s u m () comment = do _ <- char '*' _ <- manyTill anyChar eol return () -- ::= [] sequence_of_comments_or_constraints :: Stream s m Char => ParsecT s u m [Constraint] sequence_of_comments_or_constraints = do xs <- many1 comment_or_constraint return $ catMaybes xs -- ::= | comment_or_constraint :: Stream s m Char => ParsecT s u m (Maybe Constraint) comment_or_constraint = (comment >> return Nothing) <|> (liftM Just constraint) -- ::= "min:" ";" objective :: Stream s m Char => ParsecT s u m Sum objective = do _ <- string "min:" zeroOrMoreSpace obj <- sum _ <- char ';' eol return obj -- ::= ";" constraint :: Stream s m Char => ParsecT s u m Constraint constraint = do lhs <- sum op <- relational_operator zeroOrMoreSpace rhs <- integer zeroOrMoreSpace semi return (lhs, op, rhs) -- ::= | sum :: Stream s m Char => ParsecT s u m Sum sum = many1 weightedterm -- ::= weightedterm :: Stream s m Char => ParsecT s u m WeightedTerm weightedterm = do w <- integer oneOrMoreSpace t <- term oneOrMoreSpace return (w,t) -- ::= | "+" | "-" integer :: Stream s m Char => ParsecT s u m Integer integer = msum [ unsigned_integer , char '+' >> unsigned_integer , char '-' >> liftM negate unsigned_integer ] -- ::= | unsigned_integer :: Stream s m Char => ParsecT s u m Integer unsigned_integer = do ds <- many1 digit return $! readUnsignedInteger ds -- ::= ">=" | "=" relational_operator :: Stream s m Char => ParsecT s u m Op relational_operator = (string ">=" >> return Ge) <|> (string "=" >> return Eq) -- ::= "x" variablename :: Stream s m Char => ParsecT s u m Var variablename = do _ <- char 'x' i <- unsigned_integer return $! fromIntegral i -- ::= " " [] oneOrMoreSpace :: Stream s m Char => ParsecT s u m () oneOrMoreSpace = skipMany1 (char ' ') -- ::= [" " ] zeroOrMoreSpace :: Stream s m Char => ParsecT s u m () zeroOrMoreSpace = skipMany (char ' ') eol :: Stream s m Char => ParsecT s u m () eol = char '\n' >> return () semi :: Stream s m Char => ParsecT s u m () semi = char ';' >> eol {- For linear pseudo-Boolean instances, is defined as ::= For non-linear instances, is defined as ::= -} term :: Stream s m Char => ParsecT s u m Term term = oneOrMoreLiterals -- ::= | oneOrMoreLiterals :: Stream s m Char => ParsecT s u m [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 :: Stream s m Char => ParsecT s u m Lit literal = variablename <|> (char '~' >> liftM negate variablename) -- | Parse a OPB format string containing pseudo boolean problem. parseOPBString :: SourceName -> String -> Either ParseError Formula parseOPBString = parse formula -- | Parse a OPB format lazy bytestring containing pseudo boolean problem. parseOPBByteString :: SourceName -> ByteString -> Either ParseError Formula parseOPBByteString = parse formula -- | Parse a OPB file containing pseudo boolean problem. parseOPBFile :: FilePath -> IO (Either ParseError Formula) parseOPBFile = ParsecBS.parseFromFile formula -- ::= softformula :: Stream s m Char => ParsecT s u m 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 :: Stream s m Char => ParsecT s u m (Maybe Integer) softheader = do _ <- string "soft:" zeroOrMoreSpace -- XXX top <- optionMaybe unsigned_integer zeroOrMoreSpace -- XXX semi return top -- ::= [] wbo_sequence_of_comments_or_constraints :: Stream s m Char => ParsecT s u m [SoftConstraint] wbo_sequence_of_comments_or_constraints = do xs <- many1 wbo_comment_or_constraint return $ catMaybes xs -- ::= || wbo_comment_or_constraint :: Stream s m Char => ParsecT s u m (Maybe SoftConstraint) wbo_comment_or_constraint = (comment >> return Nothing) <|> m where m = liftM Just $ (constraint >>= \c -> return (Nothing, c)) <|> softconstraint -- ::= "[" "]" softconstraint :: Stream s m Char => ParsecT s u m 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. parseWBOString :: SourceName -> String -> Either ParseError SoftFormula parseWBOString = parse softformula -- | Parse a WBO format lazy bytestring containing pseudo boolean problem. parseWBOByteString :: SourceName -> ByteString -> Either ParseError SoftFormula parseWBOByteString = parse softformula -- | Parse a WBO file containing weighted boolean optimization problem. parseWBOFile :: FilePath -> IO (Either ParseError SoftFormula) parseWBOFile = ParsecBS.parseFromFile softformula