module Data.BoolExpr.Parser (-- * Parsing function parseBoolExpr -- * Language definition and components ,languageDef ,lexer ,identifier ,whiteSpace ,symbol ) where import Control.Monad import Control.Applicative hiding ((<|>)) import Data.BoolExpr import Text.ParserCombinators.Parsec import qualified Text.ParserCombinators.Parsec.Token as P {- | Parse a search query as a boolean tree using the following grammar. Note that this parser is parameterized over the parser of query simple terms (const). @ bt ::= bt AND bt | bt bt -- same as AND | bt OR bt | - bt | ( bt ) | const const ::= \ @ -} parseBoolExpr :: CharParser st a -> CharParser st (BoolExpr a) parseBoolExpr parseConst = disj where disj = conj `chainl1` orOp conj = factor `chainl1` andOp factor = parens disj <|> (((symbol "-" >> return BNot) <|> return id) `ap` (BConst `fmap` parseConst)) andOp = BAnd <$ option "" (symbol "AND") orOp = BOr <$ symbol "OR" -- | Underlying lexer of 'languageDef' lexer :: P.TokenParser st lexer = P.makeTokenParser languageDef -- | Shorthand for 'P.parens lexer'. parens :: CharParser st a -> CharParser st a parens = P.parens lexer -- | Shorthand for 'P.symbol' 'lexer'. symbol :: String -> CharParser st String symbol = P.symbol lexer -- | Shorthand for 'P.whiteSpace' 'lexer'. whiteSpace :: CharParser st () whiteSpace = P.whiteSpace lexer -- | Shorthand for 'P.identifier' 'lexer'. identifier :: CharParser st String identifier = P.identifier lexer wordLetter :: CharParser st Char wordLetter = alphaNum <|> oneOf "_:;`,~@.!#$%^&*=+?|\\{}[]<>" -- | Basic language definition for search queries. -- Reserved names are @\"AND\"@ @\"OR\"@ and @\"-\"@. -- Identifiers accepts almost every ASCII sequences without blanks nor @\'-\'@. languageDef :: P.LanguageDef st languageDef = P.LanguageDef { P.commentStart = "" , P.commentEnd = "" , P.commentLine = "" , P.nestedComments = True , P.identStart = wordLetter , P.identLetter = wordLetter <|> char '-' , P.opStart = mzero , P.opLetter = mzero , P.reservedOpNames= [] , P.reservedNames = ["AND", "OR", "-"] , P.caseSensitive = True }