92 Coming from Java or C++, you might think Scala is a rather esoteric |
92 Coming from Java or C++, you might think Scala is a rather esoteric |
93 programming language. But remember, some serious companies have built |
93 programming language. But remember, some serious companies have built |
94 their business on |
94 their business on |
95 Scala.\footnote{\url{https://en.wikipedia.org/wiki/Scala_(programming_language)\#Companies}} |
95 Scala.\footnote{\url{https://en.wikipedia.org/wiki/Scala_(programming_language)\#Companies}} |
96 I claim functional programming is not a fad. And there are far, far |
96 I claim functional programming is not a fad. And there are far, far |
97 more esoteric languages out there. One is called \emph{brainf***}. You |
97 more esoteric languages out there. One is called \emph{brainf***}. |
|
98 \here{https://esolangs.org/wiki/Brainfuck} |
|
99 You |
98 are asked in this part to implement an interpreter for |
100 are asked in this part to implement an interpreter for |
99 this language. |
101 a slight extension of this language. |
100 |
102 |
101 Urban M\"uller developed brainf*** in 1993. A close relative of this |
103 Urban M\"uller developed the original version of brainf*** in 1993. A close |
102 language was already introduced in 1964 by Corado B\"ohm, an Italian |
104 relative of this language was already introduced in 1964 by Corado |
103 computer pioneer. The main feature of brainf*** is its minimalistic |
105 B\"ohm, an Italian computer pioneer. The main feature of brainf*** is |
104 set of instructions---just 8 instructions in total and all of which |
106 its minimalistic set of instructions---just 8 instructions in total |
105 are single characters. Despite the minimalism, this language has been |
107 and all of which are single characters. Despite the minimalism, this |
106 shown to be Turing complete\ldots{}if this doesn't ring any bell with |
108 language has been shown to be Turing complete\ldots{}if this doesn't |
107 you: it roughly means that every(!) algorithm can, in principle, |
109 ring any bell with you: it roughly means that every(!) algorithm can, |
108 be implemented in brainf***. It just takes a lot of determination and |
110 in principle, be implemented in brainf***. It just takes a lot of |
109 quite a lot of memory resources. Some relatively sophisticated sample |
111 determination and quite a lot of memory resources. |
110 programs in brainf*** are given in the file \texttt{bf.scala}, including |
112 |
111 a brainf*** program for the Sierpinski triangle and the Mandelbrot set. |
113 Some relatively sophisticated sample programs in brainf*** are given |
112 There seems to be even a dedicated Windows IDE for bf programs, though |
114 in the file \texttt{bf.scala}, including a brainf*** program for the |
113 I am not sure whether this is just an elaborate April fools' joke---judge |
115 Sierpinski triangle and the Mandelbrot set. There seems to be even a |
114 yourself: |
116 dedicated Windows IDE for bf programs, though I am not sure whether |
|
117 this is just an elaborate April fools' joke---judge yourself: |
115 |
118 |
116 \begin{center} |
119 \begin{center} |
117 \url{https://www.microsoft.com/en-us/p/brainf-ck/9nblgggzhvq5} |
120 \url{https://www.microsoft.com/en-us/p/brainf-ck/9nblgggzhvq5} |
118 \end{center} \bigskip |
121 \end{center} \bigskip |
119 |
122 |
120 |
123 |
121 \noindent |
124 \noindent |
122 As mentioned above, brainf*** has 8 single-character commands, namely |
125 As mentioned above, the original brainf*** has 8 single-character |
123 \texttt{'>'}, \texttt{'<'}, \texttt{'+'}, \texttt{'-'}, \texttt{'.'}, |
126 commands. Our version of bf++ will contain the commands \texttt{'>'}, |
124 \texttt{','}, \texttt{'['} and \texttt{']'}. Every other character is |
127 \texttt{'<'}, \texttt{'+'}, \texttt{'-'}, \texttt{'.'}, \texttt{'['} |
125 considered a comment. Brainf*** operates on memory cells containing |
128 and \texttt{']'} from the original, and in addition the commands |
|
129 \texttt{'@'}, \texttt{'*'} and \texttt{'\#'}. Every other character |
|
130 is considered a comment. |
|
131 |
|
132 Our interpreter for bf++ operates on memory cells containing |
126 integers. For this it uses a single memory pointer that points at each |
133 integers. For this it uses a single memory pointer that points at each |
127 stage to one memory cell. This pointer can be moved forward by one |
134 stage to one memory cell. This pointer can be moved forward by one |
128 memory cell by using the command \texttt{'>'}, and backward by using |
135 memory cell by using the command \texttt{'>'}, and backward by using |
129 \texttt{'<'}. The commands \texttt{'+'} and \texttt{'-'} increase, |
136 \texttt{'<'}. The commands \texttt{'+'} and \texttt{'-'} increase, |
130 respectively decrease, by 1 the content of the memory cell to which |
137 respectively decrease, by 1 the content of the memory cell to which |
131 the memory pointer currently points to. The commands for input/output |
138 the memory pointer currently points to. The command for output is |
132 are \texttt{','} and \texttt{'.'}. Output works by reading the content |
139 \texttt{'.'} whereby output works by reading the content of the memory |
133 of the memory cell to which the memory pointer points to and printing |
140 cell to which the memory pointer points to and printing it out as an |
134 it out as an ASCII character. Input works the other way, taking some |
141 ASCII character.\footnote{In the originial version of bf, there is also a |
135 user input and storing it in the cell to which the memory pointer |
142 command for input, but we omit it here. All our programs will be |
136 points to. The commands \texttt{'['} and \texttt{']'} are looping |
143 ``autonomous''.} The |
|
144 commands \texttt{'['} and \texttt{']'} are looping |
137 constructs. Everything in between \texttt{'['} and \texttt{']'} is |
145 constructs. Everything in between \texttt{'['} and \texttt{']'} is |
138 repeated until a counter (memory cell) reaches zero. A typical |
146 repeated until a counter (memory cell) reaches zero. A typical |
139 program in brainf*** looks as follows: |
147 program in brainf*** looks as follows: |
140 |
148 |
141 \begin{center} |
149 \begin{center} |
144 +++++..+++.>>.<-.<.+++.------.--------.>>+.>++. |
152 +++++..+++.>>.<-.<.+++.------.--------.>>+.>++. |
145 \end{verbatim} |
153 \end{verbatim} |
146 \end{center} |
154 \end{center} |
147 |
155 |
148 \noindent |
156 \noindent |
149 This one prints out Hello World\ldots{}obviously ;o) |
157 This one prints out Hello World\ldots{}obviously \texttt{;o)} We also |
|
158 add 3 new commands to the bf++ langauge, whose purpose we explain later. |
|
159 |
150 |
160 |
151 \subsubsection*{Tasks (file bf.scala)} |
161 \subsubsection*{Tasks (file bf.scala)} |
152 |
162 |
153 \begin{itemize} |
163 \begin{itemize} |
154 \item[(1)] Write a function that takes a filename (a string) as an argument |
164 \item[(1)] Write a function that takes a filename (a string) as an argument |
155 and requests the corresponding file from disk. It returns the |
165 and requests the corresponding file from disk. It returns the |
156 content of the file as a string. If the file does not exists, |
166 content of the file as a string. If the file does not exists, |
157 the function should return the empty string.\\ |
167 the function should return the empty string.\\ |
158 \mbox{}\hfill[1 Mark] |
168 \mbox{}\hfill[1 Mark] |
159 |
169 |
160 \item[(2)] Brainf*** memory is represented by a \texttt{Map} from |
170 \item[(2)] Brainf**k++ memory is represented by a \texttt{Map} from |
161 integers to integers. The empty memory is represented by |
171 integers to integers. The empty memory is represented by |
162 \texttt{Map()}, that is nothing is stored in the |
172 \texttt{Map()}, that is nothing is stored in the |
163 memory; \texttt{Map(0 -> 1, 2 -> 3)} stores \texttt{1} at |
173 memory; \texttt{Map(0 -> 1, 2 -> 3)} stores \texttt{1} at |
164 memory location \texttt{0}, and at \texttt{2} it stores \texttt{3}. The |
174 memory location \texttt{0}, and at \texttt{2} it stores \texttt{3}. The |
165 convention is that if we query the memory at a location that is |
175 convention is that if we query the memory at a location that is |
176 with the same data, except the new value is stored at the given memory |
186 with the same data, except the new value is stored at the given memory |
177 pointer.\hfill[1 Mark] |
187 pointer.\hfill[1 Mark] |
178 |
188 |
179 \item[(3)] Write two functions, \texttt{jumpRight} and |
189 \item[(3)] Write two functions, \texttt{jumpRight} and |
180 \texttt{jumpLeft}, that are needed to implement the loop constructs |
190 \texttt{jumpLeft}, that are needed to implement the loop constructs |
181 in brainf***. They take a program (a \texttt{String}) and a program |
191 in brainf**k++. They take a program (a \texttt{String}) and a program |
182 counter (an \texttt{Int}) as arguments and move right (respectively |
192 counter (an \texttt{Int}) as arguments and move right (respectively |
183 left) in the string in order to find the \textbf{matching} |
193 left) in the string in order to find the \textbf{matching} |
184 opening/closing bracket. For example, given the following program |
194 opening/closing bracket. For example, given the following program |
185 with the program counter indicated by an arrow: |
195 with the program counter indicated by an arrow: |
186 |
196 |
187 \begin{center} |
197 \begin{center} |
188 \texttt{--[\barbelow{.}.+>--],>,++} |
198 \texttt{--[\barbelow{.}.+>--].>.++} |
189 \end{center} |
199 \end{center} |
190 |
200 |
191 then the matching closing bracket is in 9th position (counting from 0) and |
201 then the matching closing bracket is in 9th position (counting from 0) and |
192 \texttt{jumpRight} is supposed to return the position just after this |
202 \texttt{jumpRight} is supposed to return the position just after this |
193 |
203 |
194 \begin{center} |
204 \begin{center} |
195 \texttt{--[..+>--]\barbelow{,}>,++} |
205 \texttt{--[..+>--]\barbelow{.}>.++} |
196 \end{center} |
206 \end{center} |
197 |
207 |
198 meaning it jumps to after the loop. Similarly, if you are in 8th position, |
208 meaning it jumps to after the loop. Similarly, if you are in 8th position, |
199 then \texttt{jumpLeft} is supposed to jump to just after the opening |
209 then \texttt{jumpLeft} is supposed to jump to just after the opening |
200 bracket (that is jumping to the beginning of the loop): |
210 bracket (that is jumping to the beginning of the loop): |
201 |
211 |
202 \begin{center} |
212 \begin{center} |
203 \texttt{--[..+>-\barbelow{-}],>,++} |
213 \texttt{--[..+>-\barbelow{-}].>.++} |
204 \qquad$\stackrel{\texttt{jumpLeft}}{\longrightarrow}$\qquad |
214 \qquad$\stackrel{\texttt{jumpLeft}}{\longrightarrow}$\qquad |
205 \texttt{--[\barbelow{.}.+>--],>,++} |
215 \texttt{--[\barbelow{.}.+>--].>.++} |
206 \end{center} |
216 \end{center} |
207 |
217 |
208 Unfortunately we have to take into account that there might be |
218 Unfortunately we have to take into account that there might be |
209 other opening and closing brackets on the `way' to find the |
219 other opening and closing brackets on the `way' to find the |
210 matching bracket. For example in the brainf*** program |
220 matching bracket. For example in the brain*ck++ program |
211 |
221 |
212 \begin{center} |
222 \begin{center} |
213 \texttt{--[\barbelow{.}.[+>]--],>,++} |
223 \texttt{--[\barbelow{.}.[+>]--].>.++} |
214 \end{center} |
224 \end{center} |
215 |
225 |
216 we do not want to return the index for the \texttt{'-'} in the 9th |
226 we do not want to return the index for the \texttt{'-'} in the 9th |
217 position, but the program counter for \texttt{','} in 12th |
227 position, but the program counter for \texttt{'.'} in 12th |
218 position. The easiest to find out whether a bracket is matched is by |
228 position. The easiest to find out whether a bracket is matched is by |
219 using levels (which are the third argument in \texttt{jumpLeft} and |
229 using levels (which are the third argument in \texttt{jumpLeft} and |
220 \texttt{jumpLeft}). In case of \texttt{jumpRight} you increase the |
230 \texttt{jumpLeft}). In case of \texttt{jumpRight} you increase the |
221 level by one whenever you find an opening bracket and decrease by |
231 level by one whenever you find an opening bracket and decrease by |
222 one for a closing bracket. Then in \texttt{jumpRight} you are looking |
232 one for a closing bracket. Then in \texttt{jumpRight} you are looking |
223 for the closing bracket on level \texttt{0}. For \texttt{jumpLeft} you |
233 for the closing bracket on level \texttt{0}. For \texttt{jumpLeft} you |
224 do the opposite. In this way you can find \textbf{matching} brackets |
234 do the opposite. In this way you can find \textbf{matching} brackets |
225 in strings such as |
235 in strings such as |
226 |
236 |
227 \begin{center} |
237 \begin{center} |
228 \texttt{--[\barbelow{.}.[[-]+>[.]]--],>,++} |
238 \texttt{--[\barbelow{.}.[[-]+>[.]]--].>.++} |
229 \end{center} |
239 \end{center} |
230 |
240 |
231 for which \texttt{jumpRight} should produce the position: |
241 for which \texttt{jumpRight} should produce the position: |
232 |
242 |
233 \begin{center} |
243 \begin{center} |
234 \texttt{--[..[[-]+>[.]]--]\barbelow{,}>,++} |
244 \texttt{--[..[[-]+>[.]]--]\barbelow{.}>.++} |
235 \end{center} |
245 \end{center} |
236 |
246 |
237 It is also possible that the position returned by \texttt{jumpRight} or |
247 It is also possible that the position returned by \texttt{jumpRight} or |
238 \texttt{jumpLeft} is outside the string in cases where there are |
248 \texttt{jumpLeft} is outside the string in cases where there are |
239 no matching brackets. For example |
249 no matching brackets. For example |
240 |
250 |
241 \begin{center} |
251 \begin{center} |
242 \texttt{--[\barbelow{.}.[[-]+>[.]]--,>,++} |
252 \texttt{--[\barbelow{.}.[[-]+>[.]]--.>.++} |
243 \qquad$\stackrel{\texttt{jumpRight}}{\longrightarrow}$\qquad |
253 \qquad$\stackrel{\texttt{jumpRight}}{\longrightarrow}$\qquad |
244 \texttt{--[..[[-]+>[.]]-->,++\barbelow{\;\phantom{+}}} |
254 \texttt{--[..[[-]+>[.]]-->.++\barbelow{\;\phantom{+}}} |
245 \end{center} |
255 \end{center} |
246 \hfill[2 Marks] |
256 \hfill[2 Marks] |
247 |
257 |
248 |
258 |
249 \item[(4)] Write a recursive function \texttt{compute} that runs a |
259 \item[(4)] Write a recursive function \texttt{compute} that runs a |
250 brainf*** program. It takes a program, a program counter, a memory |
260 brain*u*k++ program. It takes a program, a program counter, a memory |
251 pointer and a memory as arguments. If the program counter is outside |
261 pointer and a memory as arguments. If the program counter is outside |
252 the program string, the execution stops and \texttt{compute} returns the |
262 the program string, the execution stops and \texttt{compute} returns the |
253 memory. If the program counter is inside the string, it reads the |
263 memory. If the program counter is inside the string, it reads the |
254 corresponding character and updates the program counter \texttt{pc}, |
264 corresponding character and updates the program counter \texttt{pc}, |
255 memory pointer \texttt{mp} and memory \texttt{mem} according to the |
265 memory pointer \texttt{mp} and memory \texttt{mem} according to the |
256 rules shown in Figure~\ref{comms}. It then calls recursively |
266 rules shown in Figure~\ref{comms}. It then calls recursively |
257 \texttt{compute} with the updated data. The most convenient way to |
267 \texttt{compute} with the updated data. The most convenient way to |
258 implement the brainf**k rules in Scala is to use pattern-matching |
268 implement the brainf**k++ rules in Scala is to use pattern-matching |
259 and to calculate a triple consisting of the updated \texttt{pc}, |
269 and to calculate a triple consisting of the updated \texttt{pc}, |
260 \texttt{mp} and \texttt{mem}. |
270 \texttt{mp} and \texttt{mem}. |
261 |
271 |
262 Write another function \texttt{run} that calls \texttt{compute} with a |
272 Write another function \texttt{run} that calls \texttt{compute} with a |
263 given brainfu** program and memory, and the program counter and memory pointer |
273 given brainfu*k++ program and memory, and the program counter and memory pointer |
264 set to~$0$. Like \texttt{compute}, it returns the memory after the execution |
274 set to~$0$. Like \texttt{compute}, it returns the memory after the execution |
265 of the program finishes. You can test your brainf**k interpreter with the |
275 of the program finishes. You can test your brainf**k++ interpreter with the |
266 Sierpinski triangle or the Hello world programs (they seem to be particularly |
276 Sierpinski triangle or the Hello world programs (they seem to be particularly |
267 useful for debugging purposes), or have a look at |
277 useful for debugging purposes), or have a look at |
268 |
278 |
269 \begin{center} |
279 \begin{center} |
270 \url{https://esolangs.org/wiki/Brainfuck} |
280 \url{https://esolangs.org/wiki/Brainfuck} |
297 \hfill\texttt{'.'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
307 \hfill\texttt{'.'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
298 $\bullet$ & $\texttt{pc} + 1$\\ |
308 $\bullet$ & $\texttt{pc} + 1$\\ |
299 $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\ |
309 $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\ |
300 $\bullet$ & print out \,\texttt{mem(mp)} as a character\\ |
310 $\bullet$ & print out \,\texttt{mem(mp)} as a character\\ |
301 \end{tabular}\\\hline |
311 \end{tabular}\\\hline |
302 \hfill\texttt{','} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
312 %\hfill\texttt{','} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
303 $\bullet$ & $\texttt{pc} + 1$\\ |
313 % $\bullet$ & $\texttt{pc} + 1$\\ |
304 $\bullet$ & $\texttt{mp}$ unchanged\\ |
314 % $\bullet$ & $\texttt{mp}$ unchanged\\ |
305 $\bullet$ & \texttt{mem} updated with \texttt{mp -> \textrm{input}}\\ |
315 % $\bullet$ & \texttt{mem} updated with \texttt{mp -> \textrm{input}}\\ |
306 \multicolumn{2}{@{}l}{the input is given by \texttt{Console.in.read().toByte}} |
316 % \multicolumn{2}{@{}l}{the input is given by \texttt{Console.in.read().toByte}} |
307 \end{tabular}\\\hline |
317 % \end{tabular}\\\hline |
308 \hfill\texttt{'['} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
318 \hfill\texttt{'['} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
309 \multicolumn{2}{@{}l}{if \texttt{mem(mp) == 0} then}\\ |
319 \multicolumn{2}{@{}l}{if \texttt{mem(mp) == 0} then}\\ |
310 $\bullet$ & $\texttt{pc = jumpRight(prog, pc + 1, 0)}$\\ |
320 $\bullet$ & $\texttt{pc = jumpRight(prog, pc + 1, 0)}$\\ |
311 $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\medskip\\ |
321 $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\medskip\\ |
312 \multicolumn{2}{@{}l}{otherwise if \texttt{mem(mp) != 0} then}\\ |
322 \multicolumn{2}{@{}l}{otherwise if \texttt{mem(mp) != 0} then}\\ |
319 $\bullet$ & $\texttt{pc = jumpLeft(prog, pc - 1, 0)}$\\ |
329 $\bullet$ & $\texttt{pc = jumpLeft(prog, pc - 1, 0)}$\\ |
320 $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\medskip\\ |
330 $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\medskip\\ |
321 \multicolumn{2}{@{}l}{otherwise if \texttt{mem(mp) == 0} then}\\ |
331 \multicolumn{2}{@{}l}{otherwise if \texttt{mem(mp) == 0} then}\\ |
322 $\bullet$ & $\texttt{pc} + 1$\\ |
332 $\bullet$ & $\texttt{pc} + 1$\\ |
323 $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\ |
333 $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\ |
324 \end{tabular}\\\hline |
334 \end{tabular}\\\hline |
|
335 \hfill\texttt{'@'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
|
336 $\bullet$ & $\texttt{pc} + 1$\\ |
|
337 $\bullet$ & $\texttt{mp}$ unchanged\\ |
|
338 $\bullet$ & \texttt{mem} updated with \texttt{mp -> \textrm{input}}\\ |
|
339 \multicolumn{2}{@{}l}{the input is given by \texttt{Console.in.read().toByte}} |
|
340 \end{tabular}\\\hline |
|
341 \hfill\texttt{'*'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
|
342 $\bullet$ & $\texttt{pc} + 1$\\ |
|
343 $\bullet$ & $\texttt{mp}$ unchanged\\ |
|
344 $\bullet$ & \texttt{mem} updated with \texttt{mp -> \textrm{input}}\\ |
|
345 \multicolumn{2}{@{}l}{the input is given by \texttt{Console.in.read().toByte}} |
|
346 \end{tabular}\\\hline |
|
347 \hfill\texttt{'\#'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
|
348 $\bullet$ & $\texttt{pc} + 1$\\ |
|
349 $\bullet$ & $\texttt{mp}$ unchanged\\ |
|
350 $\bullet$ & \texttt{mem} updated with \texttt{mp -> \textrm{input}}\\ |
|
351 \multicolumn{2}{@{}l}{the input is given by \texttt{Console.in.read().toByte}} |
|
352 \end{tabular}\\\hline |
325 any other char & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
353 any other char & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} |
326 $\bullet$ & $\texttt{pc} + 1$\\ |
354 $\bullet$ & $\texttt{pc} + 1$\\ |
327 $\bullet$ & \texttt{mp} and \texttt{mem} unchanged |
355 $\bullet$ & \texttt{mp} and \texttt{mem} unchanged |
328 \end{tabular}\\ |
356 \end{tabular}\\ |
329 \hline |
357 \hline |