oplang-0.1.0.0: src/Language/OpLang/Checker.hs
module Language.OpLang.Checker(check) where
import Control.Monad(join)
import Data.List((\\), nub)
import Data.Maybe(fromJust)
import Data.Text(Text)
import qualified Data.Text as T
import Data.HashMap.Strict(HashMap)
import qualified Data.HashMap.Strict as HashMap
import Language.OpLang.AST(Dict, Name, Def, DefList, calledOps)
illegalCalls :: Dict -> Def -> [Name]
illegalCalls d (name, body) = nub $ filter (not . (`HashMap.member` d)) $ calledOps (name, body)
illegalBodies :: Dict -> HashMap Name [Name]
illegalBodies d = HashMap.filter (not . null) $ HashMap.mapWithKey (curry $ illegalCalls d) d
errorMsgs :: HashMap Name [Name] -> HashMap Name [Text]
errorMsgs d = HashMap.mapWithKey errorMsg d
where
errorMsg name ops = errorMsg1 name <$> ops
errorMsg1 name op = "Error: Call to undefined operator '" <> T.singleton (fromJust op) <> "' in " <> fromName name <> "."
fromName Nothing = "top level"
fromName (Just n) = "body of '" <> T.singleton n <> T.singleton '\''
checkDups :: DefList -> Either Text Dict
checkDups l =
let
l' = fst <$> l
uniq = nub l'
toMsg a = "Error: Duplicate definition of operator '" <> T.singleton a <> "'."
concatDups = T.intercalate "\n" . map (toMsg . fromJust)
in
case l' \\ uniq of
[] -> Right $ HashMap.fromList l
dups -> Left $ concatDups $ nub dups
checkUndefs :: Dict -> Either Text Dict
checkUndefs d =
let msgs = join $ HashMap.elems $ errorMsgs $ illegalBodies d
in
case msgs of
[] -> Right d
l -> Left (T.intercalate "\n" l)
check :: DefList -> Either Text Dict
check dl = checkDups dl >>= checkUndefs