updatedHG: added solutions/cw5/fun_tokens.sc
authorChristian Urban <christian.urban@kcl.ac.uk>
Fri, 04 Nov 2022 12:07:40 +0000
changeset 894 02ef5c3abc51
parent 893 54a483a33763
child 895 2f5a87ecdc81
updatedHG: added solutions/cw5/fun_tokens.sc
hws/hw06.pdf
hws/hw06.tex
solution/cw1/cw1.scala
solution/cw1/matcher.sc
solution/cw2/collatz.while
solution/cw2/collatz2.while
solution/cw2/factors.while
solution/cw2/fib.while
solution/cw2/lexer.sc
solution/cw2/loops.while
solution/cw2/primes.while
solution/cw3/collatz.while
solution/cw3/collatz2.while
solution/cw3/factors.while
solution/cw3/fib.while
solution/cw3/lexer.sc
solution/cw3/loops.while
solution/cw3/parser.sc
solution/cw3/parser2.sc
solution/cw3/primes.while
solution/cw4/collatz2.while
solution/cw4/compiler.sc
solution/cw4/fib.while
solution/cw4/lexer.sc
solution/cw4/parser.sc
solution/cw5/fact.fun
solution/cw5/fun_llvm.sc
solution/cw5/fun_parser.sc
solution/cw5/fun_tokens.sc
solution/cw5/hanoi.fun
solution/cw5/mand.fun
solution/cw5/mand2.fun
solution/cw5/sqr.fun
solutions/cw1/cw1.scala
solutions/cw1/matcher.sc
solutions/cw2/collatz.while
solutions/cw2/collatz2.while
solutions/cw2/factors.while
solutions/cw2/fib.while
solutions/cw2/lexer.sc
solutions/cw2/loops.while
solutions/cw2/primes.while
solutions/cw3/collatz.while
solutions/cw3/collatz2.while
solutions/cw3/factors.while
solutions/cw3/fib.while
solutions/cw3/lexer.sc
solutions/cw3/loops.while
solutions/cw3/parser.sc
solutions/cw3/parser2.sc
solutions/cw3/primes.while
solutions/cw4/collatz2.while
solutions/cw4/compiler.sc
solutions/cw4/fib.while
solutions/cw4/lexer.sc
solutions/cw4/parser.sc
solutions/cw5/fact.fun
solutions/cw5/fun_llvm.sc
solutions/cw5/fun_parser.sc
solutions/cw5/fun_tokens.sc
solutions/cw5/hanoi.fun
solutions/cw5/mand.fun
solutions/cw5/mand2.fun
solutions/cw5/sqr.fun
Binary file hws/hw06.pdf has changed
--- a/hws/hw06.tex	Fri Oct 28 09:08:13 2022 +0100
+++ b/hws/hw06.tex	Fri Nov 04 12:07:40 2022 +0000
@@ -68,8 +68,20 @@
       advantages to first lex a string and then feed a
       sequence of tokens as input to the parser?
 
+\item The injection function for sequence regular expressions is defined
+      by three clauses:
 
-  
+\begin{center}
+\begin{tabular}{l@{\hspace{1mm}}c@{\hspace{1mm}}l}
+  $\inj\,(r_1 \cdot r_2)\,c\,\,Seq(v_1,v_2)$ & $\dn$  & $Seq(\inj\,r_1\,c\,v_1,v_2)$\\
+  $\inj\,(r_1 \cdot r_2)\,c\,\,\Left(Seq(v_1,v_2))$ & $\dn$  & $Seq(\inj\,r_1\,c\,v_1,v_2)$\\
+  $\inj\,(r_1 \cdot r_2)\,c\,\,Right(v)$ & $\dn$  & $Seq(\textit{mkeps}(r_1),\inj\,r_2\,c\,v)$\\
+\end{tabular}
+\end{center}
+
+Explain why there are three cases in the injection function for sequence
+regular expressions. 
+      
 \item \POSTSCRIPT        
 \end{enumerate}
 
--- a/solution/cw1/cw1.scala	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-// CW 1
-import scala.language.implicitConversions    
-import scala.language.reflectiveCalls 
-
-abstract class Rexp
-case object ZERO extends Rexp                            // matches nothing
-case object ONE extends Rexp                             // matches the empty string
-case class CHAR(c: Char) extends Rexp                    // matches a character c
-case class ALT(r1: Rexp, r2: Rexp) extends Rexp          // alternative
-case class SEQ(r1: Rexp, r2: Rexp) extends Rexp          // sequence
-case class STAR(r: Rexp) extends Rexp                    // star
-case class RANGE(cs: Set[Char]) extends Rexp             // set of characters
-case class PLUS(r: Rexp) extends Rexp                    // plus
-case class OPT(r: Rexp) extends Rexp                     // optional
-case class NTIMES(r: Rexp, n: Int) extends Rexp          // n-times
-case class UPNTIMES(r: Rexp, n: Int) extends Rexp        // up n-times
-case class FROMNTIMES(r: Rexp, n: Int) extends Rexp      // from n-times
-case class NMTIMES(r: Rexp, n: Int, m: Int) extends Rexp // between nm-times
-case class NOT(r: Rexp) extends Rexp                     // not
-case class CFUN(f: Char => Boolean) extends Rexp         // subsuming CHAR and RANGE
-
-def CHAR(c: Char) = CFUN(d => c == d)
-val ALL = CFUN(_ => true)
-def RANGE(cs: Set[Char]) = CFUN(d => cs.contains(d))
-
-def CHAR(c: Char) = CFUN(c == _)
-val ALL = CFUN(_ => true)
-def RANGE(cs: Set[Char]) = CFUN(cs.contains(_))
-
-
-// nullable function: tests whether the regular 
-// expression can recognise the empty string
-def nullable (r: Rexp) : Boolean = r match {
-  case ZERO => false
-  case ONE => true
-  case CHAR(_) => false
-  case ALT(r1, r2) => nullable(r1) || nullable(r2)
-  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
-  case STAR(_) => true
-  case RANGE(_) => false
-  case PLUS(r) => nullable(r)
-  case OPT(_) => true
-  case NTIMES(r, n) => if (n == 0) true else nullable(r)
-  case UPNTIMES(_, _) => true
-  case FROMNTIMES(r, n) => if (n == 0) true else nullable(r)
-  case NMTIMES(r, n, m) => if (n == 0) true else nullable(r)
-  case NOT(r) => !nullable(r)
-  case CFUN(_) => false
-}
-
-// derivative of a regular expression w.r.t. a character
-def der (c: Char, r: Rexp) : Rexp = r match {
-  case ZERO => ZERO
-  case ONE => ZERO
-  case CHAR(d) => if (c == d) ONE else ZERO
-  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
-  case SEQ(r1, r2) => 
-    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
-    else SEQ(der(c, r1), r2)
-  case STAR(r) => SEQ(der(c, r), STAR(r))
-  case RANGE(cs) => if (cs contains c) ONE else ZERO
-  case PLUS(r) => SEQ(der(c, r), STAR(r))
-  case OPT(r) => der(c, r)
-  case NTIMES(r, n) => 
-    if (n == 0) ZERO else SEQ(der(c, r), NTIMES(r, n - 1))
-  case UPNTIMES(r, n) =>
-    if (n == 0) ZERO else SEQ(der(c, r), UPNTIMES(r, n - 1))
-  case FROMNTIMES(r, n) =>
-    if (n == 0) SEQ(der(c, r), STAR(r)) else SEQ(der(c, r), FROMNTIMES(r, n - 1))
-  case NMTIMES(r, n, m) =>
-    if (m < n) ZERO else
-    if (n == 0 && m == 0) ZERO else 
-    if (n == 0) SEQ(der(c, r), UPNTIMES(r, m - 1)) 
-    else SEQ(der(c, r), NMTIMES(r, n - 1, m - 1)) 
-  case NOT(r) => NOT(der (c, r))
-  case CFUN(f) => if (f(c)) ONE else ZERO
-}
-
-def simp(r: Rexp) : Rexp = r match {
-  case ALT(r1, r2) => (simp(r1), simp(r2)) match {
-    case (ZERO, r2s) => r2s
-    case (r1s, ZERO) => r1s
-    case (r1s, r2s) => if (r1s == r2s) r1s else ALT (r1s, r2s)
-  }
-  case SEQ(r1, r2) =>  (simp(r1), simp(r2)) match {
-    case (ZERO, _) => ZERO
-    case (_, ZERO) => ZERO
-    case (ONE, r2s) => r2s
-    case (r1s, ONE) => r1s
-    case (r1s, r2s) => SEQ(r1s, r2s)
-  }
-  case r => r
-}
-
-
-// derivative w.r.t. a string (iterates der)
-def ders (s: List[Char], r: Rexp) : Rexp = s match {
-  case Nil => r
-  case c::s => ders(s, der(c, r))
-}
-
-def ders_simp (s: List[Char], r: Rexp) : Rexp = s match {
-  case Nil => r
-  case c::s => ders_simp(s, simp(der(c, r)))
-}
-
-// main matcher function including simplification
-def matcher(r: Rexp, s: String) : Boolean = 
-  nullable(ders_simp(s.toList, r))
-
-
-
-// some convenience for typing in regular expressions
-def charlist2rexp(s : List[Char]) : Rexp = s match {
-  case Nil => ONE
-  case c::Nil => CHAR(c)
-  case c::s => SEQ(CHAR(c), charlist2rexp(s))
-}
-implicit def string2rexp(s : String) : Rexp = charlist2rexp(s.toList)
-
-implicit def RexpOps (r: Rexp) = new {
-  def | (s: Rexp) = ALT(r, s)
-  def % = STAR(r)
-  def ~ (s: Rexp) = SEQ(r, s)
-}
-
-implicit def stringOps (s: String) = new {
-  def | (r: Rexp) = ALT(s, r)
-  def | (r: String) = ALT(s, r)
-  def % = STAR(s)
-  def ~ (r: Rexp) = SEQ(s, r)
-  def ~ (r: String) = SEQ(s, r)
-}
-
-val r1 = NTIMES("a", 3)
-val r2 = NTIMES(OPT("a"), 3)
-val r3 = UPNTIMES("a", 3)
-val r4 = UPNTIMES(OPT("a"), 3)
-val r5 = NMTIMES("a", 3, 5)
-val r6 = NMTIMES(OPT("a"), 3, 5)
-
-val rs = List(r1, r2, r3, r4, r5, r6)
-
-rs.map(matcher(_, ""))
-rs.map(matcher(_, "a"))
-rs.map(matcher(_, "aa"))
-rs.map(matcher(_, "aaa"))
-rs.map(matcher(_, "aaaa"))
-rs.map(matcher(_, "aaaaa"))
-rs.map(matcher(_, "aaaaaa"))
-
-println("EMAIL:")
-val LOWERCASE = ('a' to 'z').toSet
-val DIGITS = ('0' to '9').toSet
-val EMAIL = { PLUS(CFUN(LOWERCASE | DIGITS | ("_.-").toSet)) ~ "@" ~ 
-              PLUS(CFUN(LOWERCASE | DIGITS | (".-").toSet)) ~ "." ~
-              NMTIMES(CFUN(LOWERCASE | Set('.')), 2, 6) }
-
-val my_email = "christian.urban@kcl.ac.uk"
-
-println(EMAIL);
-println(matcher(EMAIL, my_email))
-println(ders_simp(my_email.toList, EMAIL))
-
-println("COMMENTS:")
-val ALL = CFUN((c:Char) => true)
-val COMMENT = "/*" ~ (NOT(ALL.% ~ "*/" ~ ALL.%)) ~ "*/"
-
-println(matcher(COMMENT, "/**/"))
-println(matcher(COMMENT, "/*foobar*/"))
-println(matcher(COMMENT, "/*test*/test*/"))
-println(matcher(COMMENT, "/*test/*test*/"))
-
-println("EVILS:")
-val EVIL1 = PLUS(PLUS("a" ~ "a" ~ "a"))
-val EVIL2 = PLUS(PLUS("aaaaaaaaaaaaaaaaaaa" ~ OPT("a")))  // 19 as ~ a?
-
-
-//40 * aaa matches
-//43 * aaa + aa does not
-//45 * aaa + a
-
-println("EVIL1:")
-println(matcher(EVIL1, "aaa" * 40))
-println(matcher(EVIL1, "aaa" * 43 + "aa"))
-println(matcher(EVIL1, "aaa" * 45 + "a"))
-println("EVIL2:")
-println(matcher(EVIL2, "aaa" * 40))
-println(matcher(EVIL2, "aaa" * 43 + "aa"))
-println(matcher(EVIL2, "aaa" * 45 + "a"))
-
-println("TEST for bug pointed out by Filips Ivanovs")
-val test = NMTIMES(CFUN(LOWERCASE | Set('.')), 2, 6)
-  
-println(matcher(test,"a"))
-println(matcher(test,"ab"))
-println(matcher(test,"abcdef"))
-println(matcher(test,"abc"))
-println(matcher(test,"...."))
-println(matcher(test,"asdfg"))
-println(matcher(test,"abcdefg"))
-
-println(test)
-println(ders_simp("a".toList, test))
-println(ders_simp("aa".toList, test))
-println(ders_simp("aaa".toList, test))
-println(ders_simp("aaaa".toList, test))
-println(ders_simp("aaaaa".toList, test))
-println(ders_simp("aaaaaa".toList, test))
-println(ders_simp("aaaaaaa".toList, test))
-
-def TEST(s: String, r: Rexp) = {
-  println("Rexp |" + s + "|")
-  println("Derivative:\n" + ders_simp(s.toList, r))
-  println("Is Nullable: " + nullable(ders_simp(s.toList, r)))
-  Console.readLine
-}
-
-
-val ALL2 = "a" | "f"
-val COMMENT2 = ("/*" ~ NOT(ALL2.% ~ "*/" ~ ALL2.%) ~ "*/")
-
-println("1) TEST TEST")
-TEST("", COMMENT2)
-TEST("/", COMMENT2)
-TEST("/*", COMMENT2)
-TEST("/*f", COMMENT2)
-TEST("/*f*", COMMENT2)
-TEST("/*f*/", COMMENT2)
-TEST("/*f*/ ", COMMENT2)
-
-val ALL3 = "a" | "f"
-val COMMENT3 = ("/" ~ NOT(ALL3.% ~ "&" ~ ALL3.%) ~ "&")
-
-println("2) TEST TEST")
-TEST("", COMMENT3)
-TEST("/", COMMENT3)
-TEST("/", COMMENT3)
-TEST("/f", COMMENT3)
-TEST("/f&", COMMENT3)
-TEST("/f& ", COMMENT3)
-
-//test: ("a" | "aa") ~ ("a" | "aa")*
-val auxEVIL3 = ALT(CHAR('a'), SEQ(CHAR('a'), CHAR('a')))
-val EVIL3 = SEQ(auxEVIL3, STAR(auxEVIL3))
-val EVIL3p = FROMNTIMES(auxEVIL3, 1)
-
-
-for (i <- 1 to 100 by 1) {
-  println(i + " " + "%.5f".format(time_needed(2, matcher(EVIL3, "a" * i))) + " size: " + 
-	  size(ders(("a" * i).toList, EVIL3)))
-}
-
-
-
-val t1 = EVIL3
-val t2 = simp(der('a', t1))
-val t3 = simp(der('a', t2))
-val t4 = simp(der('a', t3))
-
-val t1 = EVIL3p
-val t2 = simp(der('a', t1))
-val t3 = simp(der('a', t2))
-val t4 = simp(der('a', t3))
--- a/solution/cw1/matcher.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,325 +0,0 @@
-// CW1
-
-abstract class Rexp
-case object ZERO extends Rexp
-case object ONE extends Rexp
-case class CHAR(c: Char) extends Rexp
-case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
-case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
-case class STAR(r: Rexp) extends Rexp 
-
-// extended Rexps
-case class RANGE(s: Set[Char]) extends Rexp
-case class PLUS(r: Rexp) extends Rexp
-case class OPTIONAL(r: Rexp) extends Rexp
-case class NTIMES(r: Rexp, n: Int) extends Rexp 
-case class UPTO(r: Rexp, n: Int) extends Rexp
-case class FROM(r: Rexp, n: Int) extends Rexp
-case class BETWEEN(r: Rexp, n: Int, m: Int) extends Rexp
-case class NOT(r: Rexp) extends Rexp
-
-// functions
-case class CFUN(f: Char => Boolean) extends Rexp
-
-// abbreviations
-def FCHAR(c: Char) = CFUN((a: Char) => a == c)
-def FSET(s: Set[Char]) = CFUN((a: Char) => s.contains(a))
-val FALL = CFUN(_ => true)
-
-def nullable (r: Rexp) : Boolean = r match {
-  case ZERO => false
-  case ONE => true
-  case CHAR(_) => false
-  case ALT(r1, r2) => nullable(r1) || nullable(r2)
-  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
-  case STAR(_) => true
-
-  case RANGE(_) => false 
-  case PLUS(r1) => nullable(r1)
-  case OPTIONAL(_) => true
-  case NTIMES(r1, i) => if (i == 0) true else nullable(r1)
-  case UPTO(_, _) => true
-  case FROM(r1, i) => if (i == 0) true else nullable(r1)
-  case BETWEEN(r1, i, _) => if (i == 0) true else nullable(r1)
-  case NOT(r1) => !nullable(r1)
-
-  case CFUN(f) => false
-}
-
-
-def der (c: Char, r: Rexp) : Rexp = r match {
-  case ZERO => ZERO
-  case ONE => ZERO
-  case CHAR(d) => if (c == d) ONE else ZERO
-  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
-  case SEQ(r1, r2) => 
-    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
-    else SEQ(der(c, r1), r2)
-  case STAR(r1) => SEQ(der(c, r1), STAR(r1))
-
-  case RANGE(s) =>
-    if (s.contains(c)) ONE else ZERO 
-  case PLUS(r1) => SEQ(der(c, r1), STAR(r1))
-  case OPTIONAL(r1) => der(c, r1)
-  case NTIMES(r, i) => 
-    if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
-  case UPTO(r1, i) => 
-    if (i == 0) ZERO else SEQ(der(c, r1), UPTO(r1, i - 1)) 
-  case FROM(r1, i) =>
-    if (i == 0) SEQ(der(c, r1), FROM(r1, 0)) else
-    SEQ(der(c, r1), FROM(r1, i - 1))
-  case BETWEEN(r1, i, j) => 
-    if (i == 0) {
-      if (j == 0) ZERO else SEQ(der(c, r1), BETWEEN(r1, 0, j - 1))
-    } else SEQ(der(c, r1), BETWEEN(r1, i - 1, j - 1))
-  case NOT(r1) => NOT(der(c, r1))
-
-  case CFUN(f) => if (f(c)) ONE else ZERO
-}
-
-
-def simp(r: Rexp) : Rexp = r match {
-  case ALT(r1, r2) => (simp(r1), simp(r2)) match {
-    case (ZERO, r2s) => r2s
-    case (r1s, ZERO) => r1s
-    case (r1s, r2s) => if (r1s == r2s) r1s else ALT (r1s, r2s)
-  }
-  case SEQ(r1, r2) =>  (simp(r1), simp(r2)) match {
-    case (ZERO, _) => ZERO
-    case (_, ZERO) => ZERO
-    case (ONE, r2s) => r2s
-    case (r1s, ONE) => r1s
-    case (r1s, r2s) => SEQ(r1s, r2s)
-  }
-  case r => r
-}
-
-def ders(s: List[Char], r: Rexp) : Rexp = s match {
-  case Nil => r
-  case c::s => ders(s, simp(der(c, r)))
-}
-
-def matcher(r: Rexp, s: String) : Boolean = nullable(ders(s.toList, r))
-
-
-
-val Regex31 = NTIMES(CHAR('a'),3)
-val Regex32 = NTIMES(OPTIONAL(CHAR('a')),3)
-val Regex33 = UPTO(CHAR('a'),3)
-val Regex34 = UPTO(OPTIONAL(CHAR('a')),3)
-val Regex35 = BETWEEN(CHAR('a'),3,5)
-val Regex36 = BETWEEN(OPTIONAL(CHAR('a')),3,5)
-val string31 = ""
-val string32 = "a"
-val string33 = "aa"
-val string34 = "aaa"
-val string35 = "aaaa"
-val string36 = "aaaaa"
-val string37 = "aaaaaa"
-
-
-println("Question3")
-println(matcher(Regex31, string31))
-println(matcher(Regex31, string32))
-println(matcher(Regex31, string33))
-println(matcher(Regex31, string34))
-println(matcher(Regex31, string35))
-println(matcher(Regex31, string36))
-println(matcher(Regex31, string37)); println("")
-println(matcher(Regex32, string31))
-println(matcher(Regex32, string32))
-println(matcher(Regex32, string33))
-println(matcher(Regex32, string34))
-println(matcher(Regex32, string35))
-println(matcher(Regex32, string36))
-println(matcher(Regex32, string37)); println("")
-println(matcher(Regex33, string31))
-println(matcher(Regex33, string32))
-println(matcher(Regex33, string33))
-println(matcher(Regex33, string34))
-println(matcher(Regex33, string35))
-println(matcher(Regex33, string36))
-println(matcher(Regex33, string37)); println("")
-println(matcher(Regex34, string31))
-println(matcher(Regex34, string32))
-println(matcher(Regex34, string33))
-println(matcher(Regex34, string34))
-println(matcher(Regex34, string35))
-println(matcher(Regex34, string36))
-println(matcher(Regex34, string37)); println("")
-println(matcher(Regex35, string31))
-println(matcher(Regex35, string32))
-println(matcher(Regex35, string33))
-println(matcher(Regex35, string34))
-println(matcher(Regex35, string35))
-println(matcher(Regex35, string36))
-println(matcher(Regex35, string37)); println("")
-println(matcher(Regex36, string31))
-println(matcher(Regex36, string32))
-println(matcher(Regex36, string33))
-println(matcher(Regex36, string34))
-println(matcher(Regex36, string35))
-println(matcher(Regex36, string36))
-println(matcher(Regex36, string37)); println("")
-
-
-val RegexX = BETWEEN(CHAR('a'), 0, 5)
-val stringX = ""
-println(matcher(RegexX, stringX))
-
-
-
-val str0 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-val str1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-val str2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-
-val matchA = (c:Char) => c == 'a'
-
-val reg_1 = SEQ(SEQ(CFUN(matchA), CFUN(matchA)), CFUN(matchA))
-val reg_2 = SEQ(NTIMES(CFUN(matchA), 19), OPTIONAL(CFUN(matchA)))
-
-val reg_1_mod = PLUS(PLUS(reg_1))
-val reg_2_mod = PLUS(PLUS(reg_2))
-
-
-matcher(reg_1_mod, str0)
-matcher(reg_1_mod, str1)
-matcher(reg_1_mod, str2)
-matcher(reg_2_mod, str0)
-matcher(reg_2_mod, str1)
-matcher(reg_2_mod, str2)
-
-
-
-
-
-//Q3: matcher test (table)
-
-// a^{3}
-val re1 = NTIMES(CHAR('a'), 3)
-
-matcher(re1, "")        //false
-matcher(re1, "a")       //false
-matcher(re1, "aa")      //false
-matcher(re1, "aaa")     //true
-matcher(re1, "aaaaa")   //false
-matcher(re1, "aaaaaa")  //false
-
-// (a?)^{3}
-val re2 = NTIMES(OPTIONAL(CHAR('a')), 3)
-
-matcher(re2, "")        //true
-matcher(re2, "a")       //true
-matcher(re2, "aa")      //true
-matcher(re2, "aaa")     //true
-matcher(re2, "aaaaa")   //false
-matcher(re2, "aaaaaa")  //false
-
-// (a)^{..3}
-val re3 = UPTO((CHAR('a')), 3)
-
-matcher(re3, "")        //true
-matcher(re3, "a")       //true
-matcher(re3, "aa")      //true
-matcher(re3, "aaa")     //true
-matcher(re3, "aaaaa")   //false
-matcher(re3, "aaaaaa")  //false
-
-// (a?)^{..3}
-val re4 = UPTO(OPTIONAL(CHAR('a')), 3)
-
-matcher(re4, "")        //true
-matcher(re4, "a")       //true
-matcher(re4, "aa")      //true
-matcher(re4, "aaa")     //true
-matcher(re4, "aaaaa")   //false
-matcher(re4, "aaaaaa")  //false
-
-// (a)^{3..5}
-val re5 = BETWEEN(CHAR('a'), 3, 5)
-
-matcher(re5, "")        //false
-matcher(re5, "a")       //false
-matcher(re5, "aa")      //false
-matcher(re5, "aaa")     //true
-matcher(re5, "aaaaa")   //true
-matcher(re5, "aaaaaa")  //false
-
-// (a?)^{3..5}
-val re6 = BETWEEN(OPTIONAL(CHAR('a')), 3, 5)
-
-matcher(re6, "")        //true
-matcher(re6, "a")       //true
-matcher(re6, "aa")      //true
-matcher(re6, "aaa")     //true
-matcher(re6, "aaaaa")   //true
-matcher(re6, "aaaaaa")  //false
-
-//Q5: regular expression for email addresses
-
-val alphaNum = ('a' to 'z').toSet ++ ('0' to '9')
-val Q5email = SEQ(
-                PLUS(RANGE(alphaNum + '_' + '-' + '.')), 
-                SEQ(
-                    CHAR('@'), 
-                    SEQ(
-                        PLUS(RANGE(alphaNum + '-' + '.')), 
-                        SEQ(
-                            CHAR('.'), 
-                            BETWEEN(RANGE(('a' to 'z').toSet + '.'), 2, 6)
-                            )
-                      )
-                )
-              )
-
-ders("nicolas.volken@kcl.ac.uk".toList, Q5email)
-
-// Q6
-val Q6 = SEQ(CHAR('/'),
-            SEQ(CHAR('*'),
-                SEQ(
-                  
-                    NOT(
-                      SEQ(
-                        STAR(FALL),
-                        SEQ(
-                          CHAR('*'),
-                          SEQ(
-                            CHAR('/'),
-                            STAR(FALL)
-                          )
-                        )
-                      )
-                    )
-                  
-                    ,
-                    SEQ(CHAR('*'),
-                    CHAR('/')
-                    )
-                )
-            )
-        )
-
-matcher(Q6, "/**/")
-matcher(Q6, "/*foobar*/")
-matcher(Q6, "/*test*/test*/")
-matcher(Q6, "/*test/*test*/")
-
-// Q7
-
-val Q7r1 = SEQ(CHAR('a'), SEQ(CHAR('a'), CHAR('a')))
-val Q7r2 = SEQ(BETWEEN(CHAR('a'), 19, 19), OPTIONAL(CHAR('a')))
-
-val Q7str5 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-val Q7str6 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-val Q7str7 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
-
-matcher(PLUS(PLUS(Q7r1)), Q7str5)
-matcher(PLUS(PLUS(Q7r2)), Q7str5)
-
-matcher(PLUS(PLUS(Q7r1)), Q7str6)
-matcher(PLUS(PLUS(Q7r2)), Q7str6)
-
-matcher(PLUS(PLUS(Q7r1)), Q7str7)
-matcher(PLUS(PLUS(Q7r2)), Q7str7)
-
--- a/solution/cw2/collatz.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-write "Input a number ";
-read n;
-while n > 1 do {
-  if n % 2 == 0 
-  then n := n / 2 
-  else n := 3 * n + 1;
-};
-write "Yes\n";
--- a/solution/cw2/collatz2.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-// Collatz series
-//
-// needs writing of strings and numbers; comments
-
-bnd := 1;
-while bnd < 101 do {
-  write bnd;
-  write ": ";
-  n := bnd;
-  cnt := 0;
-
-  while n > 1 do {
-    write n;
-    write ",";
-    
-    if n % 2 == 0 
-    then n := n / 2 
-    else n := 3 * n+1;
-
-    cnt := cnt + 1
-  };
-
-  write " => ";
-  write cnt;
-  write "\n";
-  bnd := bnd + 1
-}
\ No newline at end of file
--- a/solution/cw2/factors.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-// Find all factors of a given input number
-// by J.R. Cordy August 2005
-
-write "Input n please";
-read n;
-write "The factors of n are\n";
-f := 2;
-while n != 1 do {
-    while (n / f) * f == n do {
-        write f; write "\n";
-        n := n / f
-    };
-    f := f + 1
-}
\ No newline at end of file
--- a/solution/cw2/fib.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-write "Fib";
-read n;  
-minus1 := 1;
-minus2 := 0;
-while n > 0 do {
-       temp := minus2;
-       minus2 := minus1 + minus2;
-       minus1 := temp;
-       n := n - 1
-};
-write "Result: ";
-write minus2 
-
--- a/solution/cw2/lexer.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,337 +0,0 @@
-import scala.language.implicitConversions    
-import scala.language.reflectiveCalls
-
-// Rexp
-abstract class Rexp
-case object ZERO extends Rexp
-case object ONE extends Rexp
-case class CHAR(c: Char) extends Rexp
-case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
-case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
-case class STAR(r: Rexp) extends Rexp 
-case class RECD(x: String, r: Rexp) extends Rexp
-
-case class RANGE(s: Set[Char]) extends Rexp
-case class PLUS(r: Rexp) extends Rexp
-case class OPTIONAL(r: Rexp) extends Rexp
-case class NTIMES(r: Rexp, n: Int) extends Rexp 
-
-// Values
-abstract class Val
-case object Empty extends Val
-case class Chr(c: Char) extends Val
-case class Sequ(v1: Val, v2: Val) extends Val
-case class Left(v: Val) extends Val
-case class Right(v: Val) extends Val
-case class Stars(vs: List[Val]) extends Val
-case class Rec(x: String, v: Val) extends Val
-
-
-// Convenience typing
-def charlist2rexp(s : List[Char]): Rexp = s match {
-  case Nil => ONE
-  case c::Nil => CHAR(c)
-  case c::s => SEQ(CHAR(c), charlist2rexp(s))
-}
-
-implicit def string2rexp(s : String) : Rexp = 
-  charlist2rexp(s.toList)
-
-implicit def RexpOps(r: Rexp) = new {
-  def | (s: Rexp) = ALT(r, s)
-  def % = STAR(r)
-  def ~ (s: Rexp) = SEQ(r, s)
-}
-
-implicit def stringOps(s: String) = new {
-  def | (r: Rexp) = ALT(s, r)
-  def | (r: String) = ALT(s, r)
-  def % = STAR(s)
-  def ~ (r: Rexp) = SEQ(s, r)
-  def ~ (r: String) = SEQ(s, r)
-  def $ (r: Rexp) = RECD(s, r)
-}
-
-// nullable
-def nullable(r: Rexp) : Boolean = r match {
-  case ZERO => false
-  case ONE => true
-  case CHAR(_) => false
-  case ALT(r1, r2) => nullable(r1) || nullable(r2)
-  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
-  case STAR(_) => true
-
-  case RECD(_, r1) => nullable(r1)
-  case RANGE(_) => false
-  case PLUS(r1) => nullable(r1)
-  case OPTIONAL(_) => true
-  case NTIMES(r1, i) => if (i == 0) true else nullable(r1)
-}
-
-// der
-def der(c: Char, r: Rexp) : Rexp = r match {
-  case ZERO => ZERO
-  case ONE => ZERO
-  case CHAR(d) => if (c == d) ONE else ZERO
-  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
-  case SEQ(r1, r2) => 
-    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
-    else SEQ(der(c, r1), r2)
-  case STAR(r) => SEQ(der(c, r), STAR(r))
-
-  case RECD(_, r1) => der(c, r1)
-  case RANGE(s) => if (s.contains(c)) ONE else ZERO 
-  case PLUS(r1) => SEQ(der(c, r1), STAR(r1))
-  case OPTIONAL(r1) => der(c, r1)
-  case NTIMES(r, i) => 
-    if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
-}
-
-// Flatten
-def flatten(v: Val) : String = v match {
-  case Empty => ""
-  case Chr(c) => c.toString
-  case Left(v) => flatten(v)
-  case Right(v) => flatten(v)
-  case Sequ(v1, v2) => flatten(v1) + flatten(v2)
-  case Stars(vs) => vs.map(flatten).mkString
-  case Rec(_, v) => flatten(v)
-}
-
-// Env
-def env(v: Val) : List[(String, String)] = v match {
-  case Empty => Nil
-  case Chr(c) => Nil
-  case Left(v) => env(v)
-  case Right(v) => env(v)
-  case Sequ(v1, v2) => env(v1) ::: env(v2)
-  case Stars(vs) => vs.flatMap(env)
-  case Rec(x, v) => (x, flatten(v))::env(v)
-}
-
-// Mkeps
-def mkeps(r: Rexp) : Val = r match {
-  case ONE => Empty
-  case ALT(r1, r2) => 
-    if (nullable(r1)) Left(mkeps(r1)) else Right(mkeps(r2))
-  case SEQ(r1, r2) => Sequ(mkeps(r1), mkeps(r2))
-  case STAR(r) => Stars(Nil)
-  case RECD(x, r) => Rec(x, mkeps(r))
-
-  case PLUS(r) => Stars(List(mkeps(r)))   // the first copy must match the empty string
-  case OPTIONAL(r) => if (nullable(r)) Stars(List(mkeps(r))) else Stars(Nil)
-  case NTIMES(r, i) => Stars(List.fill(i)(mkeps(r)))
-}
-
-// Inj
-def inj(r: Rexp, c: Char, v: Val) : Val = (r, v) match {
-  case (STAR(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-  case (SEQ(r1, r2), Sequ(v1, v2)) => Sequ(inj(r1, c, v1), v2)
-  case (SEQ(r1, r2), Left(Sequ(v1, v2))) => Sequ(inj(r1, c, v1), v2)
-  case (SEQ(r1, r2), Right(v2)) => Sequ(mkeps(r1), inj(r2, c, v2))
-  case (ALT(r1, r2), Left(v1)) => Left(inj(r1, c, v1))
-  case (ALT(r1, r2), Right(v2)) => Right(inj(r2, c, v2))
-  case (CHAR(d), Empty) => Chr(c) 
-  case (RECD(x, r1), _) => Rec(x, inj(r1, c, v))
-
-  case (RANGE(_), Empty) => Chr(c)
-  case (PLUS(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-  case (OPTIONAL(r), v1) => Stars(List(inj(r, c, v1)))
-  case (NTIMES(r, n), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-}
-
-// Rectification functions
-def F_ID(v: Val): Val = v
-def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))
-def F_LEFT(f: Val => Val) = (v:Val) => Left(f(v))
-def F_ALT(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
-  case Right(v) => Right(f2(v))
-  case Left(v) => Left(f1(v))
-}
-def F_SEQ(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
-  case Sequ(v1, v2) => Sequ(f1(v1), f2(v2))
-}
-def F_SEQ_Empty1(f1: Val => Val, f2: Val => Val) = 
-  (v:Val) => Sequ(f1(Empty), f2(v))
-def F_SEQ_Empty2(f1: Val => Val, f2: Val => Val) = 
-  (v:Val) => Sequ(f1(v), f2(Empty))
-def F_RECD(f: Val => Val) = (v:Val) => v match {
-  case Rec(x, v) => Rec(x, f(v))
-}
-def F_ERROR(v: Val): Val = throw new Exception("error")
-
-// Simp
-def simp(r: Rexp): (Rexp, Val => Val) = r match {
-  case ALT(r1, r2) => {
-    val (r1s, f1s) = simp(r1)
-    val (r2s, f2s) = simp(r2)
-    (r1s, r2s) match {
-      case (ZERO, _) => (r2s, F_RIGHT(f2s))
-      case (_, ZERO) => (r1s, F_LEFT(f1s))
-      case _ => if (r1s == r2s) (r1s, F_LEFT(f1s))
-                else (ALT (r1s, r2s), F_ALT(f1s, f2s)) 
-    }
-  }
-  case SEQ(r1, r2) => {
-    val (r1s, f1s) = simp(r1)
-    val (r2s, f2s) = simp(r2)
-    (r1s, r2s) match {
-      case (ZERO, _) => (ZERO, F_ERROR)
-      case (_, ZERO) => (ZERO, F_ERROR)
-      case (ONE, _) => (r2s, F_SEQ_Empty1(f1s, f2s))
-      case (_, ONE) => (r1s, F_SEQ_Empty2(f1s, f2s))
-      case _ => (SEQ(r1s,r2s), F_SEQ(f1s, f2s))
-    }
-  }
-  case r => (r, F_ID)
-}
-
-// Lex
-def lex_simp(r: Rexp, s: List[Char]) : Val = s match {
-  case Nil => if (nullable(r)) mkeps(r) else 
-    { throw new Exception("lexing error") } 
-  case c::cs => {
-    val (r_simp, f_simp) = simp(der(c, r))
-    inj(r, c, f_simp(lex_simp(r_simp, cs)))
-  }
-}
-
-def lexing_simp(r: Rexp, s: String) = env(lex_simp(r, s.toList))
-
-// Language specific code
-val KEYWORD : Rexp = "while" | "if" | "then" | "else" | "do" | "for" | "to" | "true" | "false" | "read" | "write" | "skip" 
-val OP : Rexp = "+" | "-" | "*" | "%" | "/" | "==" | "!=" | ">" | "<" | ">=" | "<=" | ":=" | "&&" | "||"
-val LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z'))
-val SYM : Rexp = LET | RANGE(Set('.', '_', '>', '<', '=', ';', ',', ':', ')', '('))
-val PARENS : Rexp = "(" | "{" | ")" | "}"
-val SEMI : Rexp = ";"
-val WHITESPACE : Rexp = PLUS(" ") | "\n" | "\t" | "\r"
-val DIGIT : Rexp = RANGE(('0' to '9').toSet)
-val DIGIT1 : Rexp = RANGE(('1' to '9').toSet)
-val STRING : Rexp = "\"" ~ (SYM | " " | "\\n" | DIGIT).% ~ "\""
-val ID : Rexp = LET ~ (LET | "_" | DIGIT).%
-val NUM : Rexp = "0" | (DIGIT1 ~ DIGIT.%)
-val COMMENT : Rexp = "//" ~ (SYM | " " | DIGIT).% ~ ("\n" | "\r\n") 
-
-val WHILE_REGS = (("k" $ KEYWORD) | 
-                  ("o" $ OP) | 
-                  ("str" $ STRING) |
-                  ("p" $ PARENS) |
-                  ("s" $ SEMI) | 
-                  ("w" $ WHITESPACE) | 
-                  ("i" $ ID) | 
-                  ("n" $ NUM) |
-		  ("c" $ COMMENT)).%
-
-def esc(raw: String): String = {
-  import scala.reflect.runtime.universe._
-  Literal(Constant(raw)).toString
-}
-
-def escape(tks: List[(String, String)]) =
-  tks.map{ case (s1, s2) => (s1, esc(s2))}
-
-// Token
-abstract class Token extends Serializable 
-case class T_KEYWORD(s: String) extends Token
-case class T_OP(s: String) extends Token
-case class T_STRING(s: String) extends Token
-case class T_PAREN(s: String) extends Token
-case object T_SEMI extends Token
-case class T_ID(s: String) extends Token
-case class T_NUM(n: Int) extends Token
-
-val token : PartialFunction[(String, String), Token] = {
-  case ("k", s) => T_KEYWORD(s)
-  case ("o", s) => T_OP(s)
-  case ("str", s) => T_STRING(s)
-  case ("p", s) => T_PAREN(s)
-  case ("s", _) => T_SEMI
-  case ("i", s) => T_ID(s)
-  case ("n", s) => T_NUM(s.toInt)
-}
-
-// Tokenise
-def tokenise(s: String) : List[Token] = 
-  lexing_simp(WHILE_REGS, s).collect(token)
-
-
-// Q2 Tests
-lex_simp(NTIMES("a", 3), "aaa".toList)
-lex_simp(NTIMES(("a" | ONE), 3), "aa".toList)
-
-// Q3 Programs
-
-val prog1 = """write "Fib";
-read n;
-minus1 := 0;
-minus2 := 1;
-while n > 0 do {
-temp := minus2;
-minus2 := minus1 + minus2;
-minus1 := temp;
-n := n - 1
-};
-write "Result";
-write minus2"""
-
-val prog2 = """start := 1000;
-x := start;
-y := start;
-z := start;
-while 0 < x do {
-while 0 < y do {
-while 0 < z do { z := z - 1 };
-z := start;
-y := y - 1
-};
-y := start;
-x := x - 1
-}"""
-
-val prog3 = """write "Input n please";
-read n;
-write "The factors of n are";
-f := 2;
-while n != 1 do {
-while (n / f) * f == n do {
-write f;
-n := n / f
-};
-f := f + 1
-}"""
-
-println(tokenise(prog1))
-println(tokenise(prog2))
-println(tokenise(prog3))
-
-
-println("MY TESTS")
-
-println(lex_simp("x" $ OPTIONAL("a"), "a".toList))
-println(lex_simp("x" $ OPTIONAL("a"), "".toList))
-println(lex_simp("x" $ NTIMES(OPTIONAL("a"),4), "aa".toList))
-println(lex_simp("x" $ OPTIONAL("aa"), "aa".toList))
-println(lex_simp("x" $ OPTIONAL("aa"), "".toList))
-
-
-
-
-//===================
-println("Fib")
-println(tokenise(os.read(os.pwd / "fib.while")))
-
-println("Factors")
-println(tokenise(os.read(os.pwd / "factors.while")))
-
-println("Loops")
-println(tokenise(os.read(os.pwd / "loops.while")))
-
-println("Collatz")
-println(tokenise(os.read(os.pwd / "collatz.while")))
-
-println("Collatz 2")
-println(tokenise(os.read(os.pwd / "collatz2.while")))
-
-println("Primes")
-println(tokenise(os.read(os.pwd / "primes.while")))
--- a/solution/cw2/loops.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-start := 1000; 
-x := start;
-y := start;
-z := start;
-while 0 < x do {
- while 0 < y do {
-  while 0 < z do { z := z - 1 };
-  z := start;
-  y := y - 1
- };     
- y := start;
- x := x - 1
-}
-
--- a/solution/cw2/primes.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-// prints out prime numbers from
-// 2 to 100 (end)
-
-end := 100;
-n := 2;
-while (n < end) do {
-  f := 2;
-  tmp := 0;
-  while ((f < n / 2 + 1) && (tmp == 0)) do {
-    if ((n / f) * f == n) then  { tmp := 1 } else { skip };
-    f := f + 1
-  };
-  if (tmp == 0) then { write(n); write("\n") } else { skip };
-  n  := n + 1
-}
\ No newline at end of file
--- a/solution/cw3/collatz.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-write "Input a number ";
-read n;
-while n > 1 do {
-  if n % 2 == 0 
-  then n := n/2 
-  else n := 3*n+1;
-};
-write "Yes\n";
--- a/solution/cw3/collatz2.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-// Collatz series
-//
-// needs writing of strings and numbers; comments
-
-bnd := 1;
-while bnd < 101 do {
-  write bnd;
-  write ": ";
-  n := bnd;
-  cnt := 0;
-
-  while n > 1 do {
-    write n;
-    write ",";
-    
-    if n % 2 == 0 
-    then n := n / 2 
-    else n := 3 * n+1;
-
-    cnt := cnt + 1
-  };
-
-  write " => ";
-  write cnt;
-  write "\n";
-  bnd := bnd + 1
-}
\ No newline at end of file
--- a/solution/cw3/factors.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-// Find all factors of a given input number
-// by J.R. Cordy August 2005
-
-write "Input n please";
-read n;
-write "The factors of n are:\n";
-f := 2;
-while n != 1 do {
-    while (n / f) * f == n do {
-        write f; write "\n";
-        n := n / f
-    };
-    f := f + 1
-}
\ No newline at end of file
--- a/solution/cw3/fib.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-write "Fib: ";
-read n;  
-minus1 := 0;
-minus2 := 1;
-while n > 0 do {
-       temp := minus2;
-       minus2 := minus1 + minus2;
-       minus1 := temp;
-       n := n - 1
-};
-write "Result: ";
-write minus2 
-
--- a/solution/cw3/lexer.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,266 +0,0 @@
-import scala.language.implicitConversions    
-import scala.language.reflectiveCalls
-
-// Rexp
-abstract class Rexp
-case object ZERO extends Rexp
-case object ONE extends Rexp
-case class CHAR(c: Char) extends Rexp
-case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
-case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
-case class STAR(r: Rexp) extends Rexp 
-case class RECD(x: String, r: Rexp) extends Rexp
-
-case class RANGE(s: Set[Char]) extends Rexp
-case class PLUS(r: Rexp) extends Rexp
-case class OPTIONAL(r: Rexp) extends Rexp
-case class NTIMES(r: Rexp, n: Int) extends Rexp 
-
-// Values
-abstract class Val
-case object Empty extends Val
-case class Chr(c: Char) extends Val
-case class Sequ(v1: Val, v2: Val) extends Val
-case class Left(v: Val) extends Val
-case class Right(v: Val) extends Val
-case class Stars(vs: List[Val]) extends Val
-case class Rec(x: String, v: Val) extends Val
-
-
-// Convenience typing
-def charlist2rexp(s : List[Char]): Rexp = s match {
-  case Nil => ONE
-  case c::Nil => CHAR(c)
-  case c::s => SEQ(CHAR(c), charlist2rexp(s))
-}
-
-implicit def string2rexp(s : String) : Rexp = 
-  charlist2rexp(s.toList)
-
-implicit def RexpOps(r: Rexp) = new {
-  def | (s: Rexp) = ALT(r, s)
-  def % = STAR(r)
-  def ~ (s: Rexp) = SEQ(r, s)
-}
-
-implicit def stringOps(s: String) = new {
-  def | (r: Rexp) = ALT(s, r)
-  def | (r: String) = ALT(s, r)
-  def % = STAR(s)
-  def ~ (r: Rexp) = SEQ(s, r)
-  def ~ (r: String) = SEQ(s, r)
-  def $ (r: Rexp) = RECD(s, r)
-}
-
-// nullable
-def nullable(r: Rexp) : Boolean = r match {
-  case ZERO => false
-  case ONE => true
-  case CHAR(_) => false
-  case ALT(r1, r2) => nullable(r1) || nullable(r2)
-  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
-  case STAR(_) => true
-
-  case RECD(_, r1) => nullable(r1)
-  case RANGE(_) => false
-  case PLUS(r1) => nullable(r1)
-  case OPTIONAL(_) => true
-  case NTIMES(r1, i) => if (i == 0) true else nullable(r1)
-}
-
-// der
-def der(c: Char, r: Rexp) : Rexp = r match {
-  case ZERO => ZERO
-  case ONE => ZERO
-  case CHAR(d) => if (c == d) ONE else ZERO
-  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
-  case SEQ(r1, r2) => 
-    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
-    else SEQ(der(c, r1), r2)
-  case STAR(r) => SEQ(der(c, r), STAR(r))
-
-  case RECD(_, r1) => der(c, r1)
-  case RANGE(s) => if (s.contains(c)) ONE else ZERO 
-  case PLUS(r1) => SEQ(der(c, r1), STAR(r1))
-  case OPTIONAL(r1) => ALT(der(c, r1), ZERO)
-  case NTIMES(r, i) => 
-    if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
-}
-
-// Flatten
-def flatten(v: Val) : String = v match {
-  case Empty => ""
-  case Chr(c) => c.toString
-  case Left(v) => flatten(v)
-  case Right(v) => flatten(v)
-  case Sequ(v1, v2) => flatten(v1) + flatten(v2)
-  case Stars(vs) => vs.map(flatten).mkString
-  case Rec(_, v) => flatten(v)
-}
-
-// Env
-def env(v: Val) : List[(String, String)] = v match {
-  case Empty => Nil
-  case Chr(c) => Nil
-  case Left(v) => env(v)
-  case Right(v) => env(v)
-  case Sequ(v1, v2) => env(v1) ::: env(v2)
-  case Stars(vs) => vs.flatMap(env)
-  case Rec(x, v) => (x, flatten(v))::env(v)
-}
-
-// Mkeps
-def mkeps(r: Rexp) : Val = r match {
-  case ONE => Empty
-  case ALT(r1, r2) => 
-    if (nullable(r1)) Left(mkeps(r1)) else Right(mkeps(r2))
-  case SEQ(r1, r2) => Sequ(mkeps(r1), mkeps(r2))
-  case STAR(r) => Stars(Nil)
-  case RECD(x, r) => Rec(x, mkeps(r))
-
-  case PLUS(r) => Stars(List(mkeps(r)))   // the first copy must match the empty string
-  case OPTIONAL(r) => Right(Empty)
-  case NTIMES(r, i) => Stars(List.fill(i)(mkeps(r)))
-}
-
-// Inj
-def inj(r: Rexp, c: Char, v: Val) : Val = (r, v) match {
-  case (STAR(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-  case (SEQ(r1, r2), Sequ(v1, v2)) => Sequ(inj(r1, c, v1), v2)
-  case (SEQ(r1, r2), Left(Sequ(v1, v2))) => Sequ(inj(r1, c, v1), v2)
-  case (SEQ(r1, r2), Right(v2)) => Sequ(mkeps(r1), inj(r2, c, v2))
-  case (ALT(r1, r2), Left(v1)) => Left(inj(r1, c, v1))
-  case (ALT(r1, r2), Right(v2)) => Right(inj(r2, c, v2))
-  case (CHAR(d), Empty) => Chr(c) 
-  case (RECD(x, r1), _) => Rec(x, inj(r1, c, v))
-
-  case (RANGE(_), Empty) => Chr(c)
-  case (PLUS(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-  case (OPTIONAL(r), Left(v1)) => Left(inj(r, c, v1))
-  case (NTIMES(r, n), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-}
-
-// Rectification functions
-def F_ID(v: Val): Val = v
-def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))
-def F_LEFT(f: Val => Val) = (v:Val) => Left(f(v))
-def F_ALT(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
-  case Right(v) => Right(f2(v))
-  case Left(v) => Left(f1(v))
-}
-def F_SEQ(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
-  case Sequ(v1, v2) => Sequ(f1(v1), f2(v2))
-}
-def F_SEQ_Empty1(f1: Val => Val, f2: Val => Val) = 
-  (v:Val) => Sequ(f1(Empty), f2(v))
-def F_SEQ_Empty2(f1: Val => Val, f2: Val => Val) = 
-  (v:Val) => Sequ(f1(v), f2(Empty))
-def F_RECD(f: Val => Val) = (v:Val) => v match {
-  case Rec(x, v) => Rec(x, f(v))
-}
-def F_ERROR(v: Val): Val = throw new Exception("error")
-
-// Simp
-def simp(r: Rexp): (Rexp, Val => Val) = r match {
-  case ALT(r1, r2) => {
-    val (r1s, f1s) = simp(r1)
-    val (r2s, f2s) = simp(r2)
-    (r1s, r2s) match {
-      case (ZERO, _) => (r2s, F_RIGHT(f2s))
-      case (_, ZERO) => (r1s, F_LEFT(f1s))
-      case _ => if (r1s == r2s) (r1s, F_LEFT(f1s))
-                else (ALT (r1s, r2s), F_ALT(f1s, f2s)) 
-    }
-  }
-  case SEQ(r1, r2) => {
-    val (r1s, f1s) = simp(r1)
-    val (r2s, f2s) = simp(r2)
-    (r1s, r2s) match {
-      case (ZERO, _) => (ZERO, F_ERROR)
-      case (_, ZERO) => (ZERO, F_ERROR)
-      case (ONE, _) => (r2s, F_SEQ_Empty1(f1s, f2s))
-      case (_, ONE) => (r1s, F_SEQ_Empty2(f1s, f2s))
-      case _ => (SEQ(r1s,r2s), F_SEQ(f1s, f2s))
-    }
-  }
-  case r => (r, F_ID)
-}
-
-// Lex
-def lex_simp(r: Rexp, s: List[Char]) : Val = s match {
-  case Nil => if (nullable(r)) mkeps(r) else 
-    { throw new Exception("lexing error") } 
-  case c::cs => {
-    val (r_simp, f_simp) = simp(der(c, r))
-    inj(r, c, f_simp(lex_simp(r_simp, cs)))
-  }
-}
-
-def lexing_simp(r: Rexp, s: String) = env(lex_simp(r, s.toList))
-
-// Language specific code
-val KEYWORD : Rexp = "while" | "if" | "then" | "else" | "do" | "for" | "to" | "true" | "false" | "read" | "write" | "skip" 
-val OP : Rexp = "+" | "-" | "*" | "%" | "/" | "==" | "!=" | ">" | "<" | ">=" | "<=" | ":=" | "&&" | "||"
-val LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z'))
-val SYM : Rexp = (LET | RANGE(Set('.', '_', '>', '<', '=', ';', ',', ':')))
-val PARENS : Rexp = "(" | "{" | ")" | "}"
-val SEMI : Rexp = ";"
-val WHITESPACE : Rexp = PLUS(" ") | "\n" | "\t" | "\r"
-val DIGIT : Rexp = RANGE(('0' to '9').toSet)
-val DIGIT1 : Rexp = RANGE(('1' to '9').toSet)
-val STRING : Rexp = "\"" ~ (SYM | " " | "\\n" | DIGIT).% ~ "\""
-val ID : Rexp = LET ~ (LET | "_" | DIGIT).%
-val NUM : Rexp = "0" | (DIGIT1 ~ DIGIT.%)
-val EOL : Rexp = "\n" | "\r\n"
-val COMMENT : Rexp = "//" ~ (SYM | PARENS | " " | DIGIT).% ~ EOL 
-
-val WHILE_REGS = (("k" $ KEYWORD) | 
-                  ("o" $ OP) | 
-                  ("str" $ STRING) |
-                  ("p" $ PARENS) |
-                  ("s" $ SEMI) | 
-                  ("w" $ WHITESPACE) | 
-                  ("i" $ ID) | 
-                  ("n" $ NUM) |
-		  ("c" $ COMMENT)).%
-
-// Token
-abstract class Token extends Serializable 
-case class T_KEYWORD(s: String) extends Token
-case class T_OP(s: String) extends Token
-case class T_STRING(s: String) extends Token
-case class T_PAREN(s: String) extends Token
-case object T_SEMI extends Token
-case class T_ID(s: String) extends Token
-case class T_NUM(n: Int) extends Token
-
-val token : PartialFunction[(String, String), Token] = {
-  case ("k", s) => T_KEYWORD(s)
-  case ("o", s) => T_OP(s)
-  case ("str", s) => T_STRING(s)
-  case ("p", s) => T_PAREN(s)
-  case ("s", _) => T_SEMI
-  case ("i", s) => T_ID(s)
-  case ("n", s) => T_NUM(s.toInt)
-}
-
-// Tokenise
-def tokenise(s: String) : List[Token] = 
-  lexing_simp(WHILE_REGS, s).collect(token)
-
-
-val fact = """
-write "Input n please";
-read n;
-write "The factors of n are";
-f := 2;
-while n != 1 do {
-    while (n / f) * f == n do {
-        write f;
-        n := n / f
-    };
-    f := f + 1
-}
-"""
-println(tokenise(fact))
-
--- a/solution/cw3/loops.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-start := 1000; 
-x := start;
-y := start;
-z := start;
-while 0 < x do {
- while 0 < y do {
-  while 0 < z do { z := z - 1 };
-  z := start;
-  y := y - 1
- };     
- y := start;
- x := x - 1
-}
-
--- a/solution/cw3/parser.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,246 +0,0 @@
-// CW3
-
-import $file.lexer
-import lexer._ 
-
-
-case class ~[+A, +B](_1: A, _2: B)
-type IsSeq[A] = A => Seq[_]
-
-abstract class Parser[I : IsSeq, T] {
-  def parse(ts: I): Set[(T, I)]
-
-  def parse_all(ts: I) : Set[T] =
-    for ((head, tail) <- parse(ts); if tail.isEmpty) yield head
-}
-
-class SeqParser[I : IsSeq, T, S](p: => Parser[I, T], q: => Parser[I, S]) extends Parser[I, ~[T, S]] {
-  def parse(sb: I) = 
-    for ((head1, tail1) <- p.parse(sb); 
-         (head2, tail2) <- q.parse(tail1)) yield (new ~(head1, head2), tail2)
-}
-
-class AltParser[I : IsSeq, T](p: => Parser[I, T], q: => Parser[I, T]) extends Parser[I, T] {
-  def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
-}
-
-class FunParser[I : IsSeq, T, S](p: => Parser[I, T], f: T => S) extends Parser[I, S] {
-  def parse(sb: I) = 
-    for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
-}
-
-// New parser that takes as input a list of tokens
-case class TokenListParser(ts: List[Token]) extends Parser[List[Token], List[Token]] {
-    def parse(tsb: List[Token]) = {
-        val (prefix, suffix) = tsb.splitAt(ts.length)
-        if (prefix == ts) Set((prefix, suffix)) else Set()
-    }
-}
-
-// Implicit definitions to go from a token 
-// or a list of tokens to a TokenListParser
-implicit def token2parser(t: Token) = TokenListParser(List(t))
-implicit def tokenList2parser(ts: List[Token]) = TokenListParser(ts)
-
-implicit def ParserOps[I : IsSeq, T](p: Parser[I, T]) = new {
-  def || (q : => Parser[I, T]) = new AltParser[I, T](p, q)
-  def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
-  def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
-}
-
-implicit def TokenOps(t: Token) = new {
-    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](List(t), q)
-    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](List(t), qs)
-    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](List(t), f)
-    def ~[S](q : => Parser[List[Token], S]) =
-        new SeqParser[List[Token], List[Token], S](List(t), q)
-    def ~ (qs : List[Token]) =
-        new SeqParser[List[Token], List[Token], List[Token]](List(t), qs)
-}
-
-implicit def TokenListOps(ts: List[Token]) = new {
-    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](ts, q)
-    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](ts, qs)
-    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](ts, f)
-    def ~[S](q : => Parser[List[Token], S]) =
-        new SeqParser[List[Token], List[Token], S](ts, q)
-    def ~ (qs : List[Token]) =
-        new SeqParser[List[Token], List[Token], List[Token]](ts, qs)
-}
-
-// Abstract Syntax Trees
-abstract class Stmt
-abstract class AExp
-abstract class BExp
-
-type Block = List[Stmt]
-
-case object Skip extends Stmt
-case class If(a: BExp, bl1: Block, bl2: Block) extends Stmt
-case class While(b: BExp, bl: Block) extends Stmt
-case class Assign(s: String, a: AExp) extends Stmt
-case class Read(s: String) extends Stmt
-case class WriteId(s: String) extends Stmt  // for printing values of variables
-case class WriteString(s: String) extends Stmt  // for printing words
-
-case class Var(s: String) extends AExp
-case class Num(i: Int) extends AExp
-case class Aop(o: String, a1: AExp, a2: AExp) extends AExp
-
-case object True extends BExp
-case object False extends BExp
-case class Bop(o: String, a1: AExp, a2: AExp) extends BExp
-case class And(b1: BExp, b2: BExp) extends BExp
-case class Or(b1: BExp, b2: BExp) extends BExp
-
-case class IdParser() extends Parser[List[Token], String] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_ID(id) :: rest => Set((id, rest))
-        case _ => Set()
-    }
-}
-
-case class NumParser() extends Parser[List[Token], Int] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_NUM(n) :: rest => Set((n, rest))
-        case _ => Set()
-    }
-}
-
-case class StringParser() extends Parser[List[Token], String] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_STRING(s) :: rest => Set((s, rest))
-        case _ => Set()
-    }
-}
-
-// WHILE Language Parsing
-lazy val AExp: Parser[List[Token], AExp] = 
-  (Te ~ T_OP("+") ~ AExp) ==> { case x ~ _ ~ z => Aop("+", x, z): AExp } ||
-  (Te ~ T_OP("-") ~ AExp) ==> { case x ~ _ ~ z => Aop("-", x, z): AExp } || Te
-lazy val Te: Parser[List[Token], AExp] = 
-  (Fa ~ T_OP("*") ~ Te) ==> { case x ~ _ ~ z => Aop("*", x, z): AExp } || 
-  (Fa ~ T_OP("/") ~ Te) ==> { case x ~ _ ~ z => Aop("/", x, z): AExp } || 
-  (Fa ~ T_OP("%") ~ Te) ==> { case x ~ _ ~ z => Aop("%", x, z): AExp } || Fa  
-lazy val Fa: Parser[List[Token], AExp] = 
-   (T_PAREN("(") ~ AExp ~ T_PAREN(")")) ==> { case _ ~ y ~ _ => y } || 
-   IdParser() ==> Var  || 
-   NumParser() ==> Num
-
-lazy val BExp: Parser[List[Token], BExp] = 
-   (AExp ~ T_OP("==") ~ AExp) ==> { case x ~ _ ~ z => Bop("==", x, z): BExp } || 
-   (AExp ~ T_OP("!=") ~ AExp) ==> { case x ~ _ ~ z => Bop("!=", x, z): BExp } || 
-   (AExp ~ T_OP("<") ~ AExp) ==> { case x ~ _ ~ z => Bop("<", x, z): BExp } || 
-   (AExp ~ T_OP(">") ~ AExp) ==> { case x ~ _ ~ z => Bop(">", x, z): BExp } ||
-   (T_PAREN("(") ~ BExp ~ T_PAREN(")") ~ T_OP("&&") ~ BExp) ==> { case _ ~ y ~ _ ~ _ ~ v => And(y, v): BExp } ||
-   (T_PAREN("(") ~ BExp ~ T_PAREN(")") ~ T_OP("||") ~ BExp) ==> { case _ ~ y ~ _ ~ _ ~ v => Or(y, v): BExp } ||
-   (T_KEYWORD("true") ==> (_ => True: BExp )) || 
-   (T_KEYWORD("false") ==> (_ => False: BExp )) ||
-   (T_PAREN("(") ~ BExp ~ T_PAREN(")")) ==> { case _ ~ x ~ _ => x }
-
-lazy val Stmt: Parser[List[Token], Stmt] =
-    T_KEYWORD("skip") ==> (_ => Skip: Stmt) ||
-    (IdParser() ~ T_OP(":=") ~ AExp) ==> { case id ~ _ ~ z => Assign(id, z): Stmt } ||
-    (T_KEYWORD("if") ~ BExp ~ T_KEYWORD("then") ~ Block ~ T_KEYWORD("else") ~ Block) ==> { case _ ~ y ~ _ ~ u ~ _ ~ w => If(y, u, w): Stmt } ||
-    (T_KEYWORD("while") ~ BExp ~ T_KEYWORD("do") ~ Block) ==> { case _ ~ y ~ _ ~ w => While(y, w) : Stmt } ||
-    (T_KEYWORD("read") ~ IdParser()) ==> { case _ ~ id => Read(id): Stmt} ||
-    (T_KEYWORD("write") ~ IdParser()) ==> { case _ ~ id => WriteId(id): Stmt} ||
-    (T_KEYWORD("write") ~ StringParser()) ==> { case _ ~ s => WriteString(s): Stmt} || 
-    (T_KEYWORD("write") ~ T_PAREN("(") ~ IdParser() ~ T_PAREN(")")) ==> { case _ ~ _ ~ id ~ _ => WriteId(id): Stmt} ||
-    (T_KEYWORD("write") ~  T_PAREN("(") ~ StringParser() ~ T_PAREN(")")) ==> { case _ ~ _ ~ s ~ _ => WriteString(s): Stmt}
-
-lazy val Stmts: Parser[List[Token], Block] =
-    (Stmt ~ T_SEMI ~ Stmts) ==> { case x ~ _ ~ z => x :: z : Block } ||
-    (Stmt ==> (s => List(s) : Block))
-
-lazy val Block: Parser[List[Token], Block] =
-    (T_PAREN("{") ~ Stmts ~ T_PAREN("}")) ==> { case x ~ y ~ z => y} ||
-    (Stmt ==> (s => List(s)))
-
-// Testing with programs 2 & 3
-
-println("Fibonacci")
-println(Stmts.parse_all(tokenise(os.read(os.pwd / "fib.while"))))
-
-println("Loops")
-println(Stmts.parse_all(tokenise(os.read(os.pwd / "loops.while"))))
-
-println("Collatz")
-println(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))))
-
-
-// Interpreter
-
-// Environment to store values of variables
-type Env = Map[String, Int]
-
-def eval_aexp(a: AExp, env: Env) : Int = a match {
-  case Num(i) => i
-  case Var(s) => env(s)
-  case Aop("+", a1, a2) => eval_aexp(a1, env) + eval_aexp(a2, env)
-  case Aop("-", a1, a2) => eval_aexp(a1, env) - eval_aexp(a2, env)
-  case Aop("*", a1, a2) => eval_aexp(a1, env) * eval_aexp(a2, env)
-  case Aop("/", a1, a2) => eval_aexp(a1, env) / eval_aexp(a2, env)
-  case Aop("%", a1, a2) => eval_aexp(a1, env) % eval_aexp(a2, env)
-}
-
-def eval_bexp(b: BExp, env: Env) : Boolean = b match {
-  case True => true
-  case False => false
-  case Bop("==", a1, a2) => eval_aexp(a1, env) == eval_aexp(a2, env)
-  case Bop("!=", a1, a2) => !(eval_aexp(a1, env) == eval_aexp(a2, env))
-  case Bop(">", a1, a2) => eval_aexp(a1, env) > eval_aexp(a2, env)
-  case Bop("<", a1, a2) => eval_aexp(a1, env) < eval_aexp(a2, env)
-  case And(b1, b2) => eval_bexp(b1, env) && eval_bexp(b2, env)
-  case Or(b1, b2) => eval_bexp(b1, env) || eval_bexp(b2, env)
-}
-
-// Import needed to take int as input from the user
-import scala.io.StdIn.readInt
-
-def eval_stmt(s: Stmt, env: Env) : Env = s match {
-  case Skip => env
-  case Assign(x, a) => env + (x -> eval_aexp(a, env))
-  case If(b, bl1, bl2) => if (eval_bexp(b, env)) eval_bl(bl1, env) else eval_bl(bl2, env) 
-  case While(b, bl) => 
-    if (eval_bexp(b, env)) eval_stmt(While(b, bl), eval_bl(bl, env))
-    else env
-
-  case WriteId(x) => { print(env(x)) ; env }
-  case WriteString(x) => {
-        print(x.replaceAll("\"", "").replaceAll("""\\n""", "\n")) ;
-        env
-       }
-
-  case Read(x) => { 
-      println("Enter an integer and press ENTER:") ; 
-      val n = readInt() ; // Note: Does not work when using the REPL
-      eval_stmt(Assign(x, Num(n)), env) 
-  }
-}
-
-def eval_bl(bl: Block, env: Env) : Env = bl match {
-  case Nil => env
-  case s::bl => eval_bl(bl, eval_stmt(s, env))
-}
-
-def eval(bl: Block) : Env = eval_bl(bl, Map())
-
-println("Primes eval")
-println(tokenise(os.read(os.pwd / "primes.while")))
-println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "primes.while"))).head))
-
-println("Factors eval")
-println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "factors.while"))).head))
-
-println("Collatz2 eval")
-println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head))
-
-/*
-println("Loops eval")
-val start = System.nanoTime()
-println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "loops.while"))).head))
-val end = System.nanoTime()
-println("Time taken in seconds: ")
-println((end - start)/(1.0e9))
-*/
--- a/solution/cw3/parser2.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-// CW3
-
-import $file.lexer
-import lexer._ 
-
-
-case class ~[+A, +B](_1: A, _2: B)
-type IsSeq[A] = A => Seq[_]
-
-abstract class Parser[I : IsSeq, T] {
-  def parse(ts: I): Set[(T, I)]
-
-  def parse_all(ts: I) : Set[T] =
-    for ((head, tail) <- parse(ts); if tail.isEmpty) yield head
-}
-
-class SeqParser[I : IsSeq, T, S](p: => Parser[I, T], q: => Parser[I, S]) extends Parser[I, ~[T, S]] {
-  def parse(sb: I) = 
-    for ((head1, tail1) <- p.parse(sb); 
-         (head2, tail2) <- q.parse(tail1)) yield (new ~(head1, head2), tail2)
-}
-
-class AltParser[I : IsSeq, T](p: => Parser[I, T], q: => Parser[I, T]) extends Parser[I, T] {
-  def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
-}
-
-class FunParser[I : IsSeq, T, S](p: => Parser[I, T], f: T => S) extends Parser[I, S] {
-  def parse(sb: I) = 
-    for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
-}
-
-// New parser that takes as input a list of tokens
-case class TokenListParser(ts: List[Token]) extends Parser[List[Token], List[Token]] {
-    def parse(tsb: List[Token]) = {
-        val (prefix, suffix) = tsb.splitAt(ts.length)
-        if (prefix == ts) Set((prefix, suffix)) else Set()
-    }
-}
-
-// Implicit definitions to go from a token 
-// or a list of tokens to a TokenListParser
-implicit def token2parser(t: Token) = TokenListParser(List(t))
-implicit def tokenList2parser(ts: List[Token]) = TokenListParser(ts)
-
-implicit def ParserOps[I : IsSeq, T](p: Parser[I, T]) = new {
-  def || (q : => Parser[I, T]) = new AltParser[I, T](p, q)
-  def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
-  def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
-}
-
-implicit def TokenOps(t: Token) = new {
-    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](List(t), q)
-    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](List(t), qs)
-    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](List(t), f)
-    def ~[S](q : => Parser[List[Token], S]) =
-        new SeqParser[List[Token], List[Token], S](List(t), q)
-    def ~ (qs : List[Token]) =
-        new SeqParser[List[Token], List[Token], List[Token]](List(t), qs)
-}
-
-implicit def TokenListOps(ts: List[Token]) = new {
-    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](ts, q)
-    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](ts, qs)
-    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](ts, f)
-    def ~[S](q : => Parser[List[Token], S]) =
-        new SeqParser[List[Token], List[Token], S](ts, q)
-    def ~ (qs : List[Token]) =
-        new SeqParser[List[Token], List[Token], List[Token]](ts, qs)
-}
-
-// Abstract Syntax Trees
-abstract class Stmt
-abstract class AExp
-abstract class BExp
-
-type Block = List[Stmt]
-
-case object Skip extends Stmt
-case class If(a: BExp, bl1: Block, bl2: Block) extends Stmt
-case class While(b: BExp, bl: Block) extends Stmt
-case class Assign(s: String, a: AExp) extends Stmt
-case class Read(s: String) extends Stmt
-case class WriteId(s: String) extends Stmt  // for printing values of variables
-case class WriteString(s: String) extends Stmt  // for printing words
-
-case class Var(s: String) extends AExp
-case class Num(i: Int) extends AExp
-case class Aop(o: String, a1: AExp, a2: AExp) extends AExp
-
-case object True extends BExp
-case object False extends BExp
-case class Bop(o: String, a1: AExp, a2: AExp) extends BExp
-case class And(b1: BExp, b2: BExp) extends BExp
-case class Or(b1: BExp, b2: BExp) extends BExp
-
-case class IdParser() extends Parser[List[Token], String] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_ID(id) :: rest => Set((id, rest))
-        case _ => Set()
-    }
-}
-
-case class NumParser() extends Parser[List[Token], Int] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_NUM(n) :: rest => Set((n, rest))
-        case _ => Set()
-    }
-}
-
-case class StringParser() extends Parser[List[Token], String] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_STRING(s) :: rest => Set((s, rest))
-        case _ => Set()
-    }
-}
-
-case class TokParser(s: String) extends Parser[List[Token], String] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_OP(o) :: rest if s == o => Set((o, rest))
-        case T_KWD(k) :: rest if s == k => Set((k, rest))
-        case _ => Set()
-    }
-}
-
-implicit def parser_interpolation(sc: StringContext) = new {
-    def p(args: Any*) = TokParser(sc.s(args:_*))
-}    
-
-
-// WHILE Language Parsing
-lazy val AExp: Parser[List[Token], AExp] = 
-  (Te ~ T_OP("+") ~ AExp) ==> { case x ~ _ ~ z => Aop("+", x, z): AExp } ||
-  (Te ~ T_OP("-") ~ AExp) ==> { case x ~ _ ~ z => Aop("-", x, z): AExp } || Te
-lazy val Te: Parser[List[Token], AExp] = 
-  (Fa ~ T_OP("*") ~ Te) ==> { case x ~ _ ~ z => Aop("*", x, z): AExp } || 
-  (Fa ~ T_OP("/") ~ Te) ==> { case x ~ _ ~ z => Aop("/", x, z): AExp } || 
-  (Fa ~ T_OP("%") ~ Te) ==> { case x ~ _ ~ z => Aop("%", x, z): AExp } || Fa  
-lazy val Fa: Parser[List[Token], AExp] = 
-   (T_PAREN("(") ~ AExp ~ T_PAREN(")")) ==> { case _ ~ y ~ _ => y } || 
-   IdParser() ==> Var  || 
-   NumParser() ==> Num
-
-lazy val BExp: Parser[List[Token], BExp] = 
-   (AExp ~ T_OP("==") ~ AExp) ==> { case x ~ _ ~ z => Bop("==", x, z): BExp } || 
-   (AExp ~ T_OP("!=") ~ AExp) ==> { case x ~ _ ~ z => Bop("!=", x, z): BExp } || 
-   (AExp ~ T_OP("<") ~ AExp) ==> { case x ~ _ ~ z => Bop("<", x, z): BExp } || 
-   (AExp ~ T_OP(">") ~ AExp) ==> { case x ~ _ ~ z => Bop(">", x, z): BExp } ||
-   (T_PAREN("(") ~ BExp ~ List(T_PAREN(")"), T_OP("&&")) ~ BExp) ==> { case _ ~ y ~ _ ~ v => And(y, v): BExp } ||
-   (T_PAREN("(") ~ BExp ~ List(T_PAREN(")"), T_OP("||")) ~ BExp) ==> { case _ ~ y ~ _ ~ v => Or(y, v): BExp } ||
-   (T_KEYWORD("true") ==> (_ => True: BExp )) || 
-   (T_KEYWORD("false") ==> (_ => False: BExp )) ||
-   (T_PAREN("(") ~ BExp ~ T_PAREN(")")) ==> { case _ ~ x ~ _ => x }
-
-lazy val Stmt: Parser[List[Token], Stmt] =
-    T_KEYWORD("skip") ==> (_ => Skip: Stmt) ||
-    (IdParser() ~ T_OP(":=") ~ AExp) ==> { case id ~ _ ~ z => Assign(id, z): Stmt } ||
-    (T_KEYWORD("if") ~ BExp ~ T_KEYWORD("then") ~ Block ~ T_KEYWORD("else") ~ Block) ==> { case _ ~ y ~ _ ~ u ~ _ ~ w => If(y, u, w): Stmt } ||
-    (T_KEYWORD("while") ~ BExp ~ T_KEYWORD("do") ~ Block) ==> { case _ ~ y ~ _ ~ w => While(y, w) : Stmt } ||
-    (T_KEYWORD("read") ~ IdParser()) ==> { case _ ~ id => Read(id): Stmt} ||
-    (T_KEYWORD("write") ~ IdParser()) ==> { case _ ~ id => WriteId(id): Stmt} ||
-    (T_KEYWORD("write") ~ StringParser()) ==> { case _ ~ s => WriteString(s): Stmt}
-
-lazy val Stmts: Parser[List[Token], Block] =
-    (Stmt ~ T_SEMI ~ Stmts) ==> { case x ~ _ ~ z => x :: z : Block } ||
-    (Stmt ==> (s => List(s) : Block))
-
-lazy val Block: Parser[List[Token], Block] =
-    (T_PAREN("{") ~ Stmts ~ T_PAREN("}")) ==> { case x ~ y ~ z => y} ||
-    (Stmt ==> (s => List(s)))
-
-// Testing with programs 2 & 3
-
-println("Fibonacci")
-println(Stmts.parse_all(tokenise(os.read(os.pwd / "fib.while"))))
-
-println("Loops")
-println(Stmts.parse_all(tokenise(os.read(os.pwd / "loops.while"))))
-
-println("Collatz")
-println(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))))
-
-
-// Interpreter
-
-// Environment to store values of variables
-type Env = Map[String, Int]
-
-def eval_aexp(a: AExp, env: Env) : Int = a match {
-  case Num(i) => i
-  case Var(s) => env(s)
-  case Aop("+", a1, a2) => eval_aexp(a1, env) + eval_aexp(a2, env)
-  case Aop("-", a1, a2) => eval_aexp(a1, env) - eval_aexp(a2, env)
-  case Aop("*", a1, a2) => eval_aexp(a1, env) * eval_aexp(a2, env)
-  case Aop("/", a1, a2) => eval_aexp(a1, env) / eval_aexp(a2, env)
-  case Aop("%", a1, a2) => eval_aexp(a1, env) % eval_aexp(a2, env)
-}
-
-def eval_bexp(b: BExp, env: Env) : Boolean = b match {
-  case True => true
-  case False => false
-  case Bop("==", a1, a2) => eval_aexp(a1, env) == eval_aexp(a2, env)
-  case Bop("!=", a1, a2) => !(eval_aexp(a1, env) == eval_aexp(a2, env))
-  case Bop(">", a1, a2) => eval_aexp(a1, env) > eval_aexp(a2, env)
-  case Bop("<", a1, a2) => eval_aexp(a1, env) < eval_aexp(a2, env)
-  case And(b1, b2) => eval_bexp(b1, env) && eval_bexp(b2, env)
-  case Or(b1, b2) => eval_bexp(b1, env) || eval_bexp(b2, env)
-}
-
-// Import needed to take int as input from the user
-import scala.io.StdIn.readInt
-
-def eval_stmt(s: Stmt, env: Env) : Env = s match {
-  case Skip => env
-  case Assign(x, a) => env + (x -> eval_aexp(a, env))
-  case If(b, bl1, bl2) => if (eval_bexp(b, env)) eval_bl(bl1, env) else eval_bl(bl2, env) 
-  case While(b, bl) => 
-    if (eval_bexp(b, env)) eval_stmt(While(b, bl), eval_bl(bl, env))
-    else env
-
-  case WriteId(x) => { print(env(x)) ; env }
-  case WriteString(x) => {
-        print(x.replaceAll("\"", "").replaceAll("""\\n""", "\n")) ;
-        env
-       }
-
-  case Read(x) => { 
-      println("Enter an integer and press ENTER:") ; 
-      val n = readInt() ; // Note: Does not work when using the REPL
-      eval_stmt(Assign(x, Num(n)), env) 
-  }
-}
-
-def eval_bl(bl: Block, env: Env) : Env = bl match {
-  case Nil => env
-  case s::bl => eval_bl(bl, eval_stmt(s, env))
-}
-
-def eval(bl: Block) : Env = eval_bl(bl, Map())
-
-println("Factors eval")
-println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "factors.while"))).head))
-
-println("Primes eval")
-println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "primes.while"))).head))
-
-
-println("Collatz2 eval")
-println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head))
-
-println("Loops eval")
-val start = System.nanoTime()
-println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "loops.while"))).head))
-val end = System.nanoTime()
-println("Time taken in seconds: ")
-println((end - start)/(1.0e9))
--- a/solution/cw3/primes.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-// prints out prime numbers from
-// 2 to 100 (end)
-
-end := 100;
-n := 2;
-while (n < end) do {
-  f := 2;
-  tmp := 0;
-  while ((f < n / 2 + 1) && (tmp == 0)) do {
-    if ((n / f) * f == n) then  { tmp := 1 } else { skip };
-    f := f + 1
-  };
-  if (tmp == 0) then { write(n); write("\n") } else { skip };
-  n  := n + 1
-}
\ No newline at end of file
--- a/solution/cw4/collatz2.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-// Collatz series
-//
-// needs writing of strings and numbers; comments
-
-bnd := 1;
-while bnd < 100001 do {
-  write bnd;
-  write ": ";
-  n := bnd;
-  cnt := 0;
-
-  while n > 1 do {
-    write n;
-    write ",";
-    
-    if n % 2 == 0 
-    then n := n / 2 
-    else n := 3 * n+1;
-
-    cnt := cnt + 1
-  };
-
-  write " => ";
-  write cnt;
-  write "\n";
-  bnd := bnd + 1
-}
\ No newline at end of file
--- a/solution/cw4/compiler.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,277 +0,0 @@
-// Compiler for JVM
-
-import $file.lexer
-import lexer._
-
-import $file.parser
-import parser._
-
-
-val beginning = """
-.class public XXX.XXX
-.super java/lang/Object
-
-.method public static write(I)V 
-    .limit locals 1 
-    .limit stack 2 
-    getstatic java/lang/System/out Ljava/io/PrintStream; 
-    iload 0
-    invokevirtual java/io/PrintStream/print(I)V 
-    return 
-.end method
-
-.method public static writes(Ljava/lang/String;)V
-    .limit stack 2
-    .limit locals 1
-    getstatic java/lang/System/out Ljava/io/PrintStream;
-    aload 0
-    invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
-    return
-.end method
-
-.method public static read()I 
-    .limit locals 10 
-    .limit stack 10
-
-    ldc 0 
-    istore 1  ; this will hold our final integer 
-Label1: 
-    getstatic java/lang/System/in Ljava/io/InputStream; 
-    invokevirtual java/io/InputStream/read()I 
-    istore 2 
-    iload 2 
-    ldc 10   ; the newline delimiter 
-    isub 
-    ifeq Label2 
-    iload 2 
-    ldc 32   ; the space delimiter 
-    isub 
-    ifeq Label2
-
-    iload 2 
-    ldc 48   ; we have our digit in ASCII, have to subtract it from 48 
-    isub 
-    ldc 10 
-    iload 1 
-    imul 
-    iadd 
-    istore 1 
-    goto Label1 
-Label2: 
-    ;when we come here we have our integer computed in local variable 1 
-    iload 1 
-    ireturn 
-.end method
-
-.method public static main([Ljava/lang/String;)V
-   .limit locals 200
-   .limit stack 200
-
-; COMPILED CODE STARTS
-
-"""
-
-val ending = """
-; COMPILED CODE ENDS
-   return
-
-.end method
-"""
-
-// Compiler
-
-var counter = -1
-
-def Fresh(x: String) = {
-  counter += 1
-  x ++ "_" ++ counter.toString()
-}
-
-implicit def string_interpolations(sc: StringContext) = new {
-    def i(args: Any*): String = "   " ++ sc.s(args:_*) ++ "\n"
-    def l(args: Any*): String = sc.s(args:_*) ++ ":\n"
-}
-
-type Env = Map[String, Int]
-
-def compile_op(op: String) = op match {
-  case "+" => i"iadd"
-  case "-" => i"isub"
-  case "*" => i"imul"
-  case "/" => i"idiv"
-  case "%" => i"irem"
-}
-
-def compile_aexp(a: AExp, env : Env) : String = a match {
-  case Num(i) => i"ldc $i"
-  case Var(s) => i"iload ${env(s)} \t\t; $s"
-  case Aop(op, a1, a2) => 
-    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ compile_op(op)
-}
-
-def compile_bexp(b: BExp, env : Env, jmp: String) : String = b match {
-  case True => ""
-  case False => i"goto $jmp"
-  case And(b1, b2) => compile_bexp(b1, env, jmp) ++ compile_bexp(b2, env, jmp)
-  case Or(b1, b2) => {
-    val b1_false = Fresh("Or_second");
-    val or_end = Fresh("Or_end");
-    compile_bexp(b1, env, b1_false) ++
-    i"goto $or_end" ++
-    l"$b1_false" ++
-    compile_bexp(b2, env, jmp) ++
-    l"$or_end"
-  }
-  case Bop("==", a1, a2) => 
-    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpne $jmp"
-  case Bop("!=", a1, a2) => 
-    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpeq $jmp"
-  case Bop("<", a1, a2) => 
-    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpge $jmp"
-  case Bop(">", a1, a2) => 
-    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmple $jmp"
-}
-
-def compile_stmt(s: Stmt, env: Env) : (String, Env) = s match {
-  case Skip => ("", env)
-  case Assign(x, a) => {
-    val index = env.getOrElse(x, env.keys.size)
-    (compile_aexp(a, env) ++ i"istore $index \t\t; $x", env + (x -> index))
-  } 
-  case If(b, bl1, bl2) => {
-    val if_else = Fresh("If_else")
-    val if_end = Fresh("If_end")
-    val (instrs1, env1) = compile_block(bl1, env)
-    val (instrs2, env2) = compile_block(bl2, env1)
-    (compile_bexp(b, env, if_else) ++
-     instrs1 ++
-     i"goto $if_end" ++
-     l"$if_else" ++
-     instrs2 ++
-     l"$if_end", env2)
-  }
-  case While(b, bl) => {
-    val loop_begin = Fresh("Loop_begin")
-    val loop_end = Fresh("Loop_end")
-    val (instrs1, env1) = compile_block(bl, env)
-    (l"$loop_begin" ++
-     compile_bexp(b, env, loop_end) ++
-     instrs1 ++
-     i"goto $loop_begin" ++
-     l"$loop_end", env1)
-  }
-  case For(id, lower, upper, code) => {
-      val (assignment_code, env1) = compile_stmt(Assign(id, lower), env)  // id := lower;
-      val while_equivalent = While(
-          Or(Bop("<", Var(id), upper), Bop("==", Var(id), upper)),    // while id <= upper do {
-          code ++                                                     //    code  
-          List(
-            Assign(id, Aop("+", Var(id), Num(1)))                     //    id := id + 1
-          ))                                                          // };
-
-      val (while_code, env2) = compile_stmt(while_equivalent, env1)
-      (assignment_code ++ while_code, env2)
-  }
-  case WriteId(x) => (i"iload ${env(x)} \t\t; $x" ++ 
-     i"invokestatic XXX/XXX/write(I)V", env)
-  case WriteString(x) => (s"   ldc ${x}\n" ++
-     i"invokestatic XXX/XXX/writes(Ljava/lang/String;)V", env)
-  case Read(x) => {
-    val index = env.getOrElse(x, env.keys.size) 
-    (i"invokestatic XXX/XXX/read()I" ++ 
-     i"istore $index \t\t; $x", env + (x -> index))
-  }
-}
-
-def compile_block(bl: Block, env: Env) : (String, Env) = bl match {
-  case Nil => ("", env)
-  case s::bl => {
-    val (instrs1, env1) = compile_stmt(s, env)
-    val (instrs2, env2) = compile_block(bl, env1)
-    (instrs1 ++ instrs2, env2)
-  }
-}
-
-def compile(bl: Block, class_name: String) : String = {
-  val instructions = compile_block(bl, Map.empty)._1
-  (beginning ++ instructions ++ ending).replaceAllLiterally("XXX", class_name)
-}
-
-// Compiling and running
-
-import scala.util._
-import scala.sys.process._
-import scala.io
-
-def compile_tofile(bl: Block, class_name: String) = {
-  val output = compile(bl, class_name)
-  val fw = new java.io.FileWriter(class_name + ".j") 
-  fw.write(output) 
-  fw.close()
-}
-
-def compile_all(bl: Block, class_name: String) : Unit = {
-  compile_tofile(bl, class_name)
-  println("compiled ")
-  val test = ("java -jar jasmin.jar " + class_name + ".j").!!
-  println("assembled ")
-}
-
-def time_needed[T](i: Int, code: => T) = {
-  val start = System.nanoTime()
-  for (j <- 1 to i) code
-  val end = System.nanoTime()
-  (end - start)/(i * 1.0e9)
-}
-
-def compile_run(bl: Block, class_name: String) : Unit = {
-  println("Start compilation")
-  compile_all(bl, class_name)
-  println("running")
-  println("Time: " + time_needed(1, ("java " + class_name + "/" + class_name).!))
-}
-
-// ---- Q1
-
-// Fibonacci
-
-val fibonacciProgram = """write "Fib";
-read n;
-minus1 := 0;
-minus2 := 1;
-while n > 0 do {
-  temp := minus2;
-  minus2 := minus1 + minus2;
-  minus1 := temp;
-  n := n - 1
-};
-write "Result";
-write minus2"""
-
-//compile_all(Stmts.parse_all(tokenise(fibonacciProgram)).head, "fib")
-
-val factorialProgram = """write "Factorial";
-read n;
-fact := 1;
-
-while n > 0 do {
-  fact := n * fact;
-  n := n - 1
-};
-
-write "Result";
-write fact
-"""
-
-compile_all(Stmts.parse_all(tokenise(factorialProgram)).head, "factorial")
-
-// ---- Q3
-
-/* compile_run(Stmts.parse_all(tokenise("""for i := 1 upto 10 do {
-  for i := 1 upto 10 do {
-    write i
-  }
-}""")).head, "nestedloop") */
-
-
-compile_run(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head, "collatz2")
--- a/solution/cw4/fib.while	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-write "Fib: ";
-read n;  
-minus1 := 0;
-minus2 := 1;
-while n > 0 do {
-       temp := minus2;
-       minus2 := minus1 + minus2;
-       minus1 := temp;
-       n := n - 1
-};
-write "Result: ";
-write minus2 
-
--- a/solution/cw4/lexer.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,251 +0,0 @@
-import scala.language.implicitConversions    
-import scala.language.reflectiveCalls
-
-// Rexp
-abstract class Rexp
-case object ZERO extends Rexp
-case object ONE extends Rexp
-case class CHAR(c: Char) extends Rexp
-case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
-case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
-case class STAR(r: Rexp) extends Rexp 
-case class RECD(x: String, r: Rexp) extends Rexp
-
-case class RANGE(s: Set[Char]) extends Rexp
-case class PLUS(r: Rexp) extends Rexp
-case class OPTIONAL(r: Rexp) extends Rexp
-case class NTIMES(r: Rexp, n: Int) extends Rexp 
-
-// Values
-abstract class Val
-case object Empty extends Val
-case class Chr(c: Char) extends Val
-case class Sequ(v1: Val, v2: Val) extends Val
-case class Left(v: Val) extends Val
-case class Right(v: Val) extends Val
-case class Stars(vs: List[Val]) extends Val
-case class Rec(x: String, v: Val) extends Val
-
-
-// Convenience typing
-def charlist2rexp(s : List[Char]): Rexp = s match {
-  case Nil => ONE
-  case c::Nil => CHAR(c)
-  case c::s => SEQ(CHAR(c), charlist2rexp(s))
-}
-
-implicit def string2rexp(s : String) : Rexp = 
-  charlist2rexp(s.toList)
-
-implicit def RexpOps(r: Rexp) = new {
-  def | (s: Rexp) = ALT(r, s)
-  def % = STAR(r)
-  def ~ (s: Rexp) = SEQ(r, s)
-}
-
-implicit def stringOps(s: String) = new {
-  def | (r: Rexp) = ALT(s, r)
-  def | (r: String) = ALT(s, r)
-  def % = STAR(s)
-  def ~ (r: Rexp) = SEQ(s, r)
-  def ~ (r: String) = SEQ(s, r)
-  def $ (r: Rexp) = RECD(s, r)
-}
-
-// nullable
-def nullable(r: Rexp) : Boolean = r match {
-  case ZERO => false
-  case ONE => true
-  case CHAR(_) => false
-  case ALT(r1, r2) => nullable(r1) || nullable(r2)
-  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
-  case STAR(_) => true
-
-  case RECD(_, r1) => nullable(r1)
-  case RANGE(_) => false
-  case PLUS(r1) => nullable(r1)
-  case OPTIONAL(_) => true
-  case NTIMES(r1, i) => if (i == 0) true else nullable(r1)
-}
-
-// der
-def der(c: Char, r: Rexp) : Rexp = r match {
-  case ZERO => ZERO
-  case ONE => ZERO
-  case CHAR(d) => if (c == d) ONE else ZERO
-  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
-  case SEQ(r1, r2) => 
-    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
-    else SEQ(der(c, r1), r2)
-  case STAR(r) => SEQ(der(c, r), STAR(r))
-
-  case RECD(_, r1) => der(c, r1)
-  case RANGE(s) => if (s.contains(c)) ONE else ZERO 
-  case PLUS(r1) => SEQ(der(c, r1), STAR(r1))
-  case OPTIONAL(r1) => ALT(der(c, r1), ZERO)
-  case NTIMES(r, i) => 
-    if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
-}
-
-// Flatten
-def flatten(v: Val) : String = v match {
-  case Empty => ""
-  case Chr(c) => c.toString
-  case Left(v) => flatten(v)
-  case Right(v) => flatten(v)
-  case Sequ(v1, v2) => flatten(v1) + flatten(v2)
-  case Stars(vs) => vs.map(flatten).mkString
-  case Rec(_, v) => flatten(v)
-}
-
-// Env
-def env(v: Val) : List[(String, String)] = v match {
-  case Empty => Nil
-  case Chr(c) => Nil
-  case Left(v) => env(v)
-  case Right(v) => env(v)
-  case Sequ(v1, v2) => env(v1) ::: env(v2)
-  case Stars(vs) => vs.flatMap(env)
-  case Rec(x, v) => (x, flatten(v))::env(v)
-}
-
-// Mkeps
-def mkeps(r: Rexp) : Val = r match {
-  case ONE => Empty
-  case ALT(r1, r2) => 
-    if (nullable(r1)) Left(mkeps(r1)) else Right(mkeps(r2))
-  case SEQ(r1, r2) => Sequ(mkeps(r1), mkeps(r2))
-  case STAR(r) => Stars(Nil)
-  case RECD(x, r) => Rec(x, mkeps(r))
-
-  case PLUS(r) => Stars(List(mkeps(r)))   // the first copy must match the empty string
-  case OPTIONAL(r) => Right(Empty)
-  case NTIMES(r, i) => Stars(List.fill(i)(mkeps(r)))
-}
-
-// Inj
-def inj(r: Rexp, c: Char, v: Val) : Val = (r, v) match {
-  case (STAR(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-  case (SEQ(r1, r2), Sequ(v1, v2)) => Sequ(inj(r1, c, v1), v2)
-  case (SEQ(r1, r2), Left(Sequ(v1, v2))) => Sequ(inj(r1, c, v1), v2)
-  case (SEQ(r1, r2), Right(v2)) => Sequ(mkeps(r1), inj(r2, c, v2))
-  case (ALT(r1, r2), Left(v1)) => Left(inj(r1, c, v1))
-  case (ALT(r1, r2), Right(v2)) => Right(inj(r2, c, v2))
-  case (CHAR(d), Empty) => Chr(c) 
-  case (RECD(x, r1), _) => Rec(x, inj(r1, c, v))
-
-  case (RANGE(_), Empty) => Chr(c)
-  case (PLUS(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-  case (OPTIONAL(r), Left(v1)) => Left(inj(r, c, v1))
-  case (NTIMES(r, n), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-}
-
-// Rectification functions
-def F_ID(v: Val): Val = v
-def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))
-def F_LEFT(f: Val => Val) = (v:Val) => Left(f(v))
-def F_ALT(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
-  case Right(v) => Right(f2(v))
-  case Left(v) => Left(f1(v))
-}
-def F_SEQ(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
-  case Sequ(v1, v2) => Sequ(f1(v1), f2(v2))
-}
-def F_SEQ_Empty1(f1: Val => Val, f2: Val => Val) = 
-  (v:Val) => Sequ(f1(Empty), f2(v))
-def F_SEQ_Empty2(f1: Val => Val, f2: Val => Val) = 
-  (v:Val) => Sequ(f1(v), f2(Empty))
-def F_RECD(f: Val => Val) = (v:Val) => v match {
-  case Rec(x, v) => Rec(x, f(v))
-}
-def F_ERROR(v: Val): Val = throw new Exception("error")
-
-// Simp
-def simp(r: Rexp): (Rexp, Val => Val) = r match {
-  case ALT(r1, r2) => {
-    val (r1s, f1s) = simp(r1)
-    val (r2s, f2s) = simp(r2)
-    (r1s, r2s) match {
-      case (ZERO, _) => (r2s, F_RIGHT(f2s))
-      case (_, ZERO) => (r1s, F_LEFT(f1s))
-      case _ => if (r1s == r2s) (r1s, F_LEFT(f1s))
-                else (ALT (r1s, r2s), F_ALT(f1s, f2s)) 
-    }
-  }
-  case SEQ(r1, r2) => {
-    val (r1s, f1s) = simp(r1)
-    val (r2s, f2s) = simp(r2)
-    (r1s, r2s) match {
-      case (ZERO, _) => (ZERO, F_ERROR)
-      case (_, ZERO) => (ZERO, F_ERROR)
-      case (ONE, _) => (r2s, F_SEQ_Empty1(f1s, f2s))
-      case (_, ONE) => (r1s, F_SEQ_Empty2(f1s, f2s))
-      case _ => (SEQ(r1s,r2s), F_SEQ(f1s, f2s))
-    }
-  }
-  case r => (r, F_ID)
-}
-
-// Lex
-def lex_simp(r: Rexp, s: List[Char]) : Val = s match {
-  case Nil => if (nullable(r)) mkeps(r) else 
-    { throw new Exception("lexing error") } 
-  case c::cs => {
-    val (r_simp, f_simp) = simp(der(c, r))
-    inj(r, c, f_simp(lex_simp(r_simp, cs)))
-  }
-}
-
-def lexing_simp(r: Rexp, s: String) = env(lex_simp(r, s.toList))
-
-// Language specific code
-val KEYWORD : Rexp = "while" | "if" | "then" | "else" | "do" | "for" | "to" | "true" | "false" | "read" | "write" | "skip" 
-val OP : Rexp = "+" | "-" | "*" | "%" | "/" | "==" | "!=" | ">" | "<" | ">=" | "<=" | ":=" | "&&" | "||"
-val LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z'))
-val SYM : Rexp = LET | RANGE(Set('.', '_', '>', '<', '=', ';', ',', ':'))
-val PARENS : Rexp = "(" | "{" | ")" | "}"
-val SEMI : Rexp = ";"
-val WHITESPACE : Rexp = PLUS(" ") | "\n" | "\t" | "\r"
-val DIGIT : Rexp = RANGE(('0' to '9').toSet)
-val DIGIT1 : Rexp = RANGE(('1' to '9').toSet)
-val STRING : Rexp = "\"" ~ (SYM | " " | "\\n" | DIGIT).% ~ "\""
-val ID : Rexp = LET ~ (LET | "_" | DIGIT).%
-val NUM : Rexp = "0" | (DIGIT1 ~ DIGIT.%)
-val EOL : Rexp = "\n" | "\r\n"
-val COMMENT : Rexp = "//" ~ (SYM | PARENS | " " | DIGIT).% ~ EOL
-
-val WHILE_REGS = (("k" $ KEYWORD) | 
-                  ("o" $ OP) | 
-                  ("str" $ STRING) |
-                  ("p" $ PARENS) |
-                  ("s" $ SEMI) | 
-                  ("w" $ WHITESPACE) | 
-                  ("i" $ ID) | 
-                  ("n" $ NUM) |
-		  ("c" $ COMMENT)).%
-
-// Token
-abstract class Token extends Serializable 
-case class T_KEYWORD(s: String) extends Token
-case class T_OP(s: String) extends Token
-case class T_STRING(s: String) extends Token
-case class T_PAREN(s: String) extends Token
-case object T_SEMI extends Token
-case class T_ID(s: String) extends Token
-case class T_NUM(n: Int) extends Token
-
-val token : PartialFunction[(String, String), Token] = {
-  case ("k", s) => T_KEYWORD(s)
-  case ("o", s) => T_OP(s)
-  case ("str", s) => T_STRING(s)
-  case ("p", s) => T_PAREN(s)
-  case ("s", _) => T_SEMI
-  case ("i", s) => T_ID(s)
-  case ("n", s) => T_NUM(s.toInt)
-}
-
-// Tokenise
-def tokenise(s: String) : List[Token] = 
-  lexing_simp(WHILE_REGS, s).collect(token)
-
-
--- a/solution/cw4/parser.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-// CW3
-
-import $file.lexer
-import lexer._ 
-
-
-case class ~[+A, +B](_1: A, _2: B)
-type IsSeq[A] = A => Seq[_]
-
-abstract class Parser[I : IsSeq, T] {
-  def parse(ts: I): Set[(T, I)]
-
-  def parse_all(ts: I) : Set[T] =
-    for ((head, tail) <- parse(ts); if tail.isEmpty) yield head
-}
-
-class SeqParser[I : IsSeq, T, S](p: => Parser[I, T], q: => Parser[I, S]) extends Parser[I, ~[T, S]] {
-  def parse(sb: I) = 
-    for ((head1, tail1) <- p.parse(sb); 
-         (head2, tail2) <- q.parse(tail1)) yield (new ~(head1, head2), tail2)
-}
-
-class AltParser[I : IsSeq, T](p: => Parser[I, T], q: => Parser[I, T]) extends Parser[I, T] {
-  def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
-}
-
-class FunParser[I : IsSeq, T, S](p: => Parser[I, T], f: T => S) extends Parser[I, S] {
-  def parse(sb: I) = 
-    for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
-}
-
-// New parser that takes as input a list of tokens
-case class TokenListParser(ts: List[Token]) extends Parser[List[Token], List[Token]] {
-    def parse(tsb: List[Token]) = {
-        val (prefix, suffix) = tsb.splitAt(ts.length)
-        if (prefix == ts) Set((prefix, suffix)) else Set()
-    }
-}
-
-// Implicit definitions to go from a token 
-// or a list of tokens to a TokenListParser
-implicit def token2parser(t: Token) = TokenListParser(List(t))
-implicit def tokenList2parser(ts: List[Token]) = TokenListParser(ts)
-
-implicit def ParserOps[I : IsSeq, T](p: Parser[I, T]) = new {
-  def || (q : => Parser[I, T]) = new AltParser[I, T](p, q)
-  def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
-  def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
-}
-
-implicit def TokenOps(t: Token) = new {
-    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](List(t), q)
-    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](List(t), qs)
-    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](List(t), f)
-    def ~[S](q : => Parser[List[Token], S]) =
-        new SeqParser[List[Token], List[Token], S](List(t), q)
-    def ~ (qs : List[Token]) =
-        new SeqParser[List[Token], List[Token], List[Token]](List(t), qs)
-}
-
-implicit def TokenListOps(ts: List[Token]) = new {
-    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](ts, q)
-    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](ts, qs)
-    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](ts, f)
-    def ~[S](q : => Parser[List[Token], S]) =
-        new SeqParser[List[Token], List[Token], S](ts, q)
-    def ~ (qs : List[Token]) =
-        new SeqParser[List[Token], List[Token], List[Token]](ts, qs)
-}
-
-// Abstract Syntax Trees
-abstract class Stmt
-abstract class AExp
-abstract class BExp
-
-type Block = List[Stmt]
-
-case object Skip extends Stmt
-case class If(a: BExp, bl1: Block, bl2: Block) extends Stmt
-case class While(b: BExp, bl: Block) extends Stmt
-case class Assign(s: String, a: AExp) extends Stmt
-case class Read(s: String) extends Stmt
-case class WriteId(s: String) extends Stmt  // for printing values of variables
-case class WriteString(s: String) extends Stmt  // for printing words
-case class For(counter: String, lower: AExp, upper: AExp, code: Block) extends Stmt
-
-
-case class Var(s: String) extends AExp
-case class Num(i: Int) extends AExp
-case class Aop(o: String, a1: AExp, a2: AExp) extends AExp
-
-case object True extends BExp
-case object False extends BExp
-case class Bop(o: String, a1: AExp, a2: AExp) extends BExp
-case class And(b1: BExp, b2: BExp) extends BExp
-case class Or(b1: BExp, b2: BExp) extends BExp
-
-case class IdParser() extends Parser[List[Token], String] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_ID(id) :: rest => Set((id, rest))
-        case _ => Set()
-    }
-}
-
-case class NumParser() extends Parser[List[Token], Int] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_NUM(n) :: rest => Set((n, rest))
-        case _ => Set()
-    }
-}
-
-case class StringParser() extends Parser[List[Token], String] {
-    def parse(tsb: List[Token]) = tsb match {
-        case T_STRING(s) :: rest => Set((s, rest))
-        case _ => Set()
-    }
-}
-
-// WHILE Language Parsing
-lazy val AExp: Parser[List[Token], AExp] = 
-  (Te ~ T_OP("+") ~ AExp) ==> { case x ~ _ ~ z => Aop("+", x, z): AExp } ||
-  (Te ~ T_OP("-") ~ AExp) ==> { case x ~ _ ~ z => Aop("-", x, z): AExp } || Te
-lazy val Te: Parser[List[Token], AExp] = 
-  (Fa ~ T_OP("*") ~ Te) ==> { case x ~ _ ~ z => Aop("*", x, z): AExp } || 
-  (Fa ~ T_OP("/") ~ Te) ==> { case x ~ _ ~ z => Aop("/", x, z): AExp } || 
-  (Fa ~ T_OP("%") ~ Te) ==> { case x ~ _ ~ z => Aop("%", x, z): AExp } || Fa  
-lazy val Fa: Parser[List[Token], AExp] = 
-   (T_PAREN("(") ~ AExp ~ T_PAREN(")")) ==> { case _ ~ y ~ _ => y } || 
-   IdParser() ==> Var  || 
-   NumParser() ==> Num
-
-lazy val BExp: Parser[List[Token], BExp] = 
-   (AExp ~ T_OP("==") ~ AExp) ==> { case x ~ _ ~ z => Bop("==", x, z): BExp } || 
-   (AExp ~ T_OP("!=") ~ AExp) ==> { case x ~ _ ~ z => Bop("!=", x, z): BExp } || 
-   (AExp ~ T_OP("<") ~ AExp) ==> { case x ~ _ ~ z => Bop("<", x, z): BExp } || 
-   (AExp ~ T_OP(">") ~ AExp) ==> { case x ~ _ ~ z => Bop(">", x, z): BExp } ||
-   (T_PAREN("(") ~ BExp ~ List(T_PAREN(")"), T_OP("&&")) ~ BExp) ==> { case _ ~ y ~ _ ~ v => And(y, v): BExp } ||
-   (T_PAREN("(") ~ BExp ~ List(T_PAREN(")"), T_OP("||")) ~ BExp) ==> { case _ ~ y ~ _ ~ v => Or(y, v): BExp } ||
-   (T_KEYWORD("true") ==> (_ => True: BExp )) || 
-   (T_KEYWORD("false") ==> (_ => False: BExp )) ||
-   (T_PAREN("(") ~ BExp ~ T_PAREN(")")) ==> { case _ ~ x ~ _ => x }
-
-lazy val Stmt: Parser[List[Token], Stmt] =
-    T_KEYWORD("skip") ==> (_ => Skip: Stmt) ||
-    (IdParser() ~ T_OP(":=") ~ AExp) ==> { case id ~ _ ~ z => Assign(id, z): Stmt } ||
-    (T_KEYWORD("if") ~ BExp ~ T_KEYWORD("then") ~ Block ~ T_KEYWORD("else") ~ Block) ==> { case _ ~ y ~ _ ~ u ~ _ ~ w => If(y, u, w): Stmt } ||
-    (T_KEYWORD("while") ~ BExp ~ T_KEYWORD("do") ~ Block) ==> { case _ ~ y ~ _ ~ w => While(y, w) : Stmt } ||
-    (T_KEYWORD("read") ~ IdParser()) ==> { case _ ~ id => Read(id): Stmt} ||
-    (T_KEYWORD("write") ~ IdParser()) ==> { case _ ~ id => WriteId(id): Stmt} ||
-    (T_KEYWORD("write") ~ StringParser()) ==> { case _ ~ s => WriteString(s): Stmt} ||
-    (T_KEYWORD("for") ~ IdParser() ~ T_OP(":=") ~ AExp ~ T_KEYWORD("upto") ~ AExp ~ T_KEYWORD("do") ~ Block) ==> {
-      case _ ~ id ~ _ ~ lower ~ _ ~ upper ~ _ ~ blck => For(id, lower, upper, blck): Stmt
-    }
-
-lazy val Stmts: Parser[List[Token], Block] =
-    (Stmt ~ T_SEMI ~ Stmts) ==> { case x ~ _ ~ z => x :: z : Block } ||
-    (Stmt ==> (s => List(s) : Block))
-
-lazy val Block: Parser[List[Token], Block] =
-    (T_PAREN("{") ~ Stmts ~ T_PAREN("}")) ==> { case x ~ y ~ z => y} ||
-    (Stmt ==> (s => List(s)))
-
--- a/solution/cw5/fact.fun	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-// a simple factorial program
-// (including a tail recursive version)
-
-
-def fact(n: Int) : Int =
-  if n == 0 then 1 else n * fact(n - 1);
-
-def facT(n: Int, acc: Int) : Int =
-  if n == 0 then acc else facT(n - 1, n * acc);
-
-def facTi(n: Int) : Int = facT(n, 1);
-
-def top() : Void = {
-  print_int(fact(6));
-  print_char(',');
-  print_int(facTi(6));
-  print_char('\n')
-};
-
-top()
-
--- a/solution/cw5/fun_llvm.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,412 +0,0 @@
-// A Small LLVM Compiler for a Simple Functional Language
-// (includes an external lexer and parser)
-//
-//
-// call with                 -- prints out llvm code
-//
-//     amm fun_llvm.sc main fact.fun
-//     amm fun_llvm.sc main defs.fun
-//
-// or                        -- writes llvm code to disk
-//
-//     amm fun_llvm.sc write fact.fun
-//     amm fun_llvm.sc write defs.fun
-//
-//       this will generate an .ll file. 
-//
-// or                       -- runs the generated llvm code via lli
-//
-//     amm fun_llvm.sc run fact.fun
-//     amm fun_llvm.sc run defs.fun
-//
-//
-// You can interpret an .ll file using lli, for example
-//
-//      lli fact.ll
-//
-// The optimiser can be invoked as
-//
-//      opt -O1 -S in_file.ll > out_file.ll
-//      opt -O3 -S in_file.ll > out_file.ll
-//
-// The code produced for the various architectures can be obtain with
-//   
-//   llc -march=x86 -filetype=asm in_file.ll -o -
-//   llc -march=arm -filetype=asm in_file.ll -o -  
-//
-// Producing an executable can be achieved by
-//
-//    llc -filetype=obj in_file.ll
-//    gcc in_file.o -o a.out
-//    ./a.out
-
-
-import $file.fun_tokens, fun_tokens._
-import $file.fun_parser, fun_parser._ 
-
-
-// for generating new labels
-var counter = -1
-
-def Fresh(x: String) = {
-  counter += 1
-  x ++ "_" ++ counter.toString()
-}
-
-// Internal CPS language for FUN
-abstract class KExp
-abstract class KVal
-
-type Ty = String
-type TyEnv = Map[String, Ty]
-
-case class KVar(s: String, ty: Ty = "UNDEF") extends KVal
-case class KLoad(v: KVal) extends KVal
-case class KNum(i: Int) extends KVal
-case class KFNum(i: Double) extends KVal
-case class KChr(c: Int) extends KVal
-case class Kop(o: String, v1: KVal, v2: KVal, ty: Ty = "UNDEF") extends KVal
-case class KCall(o: String, vrs: List[KVal], ty: Ty = "UNDEF") extends KVal
-
-case class KIf(x1: String, e1: KExp, e2: KExp) extends KExp {
-  override def toString = s"KIf $x1\nIF\n$e1\nELSE\n$e2"
-}
-case class KLet(x: String, e1: KVal, e2: KExp) extends KExp {
-  override def toString = s"let $x = $e1 in \n$e2" 
-}
-case class KReturn(v: KVal) extends KExp
-
-// typing K values
-def typ_val(v: KVal, ts: TyEnv) : (KVal, Ty) = v match {
-  case KVar(s, _) => {
-    val ty = ts.getOrElse(s, "TUNDEF")
-    (KVar(s, ty), ty)  
-  }
-  case Kop(op, v1, v2, _) => {
-    val (tv1, ty1) = typ_val(v1, ts)
-    val (tv2, ty2) = typ_val(v2, ts)
-    if (ty1 == ty2) (Kop(op, tv1, tv2, ty1), ty1) else (Kop(op, tv1, tv2, "TMISMATCH"), "TMISMATCH") 
-  }
-  case KCall(fname, args, _) => {
-    val ty = ts.getOrElse(fname, "TCALLUNDEF" ++ fname)
-    (KCall(fname, args.map(typ_val(_, ts)._1), ty), ty)
-  }  
-  case KLoad(v) => {
-    val (tv, ty) = typ_val(v, ts)
-    (KLoad(tv), ty)
-  }
-  case KNum(i) => (KNum(i), "Int")
-  case KFNum(i) => (KFNum(i), "Double")
-  case KChr(c) => (KChr(c), "Int")
-}
-
-def typ_exp(a: KExp, ts: TyEnv) : KExp = a match {
-  case KReturn(v) => KReturn(typ_val(v, ts)._1)
-  case KLet(x: String, v: KVal, e: KExp) => {
-    val (tv, ty) = typ_val(v, ts)
-    KLet(x, tv, typ_exp(e, ts + (x -> ty)))
-  }
-  case KIf(b, e1, e2) => KIf(b, typ_exp(e1, ts), typ_exp(e2, ts))
-}
-
-
-
-
-// CPS translation from Exps to KExps using a
-// continuation k.
-def CPS(e: Exp)(k: KVal => KExp) : KExp = e match {
-  case Var(s) if (s.head.isUpper) => {
-      val z = Fresh("tmp")
-      KLet(z, KLoad(KVar(s)), k(KVar(z)))
-  }
-  case Var(s) => k(KVar(s))
-  case Num(i) => k(KNum(i))
-  case ChConst(c) => k(KChr(c))
-  case FNum(i) => k(KFNum(i))
-  case Aop(o, e1, e2) => {
-    val z = Fresh("tmp")
-    CPS(e1)(y1 => 
-      CPS(e2)(y2 => KLet(z, Kop(o, y1, y2), k(KVar(z)))))
-  }
-  case If(Bop(o, b1, b2), e1, e2) => {
-    val z = Fresh("tmp")
-    CPS(b1)(y1 => 
-      CPS(b2)(y2 => 
-        KLet(z, Kop(o, y1, y2), KIf(z, CPS(e1)(k), CPS(e2)(k)))))
-  }
-  case Call(name, args) => {
-    def aux(args: List[Exp], vs: List[KVal]) : KExp = args match {
-      case Nil => {
-          val z = Fresh("tmp")
-          KLet(z, KCall(name, vs), k(KVar(z)))
-      }
-      case e::es => CPS(e)(y => aux(es, vs ::: List(y)))
-    }
-    aux(args, Nil)
-  }
-  case Sequence(e1, e2) => 
-    CPS(e1)(_ => CPS(e2)(y2 => k(y2)))
-}   
-
-//initial continuation
-def CPSi(e: Exp) = CPS(e)(KReturn)
-
-// some testcases
-val e1 = Aop("*", Var("a"), Num(3))
-CPSi(e1)
-
-val e2 = Aop("+", Aop("*", Var("a"), Num(3)), Num(4))
-CPSi(e2)
-
-val e3 = Aop("+", Num(2), Aop("*", Var("a"), Num(3)))
-CPSi(e3)
-
-val e4 = Aop("+", Aop("-", Num(1), Num(2)), Aop("*", Var("a"), Num(3)))
-CPSi(e4)
-
-val e5 = If(Bop("==", Num(1), Num(1)), Num(3), Num(4))
-CPSi(e5)
-
-val e6 = If(Bop("!=", Num(10), Num(10)), e5, Num(40))
-CPSi(e6)
-
-val e7 = Call("foo", List(Num(3)))
-CPSi(e7)
-
-val e8 = Call("foo", List(Aop("*", Num(3), Num(1)), Num(4), Aop("+", Num(5), Num(6))))
-CPSi(e8)
-
-val e9 = Sequence(Aop("*", Var("a"), Num(3)), Aop("+", Var("b"), Num(6)))
-CPSi(e9)
-
-val e = Aop("*", Aop("+", Num(1), Call("foo", List(Var("a"), Num(3)))), Num(4))
-CPSi(e)
-
-
-
-
-// convenient string interpolations 
-// for instructions, labels and methods
-import scala.language.implicitConversions
-import scala.language.reflectiveCalls
-
-
-
-
-implicit def sring_inters(sc: StringContext) = new {
-    def i(args: Any*): String = "   " ++ sc.s(args:_*) ++ "\n"
-    def l(args: Any*): String = sc.s(args:_*) ++ ":\n"
-    def m(args: Any*): String = sc.s(args:_*) ++ "\n"
-}
-
-def get_ty(s: String) = s match {
-  case "Double" => "double"
-  case "Void" => "void"
-  case "Int" => "i32"
-  case "Bool" => "i2"
-  case _ => s
-}
-
-def compile_call_arg(a: KVal) = a match {
-  case KNum(i) => s"i32 $i"
-  case KFNum(i) => s"double $i"
-  case KChr(c) => s"i32 $c"
-  case KVar(s, ty) => s"${get_ty(ty)} %$s" 
-}
-
-def compile_arg(s: (String, String)) = s"${get_ty(s._2)} %${s._1}" 
-
-
-// mathematical and boolean operations
-def compile_op(op: String) = op match {
-  case "+" => "add i32 "
-  case "*" => "mul i32 "
-  case "-" => "sub i32 "
-  case "/" => "sdiv i32 "
-  case "%" => "srem i32 "
-  case "==" => "icmp eq i32 "
-  case "!=" => "icmp ne i32 "      // not equal 
-  case "<=" => "icmp sle i32 "     // signed less or equal
-  case "<"  => "icmp slt i32 "     // signed less than
-}
-
-def compile_dop(op: String) = op match {
-  case "+" => "fadd double "
-  case "*" => "fmul double "
-  case "-" => "fsub double "
-  case "==" => "fcmp oeq double "
-  case "<=" => "fcmp ole double "   
-  case "<"  => "fcmp olt double "   
-}
-
-// compile K values
-def compile_val(v: KVal) : String = v match {
-  case KNum(i) => s"$i"
-  case KFNum(i) => s"$i"
-  case KChr(c) => s"$c"
-  case KVar(s, ty) => s"%$s" 
-  case KLoad(KVar(s, ty)) => s"load ${get_ty(ty)}, ${get_ty(ty)}* @$s"
-  case Kop(op, x1, x2, ty) => ty match { 
-    case "Int" => s"${compile_op(op)} ${compile_val(x1)}, ${compile_val(x2)}"
-    case "Double" => s"${compile_dop(op)} ${compile_val(x1)}, ${compile_val(x2)}"
-    case _ => Kop(op, x1, x2, ty).toString
-  }
-  case KCall(fname, args, ty) => 
-    s"call ${get_ty(ty)} @$fname (${args.map(compile_call_arg).mkString(", ")})"
-}
-
-// compile K expressions
-def compile_exp(a: KExp) : String = a match {
-  case KReturn(KVar("void", _)) =>
-    i"ret void"
-  case KReturn(KVar(x, ty)) =>
-    i"ret ${get_ty(ty)} %$x"
-  case KReturn(KNum(i)) =>
-    i"ret i32 $i"
-  case KLet(x: String, KCall(o: String, vrs: List[KVal], "Void"), e: KExp) => 
-    i"${compile_val(KCall(o: String, vrs: List[KVal], "Void"))}" ++ compile_exp(e)
-  case KLet(x: String, v: KVal, e: KExp) => 
-    i"%$x = ${compile_val(v)}" ++ compile_exp(e)
-  case KIf(x, e1, e2) => {
-    val if_br = Fresh("if_branch")
-    val else_br = Fresh("else_branch")
-    i"br i1 %$x, label %$if_br, label %$else_br" ++
-    l"\n$if_br" ++
-    compile_exp(e1) ++
-    l"\n$else_br" ++ 
-    compile_exp(e2)
-  }
-}
-
-
-val prelude = """
-declare i32 @printf(i8*, ...)
-
-@.str_nl = private constant [2 x i8] c"\0A\00"
-@.str_star = private constant [2 x i8] c"*\00"
-@.str_space = private constant [2 x i8] c" \00"
-
-define void @new_line() #0 {
-  %t0 = getelementptr [2 x i8], [2 x i8]* @.str_nl, i32 0, i32 0
-  %1 = call i32 (i8*, ...) @printf(i8* %t0)
-  ret void
-}
-
-define void @print_star() #0 {
-  %t0 = getelementptr [2 x i8], [2 x i8]* @.str_star, i32 0, i32 0
-  %1 = call i32 (i8*, ...) @printf(i8* %t0)
-  ret void
-}
-
-define void @print_space() #0 {
-  %t0 = getelementptr [2 x i8], [2 x i8]* @.str_space, i32 0, i32 0
-  %1 = call i32 (i8*, ...) @printf(i8* %t0)
-  ret void
-}
-
-define void @skip() #0 {
-  ret void
-}
-
-@.str_int = private constant [3 x i8] c"%d\00"
-
-define void @print_int(i32 %x) {
-   %t0 = getelementptr [3 x i8], [3 x i8]* @.str_int, i32 0, i32 0
-   call i32 (i8*, ...) @printf(i8* %t0, i32 %x) 
-   ret void
-}
-
-@.str_char = private constant [3 x i8] c"%c\00"
-
-define void @print_char(i32 %x) {
-   %t0 = getelementptr [3 x i8], [3 x i8]* @.str_char, i32 0, i32 0
-   call i32 (i8*, ...) @printf(i8* %t0, i32 %x) 
-   ret void
-}
-
-; END OF BUILD-IN FUNCTIONS (prelude)
-
-"""
-
-def get_cont(ty: Ty) = ty match {
-  case "Int" =>    KReturn
-  case "Double" => KReturn
-  case "Void" =>   { (_: KVal) => KReturn(KVar("void", "Void")) }
-} 
-
-// compile function for declarations and main
-def compile_decl(d: Decl, ts: TyEnv) : (String, TyEnv) = d match {
-  case Def(name, args, ty, body) => { 
-    val ts2 = ts + (name -> ty)
-    val tkbody = typ_exp(CPS(body)(get_cont(ty)), ts2 ++ args.toMap)
-    (m"define ${get_ty(ty)} @$name (${args.map(compile_arg).mkString(",")}) {" ++
-     compile_exp(tkbody) ++
-     m"}\n", ts2)
-  }
-  case Main(body) => {
-    val tbody = typ_exp(CPS(body)(_ => KReturn(KNum(0))), ts)
-    (m"define i32 @main() {" ++
-     compile_exp(tbody) ++
-     m"}\n", ts)
-  }
-  case Const(name, n) => {
-    (m"@$name = global i32 $n\n", ts + (name -> "Int"))
-  }
-  case FConst(name, x) => {
-    (m"@$name = global double $x\n", ts + (name -> "Double"))
-  }
-}
-
-def compile_prog(prog: List[Decl], ty: TyEnv) : String = prog match {
-  case Nil => ""
-  case d::ds => {
-    val (s2, ty2) = compile_decl(d, ty)
-    s2 ++ compile_prog(ds, ty2)
-  }
-}
-// main compiler functions
-def compile(prog: List[Decl]) : String = 
-  prelude ++ compile_prog(prog, Map("new_line" -> "Void", "skip" -> "Void", 
-				    "print_star" -> "Void", "print_space" -> "Void",
-                                    "print_int" -> "Void", "print_char" -> "Void"))
-
-
-//import ammonite.ops._
-
-
-@main
-def main(fname: String) = {
-    val path = os.pwd / fname
-    val file = fname.stripSuffix("." ++ path.ext)
-    val tks = tokenise(os.read(path))
-    val ast = parse_tks(tks)
-    val code = compile(ast)
-    println(code)
-}
-
-@main
-def write(fname: String) = {
-    val path = os.pwd / fname
-    val file = fname.stripSuffix("." ++ path.ext)
-    val tks = tokenise(os.read(path))
-    val ast = parse_tks(tks)
-    val code = compile(ast)
-    //println(code)
-    os.write.over(os.pwd / (file ++ ".ll"), code)
-}
-
-@main
-def run(fname: String) = {
-    val path = os.pwd / fname
-    val file = fname.stripSuffix("." ++ path.ext)
-    write(fname)  
-    os.proc("llc", "-filetype=obj", file ++ ".ll").call()
-    os.proc("gcc", file ++ ".o", "-o", file ++ ".bin").call()
-    os.proc(os.pwd / (file ++ ".bin")).call(stdout = os.Inherit)
-    println(s"done.")
-}
-
-
-
-
-
--- a/solution/cw5/fun_parser.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,215 +0,0 @@
-// A parser for the Fun language
-//================================
-//
-// call with 
-//
-//     amm fun_parser.sc fact.fun
-//
-//     amm fun_parser.sc defs.fun
-//
-// this will generate a parse-tree from a list
-// of tokens
-
-import scala.language.implicitConversions    
-import scala.language.reflectiveCalls
-
-import $file.fun_tokens, fun_tokens._ 
-
-
-// Parser combinators
-//    type parameter I needs to be of Seq-type
-//
-abstract class Parser[I, T](implicit ev: I => Seq[_]) {
-  def parse(ts: I): Set[(T, I)]
-
-  def parse_single(ts: I) : T = 
-    parse(ts).partition(_._2.isEmpty) match {
-      case (good, _) if !good.isEmpty => good.head._1
-      case (good, err) if err.isEmpty => {
-        println (s"Parse Error\n $good \n $err") ; sys.exit(-1) }
-      case (_, err) => { 
-	println (s"Parse Error\n${err.minBy(_._2.length)}") ; sys.exit(-1) }
-    }
-}
-
-// convenience for writing grammar rules
-case class ~[+A, +B](_1: A, _2: B)
-
-class SeqParser[I, T, S](p: => Parser[I, T], 
-                         q: => Parser[I, S])(implicit ev: I => Seq[_]) extends Parser[I, ~[T, S]] {
-  def parse(sb: I) = 
-    for ((head1, tail1) <- p.parse(sb); 
-         (head2, tail2) <- q.parse(tail1)) yield (new ~(head1, head2), tail2)
-}
-
-class AltParser[I, T](p: => Parser[I, T], 
-                      q: => Parser[I, T])(implicit ev: I => Seq[_]) extends Parser[I, T] {
-  def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
-}
-
-class FunParser[I, T, S](p: => Parser[I, T], 
-                         f: T => S)(implicit ev: I => Seq[_]) extends Parser[I, S] {
-  def parse(sb: I) = 
-    for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
-}
-
-// convenient combinators
-implicit def ParserOps[I, T](p: Parser[I, T])(implicit ev: I => Seq[_]) = new {
-  def || (q : => Parser[I, T]) = new AltParser[I, T](p, q)
-  def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
-  def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
-}
-
-def ListParser[I, T, S](p: => Parser[I, T], 
-                        q: => Parser[I, S])(implicit ev: I => Seq[_]): Parser[I, List[T]] = {
-  (p ==> ((s) => List(s))) ||
-  (p ~ q ~ ListParser(p, q)) ==> { case x ~ _ ~ z => x :: z : List[T] }
-}
-
-case class TokParser(tok: Token) extends Parser[List[Token], Token] {
-  def parse(ts: List[Token]) = ts match {
-    case t::ts if (t == tok) => Set((t, ts)) 
-    case _ => Set()
-  }
-}
-
-implicit def token2tparser(t: Token) = TokParser(t)
-
-implicit def TokOps(t: Token) = new {
-  def || (q : => Parser[List[Token], Token]) = new AltParser[List[Token], Token](t, q)
-  def ==>[S] (f: => Token => S) = new FunParser[List[Token], Token, S](t, f)
-  def ~[S](q : => Parser[List[Token], S]) = new SeqParser[List[Token], Token, S](t, q)
-}
-
-case object EmptyParser extends Parser[List[Token], String] {
-  def parse(ts: List[Token]) = Set(("", ts))
-}
-
-case object NumParser extends Parser[List[Token], Int] {
-  def parse(ts: List[Token]) = ts match {
-    case T_NUM(n)::ts => Set((n, ts)) 
-    case _ => Set ()
-  }
-}
-
-case object FNumParser extends Parser[List[Token], Double] {
-  def parse(ts: List[Token]) = ts match {
-    case T_FNUM(x)::ts => Set((x, ts)) 
-    case _ => Set()
-  }
-}
-
-case object IdParser extends Parser[List[Token], String] {
-  def parse(ts: List[Token]) = ts match {
-    case T_ID(s)::ts => Set((s, ts)) 
-    case _ => Set ()
-  }
-}
-
-case object CharConstParser extends Parser[List[Token], Int] {
-  def parse(ts: List[Token]) = ts match {
-    case T_CHR(c)::ts => Set((c, ts)) 
-    case _ => Set ()
-  }
-}
-
-case object TyParser extends Parser[List[Token], String] {
-  def parse(ts: List[Token]) = ts match {
-    case T_TY(s)::ts => Set((s, ts)) 
-    case _ => Set ()
-  }
-}
-
-
-// Abstract syntax trees for the Fun language
-abstract class Exp 
-abstract class BExp 
-abstract class Decl 
-
-case class Def(name: String, args: List[(String, String)], ty: String, body: Exp) extends Decl
-case class Main(e: Exp) extends Decl
-case class Const(name: String, v: Int) extends Decl
-case class FConst(name: String, x: Double) extends Decl
-
-case class Call(name: String, args: List[Exp]) extends Exp
-case class If(a: BExp, e1: Exp, e2: Exp) extends Exp
-case class Var(s: String) extends Exp
-case class Num(i: Int) extends Exp     // integer numbers
-case class FNum(i: Double) extends Exp  // floating numbers
-case class ChConst(c: Int) extends Exp // char constant
-case class Aop(o: String, a1: Exp, a2: Exp) extends Exp
-case class Sequence(e1: Exp, e2: Exp) extends Exp
-case class Bop(o: String, a1: Exp, a2: Exp) extends BExp
-
-
-// arithmetic expressions (there needs to be an F in the SEMICOLON case)
-lazy val Exp: Parser[List[Token], Exp] = 
-  (T_KWD("if") ~ BExp ~ T_KWD("then") ~ Exp ~ T_KWD("else") ~ Exp) ==>
-    { case _ ~ x ~ _ ~ y ~ _ ~ z => If(x, y, z): Exp } ||
-  (F ~ T_SEMI ~ Exp) ==> { case x ~ _ ~ y => Sequence(x, y): Exp } || L
-lazy val L: Parser[List[Token], Exp] = 
-  (T ~ T_OP("+") ~ Exp) ==> { case x ~ _ ~ z => Aop("+", x, z): Exp } ||
-  (T ~ T_OP("-") ~ Exp) ==> { case x ~ _ ~ z => Aop("-", x, z): Exp } || T  
-lazy val T: Parser[List[Token], Exp] = 
-  (F ~ T_OP("*") ~ T) ==> { case x ~ _ ~ z => Aop("*", x, z): Exp } || 
-  (F ~ T_OP("/") ~ T) ==> { case x ~ _ ~ z => Aop("/", x, z): Exp } || 
-  (F ~ T_OP("%") ~ T) ==> { case x ~ _ ~ z => Aop("%", x, z): Exp } || F
-lazy val F: Parser[List[Token], Exp] = 
-  (IdParser ~ T_LPAREN ~ T_RPAREN) ==> { case x ~ _ ~ _ => Call(x, Nil): Exp } ||
-  (IdParser ~ T_LPAREN ~ ListParser(Exp, T_COMMA) ~ T_RPAREN) ==> { case x ~ _ ~ z ~ _ => Call(x, z): Exp } ||
-  (T_LPAREN ~ Exp ~ T_RPAREN) ==> { case _ ~ y ~ _ => y: Exp } || 
-  IdParser ==> { case x => Var(x): Exp } || 
-  NumParser ==> { case x => Num(x): Exp } ||
-  CharConstParser ==> { case x => ChConst(x): Exp } ||
-  FNumParser ==> { case x => FNum(x): Exp }
-
-// boolean expressions
-lazy val BExp: Parser[List[Token], BExp] = 
-  (Exp ~ T_OP("==") ~ Exp) ==> { case x ~ _ ~ z => Bop("==", x, z): BExp } || 
-  (Exp ~ T_OP("!=") ~ Exp) ==> { case x ~ _ ~ z => Bop("!=", x, z): BExp } || 
-  (Exp ~ T_OP("<") ~ Exp)  ==> { case x ~ _ ~ z => Bop("<",  x, z): BExp } || 
-  (Exp ~ T_OP(">") ~ Exp)  ==> { case x ~ _ ~ z => Bop("<",  z, x): BExp } || 
-  (Exp ~ T_OP("<=") ~ Exp) ==> { case x ~ _ ~ z => Bop("<=", x, z): BExp } || 
-  (Exp ~ T_OP("=>") ~ Exp) ==> { case x ~ _ ~ z => Bop("<=", z, x): BExp } ||
-  (T_LPAREN ~ BExp ~ T_RPAREN) ==> { case _ ~ b ~ _ => b : BExp } 
-
-lazy val Arg : Parser[List[Token], (String, String)] = 
-  (IdParser ~ T_COLON ~ TyParser) ==> { case x ~ _ ~ ty => (x, ty) }  
-
-lazy val Defn: Parser[List[Token], Decl] = {
-   (T_KWD("def") ~ IdParser ~ T_LPAREN ~ T_RPAREN ~ T_COLON ~ TyParser ~ T_OP("=") ~ Exp) ==>
-     { case _ ~ y ~ _ ~ _ ~ _~ ty ~ _ ~ r => Def(y, Nil, ty, r): Decl } ||
-   (T_KWD("def") ~ IdParser ~ T_LPAREN ~ ListParser(Arg, T_COMMA) ~ T_RPAREN ~ T_COLON ~ TyParser ~ T_OP("=") ~ Exp) ==>
-     { case _ ~ y ~ _ ~ w ~ _ ~ _~ ty ~ _ ~ r => Def(y, w, ty, r): Decl }
-}
-
-lazy val Const_decl: Parser[List[Token], Decl] =
-   (T_KWD("val") ~ Arg ~ T_OP("=") ~ NumParser) ==>
-     { case _ ~ x ~ _ ~ v => Const(x._1, v): Decl } ||
-   (T_KWD("val") ~ Arg ~ T_OP("=") ~ FNumParser) ==>
-     { case _ ~ x ~ _ ~ v => FConst(x._1, v): Decl } 
-
-lazy val Prog: Parser[List[Token], List[Decl]] =
-  (Defn ~ T_SEMI ~ Prog) ==> { case x ~ _ ~ z => x :: z : List[Decl] } ||
-  (Const_decl ~ T_SEMI ~ Prog) ==> { case x ~ _ ~ z => x :: z : List[Decl] } ||
-  (Exp ==> ((s) => List(Main(s)) : List[Decl]))
-
-
-
-// Reading tokens and Writing parse trees
-
-//import ammonite.ops._
-
-def parse_tks(tks: List[Token]) : List[Decl] = {
-  //println(Prog.parse(tks))
-  Prog.parse_single(tks)
-}
-
-//@doc("Parses a file.")
-@main
-def main(fname: String) : Unit = {
-  val tks = tokenise(os.read(os.pwd / fname))
-  println(parse_tks(tks))
-}
-
-
--- a/solution/cw5/fun_tokens.sc	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,280 +0,0 @@
-// A tokeniser for the Fun language
-//==================================
-//
-// call with 
-//
-//     amm fun_tokens.sc fact.fun
-//
-//     amm fun_tokens.sc defs.fun
-//
-
-
-
-import scala.language.implicitConversions    
-import scala.language.reflectiveCalls 
-
-abstract class Rexp 
-case object ZERO extends Rexp
-case object ONE extends Rexp
-case class CHAR(c: Char) extends Rexp
-case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
-case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
-case class STAR(r: Rexp) extends Rexp 
-case class RECD(x: String, r: Rexp) extends Rexp
-  
-abstract class Val
-case object Empty extends Val
-case class Chr(c: Char) extends Val
-case class Sequ(v1: Val, v2: Val) extends Val
-case class Left(v: Val) extends Val
-case class Right(v: Val) extends Val
-case class Stars(vs: List[Val]) extends Val
-case class Rec(x: String, v: Val) extends Val
-   
-// some convenience for typing in regular expressions
-def charlist2rexp(s : List[Char]): Rexp = s match {
-  case Nil => ONE
-  case c::Nil => CHAR(c)
-  case c::s => SEQ(CHAR(c), charlist2rexp(s))
-}
-implicit def string2rexp(s : String) : Rexp = 
-  charlist2rexp(s.toList)
-
-implicit def RexpOps(r: Rexp) = new {
-  def | (s: Rexp) = ALT(r, s)
-  def % = STAR(r)
-  def ~ (s: Rexp) = SEQ(r, s)
-}
-
-implicit def stringOps(s: String) = new {
-  def | (r: Rexp) = ALT(s, r)
-  def | (r: String) = ALT(s, r)
-  def % = STAR(s)
-  def ~ (r: Rexp) = SEQ(s, r)
-  def ~ (r: String) = SEQ(s, r)
-  def $ (r: Rexp) = RECD(s, r)
-}
-
-def nullable (r: Rexp) : Boolean = r match {
-  case ZERO => false
-  case ONE => true
-  case CHAR(_) => false
-  case ALT(r1, r2) => nullable(r1) || nullable(r2)
-  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
-  case STAR(_) => true
-  case RECD(_, r1) => nullable(r1)
-}
-
-def der (c: Char, r: Rexp) : Rexp = r match {
-  case ZERO => ZERO
-  case ONE => ZERO
-  case CHAR(d) => if (c == d) ONE else ZERO
-  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
-  case SEQ(r1, r2) => 
-    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
-    else SEQ(der(c, r1), r2)
-  case STAR(r) => SEQ(der(c, r), STAR(r))
-  case RECD(_, r1) => der(c, r1)
-}
-
-
-// extracts a string from value
-def flatten(v: Val) : String = v match {
-  case Empty => ""
-  case Chr(c) => c.toString
-  case Left(v) => flatten(v)
-  case Right(v) => flatten(v)
-  case Sequ(v1, v2) => flatten(v1) + flatten(v2)
-  case Stars(vs) => vs.map(flatten).mkString
-  case Rec(_, v) => flatten(v)
-}
-
-// extracts an environment from a value;
-// used for tokenise a string
-def env(v: Val) : List[(String, String)] = v match {
-  case Empty => Nil
-  case Chr(c) => Nil
-  case Left(v) => env(v)
-  case Right(v) => env(v)
-  case Sequ(v1, v2) => env(v1) ::: env(v2)
-  case Stars(vs) => vs.flatMap(env)
-  case Rec(x, v) => (x, flatten(v))::env(v)
-}
-
-// The Injection Part of the lexer
-
-def mkeps(r: Rexp) : Val = r match {
-  case ONE => Empty
-  case ALT(r1, r2) => 
-    if (nullable(r1)) Left(mkeps(r1)) else Right(mkeps(r2))
-  case SEQ(r1, r2) => Sequ(mkeps(r1), mkeps(r2))
-  case STAR(r) => Stars(Nil)
-  case RECD(x, r) => Rec(x, mkeps(r))
-}
-
-def inj(r: Rexp, c: Char, v: Val) : Val = (r, v) match {
-  case (STAR(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
-  case (SEQ(r1, r2), Sequ(v1, v2)) => Sequ(inj(r1, c, v1), v2)
-  case (SEQ(r1, r2), Left(Sequ(v1, v2))) => Sequ(inj(r1, c, v1), v2)
-  case (SEQ(r1, r2), Right(v2)) => Sequ(mkeps(r1), inj(r2, c, v2))
-  case (ALT(r1, r2), Left(v1)) => Left(inj(r1, c, v1))
-  case (ALT(r1, r2), Right(v2)) => Right(inj(r2, c, v2))
-  case (CHAR(d), Empty) => Chr(c) 
-  case (RECD(x, r1), _) => Rec(x, inj(r1, c, v))
-  case _ => { println ("Injection error") ; sys.exit(-1) } 
-}
-
-// some "rectification" functions for simplification
-def F_ID(v: Val): Val = v
-def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))
-def F_LEFT(f: Val => Val) = (v:Val) => Left(f(v))
-def F_ALT(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
-  case Right(v) => Right(f2(v))
-  case Left(v) => Left(f1(v))
-}
-def F_SEQ(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
-  case Sequ(v1, v2) => Sequ(f1(v1), f2(v2))
-}
-def F_SEQ_Empty1(f1: Val => Val, f2: Val => Val) = 
-  (v:Val) => Sequ(f1(Empty), f2(v))
-def F_SEQ_Empty2(f1: Val => Val, f2: Val => Val) = 
-  (v:Val) => Sequ(f1(v), f2(Empty))
-def F_RECD(f: Val => Val) = (v:Val) => v match {
-  case Rec(x, v) => Rec(x, f(v))
-}
-def F_ERROR(v: Val): Val = throw new Exception("error")
-
-def simp(r: Rexp): (Rexp, Val => Val) = r match {
-  case ALT(r1, r2) => {
-    val (r1s, f1s) = simp(r1)
-    val (r2s, f2s) = simp(r2)
-    (r1s, r2s) match {
-      case (ZERO, _) => (r2s, F_RIGHT(f2s))
-      case (_, ZERO) => (r1s, F_LEFT(f1s))
-      case _ => if (r1s == r2s) (r1s, F_LEFT(f1s))
-                else (ALT (r1s, r2s), F_ALT(f1s, f2s)) 
-    }
-  }
-  case SEQ(r1, r2) => {
-    val (r1s, f1s) = simp(r1)
-    val (r2s, f2s) = simp(r2)
-    (r1s, r2s) match {
-      case (ZERO, _) => (ZERO, F_ERROR)
-      case (_, ZERO) => (ZERO, F_ERROR)
-      case (ONE, _) => (r2s, F_SEQ_Empty1(f1s, f2s))
-      case (_, ONE) => (r1s, F_SEQ_Empty2(f1s, f2s))
-      case _ => (SEQ(r1s,r2s), F_SEQ(f1s, f2s))
-    }
-  }
-  case RECD(x, r1) => {
-    val (r1s, f1s) = simp(r1)
-    (RECD(x, r1s), F_RECD(f1s))
-  }
-  case r => (r, F_ID)
-}
-
-// lexing functions including simplification
-def lex_simp(r: Rexp, s: List[Char]) : Val = s match {
-  case Nil => if (nullable(r)) mkeps(r) else { println ("Lexing Error") ; sys.exit(-1) } 
-  case c::cs => {
-    val (r_simp, f_simp) = simp(der(c, r))
-    inj(r, c, f_simp(lex_simp(r_simp, cs)))
-  }
-}
-
-def lexing_simp(r: Rexp, s: String) = env(lex_simp(r, s.toList))
-
-
-// The Lexing Rules for the Fun Language
-
-def PLUS(r: Rexp) = r ~ r.%
-def OPT(r: Rexp) = r | ONE
-
-val SYM = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | 
-          "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | 
-          "w" | "x" | "y" | "z" | "A" | "B" | "C" | "D" |"E" | "F" | "G" |
-          "H" | "I" | "J" | "K" |"L" | "M" | "N" |
-          "O" | "P" | "Q" | "R" |"S" | "T" | "U" |
-          "V" | "W" | "X" | "Y" | "Z" | "_" | ":"
-val DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
-val ID = SYM ~ (SYM | DIGIT).% 
-val NUM = PLUS(DIGIT)
-val FNUM = OPT("-") ~ NUM ~ "." ~ NUM 
-val KEYWORD : Rexp = "if" | "then" | "else" | "def" | "val"
-val TYPE : Rexp = "Void" | "Int" | "Double" 
-val SEMI: Rexp = ";"
-val COLON: Rexp = ":"
-val COMMA: Rexp = ","
-val OP: Rexp = "=" | "==" | "-" | "+" | "*" | "!=" | "<" | ">" | "<=" | ">=" | "%" | "/"
-val WHITESPACE = PLUS(" " | "\n" | "\t" | "\r")
-val RPAREN: Rexp = ")" | "}"
-val LPAREN: Rexp = "(" | "{"
-val ALL = SYM | DIGIT | OP | " " | ":" | ";" | "-" | "." | "\"" | "=" | "," | "(" | ")" | "{" | "}"
-val ALL2 = ALL | "\n"
-val COMMENT = ("/*" ~ ALL2.% ~ "*/") | ("//" ~ ALL.% ~ "\n")
-
-val CHR :Rexp = "'" ~ (ALL | "\\n") ~ "'" 
-
-
-val FUN_REGS = (("k" $ KEYWORD) | 
-                ("t" $ TYPE) |
-                ("i" $ ID) | 
-                ("ch" $ CHR) | 
-                ("o" $ OP) | 
-                ("n" $ NUM) | 
-                ("f" $ FNUM) | 
-                ("s" $ SEMI) | 
-                ("co" $ COLON) |
-                ("c" $ COMMA) |
-                ("pl" $ LPAREN) |
-                ("pr" $ RPAREN) |
-                ("w" $ (WHITESPACE | COMMENT))).%
-
-
-
-// The tokens for the Fun language
-
-abstract class Token extends Serializable 
-case object T_SEMI extends Token
-case object T_COMMA extends Token
-case object T_COLON extends Token
-case object T_LPAREN extends Token
-case object T_RPAREN extends Token
-case class T_ID(s: String) extends Token
-case class T_FID(s: String) extends Token
-case class T_OP(s: String) extends Token
-case class T_NUM(n: Int) extends Token
-case class T_FNUM(x: Double) extends Token
-case class T_KWD(s: String) extends Token
-case class T_TY(s: String) extends Token
-case class T_CHR(i: Int) extends Token
-
-val token : PartialFunction[(String, String), Token] = {
-  case ("k", s) => T_KWD(s)
-  case ("t", s) => T_TY(s)
-  case ("i", s) => T_ID(s)
-  case ("o", s) => T_OP(s)
-  case ("n", s) => T_NUM(s.toInt)
-  case ("ch", s) => if (s == "'\\n'") T_CHR(10) else T_CHR(s(1).toInt)
-  case ("f", s) => T_FNUM(s.toDouble) 
-  case ("s", _) => T_SEMI
-  case ("c", _) => T_COMMA
-  case ("co", _) => T_COLON
-  case ("pl", _) => T_LPAREN
-  case ("pr", _) => T_RPAREN
-}
-
-
-def tokenise(s: String) : List[Token] = {
-  val tks = lexing_simp(FUN_REGS, s).collect(token)
-  if (tks.length != 0) tks
-  else { println (s"Tokenise Error") ; sys.exit(-1) }     
-}
-
-//import ammonite.ops._
-
-//@doc("Tokenising a file.")
-@main
-def main(fname: String) = {
-  println(tokenise(os.read(os.pwd / fname)))
-}
--- a/solution/cw5/hanoi.fun	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-// Towers of Hanoi in Fun
-
-def hanoi(n: Int, a: Int, b: Int, c: Int) : Void =
-  if n != 0 then {
-    hanoi(n - 1, a, c, b);
-    print_int(a);
-    print_char('-'); print_char('>'); // prints out "->"
-    print_int(b);
-    print_char('\n');
-    hanoi(n - 1, c, b, a)
-  } else skip;
-
-hanoi(4,1,2,3)
--- a/solution/cw5/mand.fun	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-// Mandelbrot program (without character constants)
-
-val Ymin: Double = -1.3;
-val Ymax: Double =  1.3;
-val Ystep: Double = 0.05;  //0.025;
-
-val Xmin: Double = -2.1;
-val Xmax: Double =  1.1;
-val Xstep: Double = 0.02;  //0.01;
-
-val Maxiters: Int = 1000;
-
-def m_iter(m: Int, x: Double, y: Double,
-                   zr: Double, zi: Double) : Void = {
-  if Maxiters <= m
-  then print_star() 
-  else {
-    if 4.0 <= zi*zi+zr*zr then print_space() 
-    else m_iter(m + 1, x, y, x+zr*zr-zi*zi, 2.0*zr*zi+y) 
-  }
-};
-
-def x_iter(x: Double, y: Double) : Void = {
-  if x <= Xmax
-  then { m_iter(0, x, y, 0.0, 0.0) ; x_iter(x + Xstep, y) }
-  else skip()
-};
-
-def y_iter(y: Double) : Void = {
-  if y <= Ymax
-  then { x_iter(Xmin, y) ; new_line() ; y_iter(y + Ystep) }
-  else skip() 
-};    
-
-
-y_iter(Ymin)
\ No newline at end of file
--- a/solution/cw5/mand2.fun	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-// Mandelbrot program (with character constants)
-
-val Ymin: Double = -1.3;
-val Ymax: Double =  1.3;
-val Ystep: Double = 0.05;  //0.025;
-
-val Xmin: Double = -2.1;
-val Xmax: Double =  1.1;
-val Xstep: Double = 0.02;  //0.01;
-
-val Maxiters: Int = 1000;
-
-def m_iter(m: Int, x: Double, y: Double,
-                   zr: Double, zi: Double) : Void = {
-  if Maxiters <= m
-  then print_char(' ') 
-  else {
-    if 4.0 <= zi*zi+zr*zr then print_char('0' + (m % 10)) 
-    else m_iter(m + 1, x, y, x+zr*zr-zi*zi, 2.0*zr*zi+y) 
-  }
-};
-
-def x_iter(x: Double, y: Double) : Void = {
-  if x <= Xmax
-  then { m_iter(0, x, y, 0.0, 0.0) ; x_iter(x + Xstep, y) }
-  else skip()
-};
-
-def y_iter(y: Double) : Void = {
-  if y <= Ymax
-  then { x_iter(Xmin, y) ; print_char('\n') ; y_iter(y + Ystep) }
-  else skip() 
-};    
-
-
-y_iter(Ymin)
\ No newline at end of file
--- a/solution/cw5/sqr.fun	Fri Oct 28 09:08:13 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-val Max : Int = 10;
-
-def sqr(x: Int) : Int = x * x;
-
-def all(n: Int) : Void = {
-  if n <= Max
-  then { print_int(sqr(n)) ; new_line(); all(n + 1) }
-  else skip()
-};
-
-{
-  print_string("Squares");
-  new_line();
-  all(0)
-} 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw1/cw1.scala	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,264 @@
+// CW 1
+import scala.language.implicitConversions    
+import scala.language.reflectiveCalls 
+
+abstract class Rexp
+case object ZERO extends Rexp                            // matches nothing
+case object ONE extends Rexp                             // matches the empty string
+case class CHAR(c: Char) extends Rexp                    // matches a character c
+case class ALT(r1: Rexp, r2: Rexp) extends Rexp          // alternative
+case class SEQ(r1: Rexp, r2: Rexp) extends Rexp          // sequence
+case class STAR(r: Rexp) extends Rexp                    // star
+case class RANGE(cs: Set[Char]) extends Rexp             // set of characters
+case class PLUS(r: Rexp) extends Rexp                    // plus
+case class OPT(r: Rexp) extends Rexp                     // optional
+case class NTIMES(r: Rexp, n: Int) extends Rexp          // n-times
+case class UPNTIMES(r: Rexp, n: Int) extends Rexp        // up n-times
+case class FROMNTIMES(r: Rexp, n: Int) extends Rexp      // from n-times
+case class NMTIMES(r: Rexp, n: Int, m: Int) extends Rexp // between nm-times
+case class NOT(r: Rexp) extends Rexp                     // not
+case class CFUN(f: Char => Boolean) extends Rexp         // subsuming CHAR and RANGE
+
+def CHAR(c: Char) = CFUN(d => c == d)
+val ALL = CFUN(_ => true)
+def RANGE(cs: Set[Char]) = CFUN(d => cs.contains(d))
+
+def CHAR(c: Char) = CFUN(c == _)
+val ALL = CFUN(_ => true)
+def RANGE(cs: Set[Char]) = CFUN(cs.contains(_))
+
+
+// nullable function: tests whether the regular 
+// expression can recognise the empty string
+def nullable (r: Rexp) : Boolean = r match {
+  case ZERO => false
+  case ONE => true
+  case CHAR(_) => false
+  case ALT(r1, r2) => nullable(r1) || nullable(r2)
+  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
+  case STAR(_) => true
+  case RANGE(_) => false
+  case PLUS(r) => nullable(r)
+  case OPT(_) => true
+  case NTIMES(r, n) => if (n == 0) true else nullable(r)
+  case UPNTIMES(_, _) => true
+  case FROMNTIMES(r, n) => if (n == 0) true else nullable(r)
+  case NMTIMES(r, n, m) => if (n == 0) true else nullable(r)
+  case NOT(r) => !nullable(r)
+  case CFUN(_) => false
+}
+
+// derivative of a regular expression w.r.t. a character
+def der (c: Char, r: Rexp) : Rexp = r match {
+  case ZERO => ZERO
+  case ONE => ZERO
+  case CHAR(d) => if (c == d) ONE else ZERO
+  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
+  case SEQ(r1, r2) => 
+    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
+    else SEQ(der(c, r1), r2)
+  case STAR(r) => SEQ(der(c, r), STAR(r))
+  case RANGE(cs) => if (cs contains c) ONE else ZERO
+  case PLUS(r) => SEQ(der(c, r), STAR(r))
+  case OPT(r) => der(c, r)
+  case NTIMES(r, n) => 
+    if (n == 0) ZERO else SEQ(der(c, r), NTIMES(r, n - 1))
+  case UPNTIMES(r, n) =>
+    if (n == 0) ZERO else SEQ(der(c, r), UPNTIMES(r, n - 1))
+  case FROMNTIMES(r, n) =>
+    if (n == 0) SEQ(der(c, r), STAR(r)) else SEQ(der(c, r), FROMNTIMES(r, n - 1))
+  case NMTIMES(r, n, m) =>
+    if (m < n) ZERO else
+    if (n == 0 && m == 0) ZERO else 
+    if (n == 0) SEQ(der(c, r), UPNTIMES(r, m - 1)) 
+    else SEQ(der(c, r), NMTIMES(r, n - 1, m - 1)) 
+  case NOT(r) => NOT(der (c, r))
+  case CFUN(f) => if (f(c)) ONE else ZERO
+}
+
+def simp(r: Rexp) : Rexp = r match {
+  case ALT(r1, r2) => (simp(r1), simp(r2)) match {
+    case (ZERO, r2s) => r2s
+    case (r1s, ZERO) => r1s
+    case (r1s, r2s) => if (r1s == r2s) r1s else ALT (r1s, r2s)
+  }
+  case SEQ(r1, r2) =>  (simp(r1), simp(r2)) match {
+    case (ZERO, _) => ZERO
+    case (_, ZERO) => ZERO
+    case (ONE, r2s) => r2s
+    case (r1s, ONE) => r1s
+    case (r1s, r2s) => SEQ(r1s, r2s)
+  }
+  case r => r
+}
+
+
+// derivative w.r.t. a string (iterates der)
+def ders (s: List[Char], r: Rexp) : Rexp = s match {
+  case Nil => r
+  case c::s => ders(s, der(c, r))
+}
+
+def ders_simp (s: List[Char], r: Rexp) : Rexp = s match {
+  case Nil => r
+  case c::s => ders_simp(s, simp(der(c, r)))
+}
+
+// main matcher function including simplification
+def matcher(r: Rexp, s: String) : Boolean = 
+  nullable(ders_simp(s.toList, r))
+
+
+
+// some convenience for typing in regular expressions
+def charlist2rexp(s : List[Char]) : Rexp = s match {
+  case Nil => ONE
+  case c::Nil => CHAR(c)
+  case c::s => SEQ(CHAR(c), charlist2rexp(s))
+}
+implicit def string2rexp(s : String) : Rexp = charlist2rexp(s.toList)
+
+implicit def RexpOps (r: Rexp) = new {
+  def | (s: Rexp) = ALT(r, s)
+  def % = STAR(r)
+  def ~ (s: Rexp) = SEQ(r, s)
+}
+
+implicit def stringOps (s: String) = new {
+  def | (r: Rexp) = ALT(s, r)
+  def | (r: String) = ALT(s, r)
+  def % = STAR(s)
+  def ~ (r: Rexp) = SEQ(s, r)
+  def ~ (r: String) = SEQ(s, r)
+}
+
+val r1 = NTIMES("a", 3)
+val r2 = NTIMES(OPT("a"), 3)
+val r3 = UPNTIMES("a", 3)
+val r4 = UPNTIMES(OPT("a"), 3)
+val r5 = NMTIMES("a", 3, 5)
+val r6 = NMTIMES(OPT("a"), 3, 5)
+
+val rs = List(r1, r2, r3, r4, r5, r6)
+
+rs.map(matcher(_, ""))
+rs.map(matcher(_, "a"))
+rs.map(matcher(_, "aa"))
+rs.map(matcher(_, "aaa"))
+rs.map(matcher(_, "aaaa"))
+rs.map(matcher(_, "aaaaa"))
+rs.map(matcher(_, "aaaaaa"))
+
+println("EMAIL:")
+val LOWERCASE = ('a' to 'z').toSet
+val DIGITS = ('0' to '9').toSet
+val EMAIL = { PLUS(CFUN(LOWERCASE | DIGITS | ("_.-").toSet)) ~ "@" ~ 
+              PLUS(CFUN(LOWERCASE | DIGITS | (".-").toSet)) ~ "." ~
+              NMTIMES(CFUN(LOWERCASE | Set('.')), 2, 6) }
+
+val my_email = "christian.urban@kcl.ac.uk"
+
+println(EMAIL);
+println(matcher(EMAIL, my_email))
+println(ders_simp(my_email.toList, EMAIL))
+
+println("COMMENTS:")
+val ALL = CFUN((c:Char) => true)
+val COMMENT = "/*" ~ (NOT(ALL.% ~ "*/" ~ ALL.%)) ~ "*/"
+
+println(matcher(COMMENT, "/**/"))
+println(matcher(COMMENT, "/*foobar*/"))
+println(matcher(COMMENT, "/*test*/test*/"))
+println(matcher(COMMENT, "/*test/*test*/"))
+
+println("EVILS:")
+val EVIL1 = PLUS(PLUS("a" ~ "a" ~ "a"))
+val EVIL2 = PLUS(PLUS("aaaaaaaaaaaaaaaaaaa" ~ OPT("a")))  // 19 as ~ a?
+
+
+//40 * aaa matches
+//43 * aaa + aa does not
+//45 * aaa + a
+
+println("EVIL1:")
+println(matcher(EVIL1, "aaa" * 40))
+println(matcher(EVIL1, "aaa" * 43 + "aa"))
+println(matcher(EVIL1, "aaa" * 45 + "a"))
+println("EVIL2:")
+println(matcher(EVIL2, "aaa" * 40))
+println(matcher(EVIL2, "aaa" * 43 + "aa"))
+println(matcher(EVIL2, "aaa" * 45 + "a"))
+
+println("TEST for bug pointed out by Filips Ivanovs")
+val test = NMTIMES(CFUN(LOWERCASE | Set('.')), 2, 6)
+  
+println(matcher(test,"a"))
+println(matcher(test,"ab"))
+println(matcher(test,"abcdef"))
+println(matcher(test,"abc"))
+println(matcher(test,"...."))
+println(matcher(test,"asdfg"))
+println(matcher(test,"abcdefg"))
+
+println(test)
+println(ders_simp("a".toList, test))
+println(ders_simp("aa".toList, test))
+println(ders_simp("aaa".toList, test))
+println(ders_simp("aaaa".toList, test))
+println(ders_simp("aaaaa".toList, test))
+println(ders_simp("aaaaaa".toList, test))
+println(ders_simp("aaaaaaa".toList, test))
+
+def TEST(s: String, r: Rexp) = {
+  println("Rexp |" + s + "|")
+  println("Derivative:\n" + ders_simp(s.toList, r))
+  println("Is Nullable: " + nullable(ders_simp(s.toList, r)))
+  Console.readLine
+}
+
+
+val ALL2 = "a" | "f"
+val COMMENT2 = ("/*" ~ NOT(ALL2.% ~ "*/" ~ ALL2.%) ~ "*/")
+
+println("1) TEST TEST")
+TEST("", COMMENT2)
+TEST("/", COMMENT2)
+TEST("/*", COMMENT2)
+TEST("/*f", COMMENT2)
+TEST("/*f*", COMMENT2)
+TEST("/*f*/", COMMENT2)
+TEST("/*f*/ ", COMMENT2)
+
+val ALL3 = "a" | "f"
+val COMMENT3 = ("/" ~ NOT(ALL3.% ~ "&" ~ ALL3.%) ~ "&")
+
+println("2) TEST TEST")
+TEST("", COMMENT3)
+TEST("/", COMMENT3)
+TEST("/", COMMENT3)
+TEST("/f", COMMENT3)
+TEST("/f&", COMMENT3)
+TEST("/f& ", COMMENT3)
+
+//test: ("a" | "aa") ~ ("a" | "aa")*
+val auxEVIL3 = ALT(CHAR('a'), SEQ(CHAR('a'), CHAR('a')))
+val EVIL3 = SEQ(auxEVIL3, STAR(auxEVIL3))
+val EVIL3p = FROMNTIMES(auxEVIL3, 1)
+
+
+for (i <- 1 to 100 by 1) {
+  println(i + " " + "%.5f".format(time_needed(2, matcher(EVIL3, "a" * i))) + " size: " + 
+	  size(ders(("a" * i).toList, EVIL3)))
+}
+
+
+
+val t1 = EVIL3
+val t2 = simp(der('a', t1))
+val t3 = simp(der('a', t2))
+val t4 = simp(der('a', t3))
+
+val t1 = EVIL3p
+val t2 = simp(der('a', t1))
+val t3 = simp(der('a', t2))
+val t4 = simp(der('a', t3))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw1/matcher.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,325 @@
+// CW1
+
+abstract class Rexp
+case object ZERO extends Rexp
+case object ONE extends Rexp
+case class CHAR(c: Char) extends Rexp
+case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
+case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
+case class STAR(r: Rexp) extends Rexp 
+
+// extended Rexps
+case class RANGE(s: Set[Char]) extends Rexp
+case class PLUS(r: Rexp) extends Rexp
+case class OPTIONAL(r: Rexp) extends Rexp
+case class NTIMES(r: Rexp, n: Int) extends Rexp 
+case class UPTO(r: Rexp, n: Int) extends Rexp
+case class FROM(r: Rexp, n: Int) extends Rexp
+case class BETWEEN(r: Rexp, n: Int, m: Int) extends Rexp
+case class NOT(r: Rexp) extends Rexp
+
+// functions
+case class CFUN(f: Char => Boolean) extends Rexp
+
+// abbreviations
+def FCHAR(c: Char) = CFUN((a: Char) => a == c)
+def FSET(s: Set[Char]) = CFUN((a: Char) => s.contains(a))
+val FALL = CFUN(_ => true)
+
+def nullable (r: Rexp) : Boolean = r match {
+  case ZERO => false
+  case ONE => true
+  case CHAR(_) => false
+  case ALT(r1, r2) => nullable(r1) || nullable(r2)
+  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
+  case STAR(_) => true
+
+  case RANGE(_) => false 
+  case PLUS(r1) => nullable(r1)
+  case OPTIONAL(_) => true
+  case NTIMES(r1, i) => if (i == 0) true else nullable(r1)
+  case UPTO(_, _) => true
+  case FROM(r1, i) => if (i == 0) true else nullable(r1)
+  case BETWEEN(r1, i, _) => if (i == 0) true else nullable(r1)
+  case NOT(r1) => !nullable(r1)
+
+  case CFUN(f) => false
+}
+
+
+def der (c: Char, r: Rexp) : Rexp = r match {
+  case ZERO => ZERO
+  case ONE => ZERO
+  case CHAR(d) => if (c == d) ONE else ZERO
+  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
+  case SEQ(r1, r2) => 
+    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
+    else SEQ(der(c, r1), r2)
+  case STAR(r1) => SEQ(der(c, r1), STAR(r1))
+
+  case RANGE(s) =>
+    if (s.contains(c)) ONE else ZERO 
+  case PLUS(r1) => SEQ(der(c, r1), STAR(r1))
+  case OPTIONAL(r1) => der(c, r1)
+  case NTIMES(r, i) => 
+    if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
+  case UPTO(r1, i) => 
+    if (i == 0) ZERO else SEQ(der(c, r1), UPTO(r1, i - 1)) 
+  case FROM(r1, i) =>
+    if (i == 0) SEQ(der(c, r1), FROM(r1, 0)) else
+    SEQ(der(c, r1), FROM(r1, i - 1))
+  case BETWEEN(r1, i, j) => 
+    if (i == 0) {
+      if (j == 0) ZERO else SEQ(der(c, r1), BETWEEN(r1, 0, j - 1))
+    } else SEQ(der(c, r1), BETWEEN(r1, i - 1, j - 1))
+  case NOT(r1) => NOT(der(c, r1))
+
+  case CFUN(f) => if (f(c)) ONE else ZERO
+}
+
+
+def simp(r: Rexp) : Rexp = r match {
+  case ALT(r1, r2) => (simp(r1), simp(r2)) match {
+    case (ZERO, r2s) => r2s
+    case (r1s, ZERO) => r1s
+    case (r1s, r2s) => if (r1s == r2s) r1s else ALT (r1s, r2s)
+  }
+  case SEQ(r1, r2) =>  (simp(r1), simp(r2)) match {
+    case (ZERO, _) => ZERO
+    case (_, ZERO) => ZERO
+    case (ONE, r2s) => r2s
+    case (r1s, ONE) => r1s
+    case (r1s, r2s) => SEQ(r1s, r2s)
+  }
+  case r => r
+}
+
+def ders(s: List[Char], r: Rexp) : Rexp = s match {
+  case Nil => r
+  case c::s => ders(s, simp(der(c, r)))
+}
+
+def matcher(r: Rexp, s: String) : Boolean = nullable(ders(s.toList, r))
+
+
+
+val Regex31 = NTIMES(CHAR('a'),3)
+val Regex32 = NTIMES(OPTIONAL(CHAR('a')),3)
+val Regex33 = UPTO(CHAR('a'),3)
+val Regex34 = UPTO(OPTIONAL(CHAR('a')),3)
+val Regex35 = BETWEEN(CHAR('a'),3,5)
+val Regex36 = BETWEEN(OPTIONAL(CHAR('a')),3,5)
+val string31 = ""
+val string32 = "a"
+val string33 = "aa"
+val string34 = "aaa"
+val string35 = "aaaa"
+val string36 = "aaaaa"
+val string37 = "aaaaaa"
+
+
+println("Question3")
+println(matcher(Regex31, string31))
+println(matcher(Regex31, string32))
+println(matcher(Regex31, string33))
+println(matcher(Regex31, string34))
+println(matcher(Regex31, string35))
+println(matcher(Regex31, string36))
+println(matcher(Regex31, string37)); println("")
+println(matcher(Regex32, string31))
+println(matcher(Regex32, string32))
+println(matcher(Regex32, string33))
+println(matcher(Regex32, string34))
+println(matcher(Regex32, string35))
+println(matcher(Regex32, string36))
+println(matcher(Regex32, string37)); println("")
+println(matcher(Regex33, string31))
+println(matcher(Regex33, string32))
+println(matcher(Regex33, string33))
+println(matcher(Regex33, string34))
+println(matcher(Regex33, string35))
+println(matcher(Regex33, string36))
+println(matcher(Regex33, string37)); println("")
+println(matcher(Regex34, string31))
+println(matcher(Regex34, string32))
+println(matcher(Regex34, string33))
+println(matcher(Regex34, string34))
+println(matcher(Regex34, string35))
+println(matcher(Regex34, string36))
+println(matcher(Regex34, string37)); println("")
+println(matcher(Regex35, string31))
+println(matcher(Regex35, string32))
+println(matcher(Regex35, string33))
+println(matcher(Regex35, string34))
+println(matcher(Regex35, string35))
+println(matcher(Regex35, string36))
+println(matcher(Regex35, string37)); println("")
+println(matcher(Regex36, string31))
+println(matcher(Regex36, string32))
+println(matcher(Regex36, string33))
+println(matcher(Regex36, string34))
+println(matcher(Regex36, string35))
+println(matcher(Regex36, string36))
+println(matcher(Regex36, string37)); println("")
+
+
+val RegexX = BETWEEN(CHAR('a'), 0, 5)
+val stringX = ""
+println(matcher(RegexX, stringX))
+
+
+
+val str0 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+val str1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+val str2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+val matchA = (c:Char) => c == 'a'
+
+val reg_1 = SEQ(SEQ(CFUN(matchA), CFUN(matchA)), CFUN(matchA))
+val reg_2 = SEQ(NTIMES(CFUN(matchA), 19), OPTIONAL(CFUN(matchA)))
+
+val reg_1_mod = PLUS(PLUS(reg_1))
+val reg_2_mod = PLUS(PLUS(reg_2))
+
+
+matcher(reg_1_mod, str0)
+matcher(reg_1_mod, str1)
+matcher(reg_1_mod, str2)
+matcher(reg_2_mod, str0)
+matcher(reg_2_mod, str1)
+matcher(reg_2_mod, str2)
+
+
+
+
+
+//Q3: matcher test (table)
+
+// a^{3}
+val re1 = NTIMES(CHAR('a'), 3)
+
+matcher(re1, "")        //false
+matcher(re1, "a")       //false
+matcher(re1, "aa")      //false
+matcher(re1, "aaa")     //true
+matcher(re1, "aaaaa")   //false
+matcher(re1, "aaaaaa")  //false
+
+// (a?)^{3}
+val re2 = NTIMES(OPTIONAL(CHAR('a')), 3)
+
+matcher(re2, "")        //true
+matcher(re2, "a")       //true
+matcher(re2, "aa")      //true
+matcher(re2, "aaa")     //true
+matcher(re2, "aaaaa")   //false
+matcher(re2, "aaaaaa")  //false
+
+// (a)^{..3}
+val re3 = UPTO((CHAR('a')), 3)
+
+matcher(re3, "")        //true
+matcher(re3, "a")       //true
+matcher(re3, "aa")      //true
+matcher(re3, "aaa")     //true
+matcher(re3, "aaaaa")   //false
+matcher(re3, "aaaaaa")  //false
+
+// (a?)^{..3}
+val re4 = UPTO(OPTIONAL(CHAR('a')), 3)
+
+matcher(re4, "")        //true
+matcher(re4, "a")       //true
+matcher(re4, "aa")      //true
+matcher(re4, "aaa")     //true
+matcher(re4, "aaaaa")   //false
+matcher(re4, "aaaaaa")  //false
+
+// (a)^{3..5}
+val re5 = BETWEEN(CHAR('a'), 3, 5)
+
+matcher(re5, "")        //false
+matcher(re5, "a")       //false
+matcher(re5, "aa")      //false
+matcher(re5, "aaa")     //true
+matcher(re5, "aaaaa")   //true
+matcher(re5, "aaaaaa")  //false
+
+// (a?)^{3..5}
+val re6 = BETWEEN(OPTIONAL(CHAR('a')), 3, 5)
+
+matcher(re6, "")        //true
+matcher(re6, "a")       //true
+matcher(re6, "aa")      //true
+matcher(re6, "aaa")     //true
+matcher(re6, "aaaaa")   //true
+matcher(re6, "aaaaaa")  //false
+
+//Q5: regular expression for email addresses
+
+val alphaNum = ('a' to 'z').toSet ++ ('0' to '9')
+val Q5email = SEQ(
+                PLUS(RANGE(alphaNum + '_' + '-' + '.')), 
+                SEQ(
+                    CHAR('@'), 
+                    SEQ(
+                        PLUS(RANGE(alphaNum + '-' + '.')), 
+                        SEQ(
+                            CHAR('.'), 
+                            BETWEEN(RANGE(('a' to 'z').toSet + '.'), 2, 6)
+                            )
+                      )
+                )
+              )
+
+ders("nicolas.volken@kcl.ac.uk".toList, Q5email)
+
+// Q6
+val Q6 = SEQ(CHAR('/'),
+            SEQ(CHAR('*'),
+                SEQ(
+                  
+                    NOT(
+                      SEQ(
+                        STAR(FALL),
+                        SEQ(
+                          CHAR('*'),
+                          SEQ(
+                            CHAR('/'),
+                            STAR(FALL)
+                          )
+                        )
+                      )
+                    )
+                  
+                    ,
+                    SEQ(CHAR('*'),
+                    CHAR('/')
+                    )
+                )
+            )
+        )
+
+matcher(Q6, "/**/")
+matcher(Q6, "/*foobar*/")
+matcher(Q6, "/*test*/test*/")
+matcher(Q6, "/*test/*test*/")
+
+// Q7
+
+val Q7r1 = SEQ(CHAR('a'), SEQ(CHAR('a'), CHAR('a')))
+val Q7r2 = SEQ(BETWEEN(CHAR('a'), 19, 19), OPTIONAL(CHAR('a')))
+
+val Q7str5 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+val Q7str6 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+val Q7str7 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+matcher(PLUS(PLUS(Q7r1)), Q7str5)
+matcher(PLUS(PLUS(Q7r2)), Q7str5)
+
+matcher(PLUS(PLUS(Q7r1)), Q7str6)
+matcher(PLUS(PLUS(Q7r2)), Q7str6)
+
+matcher(PLUS(PLUS(Q7r1)), Q7str7)
+matcher(PLUS(PLUS(Q7r2)), Q7str7)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw2/collatz.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,8 @@
+write "Input a number ";
+read n;
+while n > 1 do {
+  if n % 2 == 0 
+  then n := n / 2 
+  else n := 3 * n + 1;
+};
+write "Yes\n";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw2/collatz2.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,27 @@
+// Collatz series
+//
+// needs writing of strings and numbers; comments
+
+bnd := 1;
+while bnd < 101 do {
+  write bnd;
+  write ": ";
+  n := bnd;
+  cnt := 0;
+
+  while n > 1 do {
+    write n;
+    write ",";
+    
+    if n % 2 == 0 
+    then n := n / 2 
+    else n := 3 * n+1;
+
+    cnt := cnt + 1
+  };
+
+  write " => ";
+  write cnt;
+  write "\n";
+  bnd := bnd + 1
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw2/factors.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,14 @@
+// Find all factors of a given input number
+// by J.R. Cordy August 2005
+
+write "Input n please";
+read n;
+write "The factors of n are\n";
+f := 2;
+while n != 1 do {
+    while (n / f) * f == n do {
+        write f; write "\n";
+        n := n / f
+    };
+    f := f + 1
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw2/fib.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,13 @@
+write "Fib";
+read n;  
+minus1 := 1;
+minus2 := 0;
+while n > 0 do {
+       temp := minus2;
+       minus2 := minus1 + minus2;
+       minus1 := temp;
+       n := n - 1
+};
+write "Result: ";
+write minus2 
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw2/lexer.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,357 @@
+import scala.language.implicitConversions    
+import scala.language.reflectiveCalls
+
+// Rexp
+abstract class Rexp
+case object ZERO extends Rexp
+case object ONE extends Rexp
+case class CHAR(c: Char) extends Rexp
+case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
+case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
+case class STAR(r: Rexp) extends Rexp 
+case class RECD(x: String, r: Rexp) extends Rexp
+
+case class RANGE(s: Set[Char]) extends Rexp
+case class PLUS(r: Rexp) extends Rexp
+case class OPTIONAL(r: Rexp) extends Rexp
+case class NTIMES(r: Rexp, n: Int) extends Rexp 
+
+// Values
+abstract class Val
+case object Empty extends Val
+case class Chr(c: Char) extends Val
+case class Sequ(v1: Val, v2: Val) extends Val
+case class Left(v: Val) extends Val
+case class Right(v: Val) extends Val
+case class Stars(vs: List[Val]) extends Val
+case class Rec(x: String, v: Val) extends Val
+
+
+// Convenience typing
+def charlist2rexp(s : List[Char]): Rexp = s match {
+  case Nil => ONE
+  case c::Nil => CHAR(c)
+  case c::s => SEQ(CHAR(c), charlist2rexp(s))
+}
+
+implicit def string2rexp(s : String) : Rexp = 
+  charlist2rexp(s.toList)
+
+implicit def RexpOps(r: Rexp) = new {
+  def | (s: Rexp) = ALT(r, s)
+  def % = STAR(r)
+  def ~ (s: Rexp) = SEQ(r, s)
+}
+
+implicit def stringOps(s: String) = new {
+  def | (r: Rexp) = ALT(s, r)
+  def | (r: String) = ALT(s, r)
+  def % = STAR(s)
+  def ~ (r: Rexp) = SEQ(s, r)
+  def ~ (r: String) = SEQ(s, r)
+  def $ (r: Rexp) = RECD(s, r)
+}
+
+// nullable
+def nullable(r: Rexp) : Boolean = r match {
+  case ZERO => false
+  case ONE => true
+  case CHAR(_) => false
+  case ALT(r1, r2) => nullable(r1) || nullable(r2)
+  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
+  case STAR(_) => true
+
+  case RECD(_, r1) => nullable(r1)
+  case RANGE(_) => false
+  case PLUS(r1) => nullable(r1)
+  case OPTIONAL(_) => true
+  case NTIMES(r1, i) => if (i == 0) true else nullable(r1)
+}
+
+// der
+def der(c: Char, r: Rexp) : Rexp = r match {
+  case ZERO => ZERO
+  case ONE => ZERO
+  case CHAR(d) => if (c == d) ONE else ZERO
+  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
+  case SEQ(r1, r2) => 
+    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
+    else SEQ(der(c, r1), r2)
+  case STAR(r) => SEQ(der(c, r), STAR(r))
+
+  case RECD(_, r1) => der(c, r1)
+  case RANGE(s) => if (s.contains(c)) ONE else ZERO 
+  case PLUS(r1) => SEQ(der(c, r1), STAR(r1))
+  case OPTIONAL(r1) => der(c, r1)
+  case NTIMES(r, i) => 
+    if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
+}
+
+// Flatten
+def flatten(v: Val) : String = v match {
+  case Empty => ""
+  case Chr(c) => c.toString
+  case Left(v) => flatten(v)
+  case Right(v) => flatten(v)
+  case Sequ(v1, v2) => flatten(v1) + flatten(v2)
+  case Stars(vs) => vs.map(flatten).mkString
+  case Rec(_, v) => flatten(v)
+}
+
+// Env
+def env(v: Val) : List[(String, String)] = v match {
+  case Empty => Nil
+  case Chr(c) => Nil
+  case Left(v) => env(v)
+  case Right(v) => env(v)
+  case Sequ(v1, v2) => env(v1) ::: env(v2)
+  case Stars(vs) => vs.flatMap(env)
+  case Rec(x, v) => (x, flatten(v))::env(v)
+}
+
+// Mkeps
+def mkeps(r: Rexp) : Val = r match {
+  case ONE => Empty
+  case ALT(r1, r2) => 
+    if (nullable(r1)) Left(mkeps(r1)) else Right(mkeps(r2))
+  case SEQ(r1, r2) => Sequ(mkeps(r1), mkeps(r2))
+  case STAR(r) => Stars(Nil)
+  case RECD(x, r) => Rec(x, mkeps(r))
+
+  case PLUS(r) => Stars(List(mkeps(r)))   // the first copy must match the empty string
+  case OPTIONAL(r) => if (nullable(r)) Stars(List(mkeps(r))) else Stars(Nil)
+  case NTIMES(r, i) => Stars(List.fill(i)(mkeps(r)))
+}
+
+// Inj
+def inj(r: Rexp, c: Char, v: Val) : Val = (r, v) match {
+  case (STAR(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+  case (SEQ(r1, r2), Sequ(v1, v2)) => Sequ(inj(r1, c, v1), v2)
+  case (SEQ(r1, r2), Left(Sequ(v1, v2))) => Sequ(inj(r1, c, v1), v2)
+  case (SEQ(r1, r2), Right(v2)) => Sequ(mkeps(r1), inj(r2, c, v2))
+  case (ALT(r1, r2), Left(v1)) => Left(inj(r1, c, v1))
+  case (ALT(r1, r2), Right(v2)) => Right(inj(r2, c, v2))
+  case (CHAR(d), Empty) => Chr(c) 
+  case (RECD(x, r1), _) => Rec(x, inj(r1, c, v))
+
+  case (RANGE(_), Empty) => Chr(c)
+  case (PLUS(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+  case (OPTIONAL(r), v1) => Stars(List(inj(r, c, v1)))
+  case (NTIMES(r, n), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+}
+
+// Rectification functions
+def F_ID(v: Val): Val = v
+def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))
+def F_LEFT(f: Val => Val) = (v:Val) => Left(f(v))
+def F_ALT(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
+  case Right(v) => Right(f2(v))
+  case Left(v) => Left(f1(v))
+}
+def F_SEQ(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
+  case Sequ(v1, v2) => Sequ(f1(v1), f2(v2))
+}
+def F_SEQ_Empty1(f1: Val => Val, f2: Val => Val) = 
+  (v:Val) => Sequ(f1(Empty), f2(v))
+def F_SEQ_Empty2(f1: Val => Val, f2: Val => Val) = 
+  (v:Val) => Sequ(f1(v), f2(Empty))
+def F_RECD(f: Val => Val) = (v:Val) => v match {
+  case Rec(x, v) => Rec(x, f(v))
+}
+def F_ERROR(v: Val): Val = throw new Exception("error")
+
+// Simp
+def simp(r: Rexp): (Rexp, Val => Val) = r match {
+  case ALT(r1, r2) => {
+    val (r1s, f1s) = simp(r1)
+    val (r2s, f2s) = simp(r2)
+    (r1s, r2s) match {
+      case (ZERO, _) => (r2s, F_RIGHT(f2s))
+      case (_, ZERO) => (r1s, F_LEFT(f1s))
+      case _ => if (r1s == r2s) (r1s, F_LEFT(f1s))
+                else (ALT (r1s, r2s), F_ALT(f1s, f2s)) 
+    }
+  }
+  case SEQ(r1, r2) => {
+    val (r1s, f1s) = simp(r1)
+    val (r2s, f2s) = simp(r2)
+    (r1s, r2s) match {
+      case (ZERO, _) => (ZERO, F_ERROR)
+      case (_, ZERO) => (ZERO, F_ERROR)
+      case (ONE, _) => (r2s, F_SEQ_Empty1(f1s, f2s))
+      case (_, ONE) => (r1s, F_SEQ_Empty2(f1s, f2s))
+      case _ => (SEQ(r1s,r2s), F_SEQ(f1s, f2s))
+    }
+  }
+  case r => (r, F_ID)
+}
+
+// Lex
+def lex_simp(r: Rexp, s: List[Char]) : Val = s match {
+  case Nil => if (nullable(r)) mkeps(r) else 
+    { throw new Exception("lexing error") } 
+  case c::cs => {
+    val (r_simp, f_simp) = simp(der(c, r))
+    inj(r, c, f_simp(lex_simp(r_simp, cs)))
+  }
+}
+
+def lexing_simp(r: Rexp, s: String) = env(lex_simp(r, s.toList))
+
+// Language specific code
+val KEYWORD : Rexp = "while" | "if" | "then" | "else" | "do" | "for" | "to" | "true" | "false" | "read" | "write" | "skip" 
+val OP : Rexp = "+" | "-" | "*" | "%" | "/" | "==" | "!=" | ">" | "<" | ">=" | "<=" | ":=" | "&&" | "||"
+val LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z'))
+val SYM : Rexp = LET | RANGE(Set('.', '_', '>', '<', '=', ';', ',', ':', ')', '('))
+val PARENS : Rexp = "(" | "{" | ")" | "}"
+val SEMI : Rexp = ";"
+val WHITESPACE : Rexp = PLUS(" ") | "\n" | "\t" | "\r"
+val DIGIT : Rexp = RANGE(('0' to '9').toSet)
+val DIGIT1 : Rexp = RANGE(('1' to '9').toSet)
+val STRING : Rexp = "\"" ~ (SYM | " " | "\\n" | DIGIT).% ~ "\""
+val ID : Rexp = LET ~ (LET | "_" | DIGIT).%
+val NUM : Rexp = "0" | (DIGIT1 ~ DIGIT.%)
+val COMMENT : Rexp = "//" ~ (SYM | " " | DIGIT).% ~ ("\n" | "\r\n") 
+
+val WHILE_REGS = (("k" $ KEYWORD) | 
+                  ("o" $ OP) | 
+                  ("str" $ STRING) |
+                  ("p" $ PARENS) |
+                  ("s" $ SEMI) | 
+                  ("w" $ WHITESPACE) | 
+                  ("i" $ ID) | 
+                  ("n" $ NUM) |
+		  ("c" $ COMMENT)).%
+
+def esc(raw: String): String = {
+  import scala.reflect.runtime.universe._
+  Literal(Constant(raw)).toString
+}
+
+def escape(tks: List[(String, String)]) =
+  tks.map{ case (s1, s2) => (s1, esc(s2))}
+
+// Token
+abstract class Token extends Serializable 
+case class T_KEYWORD(s: String) extends Token
+case class T_OP(s: String) extends Token
+case class T_STRING(s: String) extends Token
+case class T_PAREN(s: String) extends Token
+case object T_SEMI extends Token
+case class T_ID(s: String) extends Token
+case class T_NUM(n: Int) extends Token
+
+val token : PartialFunction[(String, String), Token] = {
+  case ("k", s) => T_KEYWORD(s)
+  case ("o", s) => T_OP(s)
+  case ("str", s) => T_STRING(s)
+  case ("p", s) => T_PAREN(s)
+  case ("s", _) => T_SEMI
+  case ("i", s) => T_ID(s)
+  case ("n", s) => T_NUM(s.toInt)
+}
+
+// Tokenise
+def tokenise(s: String) : List[Token] = 
+  lexing_simp(WHILE_REGS, s).collect(token)
+
+
+// Q2 Tests
+lex_simp(NTIMES("a", 3), "aaa".toList)
+lex_simp(NTIMES(("a" | ONE), 3), "aa".toList)
+
+// Q3 Programs
+
+val prog1 = """write "Fib";
+read n;
+minus1 := 0;
+minus2 := 1;
+while n > 0 do {
+temp := minus2;
+minus2 := minus1 + minus2;
+minus1 := temp;
+n := n - 1
+};
+write "Result";
+write minus2"""
+
+val prog2 = """start := 1000;
+x := start;
+y := start;
+z := start;
+while 0 < x do {
+while 0 < y do {
+while 0 < z do { z := z - 1 };
+z := start;
+y := y - 1
+};
+y := start;
+x := x - 1
+}"""
+
+val prog3 = """write "Input n please";
+read n;
+write "The factors of n are";
+f := 2;
+while n != 1 do {
+while (n / f) * f == n do {
+write f;
+n := n / f
+};
+f := f + 1
+}"""
+
+println(tokenise(prog1))
+println(tokenise(prog2))
+println(tokenise(prog3))
+
+
+println("MY TESTS")
+
+println(lex_simp("x" $ OPTIONAL("a"), "a".toList))
+println(lex_simp("x" $ OPTIONAL("a"), "".toList))
+println(lex_simp("x" $ NTIMES(OPTIONAL("a"),4), "aa".toList))
+println(lex_simp("x" $ OPTIONAL("aa"), "aa".toList))
+println(lex_simp("x" $ OPTIONAL("aa"), "".toList))
+
+
+
+
+//===================
+println("Fib")
+println(tokenise(os.read(os.pwd / "fib.while")))
+
+println("Factors")
+println(tokenise(os.read(os.pwd / "factors.while")))
+
+println("Loops")
+println(tokenise(os.read(os.pwd / "loops.while")))
+
+println("Collatz")
+println(tokenise(os.read(os.pwd / "collatz.while")))
+
+println("Collatz 2")
+println(tokenise(os.read(os.pwd / "collatz2.while")))
+
+println("Primes")
+println(tokenise(os.read(os.pwd / "primes.while")))
+
+
+// some testcases for simplification
+
+def size(r: Rexp): Int = r match {
+  case ZERO => 1
+  case ONE => 1
+  case CHAR(_) => 1
+  case ALT(r1, r2) => 1 + size(r1) + size (r2)
+  case SEQ(r1, r2) => 1 + size(r1) + size (r2)
+  case STAR(r1) => 1 + size(r1)
+  case NTIMES(r1, n) => 1 + size(r1)
+}
+
+val reg = SEQ(STAR(STAR(CHAR('a'))), CHAR('b'))
+size(reg) // 5
+size(der('a', reg)) // 12
+size(simp(der('a', reg))._1) // 8
+size(simp(der('a', der('a', reg)))._1) // 8
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw2/loops.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,14 @@
+start := 1000; 
+x := start;
+y := start;
+z := start;
+while 0 < x do {
+ while 0 < y do {
+  while 0 < z do { z := z - 1 };
+  z := start;
+  y := y - 1
+ };     
+ y := start;
+ x := x - 1
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw2/primes.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,15 @@
+// prints out prime numbers from
+// 2 to 100 (end)
+
+end := 100;
+n := 2;
+while (n < end) do {
+  f := 2;
+  tmp := 0;
+  while ((f < n / 2 + 1) && (tmp == 0)) do {
+    if ((n / f) * f == n) then  { tmp := 1 } else { skip };
+    f := f + 1
+  };
+  if (tmp == 0) then { write(n); write("\n") } else { skip };
+  n  := n + 1
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/collatz.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,8 @@
+write "Input a number ";
+read n;
+while n > 1 do {
+  if n % 2 == 0 
+  then n := n/2 
+  else n := 3*n+1;
+};
+write "Yes\n";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/collatz2.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,27 @@
+// Collatz series
+//
+// needs writing of strings and numbers; comments
+
+bnd := 1;
+while bnd < 101 do {
+  write bnd;
+  write ": ";
+  n := bnd;
+  cnt := 0;
+
+  while n > 1 do {
+    write n;
+    write ",";
+    
+    if n % 2 == 0 
+    then n := n / 2 
+    else n := 3 * n+1;
+
+    cnt := cnt + 1
+  };
+
+  write " => ";
+  write cnt;
+  write "\n";
+  bnd := bnd + 1
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/factors.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,14 @@
+// Find all factors of a given input number
+// by J.R. Cordy August 2005
+
+write "Input n please";
+read n;
+write "The factors of n are:\n";
+f := 2;
+while n != 1 do {
+    while (n / f) * f == n do {
+        write f; write "\n";
+        n := n / f
+    };
+    f := f + 1
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/fib.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,13 @@
+write "Fib: ";
+read n;  
+minus1 := 0;
+minus2 := 1;
+while n > 0 do {
+       temp := minus2;
+       minus2 := minus1 + minus2;
+       minus1 := temp;
+       n := n - 1
+};
+write "Result: ";
+write minus2 
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/lexer.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,266 @@
+import scala.language.implicitConversions    
+import scala.language.reflectiveCalls
+
+// Rexp
+abstract class Rexp
+case object ZERO extends Rexp
+case object ONE extends Rexp
+case class CHAR(c: Char) extends Rexp
+case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
+case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
+case class STAR(r: Rexp) extends Rexp 
+case class RECD(x: String, r: Rexp) extends Rexp
+
+case class RANGE(s: Set[Char]) extends Rexp
+case class PLUS(r: Rexp) extends Rexp
+case class OPTIONAL(r: Rexp) extends Rexp
+case class NTIMES(r: Rexp, n: Int) extends Rexp 
+
+// Values
+abstract class Val
+case object Empty extends Val
+case class Chr(c: Char) extends Val
+case class Sequ(v1: Val, v2: Val) extends Val
+case class Left(v: Val) extends Val
+case class Right(v: Val) extends Val
+case class Stars(vs: List[Val]) extends Val
+case class Rec(x: String, v: Val) extends Val
+
+
+// Convenience typing
+def charlist2rexp(s : List[Char]): Rexp = s match {
+  case Nil => ONE
+  case c::Nil => CHAR(c)
+  case c::s => SEQ(CHAR(c), charlist2rexp(s))
+}
+
+implicit def string2rexp(s : String) : Rexp = 
+  charlist2rexp(s.toList)
+
+implicit def RexpOps(r: Rexp) = new {
+  def | (s: Rexp) = ALT(r, s)
+  def % = STAR(r)
+  def ~ (s: Rexp) = SEQ(r, s)
+}
+
+implicit def stringOps(s: String) = new {
+  def | (r: Rexp) = ALT(s, r)
+  def | (r: String) = ALT(s, r)
+  def % = STAR(s)
+  def ~ (r: Rexp) = SEQ(s, r)
+  def ~ (r: String) = SEQ(s, r)
+  def $ (r: Rexp) = RECD(s, r)
+}
+
+// nullable
+def nullable(r: Rexp) : Boolean = r match {
+  case ZERO => false
+  case ONE => true
+  case CHAR(_) => false
+  case ALT(r1, r2) => nullable(r1) || nullable(r2)
+  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
+  case STAR(_) => true
+
+  case RECD(_, r1) => nullable(r1)
+  case RANGE(_) => false
+  case PLUS(r1) => nullable(r1)
+  case OPTIONAL(_) => true
+  case NTIMES(r1, i) => if (i == 0) true else nullable(r1)
+}
+
+// der
+def der(c: Char, r: Rexp) : Rexp = r match {
+  case ZERO => ZERO
+  case ONE => ZERO
+  case CHAR(d) => if (c == d) ONE else ZERO
+  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
+  case SEQ(r1, r2) => 
+    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
+    else SEQ(der(c, r1), r2)
+  case STAR(r) => SEQ(der(c, r), STAR(r))
+
+  case RECD(_, r1) => der(c, r1)
+  case RANGE(s) => if (s.contains(c)) ONE else ZERO 
+  case PLUS(r1) => SEQ(der(c, r1), STAR(r1))
+  case OPTIONAL(r1) => ALT(der(c, r1), ZERO)
+  case NTIMES(r, i) => 
+    if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
+}
+
+// Flatten
+def flatten(v: Val) : String = v match {
+  case Empty => ""
+  case Chr(c) => c.toString
+  case Left(v) => flatten(v)
+  case Right(v) => flatten(v)
+  case Sequ(v1, v2) => flatten(v1) + flatten(v2)
+  case Stars(vs) => vs.map(flatten).mkString
+  case Rec(_, v) => flatten(v)
+}
+
+// Env
+def env(v: Val) : List[(String, String)] = v match {
+  case Empty => Nil
+  case Chr(c) => Nil
+  case Left(v) => env(v)
+  case Right(v) => env(v)
+  case Sequ(v1, v2) => env(v1) ::: env(v2)
+  case Stars(vs) => vs.flatMap(env)
+  case Rec(x, v) => (x, flatten(v))::env(v)
+}
+
+// Mkeps
+def mkeps(r: Rexp) : Val = r match {
+  case ONE => Empty
+  case ALT(r1, r2) => 
+    if (nullable(r1)) Left(mkeps(r1)) else Right(mkeps(r2))
+  case SEQ(r1, r2) => Sequ(mkeps(r1), mkeps(r2))
+  case STAR(r) => Stars(Nil)
+  case RECD(x, r) => Rec(x, mkeps(r))
+
+  case PLUS(r) => Stars(List(mkeps(r)))   // the first copy must match the empty string
+  case OPTIONAL(r) => Right(Empty)
+  case NTIMES(r, i) => Stars(List.fill(i)(mkeps(r)))
+}
+
+// Inj
+def inj(r: Rexp, c: Char, v: Val) : Val = (r, v) match {
+  case (STAR(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+  case (SEQ(r1, r2), Sequ(v1, v2)) => Sequ(inj(r1, c, v1), v2)
+  case (SEQ(r1, r2), Left(Sequ(v1, v2))) => Sequ(inj(r1, c, v1), v2)
+  case (SEQ(r1, r2), Right(v2)) => Sequ(mkeps(r1), inj(r2, c, v2))
+  case (ALT(r1, r2), Left(v1)) => Left(inj(r1, c, v1))
+  case (ALT(r1, r2), Right(v2)) => Right(inj(r2, c, v2))
+  case (CHAR(d), Empty) => Chr(c) 
+  case (RECD(x, r1), _) => Rec(x, inj(r1, c, v))
+
+  case (RANGE(_), Empty) => Chr(c)
+  case (PLUS(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+  case (OPTIONAL(r), Left(v1)) => Left(inj(r, c, v1))
+  case (NTIMES(r, n), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+}
+
+// Rectification functions
+def F_ID(v: Val): Val = v
+def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))
+def F_LEFT(f: Val => Val) = (v:Val) => Left(f(v))
+def F_ALT(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
+  case Right(v) => Right(f2(v))
+  case Left(v) => Left(f1(v))
+}
+def F_SEQ(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
+  case Sequ(v1, v2) => Sequ(f1(v1), f2(v2))
+}
+def F_SEQ_Empty1(f1: Val => Val, f2: Val => Val) = 
+  (v:Val) => Sequ(f1(Empty), f2(v))
+def F_SEQ_Empty2(f1: Val => Val, f2: Val => Val) = 
+  (v:Val) => Sequ(f1(v), f2(Empty))
+def F_RECD(f: Val => Val) = (v:Val) => v match {
+  case Rec(x, v) => Rec(x, f(v))
+}
+def F_ERROR(v: Val): Val = throw new Exception("error")
+
+// Simp
+def simp(r: Rexp): (Rexp, Val => Val) = r match {
+  case ALT(r1, r2) => {
+    val (r1s, f1s) = simp(r1)
+    val (r2s, f2s) = simp(r2)
+    (r1s, r2s) match {
+      case (ZERO, _) => (r2s, F_RIGHT(f2s))
+      case (_, ZERO) => (r1s, F_LEFT(f1s))
+      case _ => if (r1s == r2s) (r1s, F_LEFT(f1s))
+                else (ALT (r1s, r2s), F_ALT(f1s, f2s)) 
+    }
+  }
+  case SEQ(r1, r2) => {
+    val (r1s, f1s) = simp(r1)
+    val (r2s, f2s) = simp(r2)
+    (r1s, r2s) match {
+      case (ZERO, _) => (ZERO, F_ERROR)
+      case (_, ZERO) => (ZERO, F_ERROR)
+      case (ONE, _) => (r2s, F_SEQ_Empty1(f1s, f2s))
+      case (_, ONE) => (r1s, F_SEQ_Empty2(f1s, f2s))
+      case _ => (SEQ(r1s,r2s), F_SEQ(f1s, f2s))
+    }
+  }
+  case r => (r, F_ID)
+}
+
+// Lex
+def lex_simp(r: Rexp, s: List[Char]) : Val = s match {
+  case Nil => if (nullable(r)) mkeps(r) else 
+    { throw new Exception("lexing error") } 
+  case c::cs => {
+    val (r_simp, f_simp) = simp(der(c, r))
+    inj(r, c, f_simp(lex_simp(r_simp, cs)))
+  }
+}
+
+def lexing_simp(r: Rexp, s: String) = env(lex_simp(r, s.toList))
+
+// Language specific code
+val KEYWORD : Rexp = "while" | "if" | "then" | "else" | "do" | "for" | "to" | "true" | "false" | "read" | "write" | "skip" 
+val OP : Rexp = "+" | "-" | "*" | "%" | "/" | "==" | "!=" | ">" | "<" | ">=" | "<=" | ":=" | "&&" | "||"
+val LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z'))
+val SYM : Rexp = (LET | RANGE(Set('.', '_', '>', '<', '=', ';', ',', ':')))
+val PARENS : Rexp = "(" | "{" | ")" | "}"
+val SEMI : Rexp = ";"
+val WHITESPACE : Rexp = PLUS(" ") | "\n" | "\t" | "\r"
+val DIGIT : Rexp = RANGE(('0' to '9').toSet)
+val DIGIT1 : Rexp = RANGE(('1' to '9').toSet)
+val STRING : Rexp = "\"" ~ (SYM | " " | "\\n" | DIGIT).% ~ "\""
+val ID : Rexp = LET ~ (LET | "_" | DIGIT).%
+val NUM : Rexp = "0" | (DIGIT1 ~ DIGIT.%)
+val EOL : Rexp = "\n" | "\r\n"
+val COMMENT : Rexp = "//" ~ (SYM | PARENS | " " | DIGIT).% ~ EOL 
+
+val WHILE_REGS = (("k" $ KEYWORD) | 
+                  ("o" $ OP) | 
+                  ("str" $ STRING) |
+                  ("p" $ PARENS) |
+                  ("s" $ SEMI) | 
+                  ("w" $ WHITESPACE) | 
+                  ("i" $ ID) | 
+                  ("n" $ NUM) |
+		  ("c" $ COMMENT)).%
+
+// Token
+abstract class Token extends Serializable 
+case class T_KEYWORD(s: String) extends Token
+case class T_OP(s: String) extends Token
+case class T_STRING(s: String) extends Token
+case class T_PAREN(s: String) extends Token
+case object T_SEMI extends Token
+case class T_ID(s: String) extends Token
+case class T_NUM(n: Int) extends Token
+
+val token : PartialFunction[(String, String), Token] = {
+  case ("k", s) => T_KEYWORD(s)
+  case ("o", s) => T_OP(s)
+  case ("str", s) => T_STRING(s)
+  case ("p", s) => T_PAREN(s)
+  case ("s", _) => T_SEMI
+  case ("i", s) => T_ID(s)
+  case ("n", s) => T_NUM(s.toInt)
+}
+
+// Tokenise
+def tokenise(s: String) : List[Token] = 
+  lexing_simp(WHILE_REGS, s).collect(token)
+
+
+val fact = """
+write "Input n please";
+read n;
+write "The factors of n are";
+f := 2;
+while n != 1 do {
+    while (n / f) * f == n do {
+        write f;
+        n := n / f
+    };
+    f := f + 1
+}
+"""
+println(tokenise(fact))
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/loops.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,14 @@
+start := 1000; 
+x := start;
+y := start;
+z := start;
+while 0 < x do {
+ while 0 < y do {
+  while 0 < z do { z := z - 1 };
+  z := start;
+  y := y - 1
+ };     
+ y := start;
+ x := x - 1
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/parser.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,246 @@
+// CW3
+
+import $file.lexer
+import lexer._ 
+
+
+case class ~[+A, +B](_1: A, _2: B)
+type IsSeq[A] = A => Seq[_]
+
+abstract class Parser[I : IsSeq, T] {
+  def parse(ts: I): Set[(T, I)]
+
+  def parse_all(ts: I) : Set[T] =
+    for ((head, tail) <- parse(ts); if tail.isEmpty) yield head
+}
+
+class SeqParser[I : IsSeq, T, S](p: => Parser[I, T], q: => Parser[I, S]) extends Parser[I, ~[T, S]] {
+  def parse(sb: I) = 
+    for ((head1, tail1) <- p.parse(sb); 
+         (head2, tail2) <- q.parse(tail1)) yield (new ~(head1, head2), tail2)
+}
+
+class AltParser[I : IsSeq, T](p: => Parser[I, T], q: => Parser[I, T]) extends Parser[I, T] {
+  def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
+}
+
+class FunParser[I : IsSeq, T, S](p: => Parser[I, T], f: T => S) extends Parser[I, S] {
+  def parse(sb: I) = 
+    for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
+}
+
+// New parser that takes as input a list of tokens
+case class TokenListParser(ts: List[Token]) extends Parser[List[Token], List[Token]] {
+    def parse(tsb: List[Token]) = {
+        val (prefix, suffix) = tsb.splitAt(ts.length)
+        if (prefix == ts) Set((prefix, suffix)) else Set()
+    }
+}
+
+// Implicit definitions to go from a token 
+// or a list of tokens to a TokenListParser
+implicit def token2parser(t: Token) = TokenListParser(List(t))
+implicit def tokenList2parser(ts: List[Token]) = TokenListParser(ts)
+
+implicit def ParserOps[I : IsSeq, T](p: Parser[I, T]) = new {
+  def || (q : => Parser[I, T]) = new AltParser[I, T](p, q)
+  def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
+  def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
+}
+
+implicit def TokenOps(t: Token) = new {
+    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](List(t), q)
+    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](List(t), qs)
+    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](List(t), f)
+    def ~[S](q : => Parser[List[Token], S]) =
+        new SeqParser[List[Token], List[Token], S](List(t), q)
+    def ~ (qs : List[Token]) =
+        new SeqParser[List[Token], List[Token], List[Token]](List(t), qs)
+}
+
+implicit def TokenListOps(ts: List[Token]) = new {
+    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](ts, q)
+    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](ts, qs)
+    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](ts, f)
+    def ~[S](q : => Parser[List[Token], S]) =
+        new SeqParser[List[Token], List[Token], S](ts, q)
+    def ~ (qs : List[Token]) =
+        new SeqParser[List[Token], List[Token], List[Token]](ts, qs)
+}
+
+// Abstract Syntax Trees
+abstract class Stmt
+abstract class AExp
+abstract class BExp
+
+type Block = List[Stmt]
+
+case object Skip extends Stmt
+case class If(a: BExp, bl1: Block, bl2: Block) extends Stmt
+case class While(b: BExp, bl: Block) extends Stmt
+case class Assign(s: String, a: AExp) extends Stmt
+case class Read(s: String) extends Stmt
+case class WriteId(s: String) extends Stmt  // for printing values of variables
+case class WriteString(s: String) extends Stmt  // for printing words
+
+case class Var(s: String) extends AExp
+case class Num(i: Int) extends AExp
+case class Aop(o: String, a1: AExp, a2: AExp) extends AExp
+
+case object True extends BExp
+case object False extends BExp
+case class Bop(o: String, a1: AExp, a2: AExp) extends BExp
+case class And(b1: BExp, b2: BExp) extends BExp
+case class Or(b1: BExp, b2: BExp) extends BExp
+
+case class IdParser() extends Parser[List[Token], String] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_ID(id) :: rest => Set((id, rest))
+        case _ => Set()
+    }
+}
+
+case class NumParser() extends Parser[List[Token], Int] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_NUM(n) :: rest => Set((n, rest))
+        case _ => Set()
+    }
+}
+
+case class StringParser() extends Parser[List[Token], String] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_STRING(s) :: rest => Set((s, rest))
+        case _ => Set()
+    }
+}
+
+// WHILE Language Parsing
+lazy val AExp: Parser[List[Token], AExp] = 
+  (Te ~ T_OP("+") ~ AExp) ==> { case x ~ _ ~ z => Aop("+", x, z): AExp } ||
+  (Te ~ T_OP("-") ~ AExp) ==> { case x ~ _ ~ z => Aop("-", x, z): AExp } || Te
+lazy val Te: Parser[List[Token], AExp] = 
+  (Fa ~ T_OP("*") ~ Te) ==> { case x ~ _ ~ z => Aop("*", x, z): AExp } || 
+  (Fa ~ T_OP("/") ~ Te) ==> { case x ~ _ ~ z => Aop("/", x, z): AExp } || 
+  (Fa ~ T_OP("%") ~ Te) ==> { case x ~ _ ~ z => Aop("%", x, z): AExp } || Fa  
+lazy val Fa: Parser[List[Token], AExp] = 
+   (T_PAREN("(") ~ AExp ~ T_PAREN(")")) ==> { case _ ~ y ~ _ => y } || 
+   IdParser() ==> Var  || 
+   NumParser() ==> Num
+
+lazy val BExp: Parser[List[Token], BExp] = 
+   (AExp ~ T_OP("==") ~ AExp) ==> { case x ~ _ ~ z => Bop("==", x, z): BExp } || 
+   (AExp ~ T_OP("!=") ~ AExp) ==> { case x ~ _ ~ z => Bop("!=", x, z): BExp } || 
+   (AExp ~ T_OP("<") ~ AExp) ==> { case x ~ _ ~ z => Bop("<", x, z): BExp } || 
+   (AExp ~ T_OP(">") ~ AExp) ==> { case x ~ _ ~ z => Bop(">", x, z): BExp } ||
+   (T_PAREN("(") ~ BExp ~ T_PAREN(")") ~ T_OP("&&") ~ BExp) ==> { case _ ~ y ~ _ ~ _ ~ v => And(y, v): BExp } ||
+   (T_PAREN("(") ~ BExp ~ T_PAREN(")") ~ T_OP("||") ~ BExp) ==> { case _ ~ y ~ _ ~ _ ~ v => Or(y, v): BExp } ||
+   (T_KEYWORD("true") ==> (_ => True: BExp )) || 
+   (T_KEYWORD("false") ==> (_ => False: BExp )) ||
+   (T_PAREN("(") ~ BExp ~ T_PAREN(")")) ==> { case _ ~ x ~ _ => x }
+
+lazy val Stmt: Parser[List[Token], Stmt] =
+    T_KEYWORD("skip") ==> (_ => Skip: Stmt) ||
+    (IdParser() ~ T_OP(":=") ~ AExp) ==> { case id ~ _ ~ z => Assign(id, z): Stmt } ||
+    (T_KEYWORD("if") ~ BExp ~ T_KEYWORD("then") ~ Block ~ T_KEYWORD("else") ~ Block) ==> { case _ ~ y ~ _ ~ u ~ _ ~ w => If(y, u, w): Stmt } ||
+    (T_KEYWORD("while") ~ BExp ~ T_KEYWORD("do") ~ Block) ==> { case _ ~ y ~ _ ~ w => While(y, w) : Stmt } ||
+    (T_KEYWORD("read") ~ IdParser()) ==> { case _ ~ id => Read(id): Stmt} ||
+    (T_KEYWORD("write") ~ IdParser()) ==> { case _ ~ id => WriteId(id): Stmt} ||
+    (T_KEYWORD("write") ~ StringParser()) ==> { case _ ~ s => WriteString(s): Stmt} || 
+    (T_KEYWORD("write") ~ T_PAREN("(") ~ IdParser() ~ T_PAREN(")")) ==> { case _ ~ _ ~ id ~ _ => WriteId(id): Stmt} ||
+    (T_KEYWORD("write") ~  T_PAREN("(") ~ StringParser() ~ T_PAREN(")")) ==> { case _ ~ _ ~ s ~ _ => WriteString(s): Stmt}
+
+lazy val Stmts: Parser[List[Token], Block] =
+    (Stmt ~ T_SEMI ~ Stmts) ==> { case x ~ _ ~ z => x :: z : Block } ||
+    (Stmt ==> (s => List(s) : Block))
+
+lazy val Block: Parser[List[Token], Block] =
+    (T_PAREN("{") ~ Stmts ~ T_PAREN("}")) ==> { case x ~ y ~ z => y} ||
+    (Stmt ==> (s => List(s)))
+
+// Testing with programs 2 & 3
+
+println("Fibonacci")
+println(Stmts.parse_all(tokenise(os.read(os.pwd / "fib.while"))))
+
+println("Loops")
+println(Stmts.parse_all(tokenise(os.read(os.pwd / "loops.while"))))
+
+println("Collatz")
+println(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))))
+
+
+// Interpreter
+
+// Environment to store values of variables
+type Env = Map[String, Int]
+
+def eval_aexp(a: AExp, env: Env) : Int = a match {
+  case Num(i) => i
+  case Var(s) => env(s)
+  case Aop("+", a1, a2) => eval_aexp(a1, env) + eval_aexp(a2, env)
+  case Aop("-", a1, a2) => eval_aexp(a1, env) - eval_aexp(a2, env)
+  case Aop("*", a1, a2) => eval_aexp(a1, env) * eval_aexp(a2, env)
+  case Aop("/", a1, a2) => eval_aexp(a1, env) / eval_aexp(a2, env)
+  case Aop("%", a1, a2) => eval_aexp(a1, env) % eval_aexp(a2, env)
+}
+
+def eval_bexp(b: BExp, env: Env) : Boolean = b match {
+  case True => true
+  case False => false
+  case Bop("==", a1, a2) => eval_aexp(a1, env) == eval_aexp(a2, env)
+  case Bop("!=", a1, a2) => !(eval_aexp(a1, env) == eval_aexp(a2, env))
+  case Bop(">", a1, a2) => eval_aexp(a1, env) > eval_aexp(a2, env)
+  case Bop("<", a1, a2) => eval_aexp(a1, env) < eval_aexp(a2, env)
+  case And(b1, b2) => eval_bexp(b1, env) && eval_bexp(b2, env)
+  case Or(b1, b2) => eval_bexp(b1, env) || eval_bexp(b2, env)
+}
+
+// Import needed to take int as input from the user
+import scala.io.StdIn.readInt
+
+def eval_stmt(s: Stmt, env: Env) : Env = s match {
+  case Skip => env
+  case Assign(x, a) => env + (x -> eval_aexp(a, env))
+  case If(b, bl1, bl2) => if (eval_bexp(b, env)) eval_bl(bl1, env) else eval_bl(bl2, env) 
+  case While(b, bl) => 
+    if (eval_bexp(b, env)) eval_stmt(While(b, bl), eval_bl(bl, env))
+    else env
+
+  case WriteId(x) => { print(env(x)) ; env }
+  case WriteString(x) => {
+        print(x.replaceAll("\"", "").replaceAll("""\\n""", "\n")) ;
+        env
+       }
+
+  case Read(x) => { 
+      println("Enter an integer and press ENTER:") ; 
+      val n = readInt() ; // Note: Does not work when using the REPL
+      eval_stmt(Assign(x, Num(n)), env) 
+  }
+}
+
+def eval_bl(bl: Block, env: Env) : Env = bl match {
+  case Nil => env
+  case s::bl => eval_bl(bl, eval_stmt(s, env))
+}
+
+def eval(bl: Block) : Env = eval_bl(bl, Map())
+
+println("Primes eval")
+println(tokenise(os.read(os.pwd / "primes.while")))
+println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "primes.while"))).head))
+
+println("Factors eval")
+println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "factors.while"))).head))
+
+println("Collatz2 eval")
+println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head))
+
+/*
+println("Loops eval")
+val start = System.nanoTime()
+println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "loops.while"))).head))
+val end = System.nanoTime()
+println("Time taken in seconds: ")
+println((end - start)/(1.0e9))
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/parser2.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,255 @@
+// CW3
+
+import $file.lexer
+import lexer._ 
+
+
+case class ~[+A, +B](_1: A, _2: B)
+type IsSeq[A] = A => Seq[_]
+
+abstract class Parser[I : IsSeq, T] {
+  def parse(ts: I): Set[(T, I)]
+
+  def parse_all(ts: I) : Set[T] =
+    for ((head, tail) <- parse(ts); if tail.isEmpty) yield head
+}
+
+class SeqParser[I : IsSeq, T, S](p: => Parser[I, T], q: => Parser[I, S]) extends Parser[I, ~[T, S]] {
+  def parse(sb: I) = 
+    for ((head1, tail1) <- p.parse(sb); 
+         (head2, tail2) <- q.parse(tail1)) yield (new ~(head1, head2), tail2)
+}
+
+class AltParser[I : IsSeq, T](p: => Parser[I, T], q: => Parser[I, T]) extends Parser[I, T] {
+  def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
+}
+
+class FunParser[I : IsSeq, T, S](p: => Parser[I, T], f: T => S) extends Parser[I, S] {
+  def parse(sb: I) = 
+    for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
+}
+
+// New parser that takes as input a list of tokens
+case class TokenListParser(ts: List[Token]) extends Parser[List[Token], List[Token]] {
+    def parse(tsb: List[Token]) = {
+        val (prefix, suffix) = tsb.splitAt(ts.length)
+        if (prefix == ts) Set((prefix, suffix)) else Set()
+    }
+}
+
+// Implicit definitions to go from a token 
+// or a list of tokens to a TokenListParser
+implicit def token2parser(t: Token) = TokenListParser(List(t))
+implicit def tokenList2parser(ts: List[Token]) = TokenListParser(ts)
+
+implicit def ParserOps[I : IsSeq, T](p: Parser[I, T]) = new {
+  def || (q : => Parser[I, T]) = new AltParser[I, T](p, q)
+  def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
+  def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
+}
+
+implicit def TokenOps(t: Token) = new {
+    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](List(t), q)
+    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](List(t), qs)
+    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](List(t), f)
+    def ~[S](q : => Parser[List[Token], S]) =
+        new SeqParser[List[Token], List[Token], S](List(t), q)
+    def ~ (qs : List[Token]) =
+        new SeqParser[List[Token], List[Token], List[Token]](List(t), qs)
+}
+
+implicit def TokenListOps(ts: List[Token]) = new {
+    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](ts, q)
+    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](ts, qs)
+    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](ts, f)
+    def ~[S](q : => Parser[List[Token], S]) =
+        new SeqParser[List[Token], List[Token], S](ts, q)
+    def ~ (qs : List[Token]) =
+        new SeqParser[List[Token], List[Token], List[Token]](ts, qs)
+}
+
+// Abstract Syntax Trees
+abstract class Stmt
+abstract class AExp
+abstract class BExp
+
+type Block = List[Stmt]
+
+case object Skip extends Stmt
+case class If(a: BExp, bl1: Block, bl2: Block) extends Stmt
+case class While(b: BExp, bl: Block) extends Stmt
+case class Assign(s: String, a: AExp) extends Stmt
+case class Read(s: String) extends Stmt
+case class WriteId(s: String) extends Stmt  // for printing values of variables
+case class WriteString(s: String) extends Stmt  // for printing words
+
+case class Var(s: String) extends AExp
+case class Num(i: Int) extends AExp
+case class Aop(o: String, a1: AExp, a2: AExp) extends AExp
+
+case object True extends BExp
+case object False extends BExp
+case class Bop(o: String, a1: AExp, a2: AExp) extends BExp
+case class And(b1: BExp, b2: BExp) extends BExp
+case class Or(b1: BExp, b2: BExp) extends BExp
+
+case class IdParser() extends Parser[List[Token], String] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_ID(id) :: rest => Set((id, rest))
+        case _ => Set()
+    }
+}
+
+case class NumParser() extends Parser[List[Token], Int] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_NUM(n) :: rest => Set((n, rest))
+        case _ => Set()
+    }
+}
+
+case class StringParser() extends Parser[List[Token], String] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_STRING(s) :: rest => Set((s, rest))
+        case _ => Set()
+    }
+}
+
+case class TokParser(s: String) extends Parser[List[Token], String] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_OP(o) :: rest if s == o => Set((o, rest))
+        case T_KWD(k) :: rest if s == k => Set((k, rest))
+        case _ => Set()
+    }
+}
+
+implicit def parser_interpolation(sc: StringContext) = new {
+    def p(args: Any*) = TokParser(sc.s(args:_*))
+}    
+
+
+// WHILE Language Parsing
+lazy val AExp: Parser[List[Token], AExp] = 
+  (Te ~ T_OP("+") ~ AExp) ==> { case x ~ _ ~ z => Aop("+", x, z): AExp } ||
+  (Te ~ T_OP("-") ~ AExp) ==> { case x ~ _ ~ z => Aop("-", x, z): AExp } || Te
+lazy val Te: Parser[List[Token], AExp] = 
+  (Fa ~ T_OP("*") ~ Te) ==> { case x ~ _ ~ z => Aop("*", x, z): AExp } || 
+  (Fa ~ T_OP("/") ~ Te) ==> { case x ~ _ ~ z => Aop("/", x, z): AExp } || 
+  (Fa ~ T_OP("%") ~ Te) ==> { case x ~ _ ~ z => Aop("%", x, z): AExp } || Fa  
+lazy val Fa: Parser[List[Token], AExp] = 
+   (T_PAREN("(") ~ AExp ~ T_PAREN(")")) ==> { case _ ~ y ~ _ => y } || 
+   IdParser() ==> Var  || 
+   NumParser() ==> Num
+
+lazy val BExp: Parser[List[Token], BExp] = 
+   (AExp ~ T_OP("==") ~ AExp) ==> { case x ~ _ ~ z => Bop("==", x, z): BExp } || 
+   (AExp ~ T_OP("!=") ~ AExp) ==> { case x ~ _ ~ z => Bop("!=", x, z): BExp } || 
+   (AExp ~ T_OP("<") ~ AExp) ==> { case x ~ _ ~ z => Bop("<", x, z): BExp } || 
+   (AExp ~ T_OP(">") ~ AExp) ==> { case x ~ _ ~ z => Bop(">", x, z): BExp } ||
+   (T_PAREN("(") ~ BExp ~ List(T_PAREN(")"), T_OP("&&")) ~ BExp) ==> { case _ ~ y ~ _ ~ v => And(y, v): BExp } ||
+   (T_PAREN("(") ~ BExp ~ List(T_PAREN(")"), T_OP("||")) ~ BExp) ==> { case _ ~ y ~ _ ~ v => Or(y, v): BExp } ||
+   (T_KEYWORD("true") ==> (_ => True: BExp )) || 
+   (T_KEYWORD("false") ==> (_ => False: BExp )) ||
+   (T_PAREN("(") ~ BExp ~ T_PAREN(")")) ==> { case _ ~ x ~ _ => x }
+
+lazy val Stmt: Parser[List[Token], Stmt] =
+    T_KEYWORD("skip") ==> (_ => Skip: Stmt) ||
+    (IdParser() ~ T_OP(":=") ~ AExp) ==> { case id ~ _ ~ z => Assign(id, z): Stmt } ||
+    (T_KEYWORD("if") ~ BExp ~ T_KEYWORD("then") ~ Block ~ T_KEYWORD("else") ~ Block) ==> { case _ ~ y ~ _ ~ u ~ _ ~ w => If(y, u, w): Stmt } ||
+    (T_KEYWORD("while") ~ BExp ~ T_KEYWORD("do") ~ Block) ==> { case _ ~ y ~ _ ~ w => While(y, w) : Stmt } ||
+    (T_KEYWORD("read") ~ IdParser()) ==> { case _ ~ id => Read(id): Stmt} ||
+    (T_KEYWORD("write") ~ IdParser()) ==> { case _ ~ id => WriteId(id): Stmt} ||
+    (T_KEYWORD("write") ~ StringParser()) ==> { case _ ~ s => WriteString(s): Stmt}
+
+lazy val Stmts: Parser[List[Token], Block] =
+    (Stmt ~ T_SEMI ~ Stmts) ==> { case x ~ _ ~ z => x :: z : Block } ||
+    (Stmt ==> (s => List(s) : Block))
+
+lazy val Block: Parser[List[Token], Block] =
+    (T_PAREN("{") ~ Stmts ~ T_PAREN("}")) ==> { case x ~ y ~ z => y} ||
+    (Stmt ==> (s => List(s)))
+
+// Testing with programs 2 & 3
+
+println("Fibonacci")
+println(Stmts.parse_all(tokenise(os.read(os.pwd / "fib.while"))))
+
+println("Loops")
+println(Stmts.parse_all(tokenise(os.read(os.pwd / "loops.while"))))
+
+println("Collatz")
+println(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))))
+
+
+// Interpreter
+
+// Environment to store values of variables
+type Env = Map[String, Int]
+
+def eval_aexp(a: AExp, env: Env) : Int = a match {
+  case Num(i) => i
+  case Var(s) => env(s)
+  case Aop("+", a1, a2) => eval_aexp(a1, env) + eval_aexp(a2, env)
+  case Aop("-", a1, a2) => eval_aexp(a1, env) - eval_aexp(a2, env)
+  case Aop("*", a1, a2) => eval_aexp(a1, env) * eval_aexp(a2, env)
+  case Aop("/", a1, a2) => eval_aexp(a1, env) / eval_aexp(a2, env)
+  case Aop("%", a1, a2) => eval_aexp(a1, env) % eval_aexp(a2, env)
+}
+
+def eval_bexp(b: BExp, env: Env) : Boolean = b match {
+  case True => true
+  case False => false
+  case Bop("==", a1, a2) => eval_aexp(a1, env) == eval_aexp(a2, env)
+  case Bop("!=", a1, a2) => !(eval_aexp(a1, env) == eval_aexp(a2, env))
+  case Bop(">", a1, a2) => eval_aexp(a1, env) > eval_aexp(a2, env)
+  case Bop("<", a1, a2) => eval_aexp(a1, env) < eval_aexp(a2, env)
+  case And(b1, b2) => eval_bexp(b1, env) && eval_bexp(b2, env)
+  case Or(b1, b2) => eval_bexp(b1, env) || eval_bexp(b2, env)
+}
+
+// Import needed to take int as input from the user
+import scala.io.StdIn.readInt
+
+def eval_stmt(s: Stmt, env: Env) : Env = s match {
+  case Skip => env
+  case Assign(x, a) => env + (x -> eval_aexp(a, env))
+  case If(b, bl1, bl2) => if (eval_bexp(b, env)) eval_bl(bl1, env) else eval_bl(bl2, env) 
+  case While(b, bl) => 
+    if (eval_bexp(b, env)) eval_stmt(While(b, bl), eval_bl(bl, env))
+    else env
+
+  case WriteId(x) => { print(env(x)) ; env }
+  case WriteString(x) => {
+        print(x.replaceAll("\"", "").replaceAll("""\\n""", "\n")) ;
+        env
+       }
+
+  case Read(x) => { 
+      println("Enter an integer and press ENTER:") ; 
+      val n = readInt() ; // Note: Does not work when using the REPL
+      eval_stmt(Assign(x, Num(n)), env) 
+  }
+}
+
+def eval_bl(bl: Block, env: Env) : Env = bl match {
+  case Nil => env
+  case s::bl => eval_bl(bl, eval_stmt(s, env))
+}
+
+def eval(bl: Block) : Env = eval_bl(bl, Map())
+
+println("Factors eval")
+println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "factors.while"))).head))
+
+println("Primes eval")
+println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "primes.while"))).head))
+
+
+println("Collatz2 eval")
+println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head))
+
+println("Loops eval")
+val start = System.nanoTime()
+println(eval(Stmts.parse_all(tokenise(os.read(os.pwd / "loops.while"))).head))
+val end = System.nanoTime()
+println("Time taken in seconds: ")
+println((end - start)/(1.0e9))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw3/primes.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,15 @@
+// prints out prime numbers from
+// 2 to 100 (end)
+
+end := 100;
+n := 2;
+while (n < end) do {
+  f := 2;
+  tmp := 0;
+  while ((f < n / 2 + 1) && (tmp == 0)) do {
+    if ((n / f) * f == n) then  { tmp := 1 } else { skip };
+    f := f + 1
+  };
+  if (tmp == 0) then { write(n); write("\n") } else { skip };
+  n  := n + 1
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw4/collatz2.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,27 @@
+// Collatz series
+//
+// needs writing of strings and numbers; comments
+
+bnd := 1;
+while bnd < 100001 do {
+  write bnd;
+  write ": ";
+  n := bnd;
+  cnt := 0;
+
+  while n > 1 do {
+    write n;
+    write ",";
+    
+    if n % 2 == 0 
+    then n := n / 2 
+    else n := 3 * n+1;
+
+    cnt := cnt + 1
+  };
+
+  write " => ";
+  write cnt;
+  write "\n";
+  bnd := bnd + 1
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw4/compiler.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,277 @@
+// Compiler for JVM
+
+import $file.lexer
+import lexer._
+
+import $file.parser
+import parser._
+
+
+val beginning = """
+.class public XXX.XXX
+.super java/lang/Object
+
+.method public static write(I)V 
+    .limit locals 1 
+    .limit stack 2 
+    getstatic java/lang/System/out Ljava/io/PrintStream; 
+    iload 0
+    invokevirtual java/io/PrintStream/print(I)V 
+    return 
+.end method
+
+.method public static writes(Ljava/lang/String;)V
+    .limit stack 2
+    .limit locals 1
+    getstatic java/lang/System/out Ljava/io/PrintStream;
+    aload 0
+    invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
+    return
+.end method
+
+.method public static read()I 
+    .limit locals 10 
+    .limit stack 10
+
+    ldc 0 
+    istore 1  ; this will hold our final integer 
+Label1: 
+    getstatic java/lang/System/in Ljava/io/InputStream; 
+    invokevirtual java/io/InputStream/read()I 
+    istore 2 
+    iload 2 
+    ldc 10   ; the newline delimiter 
+    isub 
+    ifeq Label2 
+    iload 2 
+    ldc 32   ; the space delimiter 
+    isub 
+    ifeq Label2
+
+    iload 2 
+    ldc 48   ; we have our digit in ASCII, have to subtract it from 48 
+    isub 
+    ldc 10 
+    iload 1 
+    imul 
+    iadd 
+    istore 1 
+    goto Label1 
+Label2: 
+    ;when we come here we have our integer computed in local variable 1 
+    iload 1 
+    ireturn 
+.end method
+
+.method public static main([Ljava/lang/String;)V
+   .limit locals 200
+   .limit stack 200
+
+; COMPILED CODE STARTS
+
+"""
+
+val ending = """
+; COMPILED CODE ENDS
+   return
+
+.end method
+"""
+
+// Compiler
+
+var counter = -1
+
+def Fresh(x: String) = {
+  counter += 1
+  x ++ "_" ++ counter.toString()
+}
+
+implicit def string_interpolations(sc: StringContext) = new {
+    def i(args: Any*): String = "   " ++ sc.s(args:_*) ++ "\n"
+    def l(args: Any*): String = sc.s(args:_*) ++ ":\n"
+}
+
+type Env = Map[String, Int]
+
+def compile_op(op: String) = op match {
+  case "+" => i"iadd"
+  case "-" => i"isub"
+  case "*" => i"imul"
+  case "/" => i"idiv"
+  case "%" => i"irem"
+}
+
+def compile_aexp(a: AExp, env : Env) : String = a match {
+  case Num(i) => i"ldc $i"
+  case Var(s) => i"iload ${env(s)} \t\t; $s"
+  case Aop(op, a1, a2) => 
+    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ compile_op(op)
+}
+
+def compile_bexp(b: BExp, env : Env, jmp: String) : String = b match {
+  case True => ""
+  case False => i"goto $jmp"
+  case And(b1, b2) => compile_bexp(b1, env, jmp) ++ compile_bexp(b2, env, jmp)
+  case Or(b1, b2) => {
+    val b1_false = Fresh("Or_second");
+    val or_end = Fresh("Or_end");
+    compile_bexp(b1, env, b1_false) ++
+    i"goto $or_end" ++
+    l"$b1_false" ++
+    compile_bexp(b2, env, jmp) ++
+    l"$or_end"
+  }
+  case Bop("==", a1, a2) => 
+    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpne $jmp"
+  case Bop("!=", a1, a2) => 
+    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpeq $jmp"
+  case Bop("<", a1, a2) => 
+    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmpge $jmp"
+  case Bop(">", a1, a2) => 
+    compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"if_icmple $jmp"
+}
+
+def compile_stmt(s: Stmt, env: Env) : (String, Env) = s match {
+  case Skip => ("", env)
+  case Assign(x, a) => {
+    val index = env.getOrElse(x, env.keys.size)
+    (compile_aexp(a, env) ++ i"istore $index \t\t; $x", env + (x -> index))
+  } 
+  case If(b, bl1, bl2) => {
+    val if_else = Fresh("If_else")
+    val if_end = Fresh("If_end")
+    val (instrs1, env1) = compile_block(bl1, env)
+    val (instrs2, env2) = compile_block(bl2, env1)
+    (compile_bexp(b, env, if_else) ++
+     instrs1 ++
+     i"goto $if_end" ++
+     l"$if_else" ++
+     instrs2 ++
+     l"$if_end", env2)
+  }
+  case While(b, bl) => {
+    val loop_begin = Fresh("Loop_begin")
+    val loop_end = Fresh("Loop_end")
+    val (instrs1, env1) = compile_block(bl, env)
+    (l"$loop_begin" ++
+     compile_bexp(b, env, loop_end) ++
+     instrs1 ++
+     i"goto $loop_begin" ++
+     l"$loop_end", env1)
+  }
+  case For(id, lower, upper, code) => {
+      val (assignment_code, env1) = compile_stmt(Assign(id, lower), env)  // id := lower;
+      val while_equivalent = While(
+          Or(Bop("<", Var(id), upper), Bop("==", Var(id), upper)),    // while id <= upper do {
+          code ++                                                     //    code  
+          List(
+            Assign(id, Aop("+", Var(id), Num(1)))                     //    id := id + 1
+          ))                                                          // };
+
+      val (while_code, env2) = compile_stmt(while_equivalent, env1)
+      (assignment_code ++ while_code, env2)
+  }
+  case WriteId(x) => (i"iload ${env(x)} \t\t; $x" ++ 
+     i"invokestatic XXX/XXX/write(I)V", env)
+  case WriteString(x) => (s"   ldc ${x}\n" ++
+     i"invokestatic XXX/XXX/writes(Ljava/lang/String;)V", env)
+  case Read(x) => {
+    val index = env.getOrElse(x, env.keys.size) 
+    (i"invokestatic XXX/XXX/read()I" ++ 
+     i"istore $index \t\t; $x", env + (x -> index))
+  }
+}
+
+def compile_block(bl: Block, env: Env) : (String, Env) = bl match {
+  case Nil => ("", env)
+  case s::bl => {
+    val (instrs1, env1) = compile_stmt(s, env)
+    val (instrs2, env2) = compile_block(bl, env1)
+    (instrs1 ++ instrs2, env2)
+  }
+}
+
+def compile(bl: Block, class_name: String) : String = {
+  val instructions = compile_block(bl, Map.empty)._1
+  (beginning ++ instructions ++ ending).replaceAllLiterally("XXX", class_name)
+}
+
+// Compiling and running
+
+import scala.util._
+import scala.sys.process._
+import scala.io
+
+def compile_tofile(bl: Block, class_name: String) = {
+  val output = compile(bl, class_name)
+  val fw = new java.io.FileWriter(class_name + ".j") 
+  fw.write(output) 
+  fw.close()
+}
+
+def compile_all(bl: Block, class_name: String) : Unit = {
+  compile_tofile(bl, class_name)
+  println("compiled ")
+  val test = ("java -jar jasmin.jar " + class_name + ".j").!!
+  println("assembled ")
+}
+
+def time_needed[T](i: Int, code: => T) = {
+  val start = System.nanoTime()
+  for (j <- 1 to i) code
+  val end = System.nanoTime()
+  (end - start)/(i * 1.0e9)
+}
+
+def compile_run(bl: Block, class_name: String) : Unit = {
+  println("Start compilation")
+  compile_all(bl, class_name)
+  println("running")
+  println("Time: " + time_needed(1, ("java " + class_name + "/" + class_name).!))
+}
+
+// ---- Q1
+
+// Fibonacci
+
+val fibonacciProgram = """write "Fib";
+read n;
+minus1 := 0;
+minus2 := 1;
+while n > 0 do {
+  temp := minus2;
+  minus2 := minus1 + minus2;
+  minus1 := temp;
+  n := n - 1
+};
+write "Result";
+write minus2"""
+
+//compile_all(Stmts.parse_all(tokenise(fibonacciProgram)).head, "fib")
+
+val factorialProgram = """write "Factorial";
+read n;
+fact := 1;
+
+while n > 0 do {
+  fact := n * fact;
+  n := n - 1
+};
+
+write "Result";
+write fact
+"""
+
+compile_all(Stmts.parse_all(tokenise(factorialProgram)).head, "factorial")
+
+// ---- Q3
+
+/* compile_run(Stmts.parse_all(tokenise("""for i := 1 upto 10 do {
+  for i := 1 upto 10 do {
+    write i
+  }
+}""")).head, "nestedloop") */
+
+
+compile_run(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head, "collatz2")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw4/fib.while	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,13 @@
+write "Fib: ";
+read n;  
+minus1 := 0;
+minus2 := 1;
+while n > 0 do {
+       temp := minus2;
+       minus2 := minus1 + minus2;
+       minus1 := temp;
+       n := n - 1
+};
+write "Result: ";
+write minus2 
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw4/lexer.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,251 @@
+import scala.language.implicitConversions    
+import scala.language.reflectiveCalls
+
+// Rexp
+abstract class Rexp
+case object ZERO extends Rexp
+case object ONE extends Rexp
+case class CHAR(c: Char) extends Rexp
+case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
+case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
+case class STAR(r: Rexp) extends Rexp 
+case class RECD(x: String, r: Rexp) extends Rexp
+
+case class RANGE(s: Set[Char]) extends Rexp
+case class PLUS(r: Rexp) extends Rexp
+case class OPTIONAL(r: Rexp) extends Rexp
+case class NTIMES(r: Rexp, n: Int) extends Rexp 
+
+// Values
+abstract class Val
+case object Empty extends Val
+case class Chr(c: Char) extends Val
+case class Sequ(v1: Val, v2: Val) extends Val
+case class Left(v: Val) extends Val
+case class Right(v: Val) extends Val
+case class Stars(vs: List[Val]) extends Val
+case class Rec(x: String, v: Val) extends Val
+
+
+// Convenience typing
+def charlist2rexp(s : List[Char]): Rexp = s match {
+  case Nil => ONE
+  case c::Nil => CHAR(c)
+  case c::s => SEQ(CHAR(c), charlist2rexp(s))
+}
+
+implicit def string2rexp(s : String) : Rexp = 
+  charlist2rexp(s.toList)
+
+implicit def RexpOps(r: Rexp) = new {
+  def | (s: Rexp) = ALT(r, s)
+  def % = STAR(r)
+  def ~ (s: Rexp) = SEQ(r, s)
+}
+
+implicit def stringOps(s: String) = new {
+  def | (r: Rexp) = ALT(s, r)
+  def | (r: String) = ALT(s, r)
+  def % = STAR(s)
+  def ~ (r: Rexp) = SEQ(s, r)
+  def ~ (r: String) = SEQ(s, r)
+  def $ (r: Rexp) = RECD(s, r)
+}
+
+// nullable
+def nullable(r: Rexp) : Boolean = r match {
+  case ZERO => false
+  case ONE => true
+  case CHAR(_) => false
+  case ALT(r1, r2) => nullable(r1) || nullable(r2)
+  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
+  case STAR(_) => true
+
+  case RECD(_, r1) => nullable(r1)
+  case RANGE(_) => false
+  case PLUS(r1) => nullable(r1)
+  case OPTIONAL(_) => true
+  case NTIMES(r1, i) => if (i == 0) true else nullable(r1)
+}
+
+// der
+def der(c: Char, r: Rexp) : Rexp = r match {
+  case ZERO => ZERO
+  case ONE => ZERO
+  case CHAR(d) => if (c == d) ONE else ZERO
+  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
+  case SEQ(r1, r2) => 
+    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
+    else SEQ(der(c, r1), r2)
+  case STAR(r) => SEQ(der(c, r), STAR(r))
+
+  case RECD(_, r1) => der(c, r1)
+  case RANGE(s) => if (s.contains(c)) ONE else ZERO 
+  case PLUS(r1) => SEQ(der(c, r1), STAR(r1))
+  case OPTIONAL(r1) => ALT(der(c, r1), ZERO)
+  case NTIMES(r, i) => 
+    if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
+}
+
+// Flatten
+def flatten(v: Val) : String = v match {
+  case Empty => ""
+  case Chr(c) => c.toString
+  case Left(v) => flatten(v)
+  case Right(v) => flatten(v)
+  case Sequ(v1, v2) => flatten(v1) + flatten(v2)
+  case Stars(vs) => vs.map(flatten).mkString
+  case Rec(_, v) => flatten(v)
+}
+
+// Env
+def env(v: Val) : List[(String, String)] = v match {
+  case Empty => Nil
+  case Chr(c) => Nil
+  case Left(v) => env(v)
+  case Right(v) => env(v)
+  case Sequ(v1, v2) => env(v1) ::: env(v2)
+  case Stars(vs) => vs.flatMap(env)
+  case Rec(x, v) => (x, flatten(v))::env(v)
+}
+
+// Mkeps
+def mkeps(r: Rexp) : Val = r match {
+  case ONE => Empty
+  case ALT(r1, r2) => 
+    if (nullable(r1)) Left(mkeps(r1)) else Right(mkeps(r2))
+  case SEQ(r1, r2) => Sequ(mkeps(r1), mkeps(r2))
+  case STAR(r) => Stars(Nil)
+  case RECD(x, r) => Rec(x, mkeps(r))
+
+  case PLUS(r) => Stars(List(mkeps(r)))   // the first copy must match the empty string
+  case OPTIONAL(r) => Right(Empty)
+  case NTIMES(r, i) => Stars(List.fill(i)(mkeps(r)))
+}
+
+// Inj
+def inj(r: Rexp, c: Char, v: Val) : Val = (r, v) match {
+  case (STAR(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+  case (SEQ(r1, r2), Sequ(v1, v2)) => Sequ(inj(r1, c, v1), v2)
+  case (SEQ(r1, r2), Left(Sequ(v1, v2))) => Sequ(inj(r1, c, v1), v2)
+  case (SEQ(r1, r2), Right(v2)) => Sequ(mkeps(r1), inj(r2, c, v2))
+  case (ALT(r1, r2), Left(v1)) => Left(inj(r1, c, v1))
+  case (ALT(r1, r2), Right(v2)) => Right(inj(r2, c, v2))
+  case (CHAR(d), Empty) => Chr(c) 
+  case (RECD(x, r1), _) => Rec(x, inj(r1, c, v))
+
+  case (RANGE(_), Empty) => Chr(c)
+  case (PLUS(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+  case (OPTIONAL(r), Left(v1)) => Left(inj(r, c, v1))
+  case (NTIMES(r, n), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+}
+
+// Rectification functions
+def F_ID(v: Val): Val = v
+def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))
+def F_LEFT(f: Val => Val) = (v:Val) => Left(f(v))
+def F_ALT(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
+  case Right(v) => Right(f2(v))
+  case Left(v) => Left(f1(v))
+}
+def F_SEQ(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
+  case Sequ(v1, v2) => Sequ(f1(v1), f2(v2))
+}
+def F_SEQ_Empty1(f1: Val => Val, f2: Val => Val) = 
+  (v:Val) => Sequ(f1(Empty), f2(v))
+def F_SEQ_Empty2(f1: Val => Val, f2: Val => Val) = 
+  (v:Val) => Sequ(f1(v), f2(Empty))
+def F_RECD(f: Val => Val) = (v:Val) => v match {
+  case Rec(x, v) => Rec(x, f(v))
+}
+def F_ERROR(v: Val): Val = throw new Exception("error")
+
+// Simp
+def simp(r: Rexp): (Rexp, Val => Val) = r match {
+  case ALT(r1, r2) => {
+    val (r1s, f1s) = simp(r1)
+    val (r2s, f2s) = simp(r2)
+    (r1s, r2s) match {
+      case (ZERO, _) => (r2s, F_RIGHT(f2s))
+      case (_, ZERO) => (r1s, F_LEFT(f1s))
+      case _ => if (r1s == r2s) (r1s, F_LEFT(f1s))
+                else (ALT (r1s, r2s), F_ALT(f1s, f2s)) 
+    }
+  }
+  case SEQ(r1, r2) => {
+    val (r1s, f1s) = simp(r1)
+    val (r2s, f2s) = simp(r2)
+    (r1s, r2s) match {
+      case (ZERO, _) => (ZERO, F_ERROR)
+      case (_, ZERO) => (ZERO, F_ERROR)
+      case (ONE, _) => (r2s, F_SEQ_Empty1(f1s, f2s))
+      case (_, ONE) => (r1s, F_SEQ_Empty2(f1s, f2s))
+      case _ => (SEQ(r1s,r2s), F_SEQ(f1s, f2s))
+    }
+  }
+  case r => (r, F_ID)
+}
+
+// Lex
+def lex_simp(r: Rexp, s: List[Char]) : Val = s match {
+  case Nil => if (nullable(r)) mkeps(r) else 
+    { throw new Exception("lexing error") } 
+  case c::cs => {
+    val (r_simp, f_simp) = simp(der(c, r))
+    inj(r, c, f_simp(lex_simp(r_simp, cs)))
+  }
+}
+
+def lexing_simp(r: Rexp, s: String) = env(lex_simp(r, s.toList))
+
+// Language specific code
+val KEYWORD : Rexp = "while" | "if" | "then" | "else" | "do" | "for" | "to" | "true" | "false" | "read" | "write" | "skip" 
+val OP : Rexp = "+" | "-" | "*" | "%" | "/" | "==" | "!=" | ">" | "<" | ">=" | "<=" | ":=" | "&&" | "||"
+val LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z'))
+val SYM : Rexp = LET | RANGE(Set('.', '_', '>', '<', '=', ';', ',', ':'))
+val PARENS : Rexp = "(" | "{" | ")" | "}"
+val SEMI : Rexp = ";"
+val WHITESPACE : Rexp = PLUS(" ") | "\n" | "\t" | "\r"
+val DIGIT : Rexp = RANGE(('0' to '9').toSet)
+val DIGIT1 : Rexp = RANGE(('1' to '9').toSet)
+val STRING : Rexp = "\"" ~ (SYM | " " | "\\n" | DIGIT).% ~ "\""
+val ID : Rexp = LET ~ (LET | "_" | DIGIT).%
+val NUM : Rexp = "0" | (DIGIT1 ~ DIGIT.%)
+val EOL : Rexp = "\n" | "\r\n"
+val COMMENT : Rexp = "//" ~ (SYM | PARENS | " " | DIGIT).% ~ EOL
+
+val WHILE_REGS = (("k" $ KEYWORD) | 
+                  ("o" $ OP) | 
+                  ("str" $ STRING) |
+                  ("p" $ PARENS) |
+                  ("s" $ SEMI) | 
+                  ("w" $ WHITESPACE) | 
+                  ("i" $ ID) | 
+                  ("n" $ NUM) |
+		  ("c" $ COMMENT)).%
+
+// Token
+abstract class Token extends Serializable 
+case class T_KEYWORD(s: String) extends Token
+case class T_OP(s: String) extends Token
+case class T_STRING(s: String) extends Token
+case class T_PAREN(s: String) extends Token
+case object T_SEMI extends Token
+case class T_ID(s: String) extends Token
+case class T_NUM(n: Int) extends Token
+
+val token : PartialFunction[(String, String), Token] = {
+  case ("k", s) => T_KEYWORD(s)
+  case ("o", s) => T_OP(s)
+  case ("str", s) => T_STRING(s)
+  case ("p", s) => T_PAREN(s)
+  case ("s", _) => T_SEMI
+  case ("i", s) => T_ID(s)
+  case ("n", s) => T_NUM(s.toInt)
+}
+
+// Tokenise
+def tokenise(s: String) : List[Token] = 
+  lexing_simp(WHILE_REGS, s).collect(token)
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw4/parser.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,162 @@
+// CW3
+
+import $file.lexer
+import lexer._ 
+
+
+case class ~[+A, +B](_1: A, _2: B)
+type IsSeq[A] = A => Seq[_]
+
+abstract class Parser[I : IsSeq, T] {
+  def parse(ts: I): Set[(T, I)]
+
+  def parse_all(ts: I) : Set[T] =
+    for ((head, tail) <- parse(ts); if tail.isEmpty) yield head
+}
+
+class SeqParser[I : IsSeq, T, S](p: => Parser[I, T], q: => Parser[I, S]) extends Parser[I, ~[T, S]] {
+  def parse(sb: I) = 
+    for ((head1, tail1) <- p.parse(sb); 
+         (head2, tail2) <- q.parse(tail1)) yield (new ~(head1, head2), tail2)
+}
+
+class AltParser[I : IsSeq, T](p: => Parser[I, T], q: => Parser[I, T]) extends Parser[I, T] {
+  def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
+}
+
+class FunParser[I : IsSeq, T, S](p: => Parser[I, T], f: T => S) extends Parser[I, S] {
+  def parse(sb: I) = 
+    for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
+}
+
+// New parser that takes as input a list of tokens
+case class TokenListParser(ts: List[Token]) extends Parser[List[Token], List[Token]] {
+    def parse(tsb: List[Token]) = {
+        val (prefix, suffix) = tsb.splitAt(ts.length)
+        if (prefix == ts) Set((prefix, suffix)) else Set()
+    }
+}
+
+// Implicit definitions to go from a token 
+// or a list of tokens to a TokenListParser
+implicit def token2parser(t: Token) = TokenListParser(List(t))
+implicit def tokenList2parser(ts: List[Token]) = TokenListParser(ts)
+
+implicit def ParserOps[I : IsSeq, T](p: Parser[I, T]) = new {
+  def || (q : => Parser[I, T]) = new AltParser[I, T](p, q)
+  def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
+  def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
+}
+
+implicit def TokenOps(t: Token) = new {
+    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](List(t), q)
+    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](List(t), qs)
+    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](List(t), f)
+    def ~[S](q : => Parser[List[Token], S]) =
+        new SeqParser[List[Token], List[Token], S](List(t), q)
+    def ~ (qs : List[Token]) =
+        new SeqParser[List[Token], List[Token], List[Token]](List(t), qs)
+}
+
+implicit def TokenListOps(ts: List[Token]) = new {
+    def || (q : => Parser[List[Token], List[Token]]) = new AltParser[List[Token], List[Token]](ts, q)
+    def || (qs : List[Token]) = new AltParser[List[Token], List[Token]](ts, qs)
+    def ==>[S] (f: => List[Token] => S) = new FunParser[List[Token], List[Token], S](ts, f)
+    def ~[S](q : => Parser[List[Token], S]) =
+        new SeqParser[List[Token], List[Token], S](ts, q)
+    def ~ (qs : List[Token]) =
+        new SeqParser[List[Token], List[Token], List[Token]](ts, qs)
+}
+
+// Abstract Syntax Trees
+abstract class Stmt
+abstract class AExp
+abstract class BExp
+
+type Block = List[Stmt]
+
+case object Skip extends Stmt
+case class If(a: BExp, bl1: Block, bl2: Block) extends Stmt
+case class While(b: BExp, bl: Block) extends Stmt
+case class Assign(s: String, a: AExp) extends Stmt
+case class Read(s: String) extends Stmt
+case class WriteId(s: String) extends Stmt  // for printing values of variables
+case class WriteString(s: String) extends Stmt  // for printing words
+case class For(counter: String, lower: AExp, upper: AExp, code: Block) extends Stmt
+
+
+case class Var(s: String) extends AExp
+case class Num(i: Int) extends AExp
+case class Aop(o: String, a1: AExp, a2: AExp) extends AExp
+
+case object True extends BExp
+case object False extends BExp
+case class Bop(o: String, a1: AExp, a2: AExp) extends BExp
+case class And(b1: BExp, b2: BExp) extends BExp
+case class Or(b1: BExp, b2: BExp) extends BExp
+
+case class IdParser() extends Parser[List[Token], String] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_ID(id) :: rest => Set((id, rest))
+        case _ => Set()
+    }
+}
+
+case class NumParser() extends Parser[List[Token], Int] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_NUM(n) :: rest => Set((n, rest))
+        case _ => Set()
+    }
+}
+
+case class StringParser() extends Parser[List[Token], String] {
+    def parse(tsb: List[Token]) = tsb match {
+        case T_STRING(s) :: rest => Set((s, rest))
+        case _ => Set()
+    }
+}
+
+// WHILE Language Parsing
+lazy val AExp: Parser[List[Token], AExp] = 
+  (Te ~ T_OP("+") ~ AExp) ==> { case x ~ _ ~ z => Aop("+", x, z): AExp } ||
+  (Te ~ T_OP("-") ~ AExp) ==> { case x ~ _ ~ z => Aop("-", x, z): AExp } || Te
+lazy val Te: Parser[List[Token], AExp] = 
+  (Fa ~ T_OP("*") ~ Te) ==> { case x ~ _ ~ z => Aop("*", x, z): AExp } || 
+  (Fa ~ T_OP("/") ~ Te) ==> { case x ~ _ ~ z => Aop("/", x, z): AExp } || 
+  (Fa ~ T_OP("%") ~ Te) ==> { case x ~ _ ~ z => Aop("%", x, z): AExp } || Fa  
+lazy val Fa: Parser[List[Token], AExp] = 
+   (T_PAREN("(") ~ AExp ~ T_PAREN(")")) ==> { case _ ~ y ~ _ => y } || 
+   IdParser() ==> Var  || 
+   NumParser() ==> Num
+
+lazy val BExp: Parser[List[Token], BExp] = 
+   (AExp ~ T_OP("==") ~ AExp) ==> { case x ~ _ ~ z => Bop("==", x, z): BExp } || 
+   (AExp ~ T_OP("!=") ~ AExp) ==> { case x ~ _ ~ z => Bop("!=", x, z): BExp } || 
+   (AExp ~ T_OP("<") ~ AExp) ==> { case x ~ _ ~ z => Bop("<", x, z): BExp } || 
+   (AExp ~ T_OP(">") ~ AExp) ==> { case x ~ _ ~ z => Bop(">", x, z): BExp } ||
+   (T_PAREN("(") ~ BExp ~ List(T_PAREN(")"), T_OP("&&")) ~ BExp) ==> { case _ ~ y ~ _ ~ v => And(y, v): BExp } ||
+   (T_PAREN("(") ~ BExp ~ List(T_PAREN(")"), T_OP("||")) ~ BExp) ==> { case _ ~ y ~ _ ~ v => Or(y, v): BExp } ||
+   (T_KEYWORD("true") ==> (_ => True: BExp )) || 
+   (T_KEYWORD("false") ==> (_ => False: BExp )) ||
+   (T_PAREN("(") ~ BExp ~ T_PAREN(")")) ==> { case _ ~ x ~ _ => x }
+
+lazy val Stmt: Parser[List[Token], Stmt] =
+    T_KEYWORD("skip") ==> (_ => Skip: Stmt) ||
+    (IdParser() ~ T_OP(":=") ~ AExp) ==> { case id ~ _ ~ z => Assign(id, z): Stmt } ||
+    (T_KEYWORD("if") ~ BExp ~ T_KEYWORD("then") ~ Block ~ T_KEYWORD("else") ~ Block) ==> { case _ ~ y ~ _ ~ u ~ _ ~ w => If(y, u, w): Stmt } ||
+    (T_KEYWORD("while") ~ BExp ~ T_KEYWORD("do") ~ Block) ==> { case _ ~ y ~ _ ~ w => While(y, w) : Stmt } ||
+    (T_KEYWORD("read") ~ IdParser()) ==> { case _ ~ id => Read(id): Stmt} ||
+    (T_KEYWORD("write") ~ IdParser()) ==> { case _ ~ id => WriteId(id): Stmt} ||
+    (T_KEYWORD("write") ~ StringParser()) ==> { case _ ~ s => WriteString(s): Stmt} ||
+    (T_KEYWORD("for") ~ IdParser() ~ T_OP(":=") ~ AExp ~ T_KEYWORD("upto") ~ AExp ~ T_KEYWORD("do") ~ Block) ==> {
+      case _ ~ id ~ _ ~ lower ~ _ ~ upper ~ _ ~ blck => For(id, lower, upper, blck): Stmt
+    }
+
+lazy val Stmts: Parser[List[Token], Block] =
+    (Stmt ~ T_SEMI ~ Stmts) ==> { case x ~ _ ~ z => x :: z : Block } ||
+    (Stmt ==> (s => List(s) : Block))
+
+lazy val Block: Parser[List[Token], Block] =
+    (T_PAREN("{") ~ Stmts ~ T_PAREN("}")) ==> { case x ~ y ~ z => y} ||
+    (Stmt ==> (s => List(s)))
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw5/fact.fun	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,21 @@
+// a simple factorial program
+// (including a tail recursive version)
+
+
+def fact(n: Int) : Int =
+  if n == 0 then 1 else n * fact(n - 1);
+
+def facT(n: Int, acc: Int) : Int =
+  if n == 0 then acc else facT(n - 1, n * acc);
+
+def facTi(n: Int) : Int = facT(n, 1);
+
+def top() : Void = {
+  print_int(fact(6));
+  print_char(',');
+  print_int(facTi(6));
+  print_char('\n')
+};
+
+top()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw5/fun_llvm.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,412 @@
+// A Small LLVM Compiler for a Simple Functional Language
+// (includes an external lexer and parser)
+//
+//
+// call with                 -- prints out llvm code
+//
+//     amm fun_llvm.sc main fact.fun
+//     amm fun_llvm.sc main defs.fun
+//
+// or                        -- writes llvm code to disk
+//
+//     amm fun_llvm.sc write fact.fun
+//     amm fun_llvm.sc write defs.fun
+//
+//       this will generate an .ll file. 
+//
+// or                       -- runs the generated llvm code via lli
+//
+//     amm fun_llvm.sc run fact.fun
+//     amm fun_llvm.sc run defs.fun
+//
+//
+// You can interpret an .ll file using lli, for example
+//
+//      lli fact.ll
+//
+// The optimiser can be invoked as
+//
+//      opt -O1 -S in_file.ll > out_file.ll
+//      opt -O3 -S in_file.ll > out_file.ll
+//
+// The code produced for the various architectures can be obtain with
+//   
+//   llc -march=x86 -filetype=asm in_file.ll -o -
+//   llc -march=arm -filetype=asm in_file.ll -o -  
+//
+// Producing an executable can be achieved by
+//
+//    llc -filetype=obj in_file.ll
+//    gcc in_file.o -o a.out
+//    ./a.out
+
+
+import $file.fun_tokens, fun_tokens._
+import $file.fun_parser, fun_parser._ 
+
+
+// for generating new labels
+var counter = -1
+
+def Fresh(x: String) = {
+  counter += 1
+  x ++ "_" ++ counter.toString()
+}
+
+// Internal CPS language for FUN
+abstract class KExp
+abstract class KVal
+
+type Ty = String
+type TyEnv = Map[String, Ty]
+
+case class KVar(s: String, ty: Ty = "UNDEF") extends KVal
+case class KLoad(v: KVal) extends KVal
+case class KNum(i: Int) extends KVal
+case class KFNum(i: Double) extends KVal
+case class KChr(c: Int) extends KVal
+case class Kop(o: String, v1: KVal, v2: KVal, ty: Ty = "UNDEF") extends KVal
+case class KCall(o: String, vrs: List[KVal], ty: Ty = "UNDEF") extends KVal
+
+case class KIf(x1: String, e1: KExp, e2: KExp) extends KExp {
+  override def toString = s"KIf $x1\nIF\n$e1\nELSE\n$e2"
+}
+case class KLet(x: String, e1: KVal, e2: KExp) extends KExp {
+  override def toString = s"let $x = $e1 in \n$e2" 
+}
+case class KReturn(v: KVal) extends KExp
+
+// typing K values
+def typ_val(v: KVal, ts: TyEnv) : (KVal, Ty) = v match {
+  case KVar(s, _) => {
+    val ty = ts.getOrElse(s, "TUNDEF")
+    (KVar(s, ty), ty)  
+  }
+  case Kop(op, v1, v2, _) => {
+    val (tv1, ty1) = typ_val(v1, ts)
+    val (tv2, ty2) = typ_val(v2, ts)
+    if (ty1 == ty2) (Kop(op, tv1, tv2, ty1), ty1) else (Kop(op, tv1, tv2, "TMISMATCH"), "TMISMATCH") 
+  }
+  case KCall(fname, args, _) => {
+    val ty = ts.getOrElse(fname, "TCALLUNDEF" ++ fname)
+    (KCall(fname, args.map(typ_val(_, ts)._1), ty), ty)
+  }  
+  case KLoad(v) => {
+    val (tv, ty) = typ_val(v, ts)
+    (KLoad(tv), ty)
+  }
+  case KNum(i) => (KNum(i), "Int")
+  case KFNum(i) => (KFNum(i), "Double")
+  case KChr(c) => (KChr(c), "Int")
+}
+
+def typ_exp(a: KExp, ts: TyEnv) : KExp = a match {
+  case KReturn(v) => KReturn(typ_val(v, ts)._1)
+  case KLet(x: String, v: KVal, e: KExp) => {
+    val (tv, ty) = typ_val(v, ts)
+    KLet(x, tv, typ_exp(e, ts + (x -> ty)))
+  }
+  case KIf(b, e1, e2) => KIf(b, typ_exp(e1, ts), typ_exp(e2, ts))
+}
+
+
+
+
+// CPS translation from Exps to KExps using a
+// continuation k.
+def CPS(e: Exp)(k: KVal => KExp) : KExp = e match {
+  case Var(s) if (s.head.isUpper) => {
+      val z = Fresh("tmp")
+      KLet(z, KLoad(KVar(s)), k(KVar(z)))
+  }
+  case Var(s) => k(KVar(s))
+  case Num(i) => k(KNum(i))
+  case ChConst(c) => k(KChr(c))
+  case FNum(i) => k(KFNum(i))
+  case Aop(o, e1, e2) => {
+    val z = Fresh("tmp")
+    CPS(e1)(y1 => 
+      CPS(e2)(y2 => KLet(z, Kop(o, y1, y2), k(KVar(z)))))
+  }
+  case If(Bop(o, b1, b2), e1, e2) => {
+    val z = Fresh("tmp")
+    CPS(b1)(y1 => 
+      CPS(b2)(y2 => 
+        KLet(z, Kop(o, y1, y2), KIf(z, CPS(e1)(k), CPS(e2)(k)))))
+  }
+  case Call(name, args) => {
+    def aux(args: List[Exp], vs: List[KVal]) : KExp = args match {
+      case Nil => {
+          val z = Fresh("tmp")
+          KLet(z, KCall(name, vs), k(KVar(z)))
+      }
+      case e::es => CPS(e)(y => aux(es, vs ::: List(y)))
+    }
+    aux(args, Nil)
+  }
+  case Sequence(e1, e2) => 
+    CPS(e1)(_ => CPS(e2)(y2 => k(y2)))
+}   
+
+//initial continuation
+def CPSi(e: Exp) = CPS(e)(KReturn)
+
+// some testcases
+val e1 = Aop("*", Var("a"), Num(3))
+CPSi(e1)
+
+val e2 = Aop("+", Aop("*", Var("a"), Num(3)), Num(4))
+CPSi(e2)
+
+val e3 = Aop("+", Num(2), Aop("*", Var("a"), Num(3)))
+CPSi(e3)
+
+val e4 = Aop("+", Aop("-", Num(1), Num(2)), Aop("*", Var("a"), Num(3)))
+CPSi(e4)
+
+val e5 = If(Bop("==", Num(1), Num(1)), Num(3), Num(4))
+CPSi(e5)
+
+val e6 = If(Bop("!=", Num(10), Num(10)), e5, Num(40))
+CPSi(e6)
+
+val e7 = Call("foo", List(Num(3)))
+CPSi(e7)
+
+val e8 = Call("foo", List(Aop("*", Num(3), Num(1)), Num(4), Aop("+", Num(5), Num(6))))
+CPSi(e8)
+
+val e9 = Sequence(Aop("*", Var("a"), Num(3)), Aop("+", Var("b"), Num(6)))
+CPSi(e9)
+
+val e = Aop("*", Aop("+", Num(1), Call("foo", List(Var("a"), Num(3)))), Num(4))
+CPSi(e)
+
+
+
+
+// convenient string interpolations 
+// for instructions, labels and methods
+import scala.language.implicitConversions
+import scala.language.reflectiveCalls
+
+
+
+
+implicit def sring_inters(sc: StringContext) = new {
+    def i(args: Any*): String = "   " ++ sc.s(args:_*) ++ "\n"
+    def l(args: Any*): String = sc.s(args:_*) ++ ":\n"
+    def m(args: Any*): String = sc.s(args:_*) ++ "\n"
+}
+
+def get_ty(s: String) = s match {
+  case "Double" => "double"
+  case "Void" => "void"
+  case "Int" => "i32"
+  case "Bool" => "i2"
+  case _ => s
+}
+
+def compile_call_arg(a: KVal) = a match {
+  case KNum(i) => s"i32 $i"
+  case KFNum(i) => s"double $i"
+  case KChr(c) => s"i32 $c"
+  case KVar(s, ty) => s"${get_ty(ty)} %$s" 
+}
+
+def compile_arg(s: (String, String)) = s"${get_ty(s._2)} %${s._1}" 
+
+
+// mathematical and boolean operations
+def compile_op(op: String) = op match {
+  case "+" => "add i32 "
+  case "*" => "mul i32 "
+  case "-" => "sub i32 "
+  case "/" => "sdiv i32 "
+  case "%" => "srem i32 "
+  case "==" => "icmp eq i32 "
+  case "!=" => "icmp ne i32 "      // not equal 
+  case "<=" => "icmp sle i32 "     // signed less or equal
+  case "<"  => "icmp slt i32 "     // signed less than
+}
+
+def compile_dop(op: String) = op match {
+  case "+" => "fadd double "
+  case "*" => "fmul double "
+  case "-" => "fsub double "
+  case "==" => "fcmp oeq double "
+  case "<=" => "fcmp ole double "   
+  case "<"  => "fcmp olt double "   
+}
+
+// compile K values
+def compile_val(v: KVal) : String = v match {
+  case KNum(i) => s"$i"
+  case KFNum(i) => s"$i"
+  case KChr(c) => s"$c"
+  case KVar(s, ty) => s"%$s" 
+  case KLoad(KVar(s, ty)) => s"load ${get_ty(ty)}, ${get_ty(ty)}* @$s"
+  case Kop(op, x1, x2, ty) => ty match { 
+    case "Int" => s"${compile_op(op)} ${compile_val(x1)}, ${compile_val(x2)}"
+    case "Double" => s"${compile_dop(op)} ${compile_val(x1)}, ${compile_val(x2)}"
+    case _ => Kop(op, x1, x2, ty).toString
+  }
+  case KCall(fname, args, ty) => 
+    s"call ${get_ty(ty)} @$fname (${args.map(compile_call_arg).mkString(", ")})"
+}
+
+// compile K expressions
+def compile_exp(a: KExp) : String = a match {
+  case KReturn(KVar("void", _)) =>
+    i"ret void"
+  case KReturn(KVar(x, ty)) =>
+    i"ret ${get_ty(ty)} %$x"
+  case KReturn(KNum(i)) =>
+    i"ret i32 $i"
+  case KLet(x: String, KCall(o: String, vrs: List[KVal], "Void"), e: KExp) => 
+    i"${compile_val(KCall(o: String, vrs: List[KVal], "Void"))}" ++ compile_exp(e)
+  case KLet(x: String, v: KVal, e: KExp) => 
+    i"%$x = ${compile_val(v)}" ++ compile_exp(e)
+  case KIf(x, e1, e2) => {
+    val if_br = Fresh("if_branch")
+    val else_br = Fresh("else_branch")
+    i"br i1 %$x, label %$if_br, label %$else_br" ++
+    l"\n$if_br" ++
+    compile_exp(e1) ++
+    l"\n$else_br" ++ 
+    compile_exp(e2)
+  }
+}
+
+
+val prelude = """
+declare i32 @printf(i8*, ...)
+
+@.str_nl = private constant [2 x i8] c"\0A\00"
+@.str_star = private constant [2 x i8] c"*\00"
+@.str_space = private constant [2 x i8] c" \00"
+
+define void @new_line() #0 {
+  %t0 = getelementptr [2 x i8], [2 x i8]* @.str_nl, i32 0, i32 0
+  %1 = call i32 (i8*, ...) @printf(i8* %t0)
+  ret void
+}
+
+define void @print_star() #0 {
+  %t0 = getelementptr [2 x i8], [2 x i8]* @.str_star, i32 0, i32 0
+  %1 = call i32 (i8*, ...) @printf(i8* %t0)
+  ret void
+}
+
+define void @print_space() #0 {
+  %t0 = getelementptr [2 x i8], [2 x i8]* @.str_space, i32 0, i32 0
+  %1 = call i32 (i8*, ...) @printf(i8* %t0)
+  ret void
+}
+
+define void @skip() #0 {
+  ret void
+}
+
+@.str_int = private constant [3 x i8] c"%d\00"
+
+define void @print_int(i32 %x) {
+   %t0 = getelementptr [3 x i8], [3 x i8]* @.str_int, i32 0, i32 0
+   call i32 (i8*, ...) @printf(i8* %t0, i32 %x) 
+   ret void
+}
+
+@.str_char = private constant [3 x i8] c"%c\00"
+
+define void @print_char(i32 %x) {
+   %t0 = getelementptr [3 x i8], [3 x i8]* @.str_char, i32 0, i32 0
+   call i32 (i8*, ...) @printf(i8* %t0, i32 %x) 
+   ret void
+}
+
+; END OF BUILD-IN FUNCTIONS (prelude)
+
+"""
+
+def get_cont(ty: Ty) = ty match {
+  case "Int" =>    KReturn
+  case "Double" => KReturn
+  case "Void" =>   { (_: KVal) => KReturn(KVar("void", "Void")) }
+} 
+
+// compile function for declarations and main
+def compile_decl(d: Decl, ts: TyEnv) : (String, TyEnv) = d match {
+  case Def(name, args, ty, body) => { 
+    val ts2 = ts + (name -> ty)
+    val tkbody = typ_exp(CPS(body)(get_cont(ty)), ts2 ++ args.toMap)
+    (m"define ${get_ty(ty)} @$name (${args.map(compile_arg).mkString(",")}) {" ++
+     compile_exp(tkbody) ++
+     m"}\n", ts2)
+  }
+  case Main(body) => {
+    val tbody = typ_exp(CPS(body)(_ => KReturn(KNum(0))), ts)
+    (m"define i32 @main() {" ++
+     compile_exp(tbody) ++
+     m"}\n", ts)
+  }
+  case Const(name, n) => {
+    (m"@$name = global i32 $n\n", ts + (name -> "Int"))
+  }
+  case FConst(name, x) => {
+    (m"@$name = global double $x\n", ts + (name -> "Double"))
+  }
+}
+
+def compile_prog(prog: List[Decl], ty: TyEnv) : String = prog match {
+  case Nil => ""
+  case d::ds => {
+    val (s2, ty2) = compile_decl(d, ty)
+    s2 ++ compile_prog(ds, ty2)
+  }
+}
+// main compiler functions
+def compile(prog: List[Decl]) : String = 
+  prelude ++ compile_prog(prog, Map("new_line" -> "Void", "skip" -> "Void", 
+				    "print_star" -> "Void", "print_space" -> "Void",
+                                    "print_int" -> "Void", "print_char" -> "Void"))
+
+
+//import ammonite.ops._
+
+
+@main
+def main(fname: String) = {
+    val path = os.pwd / fname
+    val file = fname.stripSuffix("." ++ path.ext)
+    val tks = tokenise(os.read(path))
+    val ast = parse_tks(tks)
+    val code = compile(ast)
+    println(code)
+}
+
+@main
+def write(fname: String) = {
+    val path = os.pwd / fname
+    val file = fname.stripSuffix("." ++ path.ext)
+    val tks = tokenise(os.read(path))
+    val ast = parse_tks(tks)
+    val code = compile(ast)
+    //println(code)
+    os.write.over(os.pwd / (file ++ ".ll"), code)
+}
+
+@main
+def run(fname: String) = {
+    val path = os.pwd / fname
+    val file = fname.stripSuffix("." ++ path.ext)
+    write(fname)  
+    os.proc("llc", "-filetype=obj", file ++ ".ll").call()
+    os.proc("gcc", file ++ ".o", "-o", file ++ ".bin").call()
+    os.proc(os.pwd / (file ++ ".bin")).call(stdout = os.Inherit)
+    println(s"done.")
+}
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw5/fun_parser.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,215 @@
+// A parser for the Fun language
+//================================
+//
+// call with 
+//
+//     amm fun_parser.sc fact.fun
+//
+//     amm fun_parser.sc defs.fun
+//
+// this will generate a parse-tree from a list
+// of tokens
+
+import scala.language.implicitConversions    
+import scala.language.reflectiveCalls
+
+import $file.fun_tokens, fun_tokens._ 
+
+
+// Parser combinators
+//    type parameter I needs to be of Seq-type
+//
+abstract class Parser[I, T](implicit ev: I => Seq[_]) {
+  def parse(ts: I): Set[(T, I)]
+
+  def parse_single(ts: I) : T = 
+    parse(ts).partition(_._2.isEmpty) match {
+      case (good, _) if !good.isEmpty => good.head._1
+      case (good, err) if err.isEmpty => {
+        println (s"Parse Error\n $good \n $err") ; sys.exit(-1) }
+      case (_, err) => { 
+	println (s"Parse Error\n${err.minBy(_._2.length)}") ; sys.exit(-1) }
+    }
+}
+
+// convenience for writing grammar rules
+case class ~[+A, +B](_1: A, _2: B)
+
+class SeqParser[I, T, S](p: => Parser[I, T], 
+                         q: => Parser[I, S])(implicit ev: I => Seq[_]) extends Parser[I, ~[T, S]] {
+  def parse(sb: I) = 
+    for ((head1, tail1) <- p.parse(sb); 
+         (head2, tail2) <- q.parse(tail1)) yield (new ~(head1, head2), tail2)
+}
+
+class AltParser[I, T](p: => Parser[I, T], 
+                      q: => Parser[I, T])(implicit ev: I => Seq[_]) extends Parser[I, T] {
+  def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
+}
+
+class FunParser[I, T, S](p: => Parser[I, T], 
+                         f: T => S)(implicit ev: I => Seq[_]) extends Parser[I, S] {
+  def parse(sb: I) = 
+    for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
+}
+
+// convenient combinators
+implicit def ParserOps[I, T](p: Parser[I, T])(implicit ev: I => Seq[_]) = new {
+  def || (q : => Parser[I, T]) = new AltParser[I, T](p, q)
+  def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
+  def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
+}
+
+def ListParser[I, T, S](p: => Parser[I, T], 
+                        q: => Parser[I, S])(implicit ev: I => Seq[_]): Parser[I, List[T]] = {
+  (p ==> ((s) => List(s))) ||
+  (p ~ q ~ ListParser(p, q)) ==> { case x ~ _ ~ z => x :: z : List[T] }
+}
+
+case class TokParser(tok: Token) extends Parser[List[Token], Token] {
+  def parse(ts: List[Token]) = ts match {
+    case t::ts if (t == tok) => Set((t, ts)) 
+    case _ => Set()
+  }
+}
+
+implicit def token2tparser(t: Token) = TokParser(t)
+
+implicit def TokOps(t: Token) = new {
+  def || (q : => Parser[List[Token], Token]) = new AltParser[List[Token], Token](t, q)
+  def ==>[S] (f: => Token => S) = new FunParser[List[Token], Token, S](t, f)
+  def ~[S](q : => Parser[List[Token], S]) = new SeqParser[List[Token], Token, S](t, q)
+}
+
+case object EmptyParser extends Parser[List[Token], String] {
+  def parse(ts: List[Token]) = Set(("", ts))
+}
+
+case object NumParser extends Parser[List[Token], Int] {
+  def parse(ts: List[Token]) = ts match {
+    case T_NUM(n)::ts => Set((n, ts)) 
+    case _ => Set ()
+  }
+}
+
+case object FNumParser extends Parser[List[Token], Double] {
+  def parse(ts: List[Token]) = ts match {
+    case T_FNUM(x)::ts => Set((x, ts)) 
+    case _ => Set()
+  }
+}
+
+case object IdParser extends Parser[List[Token], String] {
+  def parse(ts: List[Token]) = ts match {
+    case T_ID(s)::ts => Set((s, ts)) 
+    case _ => Set ()
+  }
+}
+
+case object CharConstParser extends Parser[List[Token], Int] {
+  def parse(ts: List[Token]) = ts match {
+    case T_CHR(c)::ts => Set((c, ts)) 
+    case _ => Set ()
+  }
+}
+
+case object TyParser extends Parser[List[Token], String] {
+  def parse(ts: List[Token]) = ts match {
+    case T_TY(s)::ts => Set((s, ts)) 
+    case _ => Set ()
+  }
+}
+
+
+// Abstract syntax trees for the Fun language
+abstract class Exp 
+abstract class BExp 
+abstract class Decl 
+
+case class Def(name: String, args: List[(String, String)], ty: String, body: Exp) extends Decl
+case class Main(e: Exp) extends Decl
+case class Const(name: String, v: Int) extends Decl
+case class FConst(name: String, x: Double) extends Decl
+
+case class Call(name: String, args: List[Exp]) extends Exp
+case class If(a: BExp, e1: Exp, e2: Exp) extends Exp
+case class Var(s: String) extends Exp
+case class Num(i: Int) extends Exp     // integer numbers
+case class FNum(i: Double) extends Exp  // floating numbers
+case class ChConst(c: Int) extends Exp // char constant
+case class Aop(o: String, a1: Exp, a2: Exp) extends Exp
+case class Sequence(e1: Exp, e2: Exp) extends Exp
+case class Bop(o: String, a1: Exp, a2: Exp) extends BExp
+
+
+// arithmetic expressions (there needs to be an F in the SEMICOLON case)
+lazy val Exp: Parser[List[Token], Exp] = 
+  (T_KWD("if") ~ BExp ~ T_KWD("then") ~ Exp ~ T_KWD("else") ~ Exp) ==>
+    { case _ ~ x ~ _ ~ y ~ _ ~ z => If(x, y, z): Exp } ||
+  (F ~ T_SEMI ~ Exp) ==> { case x ~ _ ~ y => Sequence(x, y): Exp } || L
+lazy val L: Parser[List[Token], Exp] = 
+  (T ~ T_OP("+") ~ Exp) ==> { case x ~ _ ~ z => Aop("+", x, z): Exp } ||
+  (T ~ T_OP("-") ~ Exp) ==> { case x ~ _ ~ z => Aop("-", x, z): Exp } || T  
+lazy val T: Parser[List[Token], Exp] = 
+  (F ~ T_OP("*") ~ T) ==> { case x ~ _ ~ z => Aop("*", x, z): Exp } || 
+  (F ~ T_OP("/") ~ T) ==> { case x ~ _ ~ z => Aop("/", x, z): Exp } || 
+  (F ~ T_OP("%") ~ T) ==> { case x ~ _ ~ z => Aop("%", x, z): Exp } || F
+lazy val F: Parser[List[Token], Exp] = 
+  (IdParser ~ T_LPAREN ~ T_RPAREN) ==> { case x ~ _ ~ _ => Call(x, Nil): Exp } ||
+  (IdParser ~ T_LPAREN ~ ListParser(Exp, T_COMMA) ~ T_RPAREN) ==> { case x ~ _ ~ z ~ _ => Call(x, z): Exp } ||
+  (T_LPAREN ~ Exp ~ T_RPAREN) ==> { case _ ~ y ~ _ => y: Exp } || 
+  IdParser ==> { case x => Var(x): Exp } || 
+  NumParser ==> { case x => Num(x): Exp } ||
+  CharConstParser ==> { case x => ChConst(x): Exp } ||
+  FNumParser ==> { case x => FNum(x): Exp }
+
+// boolean expressions
+lazy val BExp: Parser[List[Token], BExp] = 
+  (Exp ~ T_OP("==") ~ Exp) ==> { case x ~ _ ~ z => Bop("==", x, z): BExp } || 
+  (Exp ~ T_OP("!=") ~ Exp) ==> { case x ~ _ ~ z => Bop("!=", x, z): BExp } || 
+  (Exp ~ T_OP("<") ~ Exp)  ==> { case x ~ _ ~ z => Bop("<",  x, z): BExp } || 
+  (Exp ~ T_OP(">") ~ Exp)  ==> { case x ~ _ ~ z => Bop("<",  z, x): BExp } || 
+  (Exp ~ T_OP("<=") ~ Exp) ==> { case x ~ _ ~ z => Bop("<=", x, z): BExp } || 
+  (Exp ~ T_OP("=>") ~ Exp) ==> { case x ~ _ ~ z => Bop("<=", z, x): BExp } ||
+  (T_LPAREN ~ BExp ~ T_RPAREN) ==> { case _ ~ b ~ _ => b : BExp } 
+
+lazy val Arg : Parser[List[Token], (String, String)] = 
+  (IdParser ~ T_COLON ~ TyParser) ==> { case x ~ _ ~ ty => (x, ty) }  
+
+lazy val Defn: Parser[List[Token], Decl] = {
+   (T_KWD("def") ~ IdParser ~ T_LPAREN ~ T_RPAREN ~ T_COLON ~ TyParser ~ T_OP("=") ~ Exp) ==>
+     { case _ ~ y ~ _ ~ _ ~ _~ ty ~ _ ~ r => Def(y, Nil, ty, r): Decl } ||
+   (T_KWD("def") ~ IdParser ~ T_LPAREN ~ ListParser(Arg, T_COMMA) ~ T_RPAREN ~ T_COLON ~ TyParser ~ T_OP("=") ~ Exp) ==>
+     { case _ ~ y ~ _ ~ w ~ _ ~ _~ ty ~ _ ~ r => Def(y, w, ty, r): Decl }
+}
+
+lazy val Const_decl: Parser[List[Token], Decl] =
+   (T_KWD("val") ~ Arg ~ T_OP("=") ~ NumParser) ==>
+     { case _ ~ x ~ _ ~ v => Const(x._1, v): Decl } ||
+   (T_KWD("val") ~ Arg ~ T_OP("=") ~ FNumParser) ==>
+     { case _ ~ x ~ _ ~ v => FConst(x._1, v): Decl } 
+
+lazy val Prog: Parser[List[Token], List[Decl]] =
+  (Defn ~ T_SEMI ~ Prog) ==> { case x ~ _ ~ z => x :: z : List[Decl] } ||
+  (Const_decl ~ T_SEMI ~ Prog) ==> { case x ~ _ ~ z => x :: z : List[Decl] } ||
+  (Exp ==> ((s) => List(Main(s)) : List[Decl]))
+
+
+
+// Reading tokens and Writing parse trees
+
+//import ammonite.ops._
+
+def parse_tks(tks: List[Token]) : List[Decl] = {
+  //println(Prog.parse(tks))
+  Prog.parse_single(tks)
+}
+
+//@doc("Parses a file.")
+@main
+def main(fname: String) : Unit = {
+  val tks = tokenise(os.read(os.pwd / fname))
+  println(parse_tks(tks))
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw5/fun_tokens.sc	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,280 @@
+// A tokeniser for the Fun language
+//==================================
+//
+// call with 
+//
+//     amm fun_tokens.sc fact.fun
+//
+//     amm fun_tokens.sc defs.fun
+//
+
+
+
+import scala.language.implicitConversions    
+import scala.language.reflectiveCalls 
+
+abstract class Rexp 
+case object ZERO extends Rexp
+case object ONE extends Rexp
+case class CHAR(c: Char) extends Rexp
+case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
+case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
+case class STAR(r: Rexp) extends Rexp 
+case class RECD(x: String, r: Rexp) extends Rexp
+  
+abstract class Val
+case object Empty extends Val
+case class Chr(c: Char) extends Val
+case class Sequ(v1: Val, v2: Val) extends Val
+case class Left(v: Val) extends Val
+case class Right(v: Val) extends Val
+case class Stars(vs: List[Val]) extends Val
+case class Rec(x: String, v: Val) extends Val
+   
+// some convenience for typing in regular expressions
+def charlist2rexp(s : List[Char]): Rexp = s match {
+  case Nil => ONE
+  case c::Nil => CHAR(c)
+  case c::s => SEQ(CHAR(c), charlist2rexp(s))
+}
+implicit def string2rexp(s : String) : Rexp = 
+  charlist2rexp(s.toList)
+
+implicit def RexpOps(r: Rexp) = new {
+  def | (s: Rexp) = ALT(r, s)
+  def % = STAR(r)
+  def ~ (s: Rexp) = SEQ(r, s)
+}
+
+implicit def stringOps(s: String) = new {
+  def | (r: Rexp) = ALT(s, r)
+  def | (r: String) = ALT(s, r)
+  def % = STAR(s)
+  def ~ (r: Rexp) = SEQ(s, r)
+  def ~ (r: String) = SEQ(s, r)
+  def $ (r: Rexp) = RECD(s, r)
+}
+
+def nullable (r: Rexp) : Boolean = r match {
+  case ZERO => false
+  case ONE => true
+  case CHAR(_) => false
+  case ALT(r1, r2) => nullable(r1) || nullable(r2)
+  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
+  case STAR(_) => true
+  case RECD(_, r1) => nullable(r1)
+}
+
+def der (c: Char, r: Rexp) : Rexp = r match {
+  case ZERO => ZERO
+  case ONE => ZERO
+  case CHAR(d) => if (c == d) ONE else ZERO
+  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
+  case SEQ(r1, r2) => 
+    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
+    else SEQ(der(c, r1), r2)
+  case STAR(r) => SEQ(der(c, r), STAR(r))
+  case RECD(_, r1) => der(c, r1)
+}
+
+
+// extracts a string from value
+def flatten(v: Val) : String = v match {
+  case Empty => ""
+  case Chr(c) => c.toString
+  case Left(v) => flatten(v)
+  case Right(v) => flatten(v)
+  case Sequ(v1, v2) => flatten(v1) + flatten(v2)
+  case Stars(vs) => vs.map(flatten).mkString
+  case Rec(_, v) => flatten(v)
+}
+
+// extracts an environment from a value;
+// used for tokenise a string
+def env(v: Val) : List[(String, String)] = v match {
+  case Empty => Nil
+  case Chr(c) => Nil
+  case Left(v) => env(v)
+  case Right(v) => env(v)
+  case Sequ(v1, v2) => env(v1) ::: env(v2)
+  case Stars(vs) => vs.flatMap(env)
+  case Rec(x, v) => (x, flatten(v))::env(v)
+}
+
+// The Injection Part of the lexer
+
+def mkeps(r: Rexp) : Val = r match {
+  case ONE => Empty
+  case ALT(r1, r2) => 
+    if (nullable(r1)) Left(mkeps(r1)) else Right(mkeps(r2))
+  case SEQ(r1, r2) => Sequ(mkeps(r1), mkeps(r2))
+  case STAR(r) => Stars(Nil)
+  case RECD(x, r) => Rec(x, mkeps(r))
+}
+
+def inj(r: Rexp, c: Char, v: Val) : Val = (r, v) match {
+  case (STAR(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
+  case (SEQ(r1, r2), Sequ(v1, v2)) => Sequ(inj(r1, c, v1), v2)
+  case (SEQ(r1, r2), Left(Sequ(v1, v2))) => Sequ(inj(r1, c, v1), v2)
+  case (SEQ(r1, r2), Right(v2)) => Sequ(mkeps(r1), inj(r2, c, v2))
+  case (ALT(r1, r2), Left(v1)) => Left(inj(r1, c, v1))
+  case (ALT(r1, r2), Right(v2)) => Right(inj(r2, c, v2))
+  case (CHAR(d), Empty) => Chr(c) 
+  case (RECD(x, r1), _) => Rec(x, inj(r1, c, v))
+  case _ => { println ("Injection error") ; sys.exit(-1) } 
+}
+
+// some "rectification" functions for simplification
+def F_ID(v: Val): Val = v
+def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))
+def F_LEFT(f: Val => Val) = (v:Val) => Left(f(v))
+def F_ALT(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
+  case Right(v) => Right(f2(v))
+  case Left(v) => Left(f1(v))
+}
+def F_SEQ(f1: Val => Val, f2: Val => Val) = (v:Val) => v match {
+  case Sequ(v1, v2) => Sequ(f1(v1), f2(v2))
+}
+def F_SEQ_Empty1(f1: Val => Val, f2: Val => Val) = 
+  (v:Val) => Sequ(f1(Empty), f2(v))
+def F_SEQ_Empty2(f1: Val => Val, f2: Val => Val) = 
+  (v:Val) => Sequ(f1(v), f2(Empty))
+def F_RECD(f: Val => Val) = (v:Val) => v match {
+  case Rec(x, v) => Rec(x, f(v))
+}
+def F_ERROR(v: Val): Val = throw new Exception("error")
+
+def simp(r: Rexp): (Rexp, Val => Val) = r match {
+  case ALT(r1, r2) => {
+    val (r1s, f1s) = simp(r1)
+    val (r2s, f2s) = simp(r2)
+    (r1s, r2s) match {
+      case (ZERO, _) => (r2s, F_RIGHT(f2s))
+      case (_, ZERO) => (r1s, F_LEFT(f1s))
+      case _ => if (r1s == r2s) (r1s, F_LEFT(f1s))
+                else (ALT (r1s, r2s), F_ALT(f1s, f2s)) 
+    }
+  }
+  case SEQ(r1, r2) => {
+    val (r1s, f1s) = simp(r1)
+    val (r2s, f2s) = simp(r2)
+    (r1s, r2s) match {
+      case (ZERO, _) => (ZERO, F_ERROR)
+      case (_, ZERO) => (ZERO, F_ERROR)
+      case (ONE, _) => (r2s, F_SEQ_Empty1(f1s, f2s))
+      case (_, ONE) => (r1s, F_SEQ_Empty2(f1s, f2s))
+      case _ => (SEQ(r1s,r2s), F_SEQ(f1s, f2s))
+    }
+  }
+  case RECD(x, r1) => {
+    val (r1s, f1s) = simp(r1)
+    (RECD(x, r1s), F_RECD(f1s))
+  }
+  case r => (r, F_ID)
+}
+
+// lexing functions including simplification
+def lex_simp(r: Rexp, s: List[Char]) : Val = s match {
+  case Nil => if (nullable(r)) mkeps(r) else { println ("Lexing Error") ; sys.exit(-1) } 
+  case c::cs => {
+    val (r_simp, f_simp) = simp(der(c, r))
+    inj(r, c, f_simp(lex_simp(r_simp, cs)))
+  }
+}
+
+def lexing_simp(r: Rexp, s: String) = env(lex_simp(r, s.toList))
+
+
+// The Lexing Rules for the Fun Language
+
+def PLUS(r: Rexp) = r ~ r.%
+def OPT(r: Rexp) = r | ONE
+
+val SYM = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | 
+          "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | 
+          "w" | "x" | "y" | "z" | "A" | "B" | "C" | "D" |"E" | "F" | "G" |
+          "H" | "I" | "J" | "K" |"L" | "M" | "N" |
+          "O" | "P" | "Q" | "R" |"S" | "T" | "U" |
+          "V" | "W" | "X" | "Y" | "Z" | "_" | ":"
+val DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
+val ID = SYM ~ (SYM | DIGIT).% 
+val NUM = PLUS(DIGIT)
+val FNUM = OPT("-") ~ NUM ~ "." ~ NUM 
+val KEYWORD : Rexp = "if" | "then" | "else" | "def" | "val"
+val TYPE : Rexp = "Void" | "Int" | "Double" 
+val SEMI: Rexp = ";"
+val COLON: Rexp = ":"
+val COMMA: Rexp = ","
+val OP: Rexp = "=" | "==" | "-" | "+" | "*" | "!=" | "<" | ">" | "<=" | ">=" | "%" | "/"
+val WHITESPACE = PLUS(" " | "\n" | "\t" | "\r")
+val RPAREN: Rexp = ")" | "}"
+val LPAREN: Rexp = "(" | "{"
+val ALL = SYM | DIGIT | OP | " " | ":" | ";" | "-" | "." | "\"" | "=" | "," | "(" | ")" | "{" | "}"
+val ALL2 = ALL | "\n"
+val COMMENT = ("/*" ~ ALL2.% ~ "*/") | ("//" ~ ALL.% ~ "\n")
+
+val CHR :Rexp = "'" ~ (ALL | "\\n") ~ "'" 
+
+
+val FUN_REGS = (("k" $ KEYWORD) | 
+                ("t" $ TYPE) |
+                ("i" $ ID) | 
+                ("ch" $ CHR) | 
+                ("o" $ OP) | 
+                ("n" $ NUM) | 
+                ("f" $ FNUM) | 
+                ("s" $ SEMI) | 
+                ("co" $ COLON) |
+                ("c" $ COMMA) |
+                ("pl" $ LPAREN) |
+                ("pr" $ RPAREN) |
+                ("w" $ (WHITESPACE | COMMENT))).%
+
+
+
+// The tokens for the Fun language
+
+abstract class Token extends Serializable 
+case object T_SEMI extends Token
+case object T_COMMA extends Token
+case object T_COLON extends Token
+case object T_LPAREN extends Token
+case object T_RPAREN extends Token
+case class T_ID(s: String) extends Token
+case class T_FID(s: String) extends Token
+case class T_OP(s: String) extends Token
+case class T_NUM(n: Int) extends Token
+case class T_FNUM(x: Double) extends Token
+case class T_KWD(s: String) extends Token
+case class T_TY(s: String) extends Token
+case class T_CHR(i: Int) extends Token
+
+val token : PartialFunction[(String, String), Token] = {
+  case ("k", s) => T_KWD(s)
+  case ("t", s) => T_TY(s)
+  case ("i", s) => T_ID(s)
+  case ("o", s) => T_OP(s)
+  case ("n", s) => T_NUM(s.toInt)
+  case ("ch", s) => if (s == "'\\n'") T_CHR(10) else T_CHR(s(1).toInt)
+  case ("f", s) => T_FNUM(s.toDouble) 
+  case ("s", _) => T_SEMI
+  case ("c", _) => T_COMMA
+  case ("co", _) => T_COLON
+  case ("pl", _) => T_LPAREN
+  case ("pr", _) => T_RPAREN
+}
+
+
+def tokenise(s: String) : List[Token] = {
+  val tks = lexing_simp(FUN_REGS, s).collect(token)
+  if (tks.length != 0) tks
+  else { println (s"Tokenise Error") ; sys.exit(-1) }     
+}
+
+//import ammonite.ops._
+
+//@doc("Tokenising a file.")
+@main
+def main(fname: String) = {
+  println(tokenise(os.read(os.pwd / fname)))
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw5/hanoi.fun	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,13 @@
+// Towers of Hanoi in Fun
+
+def hanoi(n: Int, a: Int, b: Int, c: Int) : Void =
+  if n != 0 then {
+    hanoi(n - 1, a, c, b);
+    print_int(a);
+    print_char('-'); print_char('>'); // prints out "->"
+    print_int(b);
+    print_char('\n');
+    hanoi(n - 1, c, b, a)
+  } else skip;
+
+hanoi(4,1,2,3)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw5/mand.fun	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,36 @@
+// Mandelbrot program (without character constants)
+
+val Ymin: Double = -1.3;
+val Ymax: Double =  1.3;
+val Ystep: Double = 0.05;  //0.025;
+
+val Xmin: Double = -2.1;
+val Xmax: Double =  1.1;
+val Xstep: Double = 0.02;  //0.01;
+
+val Maxiters: Int = 1000;
+
+def m_iter(m: Int, x: Double, y: Double,
+                   zr: Double, zi: Double) : Void = {
+  if Maxiters <= m
+  then print_star() 
+  else {
+    if 4.0 <= zi*zi+zr*zr then print_space() 
+    else m_iter(m + 1, x, y, x+zr*zr-zi*zi, 2.0*zr*zi+y) 
+  }
+};
+
+def x_iter(x: Double, y: Double) : Void = {
+  if x <= Xmax
+  then { m_iter(0, x, y, 0.0, 0.0) ; x_iter(x + Xstep, y) }
+  else skip()
+};
+
+def y_iter(y: Double) : Void = {
+  if y <= Ymax
+  then { x_iter(Xmin, y) ; new_line() ; y_iter(y + Ystep) }
+  else skip() 
+};    
+
+
+y_iter(Ymin)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw5/mand2.fun	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,36 @@
+// Mandelbrot program (with character constants)
+
+val Ymin: Double = -1.3;
+val Ymax: Double =  1.3;
+val Ystep: Double = 0.05;  //0.025;
+
+val Xmin: Double = -2.1;
+val Xmax: Double =  1.1;
+val Xstep: Double = 0.02;  //0.01;
+
+val Maxiters: Int = 1000;
+
+def m_iter(m: Int, x: Double, y: Double,
+                   zr: Double, zi: Double) : Void = {
+  if Maxiters <= m
+  then print_char(' ') 
+  else {
+    if 4.0 <= zi*zi+zr*zr then print_char('0' + (m % 10)) 
+    else m_iter(m + 1, x, y, x+zr*zr-zi*zi, 2.0*zr*zi+y) 
+  }
+};
+
+def x_iter(x: Double, y: Double) : Void = {
+  if x <= Xmax
+  then { m_iter(0, x, y, 0.0, 0.0) ; x_iter(x + Xstep, y) }
+  else skip()
+};
+
+def y_iter(y: Double) : Void = {
+  if y <= Ymax
+  then { x_iter(Xmin, y) ; print_char('\n') ; y_iter(y + Ystep) }
+  else skip() 
+};    
+
+
+y_iter(Ymin)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/solutions/cw5/sqr.fun	Fri Nov 04 12:07:40 2022 +0000
@@ -0,0 +1,15 @@
+val Max : Int = 10;
+
+def sqr(x: Int) : Int = x * x;
+
+def all(n: Int) : Void = {
+  if n <= Max
+  then { print_int(sqr(n)) ; new_line(); all(n + 1) }
+  else skip()
+};
+
+{
+  print_string("Squares");
+  new_line();
+  all(0)
+} 
\ No newline at end of file