251
+ − 1
% !TEX program = xelatex
6
+ − 2
\documentclass{article}
62
+ − 3
\usepackage{../style}
78
+ − 4
\usepackage{../langs}
218
+ − 5
\usepackage{disclaimer}
+ − 6
\usepackage{tikz}
+ − 7
\usepackage{pgf}
+ − 8
\usepackage{pgfplots}
+ − 9
\usepackage{stackengine}
+ − 10
%% \usepackage{accents}
+ − 11
\newcommand\barbelow[1]{\stackunder[1.2pt]{#1}{\raisebox{-4mm}{\boldmath$\uparrow$}}}
+ − 12
6
+ − 13
+ − 14
\begin{document}
+ − 15
241
+ − 16
\section*{Coursework 10 (Scala)}
6
+ − 17
229
+ − 18
This coursework is worth 10\%. It is about a small programming
+ − 19
language called brainf***. The first part is due on 13 December at
+ − 20
11pm; the second, more advanced part, is due on 20 December at
+ − 21
11pm.\bigskip
218
+ − 22
+ − 23
\IMPORTANT{}
62
+ − 24
+ − 25
\noindent
218
+ − 26
Also note that the running time of each part will be restricted to a
+ − 27
maximum of 30 seconds on my laptop.
+ − 28
+ − 29
\DISCLAIMER{}
86
+ − 30
230
+ − 31
\subsection*{Reference Implementation}
+ − 32
+ − 33
As usual, this Scala assignment comes with a reference implementation in form of
241
+ − 34
two \texttt{jar}-files. You can download them from KEATS. They allow you to run any
230
+ − 35
test cases on your own computer. For example you can call Scala on the command line with the
+ − 36
option \texttt{-cp bf.jar} and then query any function from the
+ − 37
\texttt{bf.scala} template file. You have to
+ − 38
prefix the calls with \texttt{CW10a} and \texttt{CW10b}, respectively. For example
+ − 39
+ − 40
+ − 41
\begin{lstlisting}[language={},xleftmargin=1mm,numbers=none,basicstyle=\ttfamily\small]
+ − 42
$ scala -cp bf.jar
+ − 43
scala> import CW10a._
+ − 44
scala> run(load_bff("sierpinski.bf"))
+ − 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
\end{lstlisting}%$
+ − 78
6
+ − 79
218
+ − 80
229
+ − 81
\subsection*{Part 1 (6 Marks)}
218
+ − 82
229
+ − 83
Coming from Java or C++, you might think Scala is a rather esoteric
218
+ − 84
programming language. But remember, some serious companies have built
+ − 85
their business on
+ − 86
Scala.\footnote{\url{https://en.wikipedia.org/wiki/Scala_(programming_language)\#Companies}}
230
+ − 87
I claim functional programming is not a fad. And there are far, far
+ − 88
more esoteric languages out there. One is called \emph{brainf***}. You
+ − 89
are asked in this part to implement an interpreter for
+ − 90
this language.
218
+ − 91
+ − 92
Urban M\"uller developed brainf*** in 1993. A close relative of this
+ − 93
language was already introduced in 1964 by Corado B\"ohm, an Italian
229
+ − 94
computer pioneer. The main feature of brainf*** is its minimalistic
+ − 95
set of instructions---just 8 instructions in total and all of which
+ − 96
are single characters. Despite the minimalism, this language has been
+ − 97
shown to be Turing complete\ldots{}if this doesn't ring any bell with
237
+ − 98
you: it roughly means that every(!) algorithm can, in principle,
229
+ − 99
be implemented in brainf***. It just takes a lot of determination and
+ − 100
quite a lot of memory resources. Some relatively sophisticated sample
+ − 101
programs in brainf*** are given in the file \texttt{bf.scala}, including
247
+ − 102
a brainf*** program for the Sierpinski triangle and the Mandelbrot set.
+ − 103
There seems to be even a dedicated Windows IDE for bf programs, though
+ − 104
I am not sure whether this is just an elaborate April fools' joke---judge
+ − 105
yourself:
+ − 106
+ − 107
\begin{center}
+ − 108
\url{https://www.microsoft.com/en-us/p/brainf-ck/9nblgggzhvq5}
+ − 109
\end{center}
+ − 110
218
+ − 111
+ − 112
\noindent
+ − 113
As mentioned above, brainf*** has 8 single-character commands, namely
+ − 114
\texttt{'>'}, \texttt{'<'}, \texttt{'+'}, \texttt{'-'}, \texttt{'.'},
+ − 115
\texttt{','}, \texttt{'['} and \texttt{']'}. Every other character is
+ − 116
considered a comment. Brainf*** operates on memory cells containing
+ − 117
integers. For this it uses a single memory pointer that points at each
+ − 118
stage to one memory cell. This pointer can be moved forward by one
+ − 119
memory cell by using the command \texttt{'>'}, and backward by using
+ − 120
\texttt{'<'}. The commands \texttt{'+'} and \texttt{'-'} increase,
+ − 121
respectively decrease, by 1 the content of the memory cell to which
+ − 122
the memory pointer currently points to. The commands for input/output
+ − 123
are \texttt{','} and \texttt{'.'}. Output works by reading the content
+ − 124
of the memory cell to which the memory pointer points to and printing
+ − 125
it out as an ASCII character. Input works the other way, taking some
+ − 126
user input and storing it in the cell to which the memory pointer
+ − 127
points to. The commands \texttt{'['} and \texttt{']'} are looping
+ − 128
constructs. Everything in between \texttt{'['} and \texttt{']'} is
+ − 129
repeated until a counter (memory cell) reaches zero. A typical
+ − 130
program in brainf*** looks as follows:
+ − 131
+ − 132
\begin{center}
+ − 133
\begin{verbatim}
230
+ − 134
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.++
+ − 135
+++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
218
+ − 136
\end{verbatim}
+ − 137
\end{center}
+ − 138
+ − 139
\noindent
230
+ − 140
This one prints out Hello World\ldots{}obviously ;o)
218
+ − 141
+ − 142
\subsubsection*{Tasks (file bf.scala)}
109
+ − 143
+ − 144
\begin{itemize}
237
+ − 145
\item[(1)] Write a function that takes a filename (a string) as an argument
229
+ − 146
and requests the corresponding file from disk. It returns the
237
+ − 147
content of the file as a string. If the file does not exists,
229
+ − 148
the function should return the empty string.\\
+ − 149
\mbox{}\hfill[1 Mark]
+ − 150
+ − 151
\item[(2)] Brainf*** memory is represented by a \texttt{Map} from
218
+ − 152
integers to integers. The empty memory is represented by
+ − 153
\texttt{Map()}, that is nothing is stored in the
229
+ − 154
memory; \texttt{Map(0 -> 1, 2 -> 3)} stores \texttt{1} at
+ − 155
memory location \texttt{0}, and at \texttt{2} it stores \texttt{3}. The
218
+ − 156
convention is that if we query the memory at a location that is
+ − 157
\emph{not} defined in the \texttt{Map}, we return \texttt{0}. Write
+ − 158
a function, \texttt{sread}, that takes a memory (a \texttt{Map}) and
237
+ − 159
a memory pointer (an \texttt{Int}) as arguments, and `safely' reads the
218
+ − 160
corresponding memory location. If the \texttt{Map} is not defined at
+ − 161
the memory pointer, \texttt{sread} returns \texttt{0}.
+ − 162
+ − 163
Write another function \texttt{write}, which takes a memory, a
237
+ − 164
memory pointer and an integer value as arguments and updates the
+ − 165
\texttt{Map} with the value at the given memory location. As usual,
218
+ − 166
the \texttt{Map} is not updated `in-place' but a new map is created
237
+ − 167
with the same data, except the new value is stored at the given memory
218
+ − 168
pointer.\hfill[1 Mark]
+ − 169
229
+ − 170
\item[(3)] Write two functions, \texttt{jumpRight} and
237
+ − 171
\texttt{jumpLeft}, that are needed to implement the loop constructs
+ − 172
in brainf***. They take a program (a \texttt{String}) and a program
+ − 173
counter (an \texttt{Int}) as arguments and move right (respectively
218
+ − 174
left) in the string in order to find the \textbf{matching}
+ − 175
opening/closing bracket. For example, given the following program
+ − 176
with the program counter indicated by an arrow:
+ − 177
+ − 178
\begin{center}
+ − 179
\texttt{--[\barbelow{.}.+>--],>,++}
+ − 180
\end{center}
+ − 181
+ − 182
then the matching closing bracket is in 9th position (counting from 0) and
+ − 183
\texttt{jumpRight} is supposed to return the position just after this
109
+ − 184
218
+ − 185
\begin{center}
+ − 186
\texttt{--[..+>--]\barbelow{,}>,++}
+ − 187
\end{center}
+ − 188
237
+ − 189
meaning it jumps to after the loop. Similarly, if you are in 8th position,
218
+ − 190
then \texttt{jumpLeft} is supposed to jump to just after the opening
+ − 191
bracket (that is jumping to the beginning of the loop):
109
+ − 192
218
+ − 193
\begin{center}
+ − 194
\texttt{--[..+>-\barbelow{-}],>,++}
+ − 195
\qquad$\stackrel{\texttt{jumpLeft}}{\longrightarrow}$\qquad
+ − 196
\texttt{--[\barbelow{.}.+>--],>,++}
+ − 197
\end{center}
+ − 198
+ − 199
Unfortunately we have to take into account that there might be
+ − 200
other opening and closing brackets on the `way' to find the
+ − 201
matching bracket. For example in the brainf*** program
+ − 202
+ − 203
\begin{center}
+ − 204
\texttt{--[\barbelow{.}.[+>]--],>,++}
+ − 205
\end{center}
109
+ − 206
218
+ − 207
we do not want to return the index for the \texttt{'-'} in the 9th
+ − 208
position, but the program counter for \texttt{','} in 12th
+ − 209
position. The easiest to find out whether a bracket is matched is by
+ − 210
using levels (which are the third argument in \texttt{jumpLeft} and
+ − 211
\texttt{jumpLeft}). In case of \texttt{jumpRight} you increase the
+ − 212
level by one whenever you find an opening bracket and decrease by
+ − 213
one for a closing bracket. Then in \texttt{jumpRight} you are looking
+ − 214
for the closing bracket on level \texttt{0}. For \texttt{jumpLeft} you
+ − 215
do the opposite. In this way you can find \textbf{matching} brackets
+ − 216
in strings such as
+ − 217
+ − 218
\begin{center}
+ − 219
\texttt{--[\barbelow{.}.[[-]+>[.]]--],>,++}
+ − 220
\end{center}
109
+ − 221
218
+ − 222
for which \texttt{jumpRight} should produce the position:
+ − 223
+ − 224
\begin{center}
+ − 225
\texttt{--[..[[-]+>[.]]--]\barbelow{,}>,++}
+ − 226
\end{center}
+ − 227
+ − 228
It is also possible that the position returned by \texttt{jumpRight} or
+ − 229
\texttt{jumpLeft} is outside the string in cases where there are
+ − 230
no matching brackets. For example
+ − 231
+ − 232
\begin{center}
+ − 233
\texttt{--[\barbelow{.}.[[-]+>[.]]--,>,++}
+ − 234
\qquad$\stackrel{\texttt{jumpRight}}{\longrightarrow}$\qquad
+ − 235
\texttt{--[..[[-]+>[.]]-->,++\barbelow{\;\phantom{+}}}
+ − 236
\end{center}
229
+ − 237
\hfill[2 Marks]
109
+ − 238
+ − 239
230
+ − 240
\item[(4)] Write a recursive function \texttt{compute} that runs a
218
+ − 241
brainf*** program. It takes a program, a program counter, a memory
+ − 242
pointer and a memory as arguments. If the program counter is outside
230
+ − 243
the program string, the execution stops and \texttt{compute} returns the
218
+ − 244
memory. If the program counter is inside the string, it reads the
+ − 245
corresponding character and updates the program counter \texttt{pc},
+ − 246
memory pointer \texttt{mp} and memory \texttt{mem} according to the
+ − 247
rules shown in Figure~\ref{comms}. It then calls recursively
230
+ − 248
\texttt{compute} with the updated data. The most convenient way to
+ − 249
implement the brainf**k rules in Scala is to use pattern-matching
+ − 250
and to calculate a triple consisting of the updated \texttt{pc},
229
+ − 251
\texttt{mp} and \texttt{mem}.
218
+ − 252
230
+ − 253
Write another function \texttt{run} that calls \texttt{compute} with a
218
+ − 254
given brainfu** program and memory, and the program counter and memory pointer
230
+ − 255
set to~$0$. Like \texttt{compute}, it returns the memory after the execution
218
+ − 256
of the program finishes. You can test your brainf**k interpreter with the
229
+ − 257
Sierpinski triangle or the Hello world programs (they seem to be particularly
+ − 258
useful for debugging purposes), or have a look at
109
+ − 259
218
+ − 260
\begin{center}
+ − 261
\url{https://esolangs.org/wiki/Brainfuck}
+ − 262
\end{center}\hfill[2 Marks]
109
+ − 263
218
+ − 264
\begin{figure}[p]
+ − 265
\begin{center}
230
+ − 266
\begin{tabular}{|@{\hspace{0.5mm}}p{0.8cm}|l|}
218
+ − 267
\hline
+ − 268
\hfill\texttt{'>'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 269
$\bullet$ & $\texttt{pc} + 1$\\
+ − 270
$\bullet$ & $\texttt{mp} + 1$\\
+ − 271
$\bullet$ & \texttt{mem} unchanged
+ − 272
\end{tabular}\\\hline
+ − 273
\hfill\texttt{'<'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 274
$\bullet$ & $\texttt{pc} + 1$\\
+ − 275
$\bullet$ & $\texttt{mp} - 1$\\
+ − 276
$\bullet$ & \texttt{mem} unchanged
+ − 277
\end{tabular}\\\hline
+ − 278
\hfill\texttt{'+'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 279
$\bullet$ & $\texttt{pc} + 1$\\
+ − 280
$\bullet$ & $\texttt{mp}$ unchanged\\
+ − 281
$\bullet$ & \texttt{mem} updated with \texttt{mp -> mem(mp) + 1}\\
+ − 282
\end{tabular}\\\hline
+ − 283
\hfill\texttt{'-'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 284
$\bullet$ & $\texttt{pc} + 1$\\
+ − 285
$\bullet$ & $\texttt{mp}$ unchanged\\
+ − 286
$\bullet$ & \texttt{mem} updated with \texttt{mp -> mem(mp) - 1}\\
+ − 287
\end{tabular}\\\hline
+ − 288
\hfill\texttt{'.'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 289
$\bullet$ & $\texttt{pc} + 1$\\
+ − 290
$\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\
+ − 291
$\bullet$ & print out \,\texttt{mem(mp)} as a character\\
+ − 292
\end{tabular}\\\hline
+ − 293
\hfill\texttt{','} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 294
$\bullet$ & $\texttt{pc} + 1$\\
+ − 295
$\bullet$ & $\texttt{mp}$ unchanged\\
+ − 296
$\bullet$ & \texttt{mem} updated with \texttt{mp -> \textrm{input}}\\
+ − 297
\multicolumn{2}{@{}l}{the input is given by \texttt{Console.in.read().toByte}}
+ − 298
\end{tabular}\\\hline
+ − 299
\hfill\texttt{'['} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 300
\multicolumn{2}{@{}l}{if \texttt{mem(mp) == 0} then}\\
+ − 301
$\bullet$ & $\texttt{pc = jumpRight(prog, pc + 1, 0)}$\\
+ − 302
$\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\medskip\\
+ − 303
\multicolumn{2}{@{}l}{otherwise if \texttt{mem(mp) != 0} then}\\
+ − 304
$\bullet$ & $\texttt{pc} + 1$\\
+ − 305
$\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\
+ − 306
\end{tabular}
+ − 307
\\\hline
+ − 308
\hfill\texttt{']'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 309
\multicolumn{2}{@{}l}{if \texttt{mem(mp) != 0} then}\\
+ − 310
$\bullet$ & $\texttt{pc = jumpLeft(prog, pc - 1, 0)}$\\
+ − 311
$\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\medskip\\
+ − 312
\multicolumn{2}{@{}l}{otherwise if \texttt{mem(mp) == 0} then}\\
+ − 313
$\bullet$ & $\texttt{pc} + 1$\\
+ − 314
$\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\
+ − 315
\end{tabular}\\\hline
+ − 316
any other char & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}}
+ − 317
$\bullet$ & $\texttt{pc} + 1$\\
+ − 318
$\bullet$ & \texttt{mp} and \texttt{mem} unchanged
+ − 319
\end{tabular}\\
+ − 320
\hline
+ − 321
\end{tabular}
+ − 322
\end{center}
+ − 323
\caption{The rules for how commands in the brainf*** language update the program counter \texttt{pc},
230
+ − 324
the memory pointer \texttt{mp} and the memory \texttt{mem}.\label{comms}}
218
+ − 325
\end{figure}
+ − 326
\end{itemize}\bigskip
+ − 327
233
+ − 328
\newpage
+ − 329
229
+ − 330
\subsection*{Part 2 (4 Marks)}
218
+ − 331
233
+ − 332
I am sure you agree while it is fun to look at bf-programs, like the
+ − 333
Sierpinski triangle or the Mandelbrot program, being interpreted, it
+ − 334
is much more fun to write a compiler for the bf-language.
+ − 335
+ − 336
+ − 337
\subsubsection*{Tasks (file bfc.scala)}
+ − 338
+ − 339
\begin{itemize}
+ − 340
\item[(5)] Compilers in general attempt to make programs run
+ − 341
faster by precomputing as much information as possible
+ − 342
before running the program. In our case we can precompute the
+ − 343
addresses where we need to jump at the beginning and end of
+ − 344
loops.
+ − 345
+ − 346
For this write a function \texttt{jtable} that precomputes the ``jump
+ − 347
table'' for a bf-program. This function takes a bf-program
+ − 348
as an argument and returns a \texttt{Map[Int, Int]}. The
237
+ − 349
purpose of this Map is to record the information, in cases
+ − 350
a pc-position points to a '\texttt{[}' or a '\texttt{]}',
+ − 351
to which pc-position do we need to jump next?
233
+ − 352
+ − 353
For example for the program
+ − 354
+ − 355
\begin{center}
+ − 356
\texttt{+++++[->++++++++++<]>--<+++[->>++++++++++}
+ − 357
\texttt{<<]>>++<<----------[+>.>.<+<]}
+ − 358
\end{center}
+ − 359
+ − 360
we obtain the Map (note the precise numbers might differ depending on white
+ − 361
spaces etc.~in the bf-program):
+ − 362
+ − 363
\begin{center}
+ − 364
\texttt{Map(69 -> 61, 5 -> 20, 60 -> 70, 27 -> 44, 43 -> 28, 19 -> 6)}
+ − 365
\end{center}
+ − 366
+ − 367
This Map states that for the '\texttt{[}' on position 5, we need to
+ − 368
jump to position 20, which is just after the corresponding '\texttt{]}'.
+ − 369
Similarly, for the '\texttt{]}' on position 19, we need to jump to
+ − 370
position 6, which is just after the '\texttt{[}' on position 5, and so
+ − 371
on. The idea is to not calculate this information each time
+ − 372
we hit a bracket, but just look up this information in the
+ − 373
\texttt{jtable}.
+ − 374
+ − 375
Then adapt the \texttt{compute} and \texttt{run} functions
+ − 376
from Part 1 in order to take advantage of the information
+ − 377
stored in the \texttt{jtable}. This means whenever \texttt{jumpLeft}
+ − 378
and \texttt{jumpRight} was called previously, you should look
+ − 379
up the jump address in the \texttt{jtable}. Feel free to reuse
+ − 380
the function \texttt{jumpLeft} and \texttt{jumpRight} for
+ − 381
calculating the \texttt{jtable}.\hfill{[1 Mark]}
+ − 382
237
+ − 383
\item[(6)] Compilers try to eliminate any ``dead'' code that could
+ − 384
slow down programs and also perform what is often called
+ − 385
\emph{peephole
+ − 386
optimisations}.\footnote{\url{https://en.wikipedia.org/wiki/Peephole_optimization}}
+ − 387
For the latter consider that it is difficult for compilers to
241
+ − 388
comprehend what is intended with whole programs, but they are very good
237
+ − 389
at finding out what small snippets of code do, and then try to
+ − 390
generate faster code for such snippets.
233
+ − 391
+ − 392
In our case, dead code is everything that is not a bf-command.
+ − 393
Therefore write a function \texttt{optimise} which deletes such
237
+ − 394
dead code from a bf-program. Moreover this function should replace every substring
233
+ − 395
of the form \pcode{[-]} by a new command \texttt{0}.
+ − 396
The idea is that the loop \pcode{[-]} just resets the
+ − 397
memory at the current location to 0. It is more efficient
237
+ − 398
to do this in a single step, rather than stepwise in a loop as in
233
+ − 399
the original bf-programs.
218
+ − 400
233
+ − 401
In the extended \texttt{compute3} and \texttt{run3} functions you should
+ − 402
implement this command by writing 0 to \pcode{mem(mp)}, that is use
+ − 403
\pcode{write(mem, mp, 0)} as the rule for the command \texttt{0}.
+ − 404
The easiest way to modify a string in this way is to use the regular
+ − 405
expression \pcode{"""[^<>+-.,\\[\\]]"""}, which recognises everything that is
+ − 406
not a bf-command. Similarly, the
237
+ − 407
regular expression \pcode{"""\\[-\\]"""} finds all occurrences of \pcode{[-]}. By using the Scala method \pcode{.replaceAll} you can replace substrings
+ − 408
with new strings.\\
+ − 409
\mbox{}\hfill{[1 Mark]}
233
+ − 410
+ − 411
\item[(7)] Finally, real compilers try to take advantage of CPUs which often
+ − 412
provide complex operations in hardware that can combine many smaller
+ − 413
instructions into a single faster instruction.
+ − 414
237
+ − 415
In our case we can optimise the several single increments performed at a
233
+ − 416
memory cell, for example \pcode{++++}, by a single ``increment by
+ − 417
4''. For this optimisation we just have to make sure these single
237
+ − 418
increments are all next to each other. Similar optimisations should apply
233
+ − 419
for the bf-commands \pcode{-}, \pcode{<} and
+ − 420
\pcode{>}, which can all be replaced by extended versions that take
+ − 421
the amount of the increment (decrement) into account. We will do
+ − 422
this by introducing two-character bf-commands. For example
+ − 423
+ − 424
\begin{center}
+ − 425
\begin{tabular}{l|l}
+ − 426
original bf-cmds & replacement\\
+ − 427
\hline
+ − 428
\pcode{+} & \pcode{+A}\\
+ − 429
\pcode{++} & \pcode{+B}\\
+ − 430
\pcode{+++} & \pcode{+C}\\
+ − 431
\ldots{} & \ldots{}\\
+ − 432
\pcode{+++....++} & \pcode{+Z}\\
+ − 433
\hspace{5mm}(these are 26 \pcode{+}'s)\\
+ − 434
\end{tabular}
+ − 435
\end{center}
+ − 436
+ − 437
237
+ − 438
If there are more
+ − 439
than 26 \pcode{+}'s in a row, then more than one ``two-character''
233
+ − 440
bf-commands need to be generated (the idea is that more than
+ − 441
26 copies of a single bf-command in a row is a rare occurrence in
237
+ − 442
actual bf-programs). Similar replacements apply
+ − 443
for \pcode{-}, \pcode{<} and \pcode{>}, but
+ − 444
all other bf-commands should be unaffected by this
233
+ − 445
change.
+ − 446
+ − 447
For this write a function \texttt{combine} which replaces sequences
+ − 448
of repeated increment and decrement commands by appropriate
237
+ − 449
two-character commands. In the functions \pcode{compute4} and
233
+ − 450
\pcode{run4}, the ``combine'' and the optimisation from (6) should
+ − 451
be performed. Make sure that when a two-character bf-command is
+ − 452
encountered you need to increase the \pcode{pc}-counter by two in
237
+ − 453
order to progress to the next command. For example
233
+ − 454
+ − 455
\begin{center}
+ − 456
\pcode{combine(optimise(load_bff("benchmark.bf")))}
+ − 457
\end{center}
+ − 458
+ − 459
generates the improved program
+ − 460
+ − 461
\begin{center}
+ − 462
\pcode{>A+B[<A+M>A-A]<A[[}\hspace{3mm}\ldots{}
+ − 463
\end{center}
+ − 464
+ − 465
for the original benchmark program
+ − 466
+ − 467
\begin{center}
+ − 468
\pcode{>++[<+++++++++++++>-]<[[}\hspace{3mm}\ldots
+ − 469
\end{center}
+ − 470
241
+ − 471
As you can see, the compiler bets on saving a lot of time on the
+ − 472
\pcode{+B} and \pcode{+M} steps so that the optimisations is
233
+ − 473
worthwhile overall (of course for the \pcode{>A}'s and so on, the compiler incurs a
+ − 474
penalty). Luckily, after you have performed all
+ − 475
optimisations in (5) - (7), you can expect that the
234
+ − 476
\pcode{benchmark.bf} program runs four to five times faster.
+ − 477
You can also test whether your compiler produces the correct result
+ − 478
by for example testing
+ − 479
+ − 480
\begin{center}
+ − 481
\pcode{run(load_bff("sierpinski.bf")) == run4(load_bff("sierpinski.bf"))}
+ − 482
\end{center}
+ − 483
+ − 484
which should return true for all the different compiler stages. \\
+ − 485
\mbox{}\hfill{[2 Marks]}
233
+ − 486
\end{itemize}
6
+ − 487
+ − 488
\end{document}
+ − 489
68
+ − 490
6
+ − 491
%%% Local Variables:
+ − 492
%%% mode: latex
+ − 493
%%% TeX-master: t
+ − 494
%%% End: