Added cardinality to type checking

This commit is contained in:
macocianradu
2021-12-01 01:40:26 +01:00
parent ff25395b68
commit 9210c78beb
5 changed files with 120 additions and 70 deletions

View File

@@ -24,11 +24,11 @@ type ObservationPrimitive:
func EquityPriceObservation: <"Function specification for the observation of an equity price, based on the attributes of the 'EquityValuation' class."> func EquityPriceObservation: <"Function specification for the observation of an equity price, based on the attributes of the 'EquityValuation' class.">
inputs: inputs:
equity int (1..1) equity int (1..1)
valuationDate int (1..1) valuationDate ObservationPrimitive (1..1)
valuationTime int (0..1) valuationTime int (0..1)
timeType TestType (0..1) timeType TestType (0..1)
determinationMethod ObservationPrimitive (1..*) determinationMethod ObservationPrimitive (1..*)
output: output:
observation ObservationPrimitive (1..1) observation ObservationPrimitive (0..1)
assign-output: if equity exists then valuationDate assign-output: if equity exists then valuationDate

View File

@@ -40,4 +40,15 @@ data Cardinality =
| OneBound Integer | OneBound Integer
-- |The cardinality of no bounds (ex. * - *) -- |The cardinality of no bounds (ex. * - *)
| NoBounds | NoBounds
deriving Show deriving Show
instance Eq Cardinality where
(==) (Bounds (x1, x2)) (Bounds (y1, y2))
| x1 == y1 && x2 == y2 = True
| otherwise = False
(==) (OneBound x) (OneBound y) = x == y
(==) NoBounds NoBounds = True
(==) _ _ = False
typeAndCardinality :: TypeAttribute -> (Type, Cardinality)
typeAndCardinality (MakeTypeAttribute _ typ crd _) = (typ, crd)

View File

@@ -9,55 +9,57 @@ import Semantic.TypeChecker
-- |A declared variable or function -- |A declared variable or function
data Symbol = Var{ data Symbol = Var{
varName :: String, varName :: String,
declaredType :: Type declaredType :: Type,
cardinality :: Cardinality
} }
| Func { | Func {
funcName :: String, funcName :: String,
argsType :: [Type], argsType :: [(Type, Cardinality)],
returnType :: Type returnType :: (Type, Cardinality)
} } deriving (Show)
-- |A map of the predefined functions, their arguments and their return type -- |A map of the predefined functions, their arguments and their return type
defaultMap :: [Symbol] defaultMap :: [Symbol]
defaultMap = [ defaultMap = [
Func "or" [BasicType "Boolean", BasicType "Boolean"] (BasicType "Boolean"), Func "or" [(BasicType "Boolean", Bounds (1, 1)), (BasicType "Boolean", Bounds (1, 1))] (BasicType "Boolean", Bounds (1, 1)),
Func "and" [BasicType "Boolean", BasicType "Boolean"] (BasicType "Boolean"), Func "and" [(BasicType "Boolean", Bounds (1, 1)), (BasicType "Boolean", Bounds (1, 1))] (BasicType "Boolean", Bounds (1, 1)),
Func "exists" [BasicType "Any"] (BasicType "Boolean"), Func "exists" [(BasicType "Any", Bounds (0, 1))] (BasicType "Boolean", Bounds(1, 1)),
Func "is absent" [BasicType "Any"] (BasicType "Boolean"), Func "is absent" [(BasicType "Any", Bounds (0, 1))] (BasicType "Boolean", Bounds (1, 1)),
Func "single exists" [BasicType "Any"] (BasicType "Boolean"), Func "single exists" [(BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds (1, 1)),
Func "multiple exists" [BasicType "Any"] (BasicType "Boolean"), Func "multiple exists" [(BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds (1, 1)),
Func "contains" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "contains" [(BasicType "Any", NoBounds), (BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds (1, 1)),
Func "disjoint" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "disjoint" [(BasicType "Any", NoBounds), (BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds (1, 1)),
Func "=" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "=" [(BasicType "Any", NoBounds), (BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds(1, 1)),
Func ">=" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func ">=" [(BasicType "Any", NoBounds), (BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds(1, 1)),
Func "<=" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "<=" [(BasicType "Any", NoBounds), (BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds(1, 1)),
Func "<>" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "<>" [(BasicType "Any", NoBounds), (BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds(1, 1)),
Func ">" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func ">" [(BasicType "Any", NoBounds), (BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds(1, 1)),
Func "<" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "<" [(BasicType "Any", NoBounds), (BasicType "Any", NoBounds)] (BasicType "Boolean", Bounds(1, 1)),
Func "all =" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "all =" [(BasicType "Any", NoBounds), (BasicType "Any", Bounds(1, 1))] (BasicType "Boolean", Bounds(1, 1)),
Func "all <>" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "all <>" [(BasicType "Any", NoBounds), (BasicType "Any", Bounds(1, 1))] (BasicType "Boolean", Bounds(1, 1)),
Func "any =" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "any =" [(BasicType "Any", NoBounds), (BasicType "Any", Bounds(1, 1))] (BasicType "Boolean", Bounds(1, 1)),
Func "any <>" [BasicType "Any", BasicType "Any"] (BasicType "Boolean"), Func "any <>" [(BasicType "Any", NoBounds), (BasicType "Any", Bounds(1, 1))] (BasicType "Boolean", Bounds(1, 1)),
Func "+" [BasicType "Integer", BasicType "Integer"] (BasicType "Integer"), Func "+" [(BasicType "Integer", Bounds (1, 1)), (BasicType "Integer", Bounds (1, 1))] (BasicType "Integer", Bounds (1, 1)),
Func "+" [BasicType "Double", BasicType "Double"] (BasicType "Double"), Func "+" [(BasicType "Double", Bounds (1, 1)), (BasicType "Double", Bounds (1, 1))] (BasicType "Double", Bounds (1, 1)),
Func "-" [BasicType "Integer", BasicType "Integer"] (BasicType "Integer"), Func "-" [(BasicType "Integer", Bounds (1, 1)), (BasicType "Integer", Bounds (1, 1))] (BasicType "Integer", Bounds (1, 1)),
Func "-" [BasicType "Double", BasicType "Double"] (BasicType "Double"), Func "-" [(BasicType "Double", Bounds (1, 1)), (BasicType "Double", Bounds (1, 1))] (BasicType "Double", Bounds (1, 1)),
Func "*" [BasicType "Integer", BasicType "Integer"] (BasicType "Integer"), Func "*" [(BasicType "Integer", Bounds (1, 1)), (BasicType "Integer", Bounds (1, 1))] (BasicType "Integer", Bounds (1, 1)),
Func "*" [BasicType "Double", BasicType "Double"] (BasicType "Double"), Func "*" [(BasicType "Double", Bounds (1, 1)), (BasicType "Double", Bounds (1, 1))] (BasicType "Double", Bounds (1, 1)),
Func "/" [BasicType "Integer", BasicType "Integer"] (BasicType "Integer"), Func "/" [(BasicType "Integer", Bounds (1, 1)), (BasicType "Integer", Bounds (1, 1))] (BasicType "Integer", Bounds (1, 1)),
Func "/" [BasicType "Double", BasicType "Double"] (BasicType "Double"), Func "/" [(BasicType "Double", Bounds (1, 1)), (BasicType "Double", Bounds (1, 1))] (BasicType "Double", Bounds (1, 1)),
Func "^" [BasicType "Integer", BasicType "Integer"] (BasicType "Integer"), Func "^" [(BasicType "Integer", Bounds (1, 1)), (BasicType "Integer", Bounds (1, 1))] (BasicType "Integer", Bounds (1, 1)),
Func "^" [BasicType "Double", BasicType "Double"] (BasicType "Double"), Func "^" [(BasicType "Double", Bounds (1, 1)), (BasicType "Double", Bounds (1, 1))] (BasicType "Double", Bounds (1, 1)),
Func "count" [BasicType "Any"] (BasicType "Integer") Func "count" [(BasicType "Any", NoBounds)] (BasicType "Integer", Bounds (1, 1))
] ]
-- |Checks whether a function is valid (inputs, outputs are of valid type and all variables are defined) and adds it to the symbol table -- |Checks whether a function is valid (inputs, outputs are of valid type and all variables are defined) and adds it to the symbol table
addFunction :: ([Type], [Symbol]) -> Function -> Either [TypeCheckError] [Symbol] addFunction :: ([Type], [Symbol]) -> Function -> Either [TypeCheckError] [Symbol]
addFunction (definedTypes, definedSymbols) (MakeFunction name _ inps out _) addFunction (definedTypes, definedSymbols) (MakeFunction name _ inps out _)
| null (lefts checkedInputs) && isRight checkedOutput = Right $ Func name (map attributeType (rights checkedInputs)) (attributeType $ fromRightUnsafe checkedOutput) : allSymbols | null (lefts checkedInputs) && isRight checkedOutput = Right $ Func name (map typeAndCardinality (rights checkedInputs)) (attributeType $ fromRightUnsafe checkedOutput, Model.Type.cardinality out) : allSymbols
| isLeft checkedOutput = Left [fromLeftUnsafe checkedOutput] | isLeft checkedOutput = Left [fromLeftUnsafe checkedOutput]
| otherwise = Left $ lefts checkedInputs | otherwise = Left $ lefts checkedInputs
where where
@@ -68,15 +70,15 @@ addFunction (definedTypes, definedSymbols) (MakeFunction name _ inps out _)
-- |Adds a newly defined variable to the symbol table -- |Adds a newly defined variable to the symbol table
addVariables :: [Symbol] -> [TypeAttribute] -> [Symbol] addVariables :: [Symbol] -> [TypeAttribute] -> [Symbol]
addVariables s [] = s addVariables s [] = s
addVariables s ((MakeTypeAttribute name typ _ _) : vars) = Var name typ : addVariables s vars addVariables s ((MakeTypeAttribute name typ crd _) : vars) = Var name typ crd : addVariables s vars
-- |Checks the type of a given expression -- |Checks the type of a given expression
checkExpression :: [Symbol] -> Expression -> Either TypeCheckError Type checkExpression :: [Symbol] -> Expression -> Either TypeCheckError (Type, Cardinality)
checkExpression symbolMap (Variable var) = findVarType var symbolMap checkExpression symbolMap (Variable var) = findVarType var symbolMap
checkExpression _ (Int _) = Right $ BasicType "Integer" checkExpression _ (Int _) = Right (BasicType "Integer", Bounds (1, 1))
checkExpression _ (Real _) = Right $ BasicType "Double" checkExpression _ (Real _) = Right (BasicType "Double", Bounds (1, 1))
checkExpression _ (Boolean _) = Right $ BasicType "Boolean" checkExpression _ (Boolean _) = Right (BasicType "Boolean", Bounds (1, 1))
checkExpression _ Empty = Right $ BasicType "Empty" checkExpression _ Empty = Right (BasicType "Empty", Bounds (0, 0))
checkExpression symbolMap (Parens ex) = checkExpression symbolMap ex checkExpression symbolMap (Parens ex) = checkExpression symbolMap ex
checkExpression symbolMap (List lst) = checkList symbolMap lst checkExpression symbolMap (List lst) = checkList symbolMap lst
checkExpression symbolMap (PrefixExp name ex) = checkFunctionCall symbolMap name [checkExpression symbolMap ex] checkExpression symbolMap (PrefixExp name ex) = checkFunctionCall symbolMap name [checkExpression symbolMap ex]
@@ -85,27 +87,35 @@ checkExpression symbolMap (PostfixExp name ex) = checkFunctionCall symbolMap nam
checkExpression symbolMap (InfixExp name ex1 ex2) = checkFunctionCall symbolMap name (checkExpression symbolMap ex1: [checkExpression symbolMap ex2]) checkExpression symbolMap (InfixExp name ex1 ex2) = checkFunctionCall symbolMap name (checkExpression symbolMap ex1: [checkExpression symbolMap ex2])
-- |Checks if the condition of an if expression is of type boolean, and then checks the expression of the then statement -- |Checks if the condition of an if expression is of type boolean, and then checks the expression of the then statement
checkExpression symbolMap (IfSimple cond ex) checkExpression symbolMap (IfSimple cond ex)
| isRight condType && isRight (typeMatch (fromRightUnsafe condType) (BasicType "Boolean")) = checkExpression symbolMap ex | isRight condType && isRight (typeMatch (fromRightUnsafe condType) (BasicType "Boolean", Bounds (1, 1))) =
| otherwise = Left IfConditionNotBoolean case checkedExp of
where condType = checkExpression symbolMap cond -- |The if without else statement always has a cardinality lower bound of 0
Right (typ, Bounds(_, x)) -> Right (typ, Bounds(0, x))
-- |The unbounded or semi-bounded cardinalities already have 0 included
Right x -> Right x
Left err -> Left err
| otherwise = Left $ IfConditionNotBoolean $ show condType ++ " | " ++ show (getVars symbolMap)
where
condType = checkExpression symbolMap cond
checkedExp = checkExpression symbolMap ex
-- |Checks if the condition of the if statement is of type boolean, and then checks that both the then and else statements have the same type -- |Checks if the condition of the if statement is of type boolean, and then checks that both the then and else statements have the same type
checkExpression symbolMap (IfElse cond ex1 ex2) checkExpression symbolMap (IfElse cond ex1 ex2)
| isRight condType || isRight (typeMatch (fromRightUnsafe condType) (BasicType "Boolean")) = Left IfConditionNotBoolean | isRight condType || isRight (typeMatch (fromRightUnsafe condType) (BasicType "Boolean", Bounds (1, 1))) = Left $ IfConditionNotBoolean $ show cond
| isRight ex1Type || isRight ex2Type || isRight (typeMatch (fromRightUnsafe ex1Type) (fromRightUnsafe ex2Type)) = Left IfExpressionsDifferentTypes | isRight ex1Type || isRight ex2Type || isRight (typeMatch (fromRightUnsafe ex1Type) (fromRightUnsafe ex2Type)) = Left $ IfExpressionsDifferentTypes (show ex1) (show ex2)
| otherwise = ex1Type | otherwise = ex1Type
where condType = checkExpression symbolMap cond where condType = checkExpression symbolMap cond
ex1Type = checkExpression symbolMap ex1 ex1Type = checkExpression symbolMap ex1
ex2Type = checkExpression symbolMap ex2 ex2Type = checkExpression symbolMap ex2
-- |Checks that all the expressions in a list have compatible types -- |Checks that all the expressions in a list have compatible types
checkList :: [Symbol] -> [Expression] -> Either TypeCheckError Type checkList :: [Symbol] -> [Expression] -> Either TypeCheckError (Type, Cardinality)
checkList symbs exps checkList symbs exps
| isRight typ && fromRightUnsafe typ == BasicType "Any" = Right $ BasicType "Empty" | isRight typ && fromRightUnsafe typ == (BasicType "Any", NoBounds) = Right (BasicType "Empty", Bounds (0, 0))
| otherwise = typ | otherwise = typ
where typ = checkList1 symbs exps (BasicType "Any") where typ = checkList1 symbs exps (BasicType "Any", NoBounds)
-- |Auxiliary function for the check list function -- |Auxiliary function for the check list function
checkList1 :: [Symbol] -> [Expression] -> Type -> Either TypeCheckError Type checkList1 :: [Symbol] -> [Expression] -> (Type, Cardinality) -> Either TypeCheckError (Type, Cardinality)
checkList1 _ [] typ = Right typ checkList1 _ [] typ = Right typ
checkList1 symbs (ex : exps) typ checkList1 symbs (ex : exps) typ
| isRight exTyp = exTyp | isRight exTyp = exTyp
@@ -116,10 +126,10 @@ checkList1 symbs (ex : exps) typ
match = typeMatch typ (fromRightUnsafe exTyp) match = typeMatch typ (fromRightUnsafe exTyp)
-- |Checks whether the function that is called is already defined with the same argument types -- |Checks whether the function that is called is already defined with the same argument types
checkFunctionCall :: [Symbol] -> String -> [Either TypeCheckError Type] -> Either TypeCheckError Type checkFunctionCall :: [Symbol] -> String -> [Either TypeCheckError (Type, Cardinality)] -> Either TypeCheckError (Type, Cardinality)
checkFunctionCall [] fun args = Left $ UndefinedFunction $ "Undefined function: \"" ++ fun ++ "\" [" ++ concatMap typeName (rights args) ++ "]" checkFunctionCall [] fun args = Left $ UndefinedFunction $ "Undefined function: \"" ++ fun ++ "\" [" ++ concatMap (typeName . fst) (rights args) ++ "]"
checkFunctionCall ((Func n a r):symbolMap) name args checkFunctionCall ((Func n a r):symbolMap) name args
| length right /= length args = Left $ ErrorInsideFunction (name ++ ": " ++ show args) | length right /= length args = Left $ ErrorInsideFunction (name ++ ": " ++ show args ++ show (lefts args))
| name == n && all isRight (zipWith typeMatch a right) = Right r | name == n && all isRight (zipWith typeMatch a right) = Right r
| otherwise = checkFunctionCall symbolMap name args | otherwise = checkFunctionCall symbolMap name args
where right = rights args where right = rights args
@@ -127,21 +137,46 @@ checkFunctionCall (_:symbolMap) name args = checkFunctionCall symbolMap name arg
--Try to match 2nd type to first type --Try to match 2nd type to first type
-- |Checks whether two types are compatible -- |Checks whether two types are compatible
typeMatch :: Type -> Type -> Either TypeCheckError Type typeMatch :: (Type, Cardinality) -> (Type, Cardinality) -> Either TypeCheckError (Type, Cardinality)
typeMatch (BasicType "Any") x = Right x typeMatch (BasicType "Any", card1) (x, card2)
typeMatch (BasicType "Double") (BasicType "Integer") = Right $ BasicType "Dobule" | isRight card = Right (x, fromRightUnsafe card)
typeMatch s (BasicType s2) | otherwise = Left $ fromLeftUnsafe card
| s == BasicType s2 = Right s where card = cardinalityIncluded card2 card1
| otherwise = Left $ TypeMismatch (typeName s) s2 typeMatch (BasicType "Double", card1) (BasicType "Integer", card2)
typeMatch s s2 | isRight card = Right (BasicType "Dobule", fromRightUnsafe card)
| s == s2 = Right s | otherwise = Left $ fromLeftUnsafe card
| isJust $ superType s2 = typeMatch s (fromJust $ superType s2) where card = cardinalityIncluded card2 card1
--typeMatch (s, card1) (BasicType s2, card2)
-- | s == BasicType s2 = case cardinalityIncluded card1 card2 of
-- Right card -> Right (s, card)
-- Left err -> Left err
-- | otherwise = Left $ TypeMismatch (typeName s) s2
typeMatch (s, card1) (s2, card2)
| s == s2 = case cardinalityIncluded card2 card1 of
Right card -> Right (s, card)
Left err -> Left err
| isJust $ superType s2 = typeMatch (s, card1) (fromJust $ superType s2, card2)
| otherwise = Left $ TypeMismatch (typeName s) (typeName s2) | otherwise = Left $ TypeMismatch (typeName s) (typeName s2)
-- |Checks whether the first cardinality is included into the second one and returns the most restricted cardinality
cardinalityIncluded :: Cardinality -> Cardinality -> Either TypeCheckError Cardinality
cardinalityIncluded x NoBounds = Right x
cardinalityIncluded NoBounds x = Left $ CardinalityMismatch NoBounds x
cardinalityIncluded (OneBound x) (OneBound y)
| x >= y = Right $ OneBound x
| otherwise = Left $ CardinalityMismatch (OneBound x) (OneBound y)
cardinalityIncluded (Bounds (x1, x2)) (OneBound y)
| x1 >= y = Right $ Bounds (x1, x2)
| otherwise = Left $ CardinalityMismatch (Bounds (x1, x2)) (OneBound y)
cardinalityIncluded (OneBound x) (Bounds (y1, y2)) = Left $ CardinalityMismatch (OneBound x) (Bounds (y1, y2))
cardinalityIncluded (Bounds (x1, x2)) (Bounds (y1, y2))
| x1 >= y1 && x2 <= y2 = Right $ Bounds (x1, x2)
| otherwise = Left $ CardinalityMismatch (Bounds (x1, x2)) (Bounds (y1, y2))
-- |Looks in the symbol map for the type of a variable -- |Looks in the symbol map for the type of a variable
findVarType :: String -> [Symbol] -> Either TypeCheckError Type findVarType :: String -> [Symbol] -> Either TypeCheckError (Type, Cardinality)
findVarType var [] = Left $ UndefinedVariable var findVarType var [] = Left $ UndefinedVariable var
findVarType x ((Var name typ):symbols) findVarType x ((Var name typ crd):symbols)
| x == name = Right typ | x == name = Right (typ, crd)
| otherwise = findVarType x symbols | otherwise = findVarType x symbols
findVarType x (_:symbols) = findVarType x symbols findVarType x (_:symbols) = findVarType x symbols

View File

@@ -9,7 +9,10 @@ import Data.Either
-- |Checks if all the inputs and the out of a function call have valid types, and then checks that the assign-output expression is valid -- |Checks if all the inputs and the out of a function call have valid types, and then checks that the assign-output expression is valid
checkFunction :: ([Type], [Symbol]) -> Function -> Either [TypeCheckError] Function checkFunction :: ([Type], [Symbol]) -> Function -> Either [TypeCheckError] Function
checkFunction (definedTypes, definedFunctions) (MakeFunction name desc inp out ex) checkFunction (definedTypes, definedFunctions) (MakeFunction name desc inp out ex)
| isRight checkedEx && isRight checkedOut && null (lefts checkedIn) = Right $ MakeFunction name desc (rights checkedIn) (fromRightUnsafe checkedOut) ex | isRight checkedEx && isRight checkedOut && null (lefts checkedIn) =
case typeMatch (attributeType $ fromRightUnsafe checkedOut, Model.Type.cardinality out) (fromRightUnsafe checkedEx) of
Right _ -> Right $ MakeFunction name desc (rights checkedIn) (fromRightUnsafe checkedOut) ex
Left err -> Left [err]
| otherwise = Left $ lefts [checkedOut] ++ lefts checkedIn ++ lefts [checkedEx] | otherwise = Left $ lefts [checkedOut] ++ lefts checkedIn ++ lefts [checkedEx]
where where
checkedEx = checkExpression definedFunctions ex checkedEx = checkExpression definedFunctions ex

View File

@@ -6,12 +6,13 @@ import Data.Either
-- |A datatype for the different types of type check errors -- |A datatype for the different types of type check errors
data TypeCheckError = data TypeCheckError =
UndefinedType String UndefinedType String
| IfConditionNotBoolean | IfConditionNotBoolean String
| IfExpressionsDifferentTypes | IfExpressionsDifferentTypes String String
| UndefinedFunction String | UndefinedFunction String
| ErrorInsideFunction String | ErrorInsideFunction String
| UndefinedVariable String | UndefinedVariable String
| TypeMismatch String String | TypeMismatch String String
| CardinalityMismatch Cardinality Cardinality
deriving (Show) deriving (Show)
-- |Checks whether a data type is valid -- |Checks whether a data type is valid