1{-# OPTIONS_GHC -Wall #-}
2module Transform.Check (mistakes) where
4import qualified Control.Arrow as Arrow
5import qualified Data.List as List
6import qualified Data.Maybe as Maybe
7import qualified Data.Set as Set
9import qualified AST.Expression.Valid as Valid
10import qualified AST.Declaration as D
11import qualified AST.Pattern as Pattern
12import qualified AST.Type as T
13import qualified AST.Variable as Var
14import qualified Transform.Expression as Expr
17import Text.PrettyPrint as P
20mistakes :: [D.ValidDecl] -> [Doc]
22 concat [ infiniteTypeAliases decls
23 , illFormedTypes decls
24 , map P.text (duplicateConstructors decls)
25 , map P.text (duplicates decls) ]
27dups :: Ord a => [a] -> [a]
28dups = map head . filter ((>1) . length) . List.group . List.sort
30dupErr :: String -> String -> String
32 "Syntax Error: There can only be one " ++ err ++ " '" ++ x ++ "'."
34duplicates :: [D.ValidDecl] -> [String]
36 map msg (dups (portNames ++ concatMap Pattern.boundVarList defPatterns)) ++
37 case mapM exprDups (portExprs ++ defExprs) of
38 Left name -> [msg name]
42 msg = dupErr "definition of"
44 (defPatterns, defExprs) =
45 unzip [ (pat,expr) | D.Definition (Valid.Definition pat expr _) <- decls ]
47 (portNames, portExprs) =
48 Arrow.second concat $ unzip $
49 flip map [ port | D.Port port <- decls ] $ \port ->
51 D.Out name expr _ -> (name, [expr])
52 D.In name _ -> (name, [])
54 exprDups :: Valid.Expr -> Either String Valid.Expr
55 exprDups expr = Expr.crawlLet defsDups expr
57 defsDups :: [Valid.Def] -> Either String [Valid.Def]
59 let varsIn (Valid.Definition pattern _ _) = Pattern.boundVarList pattern in
60 case dups $ concatMap varsIn defs of
64duplicateConstructors :: [D.ValidDecl] -> [String]
65duplicateConstructors decls =
66 map (dupErr "definition of type constructor") (dups typeCtors) ++
67 map (dupErr "definition of data constructor") (dups dataCtors)
69 typeCtors = [ name | D.Datatype name _ _ <- decls ]
70 dataCtors = concat [ map fst patterns | D.Datatype _ _ patterns <- decls ]
72illFormedTypes :: [D.ValidDecl] -> [Doc]
73illFormedTypes decls = map report (Maybe.mapMaybe isIllFormed (aliases ++ adts))
75 aliases = [ (decl, tvars, [tipe]) | decl@(D.TypeAlias _ tvars tipe) <- decls ]
76 adts = [ (decl, tvars, concatMap snd ctors) | decl@(D.Datatype _ tvars ctors) <- decls ]
80 T.Lambda t1 t2 -> Set.union (freeVars t1) (freeVars t2)
81 T.Var x -> Set.singleton x
82 T.Data _ ts -> Set.unions (map freeVars ts)
83 T.Record fields ext -> Set.unions (ext' : map (freeVars . snd) fields)
84 where ext' = maybe Set.empty freeVars ext
85 T.Aliased _ t -> freeVars t
87 undeclared tvars tipes = Set.difference used declared
89 used = Set.unions (map freeVars tipes)
90 declared = Set.fromList tvars
92 isIllFormed (decl, tvars, tipes) =
93 let unbound = undeclared tvars tipes in
94 if Set.null unbound then Nothing
95 else Just (decl, Set.toList unbound)
97 report (decl, tvars) =
98 P.vcat [ P.text $ "Error: type variable" ++ listing ++ " unbound in:"
100 , nest 4 (pretty decl) ]
104 [tvar] -> " " ++ quote tvar ++ " is"
105 _ -> "s" ++ addCommas (map ((++) " ") (addAnd (map quote tvars))) ++ " are"
108 | length xs < 3 = concat xs
109 | otherwise = List.intercalate "," xs
113 | otherwise = zipWith (++) (replicate (length xs - 1) "" ++ ["and "]) xs
115 quote tvar = "'" ++ tvar ++ "'"
118infiniteTypeAliases :: [D.ValidDecl] -> [Doc]
119infiniteTypeAliases decls =
120 [ report name tvars tipe | D.TypeAlias name tvars tipe <- decls
121 , infiniteType name tipe ]
123 infiniteType :: String -> T.Type Var.Raw -> Bool
124 infiniteType name tipe =
125 let infinite = infiniteType name in
127 T.Lambda a b -> infinite a || infinite b
129 T.Data (Var.Raw name') ts -> name == name' || any infinite ts
130 T.Record fields _ -> any (infinite . snd) fields
131 T.Aliased _ t -> infinite t
133 indented :: D.ValidDecl -> Doc
134 indented decl = P.text "\n " <> pretty decl <> P.text "\n"
136 report name args tipe =
137 P.vcat [ P.text $ eightyCharLines 0 msg1
138 , indented $ D.TypeAlias name args tipe
139 , P.text $ eightyCharLines 0 msg2
140 , indented $ D.Datatype name args [(name,[tipe])]
141 , P.text $ eightyCharLines 0 msg3 ++ "\n"
144 msg1 = "Type alias '" ++ name ++ "' is an infinite type. " ++
145 "Notice that it appears in its own definition, so when \
146 \you expand it, it just keeps getting bigger:"
147 msg2 = "Try this instead:"
148 msg3 = "It looks very similar, but an algebraic data type (ADT) \
149 \actually creates a new type. Unlike with a type alias, this \
150 \freshly created type is meaningful on its own, so an ADT \
151 \does not need to be expanded."
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list