module Errors where import Frontend.Parser.Posn import Data.Maybe import qualified GHC.IO.Handle.FD import System.Posix.Internals (c_isatty) import GHC.IO.FD data AhcError = AhcError { errorMessage :: String , errorFilename :: String , errorInlineDesc :: Maybe String , errorBegin :: Posn , errorEnd :: Posn , errorPointers :: [ErrorPointer] } deriving (Eq, Show) emptyError :: AhcError emptyError = AhcError (error "parse errors must have a message") (error "parse errors must have a filename") Nothing (error "parse errors must have a start pos") (error "parse errors must have an end pos") [] data ErrorPointer = ErrorPointer { errorPointerBegin :: Posn , errorPointerEnd :: Posn , errorPointerNote :: Maybe String , errorPointerDrawSquiggle :: Bool } deriving (Eq, Show) showAhcError :: Bool -> [String] -> AhcError -> String showAhcError color code pe = do unlines $ [ bold ++ errorFilename pe ++ ":" ++ show linum ++ ":" ++ show startcol ++ ": " ++ red ++ "parse error:" ++ reset ] ++ map (render bold red sep reset) (mainPointer:errorPointers pe) ++ [ padToMax "" ++ " " ++ errorMessage pe ] where linum = posnLine (errorBegin pe) startcol = posnColm (errorBegin pe) maxln = maximum [ length (show (posnLine e)) | e <- errorEnd pe:map errorPointerEnd (errorPointers pe)] (red, bold, reset, sep) | color = ("\x1b[31m", "\x1b[1m", "\x1b[0m", "\x1b[1;34m \9474 \x1b[0m") | otherwise = ("", "", "", "") padToMax x | length x < maxln = replicate (maxln - length x) ' ' ++ x | otherwise = x mainPointer = ErrorPointer (errorBegin pe) (errorEnd pe) (errorInlineDesc pe) True render bold red sep reset (ErrorPointer start end note squig) = do let linum = posnLine start startcol = posnColm start multiline = linum /= posnLine end width | multiline = 0 | otherwise = max 0 (posnColm (errorEnd pe) - startcol - 1) linum' = padToMax $ show linum line = [ show linum ++ sep ++ code !! (linum - 1) | linum <- [ linum .. posnLine (errorEnd pe) ] ] padding = replicate (length linum') ' ' ++ sep padding' = replicate (length linum') ' ' ++ " " caret = replicate (startcol - 1) ' ' ++ red ++ "^" squiggle | squig = replicate width '~' | otherwise = "" init $ unlines [ padding , init (unlines line) , padding ++ caret ++ squiggle ++ reset ++ fromMaybe "" ((" " ++) <$> note) ] printParseError :: AhcError -> IO () printParseError pe = do code <- lines <$> readFile (errorFilename pe) color <- fmap (1 ==) . c_isatty . fdFD =<< GHC.IO.Handle.FD.handleToFd GHC.IO.Handle.FD.stdout putStr $ showAhcError color code pe pointTo :: HasPosn x => x -> x -> String -> [ErrorPointer] pointTo s e msg = [ErrorPointer (startPosn s) (endPosn e) (Just msg) False]