-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBrainfuck.hs
169 lines (142 loc) · 3.72 KB
/
Brainfuck.hs
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
module Brainfuck where
import Control.Monad.State as ST
import Data.ByteString.Internal
import Data.Either
import Data.List
import Data.Maybe
import Data.Word
import Debug.Trace
import System.Environment
import System.IO
import Text.Parsec
import Text.Parsec.String
data Token
= JEZ
| JNZ
| INC
| DEC
| INCB
| DECB
| OUT
| IN
deriving (Show, Eq)
data Jump
= Jez Int
| Jnz Int
deriving (Show, Eq)
data Stmt
= Inc
| Dec
| IncB
| DecB
deriving (Show, Eq)
data IOStmt
= In
| Out
deriving (Show, Eq)
data Instr
= IOStmt IOStmt
| Jump Jump
| Stmt Stmt
deriving (Show, Eq)
toToken :: Char -> Token
toToken '>' = INC
toToken '<' = DEC
toToken '[' = JEZ
toToken ']' = JNZ
toToken '+' = INCB
toToken '-' = DECB
toToken '.' = OUT
toToken ',' = IN
instr :: Parser Token
instr = toToken <$> oneOf "><+-[],."
type RawProgram = [Token]
type Program = [Instr]
p :: Parser [Token]
p = spaces *> sepEndBy instr spaces <* spaces
toProgram :: RawProgram -> Program -- Should be a (Maybe Program) (unbalanced jumps)
toProgram is = fromRaw pairs <$> numbered
where
numbered = zip [1 ..] is
pairs = pairJumps $ filter (\i -> snd i `elem` [JNZ, JEZ]) numbered
fromRaw :: [(Int, Int)] -> (Int, Token) -> Instr
fromRaw ps (n, JEZ) = Jump (Jez (fromJust (lookup n ps)))
fromRaw ps (n, JNZ) = Jump (Jnz (fromJust (lookup n ps)))
fromRaw ps (n, INC) = Stmt Inc
fromRaw ps (n, DEC) = Stmt Dec
fromRaw ps (n, INCB) = Stmt IncB
fromRaw ps (n, DECB) = Stmt DecB
fromRaw ps (n, IN) = IOStmt In
fromRaw ps (n, OUT) = IOStmt Out
-- Finds each pair of jumps and pairs them together
pairJumps :: [(Int, Token)] -> [(Int, Int)]
pairJumps js = concatMap (\((i, _), (i', _)) -> [(i, i'), (i', i)]) (go js [])
where
go [] s = []
go (j : js) [] = go js [j]
go (j : js) s@(j' : js') = if snd j == snd j' then go js (j : s) else (j, j') : go js js'
data VM = VM
{ dp :: Int,
ip :: Int,
memory :: [Word8]
}
deriving (Show, Eq)
incMemory :: [Word8] -> Int -> [Word8]
incMemory m i = take i m ++ [succ (m !! i)] ++ drop (i + 1) m
decMemory :: [Word8] -> Int -> [Word8]
decMemory m i = take i m ++ [pred (m !! i)] ++ drop (i + 1) m
modMemory :: [Word8] -> Int -> Word8 -> [Word8]
modMemory m i n = take i m ++ [n] ++ drop (i + 1) m
incIp :: VM -> VM
incIp s = s {ip = succ (ip s)}
currentByte :: VM -> Word8
currentByte s = memory s !! dp s
evalJump :: Jump -> VM -> VM
evalJump (Jez n) s = if currentByte s == 0 then s {ip = n - 1} else s
evalJump (Jnz n) s = if currentByte s /= 0 then s {ip = n - 1} else s
evalStmt :: Stmt -> VM -> VM
evalStmt Inc s = s {dp = succ (dp s)}
evalStmt Dec s = s {dp = pred (dp s)}
evalStmt IncB s = s {memory = incMemory (memory s) (dp s)}
evalStmt DecB s = s {memory = decMemory (memory s) (dp s)}
evalIOStmt :: IOStmt -> VM -> IO VM
evalIOStmt In s = do
input <- getChar
let n = c2w input
return $ s {memory = modMemory (memory s) (dp s) n}
evalIOStmt Out s = do
let n = w2c $ currentByte s
putChar n
return s
eval :: Program -> ST.StateT VM IO ()
eval is = do
m <- get
if length is <= ip m
then return ()
else do
let i = is !! ip m
-- liftIO $ print m
case i of
Jump j -> do
put $ incIp (evalJump j m)
eval is
Stmt s -> do
put $ incIp (evalStmt s m)
eval is
IOStmt ios -> do
newState <- liftIO $ evalIOStmt ios m
put $ incIp newState
eval is
initState = VM {dp = 0, ip = 0, memory = replicate 10 0}
run :: String -> IO VM
run is = execStateT (eval prog) initState
where
prog = case parse p "" is of
Right is -> toProgram is
Left _ -> error "Noo"
main :: IO VM
main = do
args <- getArgs
source <- readFile (head args)
-- print source
run source