@@ -3,27 +3,34 @@ module ProjectM36.AtomType where
33import ProjectM36.Base
44import ProjectM36.Relation
55import ProjectM36.DataTypes.Primitive
6+import qualified ProjectM36.TypeConstructor as TC
7+import qualified ProjectM36.DataConstructor as DC
68import qualified Data.Text as T
79import ProjectM36.Error
810import ProjectM36.Attribute
911import ProjectM36.Atom
10−import qualified Data.Map as M
1112import qualified Data.Vector as V
12−import Data.Either (isRight)
13−import Data.Maybe
13+import qualified Data.Set as S
14+import qualified Data.List as L
15+import Data.Maybe (isJust, catMaybes)
16+import Control.Monad.Writer
1417
15−typesAsRelation :: AtomTypes -> Either RelationalError Relation
16−typesAsRelation aTypes = mkRelationFromTuples attrs tuples
18+typesAsRelation :: TypeConstructors -> Either RelationalError Relation
19+typesAsRelation types = mkRelationFromTuples attrs tuples
1720 where
1821 attrs = attributesFromList [Attribute "TypeConstructor" textAtomType,
1922 Attribute "DataConstructors" dConsType]
2023 subAttrs = attributesFromList [Attribute "DataConstructor" textAtomType]
2124 dConsType = RelationAtomType subAttrs
22− tuples = map mkTypeConsDescription (M.toList aTypes)
23− mkTypeConsDescription (tConsName, (_, AtomConstructor dConsMap)) = RelationTuple attrs (V.fromList [textAtom tConsName, mkDataConsRelation dConsMap])
24− mkDataConsRelation dConsMap = case mkRelationFromTuples subAttrs $ map (\(dConsName, dConsTypeNames) -> RelationTuple subAttrs (V.singleton $ textAtom $ T.intercalate " " (dConsName:dConsTypeNames))) (M.toList dConsMap) of
25+ tuples = map mkTypeConsDescription types
26+
27+ mkTypeConsDescription (tCons, dConsList) = RelationTuple attrs (V.fromList [textAtom (TC.name tCons), mkDataConsRelation dConsList])
28+
29+ mkDataConsRelation dConsList = case mkRelationFromTuples subAttrs $ map (\dCons -> RelationTuple subAttrs (V.singleton $ textAtom $ T.intercalate " " ((DC.name dCons):(map (T.pack . show) (DC.fields dCons))))) dConsList of
2530 Left err -> error ("mkRelationFromTuples pooped " ++ show err)
2631 Right rel -> Atom rel
32+
33+
2734{-
2835-- | Scan all type constructors for the proper data constructor
2936dataConstructorTypeNamesForName :: DataConstructorName -> AtomTypes -> Either RelationalError [AtomTypeName]
@@ -38,33 +45,100 @@ dataConstructorTypeNamesForName dConsName aTypes = M.foldrWithKey typeFolder def
3845 Just aTypeNames -> Right (dConsName, aTypeNames)
3946-}
4047-- fix mostly-duplicated fold code
41−-- | Scan the atom types and return the resultant ConstructedAtomType or error.
42−atomAndArgsTypesForDataConstructorName :: DataConstructorName -> AtomTypes -> Either RelationalError (AtomType, [AtomType])
43−atomAndArgsTypesForDataConstructorName dConsName aTypes = M.foldrWithKey typeFolder defaultErr aTypes
48+
49+
50+findDataConstructor :: DataConstructorName -> TypeConstructors -> Maybe (TypeConstructor, DataConstructor)
51+findDataConstructor dName tConsList = foldr tConsFolder Nothing tConsList
4452 where
45− defaultErr = Left (NoSuchDataConstructorError dConsName)
46− typeFolder _ (aType, AtomConstructor dConsMap) accum = if isRight accum then
47− accum
48− else
49− case M.lookup dConsName dConsMap of
50− Nothing -> accum
51− Just aArgsTypeNames -> do --convert aTypeNames into AtomTypes
52− aArgsTypes <- mapM (\tName -> atomTypeForTypeConstructor tName aTypes) aArgsTypeNames
53− Right (aType, aArgsTypes)
53+ tConsFolder (tCons, dConsList) accum = if isJust accum then
54+ accum
55+ else
56+ case findDCons dConsList of
57+ Just dCons -> Just (tCons, dCons)
58+ Nothing -> Nothing
59+ findDCons dConsList = case filter (\dCons -> DC.name dCons == dName) dConsList of
60+ [] -> Nothing
61+ [dCons] -> Just dCons
62+ _ -> error "More than one data constructor with the same name found"
63+
64+-- | Scan the atom types and return the resultant ConstructedAtomType or error.
65+-- Used in typeFromAtomExpr to validate argument types.
66+atomTypeForDataConstructorName :: DataConstructorName -> [AtomType] -> TypeConstructors -> Either RelationalError AtomType
67+-- search for the data constructor and resolve the types' names
68+atomTypeForDataConstructorName dConsName atomTypesIn tConsList = do
69+ case findDataConstructor dConsName tConsList of
70+ Nothing -> Left (NoSuchDataConstructorError dConsName)
71+ Just (tCons, dCons) -> do
72+ let validation = map (\(aTypeIn, tConsArgIn) -> validAtomTypeWithTypeConstructorArg aTypeIn tConsArgIn tConsList) (zip atomTypesIn (DC.fields dCons))
73+ errors = catMaybes validation
74+ if length errors /= 0 then
75+ Left (head errors)
76+ else
77+ Right (ConstructedAtomType (TC.name tCons) atomTypesIn)
78+
79+validAtomTypeWithTypeConstructorArg :: AtomType -> TypeConstructorArg -> TypeConstructors -> Maybe RelationalError
80+validAtomTypeWithTypeConstructorArg aType (TypeConstructorArg tCons) tConsList = validAtomTypeWithTypeConstructor aType tCons tConsList
81+validAtomTypeWithTypeConstructorArg _ (TypeConstructorPolymorphicArg _) _ = Just IncompletelyDefinedAtomTypeWithConstructorError
82+
83+--reconcile the atom-in types with the type constructors
84+validAtomTypeWithTypeConstructor :: AtomType -> TypeConstructor -> TypeConstructors -> Maybe RelationalError
85+validAtomTypeWithTypeConstructor aType (PrimitiveTypeConstructor _ expectedAType) _ = if expectedAType /= aType then Just (AtomTypeMismatchError expectedAType aType) else Nothing
86+
87+--lookup constructor name and check if the incoming atom types are valid
88+validAtomTypeWithTypeConstructor (ConstructedAtomType tConsName _) (ADTypeConstructor expectedTConsName _) _ = if tConsName /= expectedTConsName then Just (TypeConstructorNameMismatch expectedTConsName tConsName) else Nothing
5489
55−atomTypeForTypeConstructor :: TypeConstructorName -> AtomTypes -> Either RelationalError AtomType
56−atomTypeForTypeConstructor tConsName aTypes = case M.lookup tConsName aTypes of
57− Nothing -> Left (NoSuchTypeConstructorError tConsName)
58− Just (aType, _) -> Right aType
90+validAtomTypeWithTypeConstructor aType tCons _ = Just (AtomTypeTypeConstructorReconciliationError aType tCons)
5991
92+-- | Used to determine if the atom arguments can be used with the data constructor.
93+atomTypeWithDataConstructor :: TypeConstructors -> DataConstructorName -> [AtomType] -> Either RelationalError AtomType
94+atomTypeWithDataConstructor tConss dConsName atomArgTypes = do
95+ --lookup the data constructor
96+ case findDataConstructor dConsName tConss of
97+ Nothing -> Left (NoSuchDataConstructorError dConsName)
98+ Just (tCons, dCons) -> do
99+ --match the data constructor arguments to the atom args
100+ let validator (tConsArg', aType) = maybe (Right ()) Left (validAtomTypeWithTypeConstructorArg aType tConsArg' tConss)
101+ mapM_ validator (zip (DC.fields dCons) atomArgTypes)
102+ pure (ConstructedAtomType (TC.name tCons) atomArgTypes)
103+
104+-- check that polymorphic vars on the right also appear on the left
105+-- check that the data constructor names are unique
106+validateTypeConstructor :: TypeConstructor -> [DataConstructor] -> [RelationalError]
107+validateTypeConstructor tCons dConsList = execWriter $ do
108+ let duplicateDConsNames = dupes (L.sort (map DC.name dConsList))
109+ mapM_ tell [map DataConstructorNameInUseError duplicateDConsNames]
110+ let leftSideVars = TC.polymorphicVariables tCons
111+ rightSideVars = concatMap DC.polymorphicVariables dConsList
112+ varsDiff = S.difference (S.fromList leftSideVars) (S.fromList rightSideVars)
113+ mapM_ tell [map DataConstructorUsesUndeclaredTypeVariable (S.toList varsDiff)]
114+ pure ()
115+
116+--returns duplicates of a pre-sorted list
117+dupes :: Eq a => [a] -> [a]
118+dupes [] = []
119+dupes (_:[]) = []
120+dupes (x:y:[]) = if x == y then [x] else []
121+dupes (x:y:xs) = dupes(x:[y]) ++ dupes(xs)
122+
123+atomTypeForTypeConstructor :: TypeConstructor -> TypeConstructors -> Either RelationalError AtomType
124+atomTypeForTypeConstructor tCons tConsList = foldr tConsFolder defaultErr (map fst tConsList)
125+ where
126+ tConsFolder tCons' accum = if TC.name tCons == TC.name tCons' then do
127+ tConsArgTypes <- mapM ((flip atomTypeForTypeConstructorArg) tConsList) (TC.arguments tCons')
128+ Right (ConstructedAtomType (TC.name tCons') tConsArgTypes)
129+ else
130+ accum
131+ defaultErr = Left (NoSuchTypeConstructorName (TC.name tCons))
132+
133+atomTypeForTypeConstructorArg :: TypeConstructorArg -> TypeConstructors -> Either RelationalError AtomType
134+atomTypeForTypeConstructorArg (TypeConstructorPolymorphicArg _) _ = Left IncompletelyDefinedAtomTypeWithConstructorError
135+atomTypeForTypeConstructorArg (TypeConstructorArg tCons) tConsList = atomTypeForTypeConstructor tCons tConsList
60136
61−-- | Validate that the arguments to the data constructors in the atom constructor are valid type names.
62−validateAtomConstructor :: AtomConstructor -> AtomTypes -> Maybe RelationalError
63−validateAtomConstructor (AtomConstructor aConsMap) validAtomTypes = mapM validateDataConstructor (M.elems aConsMap) >>= listToMaybe
137+findTypeConstructor :: TypeConstructorName -> TypeConstructors -> Maybe (TypeConstructor, [DataConstructor])
138+findTypeConstructor name tConsList = foldr tConsFolder Nothing tConsList
64139 where
65− validateDataConstructor :: [AtomTypeName] -> Maybe RelationalError
66− validateDataConstructor dConsTypeNames = mapM validateTypeConstructor dConsTypeNames >>= listToMaybe
67− validateTypeConstructor :: AtomTypeName -> Maybe RelationalError
68− validateTypeConstructor dConsTypeName = case atomTypeForTypeConstructor dConsTypeName validAtomTypes of
69− Right _ -> Nothing
70− Left err -> Just err
140+ tConsFolder (tCons, dConsList) accum = if TC.name tCons == name then
141+ Just (tCons, dConsList)
142+ else
143+ accum
144+