-- | -- This module contains all the functions you need to implement a Relapse expression. module Data.Katydid.Relapse.Expr ( Desc(..), mkDesc , AnyExpr(..), AnyFunc(..) , Expr(..), Func, params, name, hasVar , hashWithName, hashList, hashString , evalConst, isConst , assertArgs1, assertArgs2 , mkBoolExpr, mkIntExpr, mkStringExpr, mkDoubleExpr, mkBytesExpr, mkUintExpr , assertBool, assertInt, assertString, assertDouble, assertBytes, assertUint , boolExpr, intExpr, stringExpr, doubleExpr, bytesExpr, uintExpr , trimBool, trimInt, trimString, trimDouble, trimBytes, trimUint , mkBoolsExpr, mkIntsExpr, mkStringsExpr, mkDoublesExpr, mkListOfBytesExpr, mkUintsExpr , assertBools, assertInts, assertStrings, assertDoubles, assertListOfBytes, assertUints , boolsExpr, intsExpr, stringsExpr, doublesExpr, listOfBytesExpr, uintsExpr ) where import Data.Char (ord) import Data.List (intercalate) import Data.Text (Text, unpack, pack) import Data.ByteString (ByteString) import qualified Data.Katydid.Parser.Parser as Parser -- | -- assertArgs1 asserts that the list of arguments is only one argument and -- returns the argument or an error message -- containing the function name that was passed in as an argument to assertArgs1. assertArgs1 :: String -> [AnyExpr] -> Either String AnyExpr assertArgs1 _ [e1] = Right e1 assertArgs1 exprName es = Left $ exprName ++ ": expected one argument, but got " ++ show (length es) ++ ": " ++ show es -- | -- assertArgs2 asserts that the list of arguments is only two arguments and -- returns the two arguments or an error message -- containing the function name that was passed in as an argument to assertArgs2. assertArgs2 :: String -> [AnyExpr] -> Either String (AnyExpr, AnyExpr) assertArgs2 _ [e1, e2] = Right (e1, e2) assertArgs2 exprName es = Left $ exprName ++ ": expected two arguments, but got " ++ show (length es) ++ ": " ++ show es -- | -- Desc is the description of a function, -- especially built to make comparisons of user defined expressions possible. data Desc = Desc { _name :: String , _toStr :: String , _hash :: Int , _params :: [Desc] , _hasVar :: Bool } -- | -- mkDesc makes a description from a function name and a list of the argument's descriptions. mkDesc :: String -> [Desc] -> Desc mkDesc n ps = Desc { _name = n , _toStr = n ++ "(" ++ intercalate "," (map show ps) ++ ")" , _hash = hashWithName n ps , _params = ps , _hasVar = any _hasVar ps } instance Show Desc where show = _toStr instance Ord Desc where compare = cmp instance Eq Desc where (==) a b = cmp a b == EQ -- | -- AnyExpr is used by the Relapse parser to represent an Expression that can return any type of value, -- where any is a predefined list of possible types represented by AnyFunc. data AnyExpr = AnyExpr { _desc :: Desc , _eval :: AnyFunc } -- | -- Func represents the evaluation function part of a user defined expression. -- This function takes a label from a tree parser and returns a value or an error string. type Func a = (Parser.Label -> Either String a) instance Show AnyExpr where show a = show (_desc a) instance Eq AnyExpr where (==) a b = _desc a == _desc b instance Ord AnyExpr where compare a b = cmp (_desc a) (_desc b) -- | -- AnyFunc is used by the Relapse parser and represents the list all supported types of functions. data AnyFunc = BoolFunc (Func Bool) | IntFunc (Func Int) | StringFunc (Func Text) | DoubleFunc (Func Double) | UintFunc (Func Word) | BytesFunc (Func ByteString) | BoolsFunc (Func [Bool]) | IntsFunc (Func [Int]) | StringsFunc (Func [Text]) | DoublesFunc (Func [Double]) | UintsFunc (Func [Word]) | ListOfBytesFunc (Func [ByteString]) -- | -- Expr represents a user defined expression, -- which consists of a description for comparisons and an evaluation function. data Expr a = Expr { desc :: Desc , eval :: Func a } instance Show (Expr a) where show e = show (desc e) instance Eq (Expr a) where (==) x y = desc x == desc y instance Ord (Expr a) where compare x y = cmp (desc x) (desc y) -- | -- params returns the descriptions of the parameters of the user defined expression. params :: Expr a -> [Desc] params = _params . desc -- | -- name returns the name of the user defined expression. name :: Expr a -> String name = _name . desc -- | -- hasVar returns whether the expression or any of its children contains a variable expression. hasVar :: Expr a -> Bool hasVar = _hasVar . desc -- | -- mkBoolExpr generalises a bool expression to any expression. mkBoolExpr :: Expr Bool -> AnyExpr mkBoolExpr (Expr desc eval) = AnyExpr desc (BoolFunc eval) -- | -- assertBool asserts that any expression is actually a bool expression. assertBool :: AnyExpr -> Either String (Expr Bool) assertBool (AnyExpr desc (BoolFunc eval)) = Right $ Expr desc eval assertBool (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type bool" -- | -- mkIntExpr generalises an int expression to any expression. mkIntExpr :: Expr Int -> AnyExpr mkIntExpr (Expr desc eval) = AnyExpr desc (IntFunc eval) -- | -- assertInt asserts that any expression is actually an int expression. assertInt :: AnyExpr -> Either String (Expr Int) assertInt (AnyExpr desc (IntFunc eval)) = Right $ Expr desc eval assertInt (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type int" -- | -- mkDoubleExpr generalises a double expression to any expression. mkDoubleExpr :: Expr Double -> AnyExpr mkDoubleExpr (Expr desc eval) = AnyExpr desc (DoubleFunc eval) -- | -- assertDouble asserts that any expression is actually a double expression. assertDouble :: AnyExpr -> Either String (Expr Double) assertDouble (AnyExpr desc (DoubleFunc eval)) = Right $ Expr desc eval assertDouble (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type double" -- | -- mkStringExpr generalises a string expression to any expression. mkStringExpr :: Expr Text -> AnyExpr mkStringExpr (Expr desc eval) = AnyExpr desc (StringFunc eval) -- | -- assertString asserts that any expression is actually a string expression. assertString :: AnyExpr -> Either String (Expr Text) assertString (AnyExpr desc (StringFunc eval)) = Right $ Expr desc eval assertString (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type string" -- | -- mkUintExpr generalises a uint expression to any expression. mkUintExpr :: Expr Word -> AnyExpr mkUintExpr (Expr desc eval) = AnyExpr desc (UintFunc eval) -- | -- assertUint asserts that any expression is actually a uint expression. assertUint :: AnyExpr -> Either String (Expr Word) assertUint (AnyExpr desc (UintFunc eval)) = Right $ Expr desc eval assertUint (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type uint" -- | -- mkBytesExpr generalises a bytes expression to any expression. mkBytesExpr :: Expr ByteString -> AnyExpr mkBytesExpr (Expr desc eval) = AnyExpr desc (BytesFunc eval) -- | -- assertBytes asserts that any expression is actually a bytes expression. assertBytes :: AnyExpr -> Either String (Expr ByteString) assertBytes (AnyExpr desc (BytesFunc eval)) = Right $ Expr desc eval assertBytes (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type bytes" -- | -- mkBoolsExpr generalises a list of bools expression to any expression. mkBoolsExpr :: Expr [Bool] -> AnyExpr mkBoolsExpr (Expr desc eval) = AnyExpr desc (BoolsFunc eval) -- | -- assertBools asserts that any expression is actually a list of bools expression. assertBools :: AnyExpr -> Either String (Expr [Bool]) assertBools (AnyExpr desc (BoolsFunc eval)) = Right $ Expr desc eval assertBools (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type bools" -- | -- mkIntsExpr generalises a list of ints expression to any expression. mkIntsExpr :: Expr [Int] -> AnyExpr mkIntsExpr (Expr desc eval) = AnyExpr desc (IntsFunc eval) -- | -- assertInts asserts that any expression is actually a list of ints expression. assertInts :: AnyExpr -> Either String (Expr [Int]) assertInts (AnyExpr desc (IntsFunc eval)) = Right $ Expr desc eval assertInts (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type ints" -- | -- mkUintsExpr generalises a list of uints expression to any expression. mkUintsExpr :: Expr [Word] -> AnyExpr mkUintsExpr (Expr desc eval) = AnyExpr desc (UintsFunc eval) -- | -- assertUints asserts that any expression is actually a list of uints expression. assertUints :: AnyExpr -> Either String (Expr [Word]) assertUints (AnyExpr desc (UintsFunc eval)) = Right $ Expr desc eval assertUints (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type uints" -- | -- mkDoublesExpr generalises a list of doubles expression to any expression. mkDoublesExpr :: Expr [Double] -> AnyExpr mkDoublesExpr (Expr desc eval) = AnyExpr desc (DoublesFunc eval) -- | -- assertDoubles asserts that any expression is actually a list of doubles expression. assertDoubles :: AnyExpr -> Either String (Expr [Double]) assertDoubles (AnyExpr desc (DoublesFunc eval)) = Right $ Expr desc eval assertDoubles (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type doubles" -- | -- mkStringsExpr generalises a list of strings expression to any expression. mkStringsExpr :: Expr [Text] -> AnyExpr mkStringsExpr (Expr desc eval) = AnyExpr desc (StringsFunc eval) -- | -- assertStrings asserts that any expression is actually a list of strings expression. assertStrings :: AnyExpr -> Either String (Expr [Text]) assertStrings (AnyExpr desc (StringsFunc eval)) = Right $ Expr desc eval assertStrings (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type strings" -- | -- mkListOfBytesExpr generalises a list of bytes expression to any expression. mkListOfBytesExpr :: Expr [ByteString] -> AnyExpr mkListOfBytesExpr (Expr desc eval) = AnyExpr desc (ListOfBytesFunc eval) -- | -- assertListOfBytes asserts that any expression is actually a list of bytes expression. assertListOfBytes :: AnyExpr -> Either String (Expr [ByteString]) assertListOfBytes (AnyExpr desc (ListOfBytesFunc eval)) = Right $ Expr desc eval assertListOfBytes (AnyExpr desc _) = Left $ "expected <" ++ show desc ++ "> to be of type bytes" -- cmp is an efficient comparison function for expressions. -- It is very important that cmp is efficient, -- because it is a bottleneck for simplification and smart construction of large queries. cmp :: Desc -> Desc -> Ordering cmp a b = compare (_hash a) (_hash b) <> compare (_name a) (_name b) <> compare (length (_params a)) (length (_params b)) <> foldl (<>) EQ (zipWith cmp (_params a) (_params b)) <> compare (_toStr a) (_toStr b) -- | -- hashWithName calculates a hash of the function name and its parameters. hashWithName :: String -> [Desc] -> Int hashWithName s ds = hashList (31*17 + hashString s) (map _hash ds) -- | -- hashString calcuates a hash of a string. hashString :: String -> Int hashString s = hashList 0 (map ord s) -- | -- hashList folds a list of hashes into one, given a seed and the list. hashList :: Int -> [Int] -> Int hashList = foldl (\acc h -> 31*acc + h) noLabel :: Parser.Label noLabel = Parser.String (pack "not a label, trying constant evaluation") -- | -- evalConst tries to evaluate a constant expression and -- either returns the resulting constant value or nothing. evalConst :: Expr a -> Maybe a evalConst e = if hasVar e then Nothing else case eval e noLabel of (Left _) -> Nothing (Right v) -> Just v -- | -- isConst returns whether the input description is one of the six possible constant values. isConst :: Desc -> Bool isConst d = not (null (_params d)) && case _name d of "bool" -> True "int" -> True "uint" -> True "double" -> True "string" -> True "[]byte" -> True _ -> False -- | -- boolExpr creates a constant bool expression from a input value. boolExpr :: Bool -> Expr Bool boolExpr b = Expr { desc = Desc { _name = "bool" , _toStr = if b then "true" else "false" , _hash = if b then 3 else 5 , _params = [] , _hasVar = False } , eval = const $ return b } -- | -- intExpr creates a constant int expression from a input value. intExpr :: Int -> Expr Int intExpr i = Expr { desc = Desc { _name = "int" , _toStr = show i , _hash = i , _params = [] , _hasVar = False } , eval = const $ return i } -- | -- doubleExpr creates a constant double expression from a input value. doubleExpr :: Double -> Expr Double doubleExpr d = Expr { desc = Desc { _name = "double" , _toStr = show d , _hash = truncate d , _params = [] , _hasVar = False } , eval = const $ return d } -- | -- uintExpr creates a constant uint expression from a input value. uintExpr :: Word -> Expr Word uintExpr i = Expr { desc = Desc { _name = "uint" , _toStr = show i , _hash = hashString (show i) , _params = [] , _hasVar = False } , eval = const $ return i } -- | -- stringExpr creates a constant string expression from a input value. stringExpr :: Text -> Expr Text stringExpr s = Expr { desc = Desc { _name = "string" , _toStr = show s , _hash = hashString (unpack s) , _params = [] , _hasVar = False } , eval = const $ return s } -- | -- bytesExpr creates a constant bytes expression from a input value. bytesExpr :: ByteString -> Expr ByteString bytesExpr b = Expr { desc = Desc { _name = "bytes" , _toStr = "[]byte{" ++ show b ++ "}" , _hash = hashString (show b) , _params = [] , _hasVar = False } , eval = const $ return b } -- | -- trimBool tries to reduce an expression to a single constant expression, -- if it does not contain a variable. trimBool :: Expr Bool -> Expr Bool trimBool e = if hasVar e then e else case eval e noLabel of (Left _) -> e (Right v) -> boolExpr v -- | -- trimInt tries to reduce an expression to a single constant expression, -- if it does not contain a variable. trimInt :: Expr Int -> Expr Int trimInt e = if hasVar e then e else case eval e noLabel of (Left _) -> e (Right v) -> intExpr v -- | -- trimUint tries to reduce an expression to a single constant expression, -- if it does not contain a variable. trimUint :: Expr Word -> Expr Word trimUint e = if hasVar e then e else case eval e noLabel of (Left _) -> e (Right v) -> uintExpr v -- | -- trimString tries to reduce an expression to a single constant expression, -- if it does not contain a variable. trimString :: Expr Text -> Expr Text trimString e = if hasVar e then e else case eval e noLabel of (Left _) -> e (Right v) -> stringExpr v -- | -- trimDouble tries to reduce an expression to a single constant expression, -- if it does not contain a variable. trimDouble :: Expr Double -> Expr Double trimDouble e = if hasVar e then e else case eval e noLabel of (Left _) -> e (Right v) -> doubleExpr v -- | -- trimBytes tries to reduce an expression to a single constant expression, -- if it does not contain a variable. trimBytes :: Expr ByteString -> Expr ByteString trimBytes e = if hasVar e then e else case eval e noLabel of (Left _) -> e (Right v) -> bytesExpr v -- | -- boolsExpr sequences a list of expressions that each return a bool, -- to a single expression that returns a list of bools. boolsExpr :: [Expr Bool] -> Expr [Bool] boolsExpr = seqExprs "[]bool" -- | -- intsExpr sequences a list of expressions that each return an int, -- to a single expression that returns a list of ints. intsExpr :: [Expr Int] -> Expr [Int] intsExpr = seqExprs "[]int" -- | -- stringsExpr sequences a list of expressions that each return a string, -- to a single expression that returns a list of strings. stringsExpr :: [Expr Text] -> Expr [Text] stringsExpr = seqExprs "[]string" -- | -- doublesExpr sequences a list of expressions that each return a double, -- to a single expression that returns a list of doubles. doublesExpr :: [Expr Double] -> Expr [Double] doublesExpr = seqExprs "[]double" -- | -- listOfBytesExpr sequences a list of expressions that each return bytes, -- to a single expression that returns a list of bytes. listOfBytesExpr :: [Expr ByteString] -> Expr [ByteString] listOfBytesExpr = seqExprs "[][]byte" -- | -- uintsExpr sequences a list of expressions that each return a uint, -- to a single expression that returns a list of uints. uintsExpr :: [Expr Word] -> Expr [Word] uintsExpr = seqExprs "[]uint" seqExprs :: String -> [Expr a] -> Expr [a] seqExprs n es = Expr { desc = mkDesc n (map desc es) , eval = \v -> mapM (`eval` v) es }