184 lines
5.4 KiB
Haskell
184 lines
5.4 KiB
Haskell
|
-- | Handling simple submission conditions operating on tags and parameters
|
||
|
module Data.SubmissionConditions where
|
||
|
|
||
|
import Import hiding (many, try)
|
||
|
|
||
|
import Control.Monad (void)
|
||
|
import Data.Void
|
||
|
import Text.Megaparsec
|
||
|
import Text.Megaparsec.Char
|
||
|
import Text.Megaparsec.Expr
|
||
|
import qualified Text.Megaparsec.Char.Lexer as L
|
||
|
import Data.Text as T hiding (empty)
|
||
|
import Text.Read (readMaybe)
|
||
|
|
||
|
import qualified Data.Map as Map
|
||
|
|
||
|
data VariantEntry = VariantEntry {
|
||
|
variantEntryTags :: [Tag],
|
||
|
variantEntryParams :: [Parameter]
|
||
|
}
|
||
|
|
||
|
type VariantEntryMap = Map Text Text
|
||
|
|
||
|
entryToMap :: VariantEntry -> VariantEntryMap
|
||
|
entryToMap entry = Map.fromList ((Import.map (\t -> (tagName t, "TAG")) tags)
|
||
|
++ (Import.map (\p -> (parameterName p, parameterValue p)) params))
|
||
|
where tags = variantEntryTags entry
|
||
|
params = variantEntryParams entry
|
||
|
|
||
|
data SubmissionCondition =
|
||
|
Simple SubmissionSimpleCondition
|
||
|
| Neg SubmissionCondition
|
||
|
| BBinary BBinaryOp SubmissionCondition SubmissionCondition
|
||
|
deriving (Show, Eq)
|
||
|
|
||
|
data BBinaryOp = And | Or
|
||
|
deriving (Show, Eq)
|
||
|
|
||
|
data SubmissionSimpleCondition =
|
||
|
Existence ValueHolder
|
||
|
| SBinary Atom SBinaryOp Atom
|
||
|
deriving (Show, Eq)
|
||
|
|
||
|
data SBinaryOp = Equal | NotEqual | Less | Greater | LessOrEqual | GreaterOrEqual
|
||
|
deriving (Show, Eq)
|
||
|
|
||
|
data Atom =
|
||
|
ReadFrom ValueHolder
|
||
|
| IntegerAtom Integer
|
||
|
| DoubleAtom Double
|
||
|
| StringAtom Text
|
||
|
deriving (Show, Eq)
|
||
|
|
||
|
data ValueHolder = ValueHolder Text
|
||
|
deriving (Show, Eq)
|
||
|
|
||
|
type Parser = Parsec Void Text
|
||
|
|
||
|
spaceConsumer = L.space space1 empty empty
|
||
|
|
||
|
symbol :: Text -> Parser Text
|
||
|
symbol = L.symbol spaceConsumer
|
||
|
|
||
|
lexeme :: Parser a -> Parser a
|
||
|
lexeme = L.lexeme spaceConsumer
|
||
|
|
||
|
parens :: Parser a -> Parser a
|
||
|
parens = between (symbol "(") (symbol ")")
|
||
|
|
||
|
conditionP :: Parser SubmissionCondition
|
||
|
conditionP = makeExprParser bTerm bOperators
|
||
|
|
||
|
bTerm =
|
||
|
(parens conditionP)
|
||
|
<|>
|
||
|
(Simple <$> simpleConditionP)
|
||
|
|
||
|
bOperators :: [[Operator Parser SubmissionCondition]]
|
||
|
bOperators =
|
||
|
[ [Prefix (Neg <$ symbol "!") ]
|
||
|
, [InfixL (BBinary And <$ symbol "&&")
|
||
|
, InfixL (BBinary Or <$ symbol "||") ]
|
||
|
]
|
||
|
|
||
|
simpleConditionP :: Parser SubmissionSimpleCondition
|
||
|
simpleConditionP =
|
||
|
(try (SBinary <$> valP <*> sopP <*> valP))
|
||
|
<|>
|
||
|
(Existence <$> valueHolderP)
|
||
|
|
||
|
sopP :: Parser SBinaryOp
|
||
|
sopP =
|
||
|
(try (symbol "==" *> pure Equal))
|
||
|
<|>
|
||
|
symbol "=" *> pure Equal
|
||
|
<|>
|
||
|
symbol "!=" *> pure NotEqual
|
||
|
<|>
|
||
|
symbol "/=" *> pure NotEqual
|
||
|
<|>
|
||
|
(try (symbol "<>" *> pure NotEqual))
|
||
|
<|>
|
||
|
(try (symbol "<=" *> pure LessOrEqual))
|
||
|
<|>
|
||
|
(try (symbol ">=" *> pure GreaterOrEqual))
|
||
|
<|>
|
||
|
symbol "<" *> pure Less
|
||
|
<|>
|
||
|
symbol ">" *> pure Greater
|
||
|
|
||
|
valP :: Parser Atom
|
||
|
valP =
|
||
|
(ReadFrom <$> valueHolderP)
|
||
|
<|>
|
||
|
(try $ lexeme $ DoubleAtom <$> L.signed spaceConsumer L.float)
|
||
|
<|>
|
||
|
(lexeme $ IntegerAtom <$> L.signed spaceConsumer L.decimal)
|
||
|
<|>
|
||
|
(StringAtom <$> literalP)
|
||
|
|
||
|
literalP :: Parser Text
|
||
|
literalP = quotedP '"' <|> quotedP '\''
|
||
|
|
||
|
quotedP :: Char -> Parser Text
|
||
|
quotedP q = lexeme (T.pack <$> ((char q) *> (many $ notChar q) <* (char q)))
|
||
|
|
||
|
valueHolderP :: Parser ValueHolder
|
||
|
valueHolderP = lexeme $ ValueHolder <$> T.pack <$> ((:) <$> letterChar <*> many (alphaNumChar <|> char '-'))
|
||
|
|
||
|
parseCondition :: Text -> Maybe (SubmissionCondition)
|
||
|
parseCondition = parseMaybe (spaceConsumer *> conditionP)
|
||
|
|
||
|
checkCondition :: Maybe SubmissionCondition -> VariantEntry -> Bool
|
||
|
checkCondition mCondition = checkCondition' mCondition . entryToMap
|
||
|
|
||
|
checkCondition' :: Maybe SubmissionCondition -> VariantEntryMap -> Bool
|
||
|
checkCondition' Nothing _ = False
|
||
|
checkCondition' (Just condition) entry = checkCondition'' condition entry
|
||
|
|
||
|
checkCondition'' :: SubmissionCondition -> VariantEntryMap -> Bool
|
||
|
checkCondition'' (Simple simpleCondition) entry = checkSimpleCondition simpleCondition entry
|
||
|
checkCondition'' (Neg condition) entry = not (checkCondition'' (condition) entry)
|
||
|
checkCondition'' (BBinary op condA condB) entry =
|
||
|
(rfy op) (checkCondition'' condA entry) (checkCondition'' condB entry)
|
||
|
where rfy And = (&&)
|
||
|
rfy Or = (||)
|
||
|
|
||
|
data Val = IntegerVal Integer | DoubleVal Double | StringVal Text | NoVal
|
||
|
|
||
|
checkSimpleCondition :: SubmissionSimpleCondition -> VariantEntryMap -> Bool
|
||
|
checkSimpleCondition (Existence (ValueHolder holder)) entry = Map.member holder entry
|
||
|
checkSimpleCondition (SBinary valA op valB) entry = rop (getValue valA entry) (getValue valB entry)
|
||
|
where rop NoVal _ = False -- no value always brings false value
|
||
|
rop _ NoVal = False -- therefore (x < y) is not equal to !(x >= y)
|
||
|
|
||
|
rop (IntegerVal x) (IntegerVal y) = (rfy op) x y
|
||
|
rop (IntegerVal x) (DoubleVal y) = (rfy op) (fromIntegral x) y
|
||
|
rop (DoubleVal x) (IntegerVal y) = (rfy op) x (fromIntegral y)
|
||
|
rop (DoubleVal x) (DoubleVal y) = (rfy op) x y
|
||
|
rop (StringVal x) (StringVal y) = (rfy op) x y
|
||
|
rop _ _ = False
|
||
|
|
||
|
rfy :: Ord a => SBinaryOp -> a -> a -> Bool
|
||
|
rfy Equal = (==)
|
||
|
rfy NotEqual = (/=)
|
||
|
rfy Less = (<)
|
||
|
rfy Greater = (>)
|
||
|
rfy LessOrEqual = (<=)
|
||
|
rfy GreaterOrEqual = (>=)
|
||
|
|
||
|
getValue :: Atom -> VariantEntryMap -> Val
|
||
|
getValue (IntegerAtom i) _ = IntegerVal i
|
||
|
getValue (DoubleAtom d) _ = DoubleVal d
|
||
|
getValue (StringAtom t) _ = StringVal t
|
||
|
getValue (ReadFrom (ValueHolder holder)) entry = case Map.lookup holder entry of
|
||
|
Just v ->
|
||
|
let s = T.unpack v
|
||
|
in case readMaybe s of
|
||
|
Just i -> IntegerVal i
|
||
|
Nothing -> case readMaybe s of
|
||
|
Just d -> DoubleVal d
|
||
|
Nothing -> StringVal v
|
||
|
Nothing -> NoVal
|