module Exception where
import qualified Term
import Term ( Term, Range(Range) )
import qualified Control.Monad.Exception.Synchronous as Exc
import qualified Text.ParserCombinators.Parsec.Error as PErr
import qualified Text.ParserCombinators.Parsec.Pos as Pos
import qualified Text.ParserCombinators.Parsec as Parsec
import qualified Data.List as List
import Data.Bool.HT ( if' )
data Message = Message Type Range String
-- deriving (Show)
data Type = Parse | Term | InOut
deriving (Show, Eq, Ord, Enum)
lineFromMessage :: Message -> [String]
lineFromMessage (Message typ (Range pos _) descr) =
Pos.sourceName pos :
show (Pos.sourceLine pos) : show (Pos.sourceColumn pos) :
stringFromType typ :
head (lines descr) :
[]
statusFromMessage :: Message -> String
statusFromMessage (Message typ (Range pos _) descr) =
stringFromType typ ++ " - " ++
formatPos typ pos ++ " - " ++
flattenMultiline descr
multilineFromMessage :: Message -> String
multilineFromMessage (Message typ (Range pos _) descr) =
stringFromType typ ++ " - " ++
formatPos typ pos ++ "\n" ++
descr
formatPos :: Type -> Pos.SourcePos -> String
formatPos typ pos =
Pos.sourceName pos ++
(case typ of
InOut -> ""
_ ->
':' : show (Pos.sourceLine pos) ++
':' : show (Pos.sourceColumn pos))
stringFromType :: Type -> String
stringFromType typ =
case typ of
Parse -> "parse error"
Term -> "term error"
InOut -> "in/out error"
flattenMultiline :: String -> String
flattenMultiline =
List.intercalate "; " . lines
toParsec :: Message -> Parsec.Parser a
toParsec (Message _ rng msg) = do
Parsec.setPosition $ Term.start rng
fail msg
messageFromParserError :: PErr.ParseError -> Message
messageFromParserError err = Message
Parse
(let p = PErr.errorPos err
in Range p (Pos.updatePosChar p ' '))
(removeLeadingNewline $
PErr.showErrorMessages
"or" "unknown parse error"
"expecting" "unexpected" "end of input" $
PErr.errorMessages err)
removeLeadingNewline :: String -> String
removeLeadingNewline ('\n':str) = str
removeLeadingNewline str = str
dummyRange :: String -> Range
dummyRange f =
let pos = Pos.initialPos f
in Range pos pos
checkRange ::
(Bounded a) =>
Type ->
String -> (Int -> a) -> (a -> Int) ->
a -> a ->
Term ->
Exc.Exceptional Message a
checkRange excType typ fromInt toInt minb maxb (Term.Number rng x) =
if' (x < fromIntegral (toInt minb))
(Exc.throw $ Message excType rng $
typ ++ " argument " ++ show x ++
" is less than minimum value " ++ show (toInt minb)) $
if' (fromIntegral (toInt maxb) < x)
(Exc.throw $ Message excType rng $
typ ++ " argument " ++ show x ++
" is greater than maximum value " ++ show (toInt maxb)) $
return $ fromInt $ fromInteger x
checkRange excType typ _ _ _ _ t =
Exc.throw $
Message excType
(Term.termRange t) (typ ++ " argument is not a number")
checkRangeAuto ::
(Bounded a) =>
Type ->
String -> (Int -> a) -> (a -> Int) ->
Term ->
Exc.Exceptional Message a
checkRangeAuto excType typ fromInt0 toInt0 =
checkRange excType typ fromInt0 toInt0 minBound maxBound
-- also available in explicit-exception>=0.1.7
switchT ::
(Monad m) =>
(e -> m b) -> (a -> m b) ->
Exc.ExceptionalT e m a -> m b
switchT e s m = Exc.switch e s =<< Exc.runExceptionalT m
lift ::
(Monad m) =>
Exc.Exceptional e a -> Exc.ExceptionalT e m a
lift = Exc.ExceptionalT . return