module ParsePatterns (
ParsedExpr(..), newBuiltIn, newFunction, fromJson
) where
import Text.JSON (decode, Result(..), JSValue(..), fromJSString, fromJSObject)
import Patterns
import Expr
data ParsedExpr
= BoolExpr (Expr Bool)
| DoubleExpr (Expr Double)
| IntExpr (Expr Int)
| UintExpr (Expr Uint)
| StringExpr (Expr String)
| BytesExpr (Expr Bytes)
| BoolListExpr [Expr Bool]
| DoubleListExpr [Expr Double]
| IntListExpr [Expr Int]
| UintListExpr [Expr Uint]
| StringListExpr [Expr String]
| BytesListExpr [Expr Bytes]
deriving Show
fromJson :: String -> Either String Refs
fromJson s = unmarshal $ decode s
unmarshal :: Result JSValue -> Either String Refs
unmarshal (Error err) = fail err
unmarshal (Ok (JSObject o)) = uRefs $ fromJSObject o
unmarshal (Ok j) = fail $ "unexpected jsvalue = " ++ show j
uRefs :: [(String, JSValue)] -> Either String Refs
uRefs [] = return emptyRef
uRefs (("TopPattern", JSObject pattern):pairs) = do {
p <- uPattern (fromJSObject pattern);
rs <- uRefs pairs;
return $ newRef "main" p `union` rs
}
uRefs (("PatternDecls", JSArray patternDecls):pairs) = do {
p <- uPatternDecls patternDecls;
rs <- uRefs pairs;
return $ p `union` rs
}
uRefs (_:pairs) = uRefs pairs
uPatternDecls :: [JSValue] -> Either String Refs
uPatternDecls [] = return emptyRef
uPatternDecls (JSObject o:patternDecls) = do {
left <- uPatternDecl (fromJSObject o);
right <- uPatternDecls patternDecls;
return $ left `union` right
}
uPatternDecl :: [(String, JSValue)] -> Either String Refs
uPatternDecl kvs = do {
name <- getString kvs "Name";
p <- getObject kvs "Pattern";
pattern <- uPattern p;
return $ newRef name pattern
}
uPattern :: [(String, JSValue)] -> Either String Pattern
uPattern [("Empty", _)] = return Empty
uPattern [("TreeNode", JSObject o)] = uTreeNode (fromJSObject o)
uPattern [("LeafNode", JSObject o)] = uLeafNode (fromJSObject o)
uPattern [("Concat", JSObject o)] = uLeftRight Concat (fromJSObject o)
uPattern [("Or", JSObject o)] = uLeftRight Or (fromJSObject o)
uPattern [("And", JSObject o)] = uLeftRight And (fromJSObject o)
uPattern [("ZeroOrMore", JSObject o)] = uZeroOrMore (fromJSObject o)
uPattern [("Reference", JSObject o)] = uReference (fromJSObject o)
uPattern [("Not", JSObject o)] = uNot (fromJSObject o)
uPattern [("ZAny", JSObject o)] = return ZAny
uPattern [("Contains", JSObject o)] = uContains (fromJSObject o)
uPattern [("Optional", JSObject o)] = uOptional (fromJSObject o)
uPattern [("Interleave", JSObject o)] = uLeftRight Interleave (fromJSObject o)
uTreeNode :: [(String, JSValue)] -> Either String Pattern
uTreeNode kvs = do {
name <- getObject kvs "Name";
nameExpr <- uNameExpr name;
p <- getObject kvs "Pattern";
pattern <- uPattern p;
return $ Node nameExpr pattern
}
uLeafNode :: [(String, JSValue)] -> Either String Pattern
uLeafNode kvs = flip Node Empty <$> (getObject kvs "Expr" >>= uBoolExpr)
uReference :: [(String, JSValue)] -> Either String Pattern
uReference kvs = Reference <$> getString kvs "Name"
uLeftRight :: (Pattern -> Pattern -> Pattern) -> [(String, JSValue)] -> Either String Pattern
uLeftRight combine kvs = do {
left <- getObject kvs "LeftPattern";
leftPattern <- uPattern left;
right <- getObject kvs "RightPattern";
rightPattern <- uPattern right;
return $ combine leftPattern rightPattern
}
uZeroOrMore :: [(String, JSValue)] -> Either String Pattern
uZeroOrMore kvs = ZeroOrMore <$> (getObject kvs "Pattern" >>= uPattern)
uNot :: [(String, JSValue)] -> Either String Pattern
uNot kvs = Not <$> (getObject kvs "Pattern" >>= uPattern)
uContains :: [(String, JSValue)] -> Either String Pattern
uContains kvs = Contains <$> (getObject kvs "Pattern" >>= uPattern)
uOptional :: [(String, JSValue)] -> Either String Pattern
uOptional kvs = Optional <$> (getObject kvs "Pattern" >>= uPattern)
uNameExpr :: [(String, JSValue)] -> Either String (Expr Bool)
uNameExpr [("Name", JSObject o)] = return $ uName (fromJSObject o)
uNameExpr [("AnyName", JSObject o)] = return $ Const True
uNameExpr [("AnyNameEither", JSObject o)] = uNameEither (fromJSObject o)
uNameExpr [("NameChoice", JSObject o)] = uNameChoice (fromJSObject o)
uName :: [(String, JSValue)] -> Expr Bool
uName kvs = uName' $ head $ filter (\(k,v) -> (k /= "Before")) kvs
uName' :: (String, JSValue) -> Expr Bool
uName' ("DoubleValue", JSRational _ num) = DoubleEqualFunc (Const (fromRational num)) DoubleVariable
uName' ("IntValue", JSRational _ num) = IntEqualFunc (Const $ truncate num) IntVariable
uName' ("UintValue", JSRational _ num) = UintEqualFunc (Const $ truncate num) UintVariable
uName' ("BoolValue", JSBool b) = BoolEqualFunc (Const b) BoolVariable
uName' ("StringValue", JSString s) = StringEqualFunc (Const $ fromJSString s) StringVariable
uName' ("BytesValue", JSString s) = BytesEqualFunc (Const $ fromJSString s) BytesVariable
uNameEither :: [(String, JSValue)] -> Either String (Expr Bool)
uNameEither kvs = NotFunc <$> (getObject kvs "Either" >>= uNameExpr)
uNameChoice :: [(String, JSValue)] -> Either String (Expr Bool)
uNameChoice kvs = do {
left <- getObject kvs "Left";
leftName <- uNameExpr left;
right <- getObject kvs "Right";
rightName <- uNameExpr right;
return $ OrFunc leftName rightName
}
uBoolExpr :: [(String, JSValue)] -> Either String (Expr Bool)
uBoolExpr kvs = uExprs kvs >>= (\e ->
case e of
(BoolExpr v) -> return v
_ -> fail $ "not a BoolExpr, but a " ++ show e
)
uDoubleExpr :: [(String, JSValue)] -> Either String (Expr Double)
uDoubleExpr kvs = uExprs kvs >>= (\e ->
case e of
(DoubleExpr v) -> return v
_ -> fail $ "not a DoubleExpr, but a " ++ show e
)
uIntExpr :: [(String, JSValue)] -> Either String (Expr Int)
uIntExpr kvs = uExprs kvs >>= (\e ->
case e of
(IntExpr v) -> return v
_ -> fail $ "not a IntExpr, but a " ++ show e
)
uUintExpr :: [(String, JSValue)] -> Either String (Expr Uint)
uUintExpr kvs = uExprs kvs >>= (\e ->
case e of
(UintExpr v) -> return v
_ -> fail $ "not a UintExpr, but a " ++ show e
)
uStringExpr :: [(String, JSValue)] -> Either String (Expr String)
uStringExpr kvs = uExprs kvs >>= (\e ->
case e of
(StringExpr v) -> return v
_ -> fail $ "not a StringExpr, but a " ++ show e
)
uBytesExpr :: [(String, JSValue)] -> Either String (Expr Bytes)
uBytesExpr kvs = uExprs kvs >>= (\e ->
case e of
(BytesExpr v) -> return v
_ -> fail $ "not a BytesExpr, but a " ++ show e
)
uExprs :: [(String, JSValue)] -> Either String ParsedExpr
uExprs kvs = uExpr $ head $ filter (\(k,v) -> k /= "RightArrow" && k /= "Comma") kvs
uExpr :: (String, JSValue) -> Either String ParsedExpr
uExpr ("Terminal", JSObject o) = return $ uTerminals $ fromJSObject o
uExpr ("List", JSObject o) = uList $ fromJSObject o
uExpr ("Function", JSObject o) = uFunction $ fromJSObject o
uExpr ("BuiltIn", JSObject o) = uBuiltIn $ fromJSObject o
uTerminals :: [(String, JSValue)] -> ParsedExpr
uTerminals kvs = uTerminal $ head $ filter (\(k,v) -> k /= "Before" && k /= "Literal") kvs
uTerminal :: (String, JSValue) -> ParsedExpr
uTerminal ("DoubleValue", JSRational _ n) = DoubleExpr (Const (fromRational n))
uTerminal ("IntValue", JSRational _ n) = IntExpr (Const $ truncate n)
uTerminal ("UintValue", JSRational _ n) = UintExpr (Const $ truncate n)
uTerminal ("BoolValue", JSBool b) = BoolExpr (Const b)
uTerminal ("StringValue", JSString s) = StringExpr (Const $ fromJSString s)
uTerminal ("BytesValue", JSString s) = BytesExpr (Const $ fromJSString s)
uTerminal ("Variable", JSObject o) = uVariable $ fromJSObject o
uVariable :: [(String, JSValue)] -> ParsedExpr
uVariable [("Type", JSRational _ 101)] = DoubleExpr DoubleVariable
uVariable [("Type", JSRational _ 103)] = IntExpr IntVariable
uVariable [("Type", JSRational _ 104)] = UintExpr UintVariable
uVariable [("Type", JSRational _ 108)] = BoolExpr BoolVariable
uVariable [("Type", JSRational _ 109)] = StringExpr StringVariable
uVariable [("Type", JSRational _ 112)] = BytesExpr BytesVariable
uList :: [(String, JSValue)] -> Either String ParsedExpr
uList kvs = do {
arr <- getArrayOfObjects kvs "Elems";
typ <- getInt kvs "Type";
case typ of
101 -> DoubleListExpr <$> mapM uDoubleExpr arr
103 -> IntListExpr <$> mapM uIntExpr arr
104 -> UintListExpr <$> mapM uUintExpr arr
108 -> BoolListExpr <$> mapM uBoolExpr arr
109 -> StringListExpr <$> mapM uStringExpr arr
112 -> BytesListExpr <$> mapM uBytesExpr arr
201 -> DoubleListExpr <$> mapM uDoubleExpr arr
203 -> IntListExpr <$> mapM uIntExpr arr
204 -> UintListExpr <$> mapM uUintExpr arr
208 -> BoolListExpr <$> mapM uBoolExpr arr
209 -> StringListExpr <$> mapM uStringExpr arr
212 -> BytesListExpr <$> mapM uBytesExpr arr
}
uFunction :: [(String, JSValue)] -> Either String ParsedExpr
uFunction kvs = do {
name <- getString kvs "Name";
arrayObjects <- getArrayOfObjects kvs "Params";
exprs <- mapM uExprs arrayObjects;
newFunction name exprs
}
newFunction :: String -> [ParsedExpr] -> Either String ParsedExpr
newFunction "not" [BoolExpr b] = Right $ BoolExpr $ NotFunc b
newFunction "and" [BoolExpr b1, BoolExpr b2] = Right $ BoolExpr $ AndFunc b1 b2
newFunction "or" [BoolExpr b1, BoolExpr b2] = Right $ BoolExpr $ OrFunc b1 b2
newFunction "contains" [IntExpr i,IntListExpr is] = Right $ BoolExpr $ IntListContainsFunc i is
newFunction "contains" [StringExpr s, StringListExpr ss] = Right $ BoolExpr $ StringListContainsFunc s ss
newFunction "contains" [UintExpr u, UintListExpr us] = Right $ BoolExpr $ UintListContainsFunc u us
newFunction "contains" [StringExpr s, StringExpr ss] = Right $ BoolExpr $ StringContainsFunc s ss
newFunction "elem" [BytesListExpr es, IntExpr i] = Right $ BytesExpr $ BytesListElemFunc es i
newFunction "elem" [BoolListExpr es, IntExpr i] = Right $ BoolExpr $ BoolListElemFunc es i
newFunction "elem" [DoubleListExpr es, IntExpr i] = Right $ DoubleExpr $ DoubleListElemFunc es i
newFunction "elem" [IntListExpr es, IntExpr i] = Right $ IntExpr $ IntListElemFunc es i
newFunction "elem" [StringListExpr es, IntExpr i] = Right $ StringExpr $ StringListElemFunc es i
newFunction "elem" [UintListExpr es, IntExpr i] = Right $ UintExpr $ UintListElemFunc es i
newFunction "eq" [BytesExpr v1, BytesExpr v2] = Right $ BoolExpr $ BytesEqualFunc v1 v2
newFunction "eq" [BoolExpr v1, BoolExpr v2] = Right $ BoolExpr $ BoolEqualFunc v1 v2
newFunction "eq" [DoubleExpr v1, DoubleExpr v2] = Right $ BoolExpr $ DoubleEqualFunc v1 v2
newFunction "eq" [IntExpr v1, IntExpr v2] = Right $ BoolExpr $ IntEqualFunc v1 v2
newFunction "eq" [StringExpr v1, StringExpr v2] = Right $ BoolExpr $ StringEqualFunc v1 v2
newFunction "eq" [UintExpr v1, UintExpr v2] = Right $ BoolExpr $ UintEqualFunc v1 v2
newFunction "eqFold" _ = Left "eqFold function is not supported"
newFunction "ge" [BytesExpr v1, BytesExpr v2] = Right $ BoolExpr $ BytesGreaterOrEqualFunc v1 v2
newFunction "ge" [DoubleExpr v1, DoubleExpr v2] = Right $ BoolExpr $ DoubleGreaterOrEqualFunc v1 v2
newFunction "ge" [IntExpr v1, IntExpr v2] = Right $ BoolExpr $ IntGreaterOrEqualFunc v1 v2
newFunction "ge" [UintExpr v1, UintExpr v2] = Right $ BoolExpr $ UintGreaterOrEqualFunc v1 v2
newFunction "gt" [BytesExpr v1, BytesExpr v2] = Right $ BoolExpr $ BytesGreaterThanFunc v1 v2
newFunction "gt" [DoubleExpr v1, DoubleExpr v2] = Right $ BoolExpr $ DoubleGreaterThanFunc v1 v2
newFunction "gt" [IntExpr v1, IntExpr v2] = Right $ BoolExpr $ IntGreaterThanFunc v1 v2
newFunction "gt" [UintExpr v1, UintExpr v2] = Right $ BoolExpr $ UintGreaterThanFunc v1 v2
newFunction "hasPrefix" [StringExpr v1, StringExpr v2] = Right $ BoolExpr $ StringHasPrefixFunc v1 v2
newFunction "hasSuffix" [StringExpr v1, StringExpr v2] = Right $ BoolExpr $ StringHasSuffixFunc v1 v2
newFunction "le" [BytesExpr v1, BytesExpr v2] = Right $ BoolExpr $ BytesLessOrEqualFunc v1 v2
newFunction "le" [DoubleExpr v1, DoubleExpr v2] = Right $ BoolExpr $ DoubleLessOrEqualFunc v1 v2
newFunction "le" [IntExpr v1, IntExpr v2] = Right $ BoolExpr $ IntLessOrEqualFunc v1 v2
newFunction "le" [UintExpr v1, UintExpr v2] = Right $ BoolExpr $ UintLessOrEqualFunc v1 v2
newFunction "length" [BytesListExpr vs] = Right $ IntExpr $ BytesListLengthFunc vs
newFunction "length" [BoolListExpr vs] = Right $ IntExpr $ BoolListLengthFunc vs
newFunction "length" [BytesExpr vs] = Right $ IntExpr $ BytesLengthFunc vs
newFunction "length" [DoubleListExpr vs] = Right $ IntExpr $ DoubleListLengthFunc vs
newFunction "length" [IntListExpr vs] = Right $ IntExpr $ IntListLengthFunc vs
newFunction "length" [StringListExpr vs] = Right $ IntExpr $ StringListLengthFunc vs
newFunction "length" [UintListExpr vs] = Right $ IntExpr $ UintListLengthFunc vs
newFunction "length" [StringExpr vs] = Right $ IntExpr $ StringLengthFunc vs
newFunction "lt" [BytesExpr v1, BytesExpr v2] = Right $ BoolExpr $ BytesLessThanFunc v1 v2
newFunction "lt" [DoubleExpr v1, DoubleExpr v2] = Right $ BoolExpr $ DoubleLessThanFunc v1 v2
newFunction "lt" [IntExpr v1, IntExpr v2] = Right $ BoolExpr $ IntLessThanFunc v1 v2
newFunction "lt" [UintExpr v1, UintExpr v2] = Right $ BoolExpr $ UintLessThanFunc v1 v2
newFunction "ne" [BytesExpr v1, BytesExpr v2] = Right $ BoolExpr $ BytesNotEqualFunc v1 v2
newFunction "ne" [BoolExpr v1, BoolExpr v2] = Right $ BoolExpr $ BoolNotEqualFunc v1 v2
newFunction "ne" [DoubleExpr v1, DoubleExpr v2] = Right $ BoolExpr $ DoubleNotEqualFunc v1 v2
newFunction "ne" [IntExpr v1, IntExpr v2] = Right $ BoolExpr $ IntNotEqualFunc v1 v2
newFunction "ne" [StringExpr v1, StringExpr v2] = Right $ BoolExpr $ StringNotEqualFunc v1 v2
newFunction "ne" [UintExpr v1, UintExpr v2] = Right $ BoolExpr $ UintNotEqualFunc v1 v2
newFunction "now" _ = Left "now function is not supported"
newFunction "print" _ = Left "print function is not supported"
newFunction "range" _ = Left "range function is not supported"
newFunction "toLower" [StringExpr s] = Right $ StringExpr $ StringToLowerFunc s
newFunction "toUpper" [StringExpr s] = Right $ StringExpr $ StringToUpperFunc s
newFunction "type" [BytesExpr b] = Right $ BoolExpr $ BytesTypeFunc b
newFunction "type" [BoolExpr b] = Right $ BoolExpr $ BoolTypeFunc b
newFunction "type" [DoubleExpr b] = Right $ BoolExpr $ DoubleTypeFunc b
newFunction "type" [IntExpr b] = Right $ BoolExpr $ IntTypeFunc b
newFunction "type" [UintExpr b] = Right $ BoolExpr $ UintTypeFunc b
newFunction "type" [StringExpr b] = Right $ BoolExpr $ StringTypeFunc b
newFunction "regex" [StringExpr v1, StringExpr v2] = Right $ BoolExpr $ RegexFunc v1 v2
newFunction s t = Left $ "unknown function: " ++ s ++ " for types: " ++ show t
uBuiltIn :: [(String, JSValue)] -> Either String ParsedExpr
uBuiltIn kvs = do {
exprObject <- getObject kvs "Expr";
symbolObject <- getObject kvs "Symbol";
symbol <- getString symbolObject "Value";
exprs <- uExprs exprObject;
newBuiltIn symbol exprs;
}
newBuiltIn :: String -> ParsedExpr -> Either String ParsedExpr
newBuiltIn symbol constExpr = funcName symbol >>= (\name ->
if name == "type" then
newFunction name [constExpr]
else if name == "regex" then
newFunction name [constExpr, constToVar constExpr]
else
newFunction name [constToVar constExpr, constExpr]
)
constToVar :: ParsedExpr -> ParsedExpr
constToVar (BoolExpr Const{}) = BoolExpr BoolVariable
constToVar (DoubleExpr Const{}) = DoubleExpr DoubleVariable
constToVar (IntExpr Const{}) = IntExpr IntVariable
constToVar (UintExpr Const{}) = UintExpr UintVariable
constToVar (BytesExpr Const{}) = BytesExpr BytesVariable
constToVar (StringExpr Const{}) = StringExpr StringVariable
funcName :: String -> Either String String
funcName "==" = return "eq"
funcName "!=" = return "ne"
funcName "<" = return "lt"
funcName ">" = return "gt"
funcName "<=" = return "le"
funcName ">=" = return "ge"
funcName "~=" = return "regex"
funcName "*=" = return "contains"
funcName "^=" = return "hasPrefix"
funcName "$=" = return "hasSuffix"
funcName "::" = return "type"
funcName name = fail $ "unexpected funcName: <" ++ name ++ ">"
getField :: [(String, JSValue)] -> String -> Either String JSValue
getField pairs name = let filtered = filter (\(k,_) -> (k == name)) pairs
in case filtered of
[] -> fail $ "no field with name: " ++ name
vs -> return $ snd $ head vs
getString :: [(String, JSValue)] -> String -> Either String String
getString pairs name = getField pairs name >>= (\v ->
case v of
(JSString s) -> return $ fromJSString s
_ -> fail $ name ++ " is not a JSString, but a " ++ show v
)
getInt :: [(String, JSValue)] -> String -> Either String Int
getInt pairs name = getField pairs name >>= (\v ->
case v of
(JSRational _ n) -> return $ truncate n
_ -> fail $ name ++ " is not a JSRational, but a " ++ show v
)
getArrayOfObjects :: [(String, JSValue)] -> String -> Either String [[(String, JSValue)]]
getArrayOfObjects pairs name = getField pairs name >>= (\v ->
case v of
(JSArray vs) -> mapM assertObject vs
_ -> fail $ name ++ " is not a JSArray, but a " ++ show v
)
assertObject :: JSValue -> Either String [(String, JSValue)]
assertObject (JSObject o) = return $ fromJSObject o
assertObject v = fail $ "not an JSObject, but a " ++ show v
getObject :: [(String, JSValue)] -> String -> Either String [(String, JSValue)]
getObject pairs name = getField pairs name >>= (\v ->
case v of
(JSObject o) -> return $ fromJSObject o
_ -> fail $ name ++ " is not an JSObject, but a " ++ show v
)