elm/compiler/Transform/Check.hs

90 lines
2.9 KiB
Haskell
Raw Normal View History

module Transform.Check (mistakes) where
import Transform.SortDefinitions (boundVars)
import SourceSyntax.Everything
import qualified SourceSyntax.Type as T
import Data.List as List
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Data
import Data.Generics.Uniplate.Data
import Text.PrettyPrint as P
mistakes :: (Data t, Data v) => [Declaration t v] -> [Doc]
mistakes decls =
map prettify $ concatMap findErrors (getLets decls)
where
prettify = P.sep . map P.text . words
findErrors defs = duplicates defs ++ badOrder defs
getLets :: (Data t, Data v) => [Declaration t v] -> [[Def t v]]
getLets decls = defs : concatMap getSubLets defs
where
defs = concatMap (\d -> case d of Definition d -> [d] ; _ -> []) decls
getSubLets def =
case def of
Def pattern expr -> [ defs | Let defs _ <- universeBi expr ]
TypeAnnotation _ _ -> []
duplicates :: [Def t v] -> [String]
duplicates defs =
map defMsg (dup definition) ++ map annMsg (dup annotation)
where
dup f = map head . filter ((>1) . length) . List.group . List.sort $ concatMap f defs
msg = "Syntax Error: There can only be one "
defMsg x = msg ++ " definition of '" ++ x ++ "'."
annMsg x = msg ++ " type annotation for '" ++ x ++ "'."
badOrder :: [Def t v] -> [String]
badOrder defs =
map msg $ missings (List.sort $ expectedPairs as ds) (List.sort $ actualPairs defs)
where
msg x = "Syntax Error: The type annotation for '" ++ x ++
"' must be directly above its definition."
as = List.sort $ concatMap annotation defs
ds = List.sort $ concatMap definition defs
expectedPairs :: [String] -> [String] -> [String]
expectedPairs as ds =
case (as,ds) of
(x:xs, y:ys) -> case compare x y of
LT -> expectedPairs xs (y:ys)
EQ -> x : expectedPairs xs ys
GT -> expectedPairs (x:xs) ys
( _ , _ ) -> []
actualPairs defs =
case defs of
TypeAnnotation name _ : Def (PVar name') _ : rest ->
(if name == name' then [name] else []) ++ actualPairs rest
t:s:rest -> actualPairs (s:rest)
_ -> []
missings :: [String] -> [String] -> [String]
missings expected actual =
case (expected, actual) of
(e:es, a:as) -> case compare e a of
LT -> e : missings es (a:as)
EQ -> missings es as
GT -> a : missings (e:es) as
( [] , _ ) -> actual
( _ , [] ) -> expected
annotation :: Def t v -> [String]
annotation def =
case def of
TypeAnnotation name _ -> [name]
_ -> []
definition :: Def t v -> [String]
definition def =
case def of
Def pattern _ -> Set.toList (boundVars pattern)
_ -> []