Repository navigation
Expand file tree
/
Copy pathBFInterpreter.hs
More file actions
150 lines (132 loc) · 5.81 KB
/
Copy pathBFInterpreter.hs
File metadata and controls
150 lines (132 loc) · 5.81 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
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
module BFInterpreter3 (runCode) where
{-
An interpreter for BrainFuck. Takes a string that is a BrainFuck program,
and TIMEOUT, prints the output of the program. This interpreter differs
from others in a few important ways:
- No error checking; all input will be valid BF programs
- Runs every program, regardless of if it compiles. It does this by checking
syntax, and discarding the portion of the program that is "faulty"
- Implements a timeout so infinite loops exit; this should take a time
The basic interpreter is taken from:
https://github.com/quchen/articles/blob/master/write_yourself_a_brainfuck.md
-}
--POSSIBILITY OF RUNNING TWO INSTANCES OF INTERPRETER TO ALLOW FOR READING; ONE
--COUNTS THE NUMBER OF READS, AND THE NEXT DOES THEM SEQUENTIALLY AND PASSES
--THE INPUT TO A FUNCTION THAT COMBINES THE TWO OF THESE?
runCode :: String -> [Int]
runCode code = runBrainfuck . parseBrainfuck $ checkSyntax 0 "" code
data BrainfuckCommand = GoRight -- >
| GoLeft -- <
| Increment -- +
| Decrement -- -
| Print -- .
| Read -- ,
| LoopL -- [
| LoopR -- ]
| Comment Char -- anything else
deriving Show
type BrainfuckSource = [BrainfuckCommand]
checkSyntax :: Int -> String -> String -> String
checkSyntax 0 s (c:cs) = s ++ checkSyntax (updateB 0 c) [c] cs
checkSyntax b s (c:cs) | b < 0 = ""
| otherwise = checkSyntax (updateB b c) (s++[c]) cs
checkSyntax b s [] | b == 0 = s
| otherwise = ""
updateB :: Int -> Char -> Int
updateB b '[' = b+1
updateB b ']' = b-1
updateB b _ = b
parseBrainfuck :: String -> BrainfuckSource
parseBrainfuck = map charToBF
where
charToBF x = case x of
'>' -> GoRight
'<' -> GoLeft
'+' -> Increment
'-' -> Decrement
'.' -> Print
',' -> Read
'[' -> LoopL
']' -> LoopR
c -> Comment c
data Tape a = Tape [a] -- Left of the pivot element
a -- Pivot element
[a] -- Right of the pivot element
emptyTape :: Tape Int
emptyTape = Tape zeros 0 zeros
where zeros = repeat 0
moveRight :: Tape a -> Tape a
moveRight (Tape ls p (r:rs)) = Tape (p:ls) r rs
moveLeft :: Tape a -> Tape a
moveLeft (Tape (l:ls) p rs) = Tape ls l (p:rs)
runBrainfuck :: BrainfuckSource -> [Int]
runBrainfuck = run 2000 emptyTape . bfSource2Tape
where
bfSource2Tape [] = Tape [] Print []
bfSource2Tape (b:bs) = Tape [] b bs
-- (`run` is defined below)
-- Interpret the command currently focussed on the instruction tape
run :: Int -- Counter
-> Tape Int -- Data tape
-> Tape BrainfuckCommand -- Instruction tape
-> [Int]
run 0 _ _ = []
run n dataTape source@(Tape _ GoRight _) =
advance n (moveRight dataTape) source
run n dataTape source@(Tape _ GoLeft _) =
advance n (moveLeft dataTape) source
run n (Tape l p r) source@(Tape _ Increment _) =
advance n (Tape l (p+1) r) source
run n (Tape l p r) source@(Tape _ Decrement _) =
advance n (Tape l (p-1) r) source
run n dataTape@(Tape _ p _) source@(Tape _ Print _) =
p:advance n dataTape source
run n dataTape@(Tape l _ r) source@(Tape _ Read _) =
advance n (moveRight dataTape) source
--Ignoring reads because we won't need these for our purposes
--Ask Pete about this; can we read an item and unpack it?
run n dataTape@(Tape _ p _) source@(Tape _ LoopL _)
-- If the pivot is zero, jump to the
-- corresponding LoopR instruction
| p == 0 = seekLoopR n 0 dataTape source
-- Otherwise just ignore the `[` and continue
| otherwise = advance n dataTape source
run n dataTape@(Tape _ p _) source@(Tape _ LoopR _)
| p /= 0 = seekLoopL n 0 dataTape source
| otherwise = advance n dataTape source
run n dataTape source@(Tape _ (Comment _) _) = advance n dataTape source
advance :: Int -- Counter
-> Tape Int -- Data tape
-> Tape BrainfuckCommand -- Instruction tape
-> [Int]
advance _ dataTape (Tape _ _ []) = []
advance n dataTape source = run (n-1) dataTape (moveRight source)
-- Move the instruction pointer left until a "[" is found.
-- The first parameter ("b" for balance) retains the current
-- bracket balance to find the matching partner. When b is 1,
-- then the found LoopR would reduce the counter to zero,
-- hence we break even and the search is successful.
seekLoopR :: Int -- Counter
-> Int -- Parenthesis balance
-> Tape Int -- Data tape
-> Tape BrainfuckCommand -- Instruction tape
-> [Int]
seekLoopR n 1 dataTape source@(Tape _ LoopR _) = advance n dataTape source
seekLoopR n b dataTape source@(Tape _ LoopR _) =
seekLoopR n (b-1) dataTape (moveRight source)
seekLoopR n b dataTape source@(Tape _ LoopL _) =
seekLoopR n (b+1) dataTape (moveRight source)
seekLoopR n b dataTape source =
seekLoopR n b dataTape (moveRight source)
seekLoopL :: Int -- Counter
-> Int -- Parenthesis balance
-> Tape Int -- Data tape
-> Tape BrainfuckCommand -- Instruction tape
-> [Int]
seekLoopL n 1 dataTape source@(Tape _ LoopL _) = advance n dataTape source
seekLoopL n b dataTape source@(Tape _ LoopL _) =
seekLoopL n (b-1) dataTape (moveLeft source)
seekLoopL n b dataTape source@(Tape _ LoopR _) =
seekLoopL n (b+1) dataTape (moveLeft source)
seekLoopL n b dataTape source =
seekLoopL n b dataTape (moveLeft source)