diff options
author | Joey Hess <joey@kitenet.net> | 2011-09-18 16:36:30 -0400 |
---|---|---|
committer | Joey Hess <joey@kitenet.net> | 2011-09-18 16:36:30 -0400 |
commit | 3e15187ac1082bb086c79109c8b35dd8c20017ab (patch) | |
tree | 02f2943ae82abc0c3f45af551b7314c7c5bc12e5 /Utility/Matcher.hs | |
parent | 0e499e67be1ba32d30eb0a65537d2c6a0f4d6e05 (diff) |
move to Utility
Diffstat (limited to 'Utility/Matcher.hs')
-rw-r--r-- | Utility/Matcher.hs | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/Utility/Matcher.hs b/Utility/Matcher.hs new file mode 100644 index 000000000..0f589ec2c --- /dev/null +++ b/Utility/Matcher.hs @@ -0,0 +1,82 @@ +{- A generic matcher. + - + - Can be used to check if a user-supplied condition, + - like "foo and ( bar or not baz )" matches. The condition must already + - be tokenized, and can contain arbitrary operations. + - + - If operations are not separated by and/or, they are defaulted to being + - anded together, so "foo bar baz" all must match. + - + - Is forgiving about misplaced closing parens, so "foo and (bar or baz" + - will be handled, as will "foo and ( bar or baz ) )" + - + - Copyright 2011 Joey Hess <joey@kitenet.net> + - + - Licensed under the GNU GPL version 3 or higher. + -} + +module Utility.Matcher ( + Token(..), + generate, + match, + run +) where + +import Control.Monad + +{- A Token can either be a single word, or an Operation of an arbitrary type. -} +data Token op = Token String | Operation op + deriving (Show, Eq) + +data Matcher op = Any + | And (Matcher op) (Matcher op) + | Or (Matcher op) (Matcher op) + | Not (Matcher op) + | Op op + deriving (Show, Eq) + +{- Converts a list of Tokens into a Matcher. -} +generate :: [Token op] -> Matcher op +generate ts = generate' Any ts +generate' :: Matcher op -> [Token op] -> Matcher op +generate' m [] = m +generate' m ts = generate' m' rest + where + (m', rest) = consume m ts + +{- Consumes one or more Tokens, constructs a new Matcher, + - and returns unconsumed Tokens. -} +consume :: Matcher op -> [Token op] -> (Matcher op, [Token op]) +consume m [] = (m, []) +consume m ((Operation o):ts) = (m `And` Op o, ts) +consume m ((Token t):ts) + | t == "and" = cont $ m `And` next + | t == "or" = cont $ m `Or` next + | t == "not" = cont $ m `And` (Not next) + | t == "(" = let (n, r) = consume next rest in (m `And` n, r) + | t == ")" = (m, ts) + | otherwise = (m, ts) -- ignore unknown token + where + (next, rest) = consume Any ts + cont v = (v, rest) + +{- Checks if a Matcher matches, using a supplied function to check + - the value of Operations. -} +match :: (op -> Bool) -> Matcher op -> Bool +match a = go + where + go Any = True + go (And m1 m2) = go m1 && go m2 + go (Or m1 m2) = go m1 || go m2 + go (Not m1) = not (go m1) + go (Op v) = a v + +{- Runs a monadic Matcher, where Operations are actions in the monad. -} +run :: Monad m => Matcher (m Bool) -> m Bool +run = go + where + go Any = return True + go (And m1 m2) = liftM2 (&&) (go m1) (go m2) + go (Or m1 m2) = liftM2 (||) (go m1) (go m2) + go (Not m1) = liftM not (go m1) + go (Op o) = o -- run o |