-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbrainf.hs
More file actions
115 lines (106 loc) · 3.63 KB
/
brainf.hs
File metadata and controls
115 lines (106 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import Control.Monad (when)
import Control.Monad.Except
import Control.Monad.State (MonadIO, evalStateT)
import Control.Monad.Trans.State (StateT, get, put, runStateT)
import Data.Array (Array, listArray, (!))
import Data.Binary (Word8)
import Data.Char (ord)
import qualified Data.IntMap as IntMap
import System.Environment (getArgs)
import System.IO
data BfState = BfState
{ mem :: IntMap.IntMap Word8
, loop :: Int
, srcIx :: Int
, memIx :: Int
}
data BfError = OutOfBoundsError
deriving (Show)
type BfEval = (StateT BfState (ExceptT BfError IO)) ()
type Source = Array Int Char
forward :: Source -> BfEval
forward src = do
st <- get
let l = loop st
sIx = srcIx st
when (l > 0) $ do
if sIx == length src
then throwError OutOfBoundsError
else case src ! sIx of
'[' -> put st{loop = l + 1, srcIx = sIx + 1} >> forward src
']' ->
if l == 1
then put st{loop = 0}
else put st{loop = l - 1, srcIx = sIx + 1} >> forward src
_ -> put st{srcIx = sIx + 1} >> forward src
backward :: Source -> BfEval
backward src = do
st <- get
let l = loop st
sIx = srcIx st
when (l > 0) $ do
if sIx < 0
then throwError OutOfBoundsError
else case src ! sIx of
'[' ->
if l == 1
then put st{loop = 0}
else put st{loop = l - 1, srcIx = sIx - 1} >> backward src
']' -> put st{loop = l + 1, srcIx = sIx - 1} >> backward src
_ -> put st{srcIx = sIx - 1} >> backward src
brainf :: Source -> BfEval
brainf src = do
st <- get
let m = mem st
mIx = memIx st
sIx = srcIx st
v = IntMap.findWithDefault 0 mIx m
if sIx == length src
then liftIO $ putStr "\n"
else do
case src ! sIx of
'+' -> do
put st{mem = IntMap.insert mIx (v + 1) m, srcIx = sIx + 1}
'-' -> do
put st{mem = IntMap.insert mIx (v - 1) m, srcIx = sIx + 1}
'>' -> do
put st{memIx = (mIx + 1) `rem` 30000, srcIx = sIx + 1}
'<' -> do
put st{memIx = if mIx == 0 then 29999 else mIx - 1, srcIx = sIx + 1}
'[' -> do
if v == 0
then put st{loop = 1, srcIx = sIx + 1} >> forward src
else put st{srcIx = sIx + 1}
']' -> do
if v /= 0
then put st{loop = 1, srcIx = sIx - 1} >> backward src
else put st{srcIx = sIx + 1}
',' -> do
c <- liftIO getChar
put st{mem = IntMap.insert mIx ((toEnum . fromEnum) c) m, srcIx = sIx + 1}
'.' -> do
liftIO $ putChar $ (toEnum . fromEnum) v
liftIO $ hFlush stdout
put st{srcIx = sIx + 1}
_ -> put st{srcIx = sIx + 1}
brainf src
initState :: BfState
initState =
BfState
{ mem = IntMap.empty
, srcIx = 0
, memIx = 0
, loop = 0
}
main :: IO ()
main = do
args <- getArgs
case args of
[fname] -> do
file <- readFile fname
let src = listArray (0, length file - 1) file
res <- runExceptT (runStateT (brainf src) (initState))
case res of
Left e -> print e
Right _ -> return ()
_ -> print "usage: ./brainf <filepath>"