penny-lib-0.2.0.0: Penny/Zinc/Parser/Filter.hs
module Penny.Zinc.Parser.Filter (
parseFilter
, Error(LibertyError, TokenParseError)
, NeedsHelp(NeedsHelp)
, Result(Result, resultFactory, resultSensitive, sorterFilterer)
) where
import Control.Applicative ((<|>), (<$>), Applicative, pure, many)
import Control.Monad ((>=>))
import qualified Control.Monad.Exception.Synchronous as Ex
import Data.Monoid (mempty, mappend)
import Data.Text (Text)
import qualified Text.Matchers.Text as M
import qualified System.Console.MultiArg.Combinator as C
import System.Console.MultiArg.Prim (Parser)
import qualified Penny.Copper as Cop
import qualified Penny.Lincoln as L
import qualified Penny.Liberty as Ly
import qualified Penny.Liberty.Expressions as X
import qualified Penny.Zinc.Parser.Defaults as D
import qualified Penny.Zinc.Parser.Defaults as Defaults
-- | Parses all filtering options. Returns a parser that contains an
-- Exception if some error occurred after parsing the options, or a
-- Success with a result if the parse was successful.
parseFilter ::
Defaults.T
-> Parser (Ex.Exceptional Error (Either NeedsHelp Result))
parseFilter d = fmap f (many parser) where
f ls =
let k = foldl (>=>) return ls
in case k (newState d) of
Ex.Success st' ->
if help st'
then return . Left $ NeedsHelp
else
case Ly.parsePredicate . tokens $ st' of
Nothing -> Ex.throw TokenParseError
Just pdct ->
let fn = Ly.xactionsToFiltered pdct
(postFilter st') (orderer st')
r = Result { resultFactory = factory st'
, resultSensitive = sensitive st'
, sorterFilterer = fn }
in return . Right $ r
Ex.Exception e -> Ex.Exception e
data Error = LibertyError Ly.Error
| TokenParseError
deriving Show
-- | Returned if the user requested help.
data NeedsHelp = NeedsHelp
deriving Show
-- | Indicates the result of a successful parse of filtering options.
data Result =
Result { resultFactory :: M.CaseSensitive
-> Text -> Ex.Exceptional Text (Text -> Bool)
-- ^ The factory indicated, so that it can be used in
-- subsequent parses of the same command line.
, resultSensitive :: M.CaseSensitive
-- ^ Indicated case sensitivity, so that it can be used in
-- subsequent parses of the command line.
, sorterFilterer :: [L.Transaction] -> [L.Box Ly.LibertyMeta]
-- ^ Applied to a list of Transaction, will sort and filter
-- the transactions and assign them LibertyMeta.
}
data State =
State { sensitive :: M.CaseSensitive
, factory :: M.CaseSensitive
-> Text -> Ex.Exceptional Text (Text -> Bool)
, tokens :: [X.Token (L.PostFam -> Bool)]
, postFilter :: [Ly.PostFilterFn]
, orderer :: Ly.Orderer
, help :: Bool
, currentTime :: L.DateTime
, defaultTimeZone :: Cop.DefaultTimeZone
, radGroup :: Cop.RadGroup }
newState ::
Defaults.T
-> State
newState d =
State { sensitive = D.sensitive d
, factory = D.factory d
, tokens = []
, postFilter = []
, orderer = mempty
, help = False
, currentTime = D.currentTime d
, defaultTimeZone = D.defaultTimeZone d
, radGroup = D.radGroup d }
parser :: Parser (State -> Ex.Exceptional Error State)
parser =
operand
<|> parsePostFilter
<|> impurify parseMatcherSelect
<|> impurify parseCaseSelect
<|> impurify parseOperator
<|> parseSort
<|> impurify parseHelp
option :: [String] -> [Char] -> C.ArgSpec a -> Parser a
option ss cs a = C.parseOption [C.OptSpec ss cs a]
operand :: Parser (State -> Ex.Exceptional Error State)
operand = f <$> Ly.parseOperand
where
f lyFn =
let g st =
let r = lyFn (currentTime st) (defaultTimeZone st)
(radGroup st) (sensitive st) (factory st)
in case r of
Ex.Exception e -> Ex.throw . LibertyError $ e
Ex.Success (X.Operand o) ->
let tok' = tokens st ++ [X.TokOperand o]
in return st { tokens = tok' }
in g
parsePostFilter :: Parser (State -> Ex.Exceptional Error State)
parsePostFilter = f <$> Ly.parsePostFilter
where
f lyResult =
let g st = case lyResult of
Ex.Exception e -> Ex.throw . LibertyError $ e
Ex.Success pf ->
let ls' = postFilter st ++ [pf]
in return st { postFilter = ls' }
in g
impurify ::
(Functor f, Applicative a)
=> f (b -> b)
-> f (b -> a b)
impurify = fmap (pure .)
parseMatcherSelect :: Parser (State -> State)
parseMatcherSelect = f <$> Ly.parseMatcherSelect
where
f fty = g
where
g st = st { factory = fty }
parseCaseSelect :: Parser (State -> State)
parseCaseSelect = f <$> Ly.parseCaseSelect
where
f sel = g
where
g st = st { sensitive = sel }
parseOperator :: Parser (State -> State)
parseOperator = f <$> Ly.parseOperator
where
f tok = g
where
g st = st { tokens = tokens st ++ [tok] }
parseSort :: Parser (State -> Ex.Exceptional Error State)
parseSort = f <$> Ly.parseSort
where
f exOrd = g
where
g st = case exOrd of
Ex.Exception e -> Ex.throw . LibertyError $ e
Ex.Success o ->
return st { orderer = mappend o (orderer st) }
parseHelp :: Parser (State -> State)
parseHelp = option ["help"] ['h'] (C.NoArg f)
where
f st = st { help = True }
{-
wrapLiberty ::
DefaultTimeZone
-> DateTime
-> RadGroup
-> State
-> ParserE Error State
wrapLiberty dtz dt rg st = let
toLibSt = LF.State { LF.sensitive = sensitive st
, LF.factory = factory st
, LF.tokens = tokens st
, LF.postFilter = postFilter st }
fromLibSt libSt = State { sensitive = LF.sensitive libSt
, factory = LF.factory libSt
, tokens = LF.tokens libSt
, postFilter = LF.postFilter libSt
, orderer = orderer st
, help = help st }
in fromLibSt <$> LF.parseOption dtz dt rg toLibSt
wrapOrderer :: State -> ParserE Error State
wrapOrderer st = mkSt <$> S.sort where
mkSt o = st { orderer = o `mappend` (orderer st) }
helpOpt :: ParserE Error ()
helpOpt = do
let lo = makeLongOpt . pack $ "help"
so = makeShortOpt 'h'
_ <- mixedNoArg lo [] [so]
return ()
wrapHelp :: State -> ParserE Error State
wrapHelp st = (\_ -> st { help = Help }) <$> helpOpt
parseOption ::
DefaultTimeZone
-> DateTime
-> RadGroup
-> State
-> ParserE Error State
parseOption dtz dt rg st =
wrapLiberty dtz dt rg st
<|> wrapOrderer st
<|> wrapHelp st
parseOptions ::
DefaultTimeZone
-> DateTime
-> RadGroup
-> State
-> ParserE Error State
parseOptions dtz dt rg st =
option st $ do
rs <- runUntilFailure (parseOption dtz dt rg) st
if null rs then return st else return (last rs)
parseFilter ::
DefaultTimeZone
-> DateTime
-> RadGroup
-> ParserE Error (Either NeedsHelp Result)
parseFilter dtz dt rg = do
st' <- parseOptions dtz dt rg newState
case help st' of
Help -> return . Left $ NeedsHelp
NoHelp -> do
p <- case Oo.getPredicate (tokens st') of
Just pr -> return pr
Nothing -> throw E.BadExpression
let f = sortFilterAndPostFilter (orderer st') p (postFilter st')
r = Result { resultFactory = factory st'
, resultSensitive = sensitive st'
, sorterFilterer = f }
return . Right $ r
sortFilterAndPostFilter ::
S.Orderer
-> (T.PostingInfo -> Bool)
-> ([T.PostingInfo] -> [T.PostingInfo])
-> [PostingBox] -> [T.PostingInfo]
sortFilterAndPostFilter o p pf =
pf
. filter p
. PSq.sortedPostingInfos o
-}