module CCO.Tree.Parser.Validation (
Scheme (..)
, validate
) where
import CCO.Feedback (Feedback, Message (Error), message)
import CCO.Printing (Doc, above, text, wrapped, (>|<), Printable (pp))
import CCO.Tree.ATerm (Con, ATerm (..))
import Data.List (nub, sort)
data Scheme = IntegerTerm
| FloatTerm
| StringTerm
| AppTerm Con Int
| TupleTerm Int
| ListTerm
deriving (Eq)
schemeOf :: ATerm -> Scheme
schemeOf (Integer _) = IntegerTerm
schemeOf (Float _) = FloatTerm
schemeOf (String _) = StringTerm
schemeOf (App conid as) = AppTerm conid (length as)
schemeOf (Tuple as) = TupleTerm (length as)
schemeOf (List _) = ListTerm
schemeOf (Ann a _) = schemeOf a
data Focus = ATerm ::: Scheme
focusOn :: ATerm -> Focus
focusOn aterm = aterm ::: schemeOf aterm
validate :: ATerm -> [Scheme] -> Feedback Scheme
validate aterm schemes
| scheme `elem` schemes = return scheme
| otherwise = do message (Error (pp (diagnose focus schemes)))
return scheme
where
focus@(_ ::: scheme) = focusOn aterm
data Diagnosis = NoScheme ATerm
| ArityMismatch ATerm Con Int [Int]
| SchemeMismatch ATerm Scheme [Scheme]
instance Printable Diagnosis where pp = ppDiagnosis
diagnose :: Focus -> [Scheme] -> Diagnosis
diagnose (aterm ::: _) [] = NoScheme aterm
diagnose (aterm ::: scheme@(AppTerm conid arity)) expected
| null expectedArities = SchemeMismatch aterm scheme expected
| otherwise = ArityMismatch aterm conid arity expectedArities
where
expectedArities =
[ expectedArity |
AppTerm conid' expectedArity <- expected, conid' == conid ]
diagnose (aterm ::: scheme) expected = SchemeMismatch aterm scheme expected
ppDiagnosis :: Diagnosis -> Doc
ppDiagnosis (NoScheme aterm) = above [ppHeader, text " ", ppTerm]
where
ppHeader = wrapped "Error in ATerm."
ppTerm = text "? in term : " >|< pp aterm
ppDiagnosis (ArityMismatch aterm conid arity expectedArities)
= above [ppHeader, text " ", ppTerm]
where
ppHeader = wrapped $ "Error in ATerm: " ++ conid ++ " " ++
describeExpectedArities expectedArities ++
", but " ++ describeGivenArity arity ++ "."
ppTerm = text "? in term : " >|< pp aterm
ppDiagnosis (SchemeMismatch aterm scheme expected)
= above [ppHeader, text " ", ppUnexpected, ppExpected, ppTerm]
where
ppHeader = wrapped $ "Error in ATerm."
ppUnexpected = text "? unexpected : " >|< wrapped (describeScheme scheme)
ppExpected = text "? expected : " >|< (wrapped . disjunction)
(map describeScheme expected)
ppTerm = text "? in term : " >|< pp aterm
describeScheme :: Scheme -> String
describeScheme IntegerTerm = "integer literal"
describeScheme FloatTerm = "floating-point literal"
describeScheme StringTerm = "string literal"
describeScheme (AppTerm conid _) = conid
describeScheme (TupleTerm n) = show n ++ "-tuple"
describeScheme ListTerm = "list"
describeGivenArity :: Int -> String
describeGivenArity 0 = "none were given"
describeGivenArity 1 = "1 was given"
describeGivenArity arity = show arity ++ " were given"
describeExpectedArities :: [Int] -> String
describeExpectedArities = descr . sort . nub
where
descr [0] = "takes no arguments"
descr [0, 1] = "takes 0 or 1 argument"
descr arities = "takes " ++ disjunction (map show arities) ++ " arguments"
disjunction :: [String] -> String
disjunction [x] = x
disjunction [x, y] = x ++ " or " ++ y
disjunction [x, y, z] = x ++ ", " ++ y ++ ", or " ++ z
disjunction (x : xs) = x ++ ", " ++ disjunction xs