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
116
117
118
119
120
121
122
123
124
125
|
module Brainfuck where
import Control.Monad
import Control.Monad.State.Class
import Control.Monad.Trans.State (StateT, evalStateT)
import Control.Monad.IO.Class
import Data.Word
import Data.Maybe
import Data.Char
newtype Zipper a = Zipper ([a], [a])
fromList :: [a] -> Zipper a
fromList l = Zipper ([], l)
infiniteZipper :: a -> Zipper a
infiniteZipper x = Zipper (repeat x, repeat x)
forward :: Zipper a -> Zipper a
forward (Zipper ( _, [])) = error "Can't go forward on the last element"
forward (Zipper (xs, y:ys)) = Zipper (y:xs, ys)
back :: Zipper a -> Zipper a
back (Zipper ( [], _)) = error "Can't go back on the first element"
back (Zipper (x:xs, ys)) = Zipper (xs, x:ys)
peek :: Zipper a -> Maybe a
peek (Zipper (_, x:_)) = Just x
peek _ = Nothing
write :: a -> Zipper a -> Zipper a
write _ (Zipper ( _, [])) = error "Can't write after last element"
write x (Zipper (ys, _:xs)) = Zipper (ys, x:xs)
applyTo :: (a -> a) -> Zipper a -> Zipper a
applyTo f z = case peek z of
Nothing -> error "Can't apply to the end of the zipper"
Just x -> flip write z . f $ x
newtype Brainfuck a = Brainfuck { exitBrainfuck :: StateT BrainfuckState IO a }
deriving (Functor, Applicative, Monad, MonadState BrainfuckState, MonadIO)
data BrainfuckState = BrainfuckState {
cells :: Zipper Word8
, code :: Zipper Char
}
runBrainfuck :: String -> IO ()
runBrainfuck code = flip evalStateT initial . exitBrainfuck $ interpret
>> (liftIO . putStr $ "
")
where initial = BrainfuckState (infiniteZipper 0) (fromList code)
peekB :: (BrainfuckState -> Zipper a) -> Brainfuck (Maybe a)
peekB = fmap peek . gets
modifyCode :: (Zipper Char -> Zipper Char) -> Brainfuck ()
modifyCode f = modify $
\(BrainfuckState cells code) -> BrainfuckState cells . f $ code
modifyCells :: (Zipper Word8 -> Zipper Word8) -> Brainfuck ()
modifyCells f = modify $
\(BrainfuckState cells code) -> flip BrainfuckState code . f $ cells
seekBracket :: Char -> Char -> (Zipper Char -> Zipper Char) -> Brainfuck ()
seekBracket target opposite move = go 0
where go n = modifyCode move >> peekB code >>= \case
Nothing -> return ()
Just c | c == target -> unless (n == 0) $ go (n1)
Just c | c == opposite -> go (n+1)
_ -> go n
interpret :: Brainfuck ()
interpret =
peekB code >>= \case
Nothing -> return ()
Just c -> do
handleChar c
modifyCode forward
interpret
handleChar :: Char -> Brainfuck ()
handleChar '.' = peekB cells >>=
liftIO . putStr . (:[]) . chr . fromIntegral . fromJust
handleChar ',' = liftIO getChar >>=
modifyCells . write . fromIntegral . ord
handleChar '>' = modifyCells forward
handleChar '<' = modifyCells back
handleChar '+' = modifyCells . applyTo $ (+1)
handleChar '-' = modifyCells . applyTo $ (\x -> x1)
handleChar '[' = (fmap (fromIntegral . fromJust) . peekB $ cells)
>>= \case
0 -> seekBracket ']' '[' forward
_ -> return ()
handleChar ']' = (fmap (fromIntegral . fromJust) . peekB $ cells)
>>= \case
0 -> return ()
_ -> seekBracket '[' ']' back
handleChar _ = return () |