gdritter repos simple-befunge / 38ff1f1
Improved error messages Getty Ritter 9 years ago
1 changed file(s) with 46 addition(s) and 15 deletion(s). Collapse all Expand all
55 import Control.Monad (forever, void)
66 import Control.Monad.IO.Class (liftIO)
77 import Control.Monad.State (StateT (runStateT), get, modify, put)
8 import Data.Array (Array, array, (!), (//))
8 import Data.Array (Array, array, bounds, (!), (//))
99 import Data.Char (chr, isDigit, ord)
10 import Data.Maybe (fromMaybe)
10 import Data.Maybe (fromMaybe, listToMaybe)
1111 import System.Environment (getArgs)
12 import System.Exit (exitSuccess)
12 import System.Exit (exitFailure, exitSuccess)
1313 import System.IO (hFlush, stdout)
1414 import System.Random (randomIO)
1515
2424 } deriving (Eq, Show)
2525 type FungeM a = StateT FungeState IO a
2626
27 maybeRead :: Read a => String -> Maybe a
28 maybeRead = fmap fst . listToMaybe . reads
29
30 finish :: FungeM a
31 finish = liftIO $ do { putStrLn ""; exitSuccess }
32
2733 terminate :: String -> FungeM a
28 terminate s = liftIO $ do
29 putStrLn ("\n" ++ s)
30 exitSuccess
34 terminate s = do
35 st <- get
36 liftIO $ do
37 let l = location st
38 let i = board st ! l
39 putStr ("\nbefunge: instr (" ++ [i] ++ ") at " ++ show l ++ "\n ")
40 putStrLn s
41 exitFailure
3142
3243 debug :: FungeM ()
3344 debug = do
7081 x <- liftIO randomIO
7182 setDirection (toEnum x)
7283
84 inBounds :: (Int, Int) -> Board -> Bool
85 inBounds (x, y) board = x >= lx && y >= ly && x <= hx && y <= hy
86 where ((lx, ly), (hx, hy)) = bounds board
87
7388 fungeIf :: Direction -> Direction -> FungeM ()
7489 fungeIf thenDir elseDir = do
7590 c <- pop
85100 goDir L (x, y) = (x - 1, y)
86101
87102 move :: FungeM ()
88 move = modify go
89 where go st@(FS { direction = d, location = l }) =
90 st { location = goDir d l }
103 move = do
104 st <- get
105 let newLoc = goDir (direction st) (location st)
106 if inBounds newLoc (board st)
107 then put st { location = newLoc }
108 else terminate ("About to move to invalid location: " ++ show newLoc)
91109
92110 getInstr :: FungeM ()
93111 getInstr = do
94112 y <- pop
95113 x <- pop
96114 st <- get
97 push (ord (board st ! (x, y)))
115 if inBounds (x, y) (board st)
116 then push (ord (board st ! (x, y)))
117 else terminate ("Invalid board location: " ++ show (x, y))
98118
99119 putInstr :: FungeM ()
100120 putInstr = do
101121 y <- pop
102122 x <- pop
103123 c <- pop
104 modify (\ st -> st { board = board st // [((x, y), chr c)] })
124 st <- get
125 if inBounds (x, y) (board st)
126 then modify (\ st -> st { board = board st // [((x, y), chr c)] })
127 else terminate ("Invalid board location: " ++ show (x, y))
105128
106129 step :: Char -> FungeM ()
107130 step '+' = binOp (+)
130153 step '&' = do
131154 liftIO (putStr "num> " >> hFlush stdout)
132155 n <- liftIO getLine
133 push (read n)
156 case maybeRead n of
157 Just n' -> push n'
158 Nothing -> terminate ("Invalid number: " ++ show n)
134159 step '~' = do
135160 liftIO (putStr "chr> " >> hFlush stdout)
136161 n <- liftIO getLine
137 push (ord (head n))
138 step '@' = terminate "Finished"
162 case n of
163 [c] -> push (ord c)
164 [] -> terminate "No character given"
165 _ -> terminate ("Expected character, got string: " ++ show n)
166 step '@' = finish
139167 step n | isDigit n = push (ord n - ord '0')
140168 step _ = return ()
141169
179207 runFunge :: FungeState -> IO ()
180208 runFunge st = void (runStateT (forever run) st)
181209
210 usage :: String
211 usage = "USAGE: befunge [filename]"
212
182213 main :: IO ()
183214 main = do
184215 args <- getArgs
185216 case args of
186 [] -> putStrLn "No filename given!"
217 [] -> putStrLn usage
187218 (f:_) -> do
188219 c <- readFile f
189220 runFunge (buildInitialState c)