csv-parser / src / CSVQLTypes.hs
CSVQLTypes.hs
Raw
module CSVQLTypes where

import CSVQLGrammar
import System.IO

-- variable name, type
type VariableTypeMap = [(String, UnTypes)]

-- function name, function parent type, function return type
type BuiltInTypeMap = [(String, [UnTypes], UnTypes)]
data UnTypes = TypeNull | TypeInt | TypeBool | TypeString | Iterable Iterables deriving (Eq, Show)
data Iterables = TypeRow | TypeTable | TypeList UnTypes deriving (Eq, Show)

-- see what type a given expression is
getType :: E -> VariableTypeMap -> BuiltInTypeMap -> UnTypes
getType (For _ _ _) _ _ = TypeNull -- things that dont need to return anything
getType (Int _) _ _ = TypeInt
getType (String _) _ _ = TypeString
getType (Semi _ e2) v f = getType e2 v f
getType (Sub _ funcName _) _ fmap = do
    let func = fOutputTypeLookup fmap funcName
    case func of
        Just fType -> fType
        _ -> error ("ERROR: Function \"" ++ funcName ++ "\" does not exist.")
getType (Func fname _) v fmap = do
    let func = fOutputTypeLookup fmap fname
    case func of
        Just fType -> fType
        _ -> error ("ERROR: Function \"" ++ fname ++ "\" does not exist.")
getType (ListBody e1 e2) v f = getType e2 v f --recurses down
getType (Var name) vars _ = do
    let var = lookupVarType name vars
    case var of
        Just v -> v
        _ -> error ("ERROR: Variable \"" ++ name ++ "\" does not exist.")
getType (Assign _ e) v _ = TypeNull --doesnt return anything
getType (If e1 e2) v f = getType e2 v f
getType (IfElse e1 e2 e3) v f | getType e2 v f == getType e3 v f = getType e2 v f
                              | otherwise = error "ERROR: One of your IfElse statements does not return the same type in the If and Else brackets."
getType (Equals _ _) _ _ = TypeBool
getType (Bool _) _ _ = TypeBool
getType (List e1) v f = Iterable (TypeList (getType e1 v f))
getType Null _ _ = TypeNull
getType (Row _) _ _ = Iterable TypeRow --probably??
getType (Table _ _) _ _ = Iterable TypeTable
getType (Index x y) v f = getType x v f
getType (Not _) _ _ = TypeBool -- since Not negates a boolean it itself returns a bool
getType (And _ _) _ _ = TypeBool
getType (Or _ _) _ _ = TypeBool
getType (LessThan _ _) _ _ = TypeBool
getType (GreaterThan _ _) _ _ = TypeBool


-- make sure a type for a given expression is correct
typesValid :: E -> VariableTypeMap -> BuiltInTypeMap -> IO (Bool, VariableTypeMap)
typesValid (Bool _) v _ = return (True, v)
typesValid (Int _) v _ = return (True, v)
typesValid (String _) v _ = return (True, v)
typesValid (Var _) v _ = return (True, v)
typesValid Null v _ = return (True, v)
-- etc etc; true for all of the base cases as they dont need further proving
typesValid (If expr1 expr2) vars fmap = do
    (valid1, vars') <- typesValid expr1 vars fmap
    (valid2, vars'') <- typesValid expr2 vars' fmap
    case (valid1, valid2) of
        (True, True) -> do
            let type1 = getType expr1 vars fmap
            case type1 of
                TypeBool -> return (True, vars'')
                _ -> do
                    hPutStrLn stderr $ "ERROR: One of your If statements contains a " ++ show type1 ++ " instead of a Bool."
                    return (False, vars'')
        (False, False) -> do
            hPutStrLn stderr "ERROR: One of your If statements has an invalid conditional expression and consequent function."
            return (False, vars'')
        (False, _) -> do
            hPutStrLn stderr "ERROR: One of your If statements has an invalid conditional expression."
            return (False, vars'')
        (_, False) -> do
            hPutStrLn stderr "ERROR: One of your If statements has an invalid consequent function."
            return (False, vars'')
typesValid (IfElse e1 e2 e3) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    (v2, _) <- typesValid e2 vars fmap
    (v3, _) <- typesValid e3 vars fmap
    case (v1, v2, v3) of
        (True, True, True) -> do
            let type1 = getType e1 vars fmap
            let type2 = getType e2 vars fmap
            let type3 = getType e3 vars fmap
            let matching = type2 == type3
            case (type1, matching) of
                (TypeBool, True) -> return (True, vars)
                (TypeBool, _) -> do
                    hPutStrLn stderr "ERROR: Return types of an IfElse body do not match."
                    return (False, vars)
                _ -> do
                    hPutStrLn stderr ("ERROR: One of your IfElse statements contains a " ++ show type1 ++ " instead of a Bool.")
                    return (False, vars)
        (False, False, False) -> do
            hPutStrLn stderr "ERROR: One of your IfElse statements has an invalid conditional expression, consequent function, and alternate function. How impressive."
            return (False, vars)
        (False, False, _) -> do
            hPutStrLn stderr "ERROR: One of your IfElse statements has an invalid conditional expression and consequent function."
            return (False, vars)
        (False, _, False) -> do
            hPutStrLn stderr "ERROR: One of your IfElse statements has an invalid conditional expression and alternate function."
            return (False, vars)
        (_, False, False) -> do
            hPutStrLn stderr "ERROR: One of your IfElse statements has an invalid consequent function and alternate function."
            return (False, vars)
        (False, _, _) -> do
            hPutStrLn stderr "ERROR: One of your IfElse statements has an invalid conditional expression."
            return (False, vars)
        (_, False, _) -> do
            hPutStrLn stderr "ERROR: One of your IfElse statements has an invalid consequent function."
            return (False, vars)
        (_, _, False) -> do
            hPutStrLn stderr "ERROR: One of your IfElse statements has an invalid alternate function."
            return (False, vars)
typesValid (Equals e1 e2) vars fmap = do
    (v1, vars') <- typesValid e1 vars fmap
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> return (True, vars')
        (False, False) -> do
            hPutStrLn stderr "ERROR: One of your == statements has an invalid type on both sides."
            return (False, vars')
        (False, _) -> do
            hPutStrLn stderr "ERROR: One of your == statements has an invalid type on the left."
            return (False, vars')
        (_, False) -> do
            hPutStrLn stderr "ERROR: One of your == statements has an invalid type on the right."
            return (False, vars')
typesValid (LessThan e1 e2) vars fmap = do
    (v1, vars') <- typesValid e1 vars fmap
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> do
            let t1 = getType e1 vars fmap
            let t2 = getType e2 vars fmap
            case (t1, t2) of
                (TypeInt, TypeInt) -> return (True, vars)
                _ -> do
                    hPutStrLn stderr $ "ERROR: '<' expression comparing non-ints."
                    return (False, vars)
        (False, _) -> do
            hPutStrLn stderr $ "ERROR: Left side of '<' expression has invalid types."
            return (False, vars)
        (_, False) -> do
            hPutStrLn stderr $ "ERROR: Left side of '<' expression has invalid types."
            return (False, vars)
typesValid (GreaterThan e1 e2) vars fmap = do
    (v1, vars') <- typesValid e1 vars fmap
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> do
            let t1 = getType e1 vars fmap
            let t2 = getType e2 vars fmap
            case (t1, t2) of
                (TypeInt, TypeInt) -> return (True, vars)
                _ -> do
                    hPutStrLn stderr $ "ERROR: '>' expression comparing non-ints."
                    return (False, vars)
        (False, _) -> do
            hPutStrLn stderr $ "ERROR: Left side of '>' expression has invalid types."
            return (False, vars)
        (_, False) -> do
            hPutStrLn stderr $ "ERROR: Left side of '>' expression has invalid types."
            return (False, vars)

typesValid (For e1 e2 e3) vars fmap = do
    let v2 = lookupVarType e2 vars
    case v2 of
        Just (Iterable t) -> do
            case t of
                TypeRow -> do
                    (v3, vars') <- typesValid e3 ((e1, TypeString):vars) fmap
                    case v3 of
                        True -> return (True, vars')
                        _ -> do
                            hPutStrLn stderr ("ERROR: One of your For loops iterates over Row \"" ++ e2 ++ "\" which has an invalid body.")
                            return (False, vars')
                TypeTable -> do
                    (v3, vars') <- typesValid e3 ((e1, Iterable TypeRow):vars) fmap
                    case v3 of
                        True -> return (True, vars')
                        _ -> do
                            hPutStrLn stderr ("ERROR: One of your For loops iterates over Table \"" ++ e2 ++ "\" which has an invalid body.")
                            return (False, vars')
                TypeList x -> do
                    (v3, vars') <- typesValid e3 ((e1, x):vars) fmap
                    case v3 of
                        True -> return (True, vars')
                        _ -> do
                            hPutStrLn stderr ("ERROR: One of your For loops iterates over List \"" ++ e2 ++ "\" which has an invalid body.")
                            return (False, vars')
        _ -> do
            hPutStrLn stderr ("ERROR: One of your For loops tries to iterate over \"" ++ e2 ++ "\" which is of a non-iterable type.")
            return (False, vars)
typesValid (Semi e1 e2) vars fmap = do
    (v1, vars') <- typesValid e1 vars fmap
    (v2, vars'') <- typesValid e2 vars' fmap
    case (v1, v2) of
        (True, True) -> return (True, vars'')
        _ -> return (False, vars'')
typesValid (ListBody e1 e2) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> return (True, vars)
        _ -> return (False, vars)
    -- let sameType = getType e1 vars == getType e2 vars
    -- case (v1, v2, sameType) of
    --     (True, True, True) -> True
    --     _ -> False
typesValid (Assign name e1) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    case v1 of
        True -> return (True, (name, t):vars)
            where t = getType e1 vars fmap
        _ -> do
            hPutStrLn stderr ("ERROR: One of your Assign statements tries to assign \"" ++ name ++ "\" to an invalid expression.")
            return (False, vars)
typesValid (List e1) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    case v1 of
        True -> return (True, vars)
        _ -> do
            hPutStrLn stderr "ERROR: One of your Lists contains an invalid expression."
            return (False, vars)
typesValid (Row e1) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    case v1 of
        True -> if checkListBody vars (TypeString) e1
                    then return (True, vars)
                    else do
                        hPutStrLn stderr "ERROR: One of your Rows contains a variable that isn't a String."
                        return (False, vars)
        _ -> do
            hPutStrLn stderr "ERROR: One of your Rows contains an invalid expression."
            return (False, vars)
typesValid (Table e1 e2) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> if (checkListBody vars (Iterable TypeRow) e2 || getType e2 vars [] == TypeNull ) -- do "check if table has int and contains rows"
                            then return (True, vars)
                            else do
                                hPutStrLn stderr "ERROR: One of your Tables contains something that isn't a Row"
                                return (False, vars)
        _ -> do
            hPutStrLn stderr "ERROR: One of your Tables contains an invalid expression."
            return (False, vars)
typesValid (Not e1) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    case v1 of
        True -> do
            let type1 = getType e1 vars fmap
            case type1 of
                TypeBool -> return (True, vars)
                _ -> do
                    hPutStrLn stderr ("ERROR: One of your Not statements contains a " ++ show type1 ++ " rather than a Bool.")
                    return (False, vars)
        _ -> do
            hPutStrLn stderr "ERROR: One of your Not statements contains an invalid expression."
            return (False, vars)
typesValid (Index e1 e2) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap 
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> do
            let type1 = getType e1 vars fmap --uh is this needed?
            let type2 = getType e2 vars fmap
            case type2 of 
                TypeInt -> return (True, vars)
                _ -> do
                    hPutStrLn stderr ("ERROR: One of your Index statements contains a " ++ show type2 ++ " rather than the expected Int for the index number.")
                    return (False, vars)
        (False, _) -> do
                    hPutStrLn stderr "ERROR: One of your Index statements tries to get the index of an invalid expression."
                    return (False, vars)
        _ -> do
            hPutStrLn stderr "ERROR: You managed to provide something other than an Int for the index number. And it's not a valid expression. Impressive."
            return (False, vars)
typesValid subExpr@(Sub parentName childName e3) vars fmap = do
    let parentType = getType (Var parentName) vars fmap
    let fTypes = fInputTypeLookup fmap childName
    -- let childType = getType subExpr vars fmap
    case (parentType, fTypes) of
        (pType, Just fTypes') -> do
            let matching = parentType `elem` fTypes'
            (v3, _) <- typesValid e3 vars fmap
            case (matching, v3) of
                (True, True) -> return (True, vars)
                (False, _) -> do
                    hPutStrLn stderr ("ERROR: Function \"" ++ childName ++ "\" cannot be used on " ++ show parentType ++ ".")
                    return (False, vars)
                (_, False) -> do
                    hPutStrLn stderr ("ERROR: Function \"" ++ childName ++ "\" contains invalid arguments.")
                    return (False, vars)
        _ -> do
            hPutStrLn stderr ("ERROR: Function \"" ++ childName ++ "\" does not exist.")
            return (False, vars)
typesValid plusExpr@(Plus e1 e2) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap 
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> do
            let t1 = getType e1 vars fmap
            let t2 = getType e2 vars fmap
            case (t1, t2) of
                (Iterable TypeRow, Iterable TypeRow) -> return (True, vars)
                (TypeInt, TypeInt) -> return (True, vars)
                (TypeInt, Iterable TypeRow) -> do
                    hPutStrLn stderr "ERROR: You cannot add an Int and a Row together."
                    return (False, vars)
                (Iterable TypeRow, TypeInt) -> do
                    hPutStrLn stderr "ERROR: You cannot add an Int and a Row together."
                    return (False, vars)
                (_, TypeInt) -> do
                    hPutStrLn stderr ("ERROR: " ++ show t1 ++ " is not valid in a Plus expression.")
                    return (False, vars)
                (_, Iterable TypeRow) -> do
                    hPutStrLn stderr ("ERROR: " ++ show t1 ++ " is not valid in a Plus expression.")
                    return (False, vars)
                (TypeInt, _) -> do
                    hPutStrLn stderr ("ERROR: " ++ show t2 ++ " is not valid in a Plus expression.")
                    return (False, vars)
                (Iterable TypeRow, _) -> do
                    hPutStrLn stderr ("ERROR: " ++ show t2 ++ " is not valid in a Plus expression.")
                    return (False, vars)
                _ -> do
                    hPutStrLn stderr ("ERROR: Neither " ++ show t1 ++ " nor " ++ show t2 ++ " are valid in a Plus expression.")
                    return (False, vars)
        _ -> do
            hPutStrLn stderr "ERROR: One of your Plus statements contains an invalid expression."
            return (False, vars)
typesValid (And e1 e2) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> do
            let t1 = getType e1 vars fmap
            let t2 = getType e2 vars fmap
            case (t1, t2) of
                (TypeBool, TypeBool) -> return (True, vars)
                (_, TypeBool) -> do
                    hPutStrLn stderr ("ERROR: One of your And statements contains a " ++ show t1 ++ " rather than a Bool as the first variable.")
                    return (False, vars)
                (TypeBool, _) -> do
                    hPutStrLn stderr ("ERROR: One of your And statements contains a " ++ show t2 ++ " rather than a Bool as the second variable.")
                    return (False, vars)
                (_, _) -> do
                    hPutStrLn stderr ("ERROR: One of your And statements contains a " ++ show t1 ++ " and a " ++ show t2 ++ " rather Bools as variables.")
                    return (False, vars)
        _ -> do
            hPutStrLn stderr "ERROR: One of your And statements contains an invalid expression."
            return (False, vars)
typesValid (Or e1 e2) vars fmap = do
    (v1, _) <- typesValid e1 vars fmap
    (v2, _) <- typesValid e2 vars fmap
    case (v1, v2) of
        (True, True) -> do
            let t1 = getType e1 vars fmap
            let t2 = getType e2 vars fmap
            case (t1, t2) of
                (TypeBool, TypeBool) -> return (True, vars)
                (_, TypeBool) -> do
                    hPutStrLn stderr ("ERROR: One of your Or statements contains a " ++ show t1 ++ " rather than a Bool as the first variable.")
                    return (False, vars)
                (TypeBool, _) -> do
                    hPutStrLn stderr ("ERROR: One of your Or statements contains a " ++ show t2 ++ " rather than a Bool as the second variable.")
                    return (False, vars)
                (_, _) -> do
                    hPutStrLn stderr ("ERROR: One of your Or statements contains a " ++ show t1 ++ " and a " ++ show t2 ++ " rather Bools as variables.")
                    return (False, vars)
        _ -> do
            hPutStrLn stderr "ERROR: One of your Or statements contains an invalid expression."
            return (False, vars)
typesValid (Func name args) vars fmap = do
    (valid, _) <- typesValid args vars fmap
    if valid
        then return (True, vars)
        else do
            hPutStrLn stderr ("ERROR: Function \"" ++ name ++ "\" contains invalid arguments.")
            return (False, vars)
   


-- get a type assigned to a variable
lookupVarType :: String -> VariableTypeMap -> Maybe UnTypes
lookupVarType = lookup

-- find type for function
fInputTypeLookup :: BuiltInTypeMap -> String -> Maybe [UnTypes]
fInputTypeLookup [] _ = Nothing
fInputTypeLookup ((x, fInputType, _):fMap) name | name == x = Just fInputType
                                  | otherwise = fInputTypeLookup fMap name

fOutputTypeLookup :: BuiltInTypeMap -> String -> Maybe UnTypes
fOutputTypeLookup [] _ = Nothing
fOutputTypeLookup ((x, _, outType):fMap) name | name == x = Just outType
                                              | otherwise = fOutputTypeLookup fMap name

checkListBody :: VariableTypeMap -> UnTypes -> E -> Bool
checkListBody v t (ListBody e1 e2) = getType e1 v [] == t && checkListBody v t e2
checkListBody v t e1 = getType e1 v [] == t