Skip to content

Commit 354a2be

Browse files
authored
Merge pull request #4 from mheinzel/errorhandling
Improve error handling
2 parents 01890f7 + aca9b01 commit 354a2be

File tree

4 files changed

+38
-26
lines changed

4 files changed

+38
-26
lines changed

Diff for: hython.cabal

+3
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@ executable hython
2929
build-tools: happy
3030
other-modules: Language.Python.Lexer,
3131
Language.Python.Parser,
32+
Language.Python,
3233
REPL,
3334
Hython.Interpreter,
3435
Hython.Statement,
3536
Hython.Environment,
3637
Hython.Types,
3738
Hython.Expression,
3839
Hython.Builtins,
40+
Hython.Name,
41+
Hython.ExceptionHandling
3942
Hython.ControlFlow,
4043
Hython.Call,
4144
Hython.Ref,

Diff for: src/Hython/Interpreter.hs

+21-15
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ import Prelude hiding (readFile)
77

88
import Control.Monad (filterM, forM_, unless, when)
99
import Control.Monad.Cont.Class (MonadCont)
10-
import Control.Monad.Cont (ContT, runContT)
10+
import Control.Monad.Cont (ContT, runContT, callCC)
1111
import Control.Monad.IO.Class (MonadIO, liftIO)
1212
import Control.Monad.State.Strict (StateT, gets, modify, runStateT)
1313
import Data.List (find)
1414
import Data.Text (Text)
1515
import Data.Text.IO (readFile)
1616
import qualified Data.Text as T
1717
import System.Directory (canonicalizePath, doesFileExist, getDirectoryContents)
18-
import System.Exit (exitFailure)
1918
import System.Environment.Executable (splitExecutablePath)
2019
import System.FilePath
2120

@@ -38,6 +37,7 @@ data InterpreterState = InterpreterState
3837
{ stateEnv :: Env
3938
, stateFlow :: Flow Object Continuation
4039
, stateNew :: Bool
40+
, stateErrorMsg :: Maybe String
4141
, stateCurModule :: ModuleInfo
4242
, stateModules :: [ModuleInfo]
4343
, stateResults :: [String]
@@ -87,6 +87,7 @@ defaultInterpreterState path = do
8787
stateCurModule = main,
8888
stateModules = [main],
8989
stateNew = True,
90+
stateErrorMsg = Nothing,
9091
stateResults = []
9192
}
9293
where
@@ -110,17 +111,13 @@ defaultContinueHandler _ = raise "SyntaxError" "'continue' not properly in loop"
110111

111112
defaultExceptionHandler :: Object -> Interpreter ()
112113
defaultExceptionHandler ex = do
113-
case ex of
114+
message <- case ex of
114115
Object info -> do
116+
let cls = T.unpack . className . objectClass $ info
115117
msg <- toStr =<< invoke ex "__str__" []
116-
117-
liftIO $ do
118-
putStr . T.unpack . className . objectClass $ info
119-
putStr ": "
120-
putStrLn msg
121-
_ -> liftIO $ putStrLn "o_O: raised a non-object exception"
122-
123-
liftIO exitFailure
118+
return $ cls ++ ": " ++ msg
119+
_ -> return "o_O: raised a non-object exception"
120+
Interpreter $ modify (\s -> s { stateErrorMsg = Just message })
124121

125122
defaultReturnHandler :: Object -> Interpreter ()
126123
defaultReturnHandler _ = raise "SyntaxError" "'return' outside function"
@@ -149,13 +146,22 @@ runInterpreter state code = case parse code of
149146
let firstTime = stateNew state
150147

151148
(_, newState) <- runStateT (runContT (unwrap $ run firstTime stmts) return) state
152-
let results = stateResults newState
153-
return (Right results, newState { stateNew = False, stateResults = [] })
149+
let results = case stateErrorMsg newState of
150+
Just msg -> Left msg
151+
Nothing -> Right $ stateResults newState
152+
return (results, newState
153+
{ stateNew = False
154+
, stateErrorMsg = Nothing
155+
, stateResults = [] })
154156
where
155157
run firstTime stmts = do
156158
when firstTime $ do
157159
loadBuiltinModules
158160
Environment.moveLocalsToBuiltins
159161

160-
evalBlock stmts
161-
return None
162+
callCC $ \done -> do
163+
ControlFlow.setExceptionHandler (\ex -> do
164+
defaultExceptionHandler ex
165+
done ex)
166+
evalBlock stmts
167+
return None

Diff for: src/Language/Python/Parser.y

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import qualified Language.Python.Lexer as L
1313

1414
%tokentype {L.Token}
1515
%error { parseError }
16+
%monad { Either String } { (>>=) } { return }
1617

1718
%name parseTokens file_input
1819
%name parseLine single_input
@@ -691,15 +692,15 @@ mkName s = Name . T.pack $ s
691692
parse :: Text -> Either String [Statement]
692693
parse code = do
693694
case L.lex code of
694-
Right tokens -> Right $ parseTokens tokens
695-
Left err -> Left $ show err
695+
Right tokens -> parseTokens tokens
696+
Left err -> Left $ "SyntaxError: " ++ show err
696697

697698
parseRepl :: Text -> Either String [Statement]
698699
parseRepl code = do
699700
case L.lex code of
700-
Right tokens -> Right $ parseLine tokens
701+
Right tokens -> parseLine tokens
701702
Left err -> Left $ show err
702703

703-
parseError :: [L.Token] -> a
704-
parseError t = error $ "Parse error: " ++ show t
704+
parseError t = Left $ "SyntaxError: at " ++ show t
705+
705706
}

Diff for: src/Main.hs

+8-6
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import Data.Text.IO (readFile)
1010

1111
import System.Environment
1212
import System.Exit (exitFailure)
13+
import System.IO (hPutStrLn, stderr)
1314
import System.IO.Error
14-
import Text.Printf
1515

1616
import Hython.Interpreter (defaultInterpreterState, runInterpreter)
1717
import REPL (runREPL)
@@ -23,7 +23,8 @@ main = do
2323
[] -> runREPL
2424
[filename] -> runScript filename
2525
_ -> do
26-
putStrLn "Usage: hython <filename>"
26+
progName <- getProgName
27+
hPutStrLn stderr $ "Usage: " ++ progName ++ " [filename]"
2728
exitFailure
2829

2930
runScript :: FilePath -> IO ()
@@ -33,12 +34,13 @@ runScript path = do
3334

3435
(result, _) <- runInterpreter state code
3536
case result of
36-
Left msg -> putStrLn msg
37+
Left msg -> do
38+
hPutStrLn stderr msg
39+
exitFailure
3740
Right _ -> return ()
3841

3942
where
4043
errorHandler :: String -> IOError -> IO Text
4144
errorHandler _ err = do
42-
putStrLn $ printf "Unable to open '%s': file %s" path (ioeGetErrorString err)
43-
_ <- exitFailure
44-
return ""
45+
putStrLn $ "Unable to open '" ++ path ++ "': " ++ ioeGetErrorString err
46+
exitFailure

0 commit comments

Comments
 (0)