-
Notifications
You must be signed in to change notification settings - Fork 0
/
brainfuck.hs
174 lines (132 loc) · 6.08 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
170
171
172
173
174
import Data.Char
import Data.List
import Data.Array.IArray
import qualified Test.QuickCheck as QC
import System.IO
zeroMemory = 0 : zeroMemory
data State = State {
memory :: [Int],
dataPtr :: Int,
codePtr :: Int,
codes :: String,
output :: String,
loopStartCount :: Int,
loopEndCount :: Int
}
instance Show State where
show (State memory dataPtr codePtr codes output lsc lec) =
"State ( memory [ " ++( show (take 30 memory) ) ++ ", " ++
"dataPtr: " ++ (show dataPtr) ++ ", " ++
"codePtr: " ++ (show codePtr) ++ ", " ++
"currentCode: " ++ (if ( (length codes) <= codePtr) then "!EOF!" else (show (codes!!codePtr))) ++ ", " ++
"currentMemory: " ++ (show (memory!!dataPtr)) ++ ", " ++
"loopStartCount: " ++ (show lsc) ++ ", " ++
"loopEndCount: " ++ (show lec) ++ ", " ++
"output: " ++ (show output) ++ ", " ++
" ] ) "
newState codes = State zeroMemory 0 0 codes [] 0 0
helloWorld = newState "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>."
sierpinski = newState "[-]>++++[<++++++++>-]>++++++++[>++++<-]>>++>>>+>>>+<<<<<<<<<<[-[->+<]>[-<+>>>.<<]>>>[[->++++++++[>++++<-]>.<<[->+<]+>[->++++++++++<<+>]>.[-]>]]+<<<[-[->+<]+>[-<+>>>-[->+<]++>[-<->]<<<]<<<<]++++++++++.+++.[-]<]+++++"
twentySix = newState "+++++[>+++++<-]>+"
hello = newState "++++++++[>++++++++<-]>++++++++.---.+++++++..+++."
nested = newState "+[[[++++>+[-]]]]"
parserTest :: Bool
parserTest = outputMustBe helloWorld "Hello World!\n" &&
outputMustBe hello "HELLO" &&
(memory (run twentySix))!!1 == 26
outputMustBe :: State -> String -> Bool
outputMustBe state desiredOutput = ((output (run state))) == desiredOutput
stepMemory :: State -> Int -> State
stepMemory state stepSize = step (state{ dataPtr = ((dataPtr state) + stepSize) })
stepMemoryRight state = stepMemory state 1
stepMemoryLeft state = stepMemory state (-1)
incMemory state = step (state { memory = modify (memory state) (dataPtr state) (+1) })
decMemory state = step (state { memory = modify (memory state) (dataPtr state) (+(-1)) })
printToOutput state = step ( state { output = ( (output state) ++ ([chr ( (memory state)!!( dataPtr state)) ] ))} )
jumpTo :: Int -> State -> State
jumpTo nextCodePtr state = state { codePtr = nextCodePtr }
step :: State -> State
step state = state{codePtr = ( (codePtr state) + 1)}
-- if the byte at the data pointer is zero, jump it forward to the command after the matching ]
jumpRightIfNeeded :: State -> State
jumpRightIfNeeded state = if (isZero (dataPtr state) (memory state)) then
jumpTo (findLoopEnd (codes state) (codePtr state)) state
else
step state
-- if the byte at the data pointer is nonzero, jump it back to the command after the matching [ command
jumpLeftIfNeeded :: State -> State
jumpLeftIfNeeded state = if (not (isZero (dataPtr state) (memory state))) then
jumpTo (findLoopStart (codes state) (codePtr state)) state
else
step (loopEnded state)
currentAction :: State -> (State -> State)
currentAction (State memory dataPtr codePtr codes output loopStartCount loopEndCount) = case codes!!codePtr of
'>' -> stepMemoryRight
'<' -> stepMemoryLeft
'+' -> incMemory
'-' -> decMemory
'.' -> printToOutput
'[' -> \x -> loopStarted (jumpRightIfNeeded x)
']' -> jumpLeftIfNeeded
' ' -> step
loopStarted :: State -> State
loopStarted state = state{loopStartCount = (loopStartCount state)+1}
loopEnded :: State -> State
loopEnded state = state{loopEndCount = (loopEndCount state)+1}
stepState :: State -> State
stepState state = (currentAction state state)
run :: State -> State
run state = if (not (finished state)) then
run (stepState state)
else
state
finished :: State -> Bool
finished state = (codePtr state) >= length (codes state)
isZero :: Int -> [Int] -> Bool
isZero index list = list!!index == 0
setMemValue :: [Int] -> Int -> Int -> [Int]
setMemValue memory dataPtr value = replace dataPtr ( mod value 255 ) memory
modify :: [Int] -> Int -> (Int -> Int) -> [Int]
modify memory dataPtr apply =
replace dataPtr (mod (apply (memory!!dataPtr)) 255 ) memory
replace :: Int -> Int -> [Int] -> [Int]
replace pos newVal list = take pos list ++ newVal : drop (pos+1) list
--findBackwardsStart :: [Int] -> Int -> Int
--findBackwardsStart :: memory currentPos = 1
findLoopStart :: [Char] -> Int -> Int
findLoopStart codes current =
countUntilLoopsClosed codes (current - 1) 0 0 (-1)
findLoopEnd :: [Char] -> Int -> Int
findLoopEnd codes current =
countUntilLoopsClosed codes (current + 1) 0 0 (1)
loopMarker direction = if (direction < 0) then '[' else ']'
-- count bakcwards or forward while for current loop start/end point
countUntilLoopsClosed :: [Char] -> Int -> Int -> Int -> Int -> Int
countUntilLoopsClosed codes current opened closed direction = if (codes!!current == (loopMarker direction) && (opened - closed) == 0) then
current
else (
case codes!!current of
']' -> countUntilLoopsClosed codes (current + direction) (opened) (closed + 1) direction
'[' -> countUntilLoopsClosed codes (current + direction) (opened + 1) (closed) direction
otherwise -> countUntilLoopsClosed codes (current + direction) (opened) (closed) direction
)
testCountBackwards :: Bool
testCountBackwards =
((countUntilLoopsClosed "[]" 0 0 0 (-1) ) == 0) &&
((countUntilLoopsClosed "[a]" 1 0 0 (-1) ) == 0) &&
((countUntilLoopsClosed ">>[abc]<<" 5 0 0 (-1)) == 2) &&
((countUntilLoopsClosed "abc[a[bcd]e[f]g]9" 14 0 0 (-1)) == 3)
testLoopStartFind :: Bool
testLoopStartFind =
(findLoopStart "a[b[c[d[e]f]g]h]i" 15) == 1 &&
(findLoopStart "[[]]a[[]]" 8) == 5 &&
(findLoopStart "[]" 1) == 0 &&
(findLoopStart "[[]]" 3) == 0 &&
True
testLoopEndFind :: Bool
testLoopEndFind =
(findLoopEnd "a[b[c[d[e]f]g]h]i" 1) == 15 && True
writeResultToFile state fileName = do
outh <- openFile fileName WriteMode
let res = run state
hPutStrLn outh (output res)