diff -r 7d9b765d4012 -r b17a98b0c52f cws/main_cw05.tex --- a/cws/main_cw05.tex Fri Nov 05 17:20:53 2021 +0000 +++ b/cws/main_cw05.tex Sat Nov 06 00:06:39 2021 +0000 @@ -15,7 +15,7 @@ \begin{document} -\section*{Part 5 (Scala, 10 Marks)} +\section*{Main Part 5 (Scala, 9 Marks)} \mbox{}\hfill\textit{``If there's one feature that makes Scala, `Scala',}\\ \mbox{}\hfill\textit{ I would pick implicits.''}\smallskip\\ @@ -24,8 +24,8 @@ \noindent This part is about a small (esoteric) programming language called -brainf***. Actually, we will implement an interpreter for our own version -of this language called brainf*ck++.\bigskip +brainf***. We will implement an interpreter and compiler for +this language.\bigskip \IMPORTANT{This part is worth 10\% and you need to submit it on \cwTEN{} at 5pm. Any 1\% you achieve counts as your ``weekly engagement''.} @@ -44,13 +44,13 @@ allow you to run any test cases on your own computer. For example you can call Scala on the command line with the option \texttt{-cp bf.jar} and then query any function from the \texttt{bf.scala} template file. -You have to prefix the calls with \texttt{CW10a} and \texttt{CW10b}, +You have to prefix the calls with \texttt{M5a} and \texttt{M5b}, respectively. For example \begin{lstlisting}[language={},xleftmargin=1mm,numbers=none,basicstyle=\ttfamily\small] $ scala -cp bf.jar -scala> import CW10a._ +scala> import M5a._ scala> run(load_bff("sierpinski.bf")) ; () * * * @@ -88,7 +88,7 @@ \newpage -\subsection*{Part A (6 Marks)} +\subsection*{Part A (5 Marks)} Coming from Java or C++, you might think Scala is a rather esoteric programming language. But remember, some serious companies have built @@ -124,13 +124,12 @@ \noindent As mentioned above, the original brainf*** has 8 single-character -commands. Our version of bf++ will contain the commands \texttt{'>'}, +commands. Our version of bf will contain the commands \texttt{'>'}, \texttt{'<'}, \texttt{'+'}, \texttt{'-'}, \texttt{'.'}, \texttt{'['} -and \texttt{']'} from the original, and in addition the commands -\texttt{'@'}, \texttt{'*'} and \texttt{'\#'}. Every other character +and \texttt{']'}. Every other character is considered a comment. -Our interpreter for bf++ operates on memory cells containing +Our interpreter for bf operates on memory cells containing integers. For this it uses a single memory pointer, called \texttt{mp}, that points at each stage to one memory cell. @@ -161,7 +160,7 @@ command \texttt{'>'}, and backward by using \texttt{'<'}. The commands \texttt{'+'} and \texttt{'-'} increase, respectively decrease, by 1 the content of the memory cell to which the memory pointer currently -points to. The command for output in bf++ is \texttt{'.'} whereby output works +points to. The command for output in bf is \texttt{'.'} whereby output works by reading the content of the memory cell to which the memory pointer points to and printing it out as an ASCII character.\footnote{In the original version of bf, there is also a command for input, but we @@ -179,9 +178,7 @@ \end{center} \noindent -This one prints out Hello World\ldots{}obviously \texttt{;o)} We also -add 3 new commands in the bf++-version of the bf-language. The purpose -of these commands we explain later. +This one prints out Hello World\ldots{}obviously \texttt{;o)} \subsubsection*{Tasks (file bf.scala)} @@ -191,9 +188,9 @@ and requests the corresponding file from disk. It returns the content of the file as a string. If the file does not exists, the function should return the empty string. - \mbox{}\hfill[1 Mark] + \mbox{}\hfill[0.5 Marks] -\item[(2)] Brainf**k++ memory is represented by a \texttt{Map} from +\item[(2)] Brainf**k memory is represented by a \texttt{Map} from integers to integers. The empty memory is represented by \texttt{Map()}, that is nothing is stored in the memory; \texttt{Map(0 -> 1, 2 -> 3)} stores \texttt{1} at @@ -210,11 +207,11 @@ \texttt{Map} with the value at the given memory location. As usual, the \texttt{Map} is not updated `in-place' but a new map is created with the same data, except the new value is stored at the given memory - pointer.\hfill[1 Mark] + pointer.\hfill[0.5 Marks] \item[(3)] Write two functions, \texttt{jumpRight} and \texttt{jumpLeft}, that are needed to implement the loop constructs - in brainf**k++. They take a program (a \texttt{String}) and a program + in brainf**k. They take a program (a \texttt{String}) and a program counter (an \texttt{Int}) as arguments and move right (respectively left) in the string in order to find the \textbf{matching} opening/closing bracket. For example, given the following program @@ -243,7 +240,7 @@ Unfortunately we have to take into account that there might be other opening and closing brackets on the `way' to find the - matching bracket. For example in the brain*ck++ program + matching bracket. For example in the brain*ck program \begin{center} \texttt{--[\barbelow{.}.[+>]--].>.++} @@ -283,7 +280,7 @@ \item[(4)] Write a recursive function \texttt{compute} that runs a - brain*u*k++ program. It takes a program, a program counter, a memory + brain*u*k program. It takes a program, a program counter, a memory pointer and a memory as arguments. If the program counter is outside the program string, the execution stops and \texttt{compute} returns the memory. If the program counter is inside the string, it reads the @@ -291,14 +288,14 @@ memory pointer \texttt{mp} and memory \texttt{mem} according to the rules shown in Figure~\ref{comms}. It then calls recursively \texttt{compute} with the updated data. The most convenient way to - implement the brainf**k++ rules in Scala is to use pattern-matching + implement the brainf**k rules in Scala is to use pattern-matching and to calculate a triple consisting of the updated \texttt{pc}, \texttt{mp} and \texttt{mem}. Write another function \texttt{run} that calls \texttt{compute} with a - given brainfu*k++ program and memory, and the program counter and memory pointer + given brainfu*k program and memory, and the program counter and memory pointer set to~$0$. Like \texttt{compute}, it returns the memory after the execution - of the program finishes. You can test your brainf**k++ interpreter with the + of the program finishes. You can test your brainf**k interpreter with the Sierpinski triangle or the Hello world programs (they seem to be particularly useful for debugging purposes), or have a look at @@ -306,7 +303,7 @@ \url{https://esolangs.org/wiki/Brainfuck} \end{center} - \noindent for more bf/bf++-programs and the test cases given in \texttt{bf.scala}.\\ + \noindent for more bf-programs and the test cases given in \texttt{bf.scala}.\\ \mbox{}\hfill[2 Marks] \begin{figure}[p] @@ -361,36 +358,36 @@ $\bullet$ & $\texttt{pc} + 1$\\ $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\ \end{tabular}\\\hline - \hfill\texttt{'*'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} - $\bullet$ & $\texttt{pc} + 1$\\ - $\bullet$ & $\texttt{mp}$ unchanged\\ - $\bullet$ & \texttt{mem} updated with \texttt{mp -> mem(mp) * mem(mp - 1)}\\ - \multicolumn{2}{@{}l}{this multiplies the content of the memory cells at - \texttt{mp} and \texttt{mp - 1}}\\ - \multicolumn{2}{@{}l}{and stores the result at \texttt{mp}} - \end{tabular}\\\hline - \hfill\texttt{'@'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} - $\bullet$ & $\texttt{pc} + 1$\\ - $\bullet$ & $\texttt{mp}$ unchanged\\ - $\bullet$ & \texttt{mem} updated with - \texttt{mem(mp) -> mem(mp - 1)}\\ - \multicolumn{2}{@{}l}{this updates the memory cell having the index stored at \texttt{mem(mp)},}\\ - \multicolumn{2}{@{}l}{with the value stored at \texttt{mem(mp - 1)},} - \end{tabular}\\\hline - \hfill\texttt{'\#'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} - $\bullet$ & $\texttt{pc} + 1$\\ - $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\ - $\bullet$ & print out \,\texttt{mem(mp)} as a number\\ - \end{tabular}\\\hline + %\hfill\texttt{'*'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} + % $\bullet$ & $\texttt{pc} + 1$\\ + % $\bullet$ & $\texttt{mp}$ unchanged\\ + % $\bullet$ & \texttt{mem} updated with \texttt{mp -> mem(mp) * mem(mp - 1)}\\ + % \multicolumn{2}{@{}l}{this multiplies the content of the memory cells at + % \texttt{mp} and \texttt{mp - 1}}\\ + % \multicolumn{2}{@{}l}{and stores the result at \texttt{mp}} + % \end{tabular}\\\hline + %\hfill\texttt{'@'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} + % $\bullet$ & $\texttt{pc} + 1$\\ + % $\bullet$ & $\texttt{mp}$ unchanged\\ + % $\bullet$ & \texttt{mem} updated with + % \texttt{mem(mp) -> mem(mp - 1)}\\ + % \multicolumn{2}{@{}l}{this updates the memory cell having the index stored at \texttt{mem(mp)},}\\ + % \multicolumn{2}{@{}l}{with the value stored at \texttt{mem(mp - 1)},} + % \end{tabular}\\\hline + %\hfill\texttt{'\#'} & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} + % $\bullet$ & $\texttt{pc} + 1$\\ + % $\bullet$ & $\texttt{mp}$ and \texttt{mem} unchanged\\ + % $\bullet$ & print out \,\texttt{mem(mp)} as a number\\ + % \end{tabular}\\\hline any other char & \begin{tabular}[t]{@{}l@{\hspace{2mm}}l@{}} - $\bullet$ & $\texttt{pc} + 1$\\ + % $\bullet$ & $\texttt{pc} + 1$\\ $\bullet$ & \texttt{mp} and \texttt{mem} unchanged \end{tabular}\\ \hline \end{tabular} \\\mbox{}\\[-10mm]\mbox{} \end{center} - \caption{The rules for how commands in the brainf***++ language update the + \caption{The rules for how commands in the brainf*** language update the program counter \texttt{pc}, the memory pointer \texttt{mp} and the memory \texttt{mem}.\label{comms}} \end{figure} @@ -400,9 +397,9 @@ \subsection*{Part B (4 Marks)} -I am sure you agree while it is fun to marvel at bf++-programs, like the +I am sure you agree while it is fun to marvel at bf-programs, like the Sierpinski triangle or the Mandelbrot program, being interpreted, it -is much more fun to write a compiler for the bf++-language. +is much more fun to write a compiler for the bf-language. \subsubsection*{Tasks (file bfc.scala)} @@ -415,7 +412,7 @@ loops. For this write a function \texttt{jtable} that precomputes the ``jump - table'' for a bf++-program. This function takes a bf++-program + table'' for a bf-program. This function takes a bf-program as an argument and returns a \texttt{Map[Int, Int]}. The purpose of this Map is to record the information, in cases a pc-position points to a '\texttt{[}' or a '\texttt{]}', @@ -460,21 +457,21 @@ at finding out what small snippets of code do, and then try to generate faster code for such snippets. - In our case, dead code is everything that is not a bf++-command. + In our case, dead code is everything that is not a bf-command. Therefore write a function \texttt{optimise} which deletes such - dead code from a bf++-program. Moreover this function should replace every substring + dead code from a bf-program. Moreover this function should replace every substring of the form \pcode{[-]} by a new command \texttt{0}. The idea is that the loop \pcode{[-]} just resets the memory at the current location to 0. It is more efficient to do this in a single step, rather than stepwise in a loop as in - the original bf++-programs. + the original bf-programs. In the extended \texttt{compute3} and \texttt{run3} functions you should implement this command by writing 0 to \pcode{mem(mp)}, that is use \pcode{write(mem, mp, 0)} as the rule for the command \texttt{0}. The easiest way to modify a string in this way is to use the regular - expression \pcode{"""[^<>+-.\\[\\]@\#*]"""}, which recognises everything that is - not a bf++-command. Similarly, the + expression \pcode{"""[^<>+-.\\[\\]]"""}, which recognises everything that is + not a bf-command. Similarly, the regular expression \pcode{"""\\[-\\]"""} finds all occurrences of \pcode{[-]}. By using the Scala method \pcode{.replaceAll} you can replace substrings with new strings.\\ \mbox{}\hfill{[1 Mark]} @@ -490,7 +487,7 @@ for the bf-commands \pcode{-}, \pcode{<} and \pcode{>}, which can all be replaced by extended versions that take the amount of the increment (decrement) into account. We will do - this by introducing two-character bf++-commands. For example + this by introducing two-character bf-commands. For example \begin{center} \begin{tabular}{l|l} @@ -509,17 +506,17 @@ If there are more than 26 \pcode{+}'s in a row, then more than one ``two-character'' bf-commands need to be generated (the idea is that more than - 26 copies of a single bf++-command in a row is a rare occurrence in - actual bf++-programs). Similar replacements apply + 26 copies of a single bf-command in a row is a rare occurrence in + actual bf-programs). Similar replacements apply for \pcode{-}, \pcode{<} and \pcode{>}, but - all other bf++-commands should be unaffected by this + all other bf-commands should be unaffected by this change. For this write a function \texttt{combine} which replaces sequences of repeated increment and decrement commands by appropriate two-character commands. In the functions \pcode{compute4} and \pcode{run4}, the ``combine'' and the optimisation from (6) should - be performed. Make sure that when a two-character bf++-command is + be performed. Make sure that when a two-character bf-command is encountered you need to increase the \pcode{pc}-counter by two in order to progress to the next command. For example