Binary file cws/cw04.pdf has changed
--- a/cws/cw04.tex Sun Sep 17 19:12:57 2023 +0100
+++ b/cws/cw04.tex Tue Sep 19 09:54:41 2023 +0100
@@ -22,9 +22,11 @@
language you like, but you need to submit the source code with
which you answered the questions, otherwise a mark of 0\% will
be awarded. You should use the lexer and parser from the
-previous courseworks. Please package \emph{everything}(!) in
-a zip-file that creates a directory with the name
-\texttt{YournameYourFamilyname} on my end.
+previous courseworks.
+
+%Please package \emph{everything}(!) in
+%a zip-file that creates a directory with the name
+%\texttt{YournameYourFamilyname} on my end.
\subsection*{Disclaimer\alert}
@@ -135,11 +137,11 @@
%will \emph{only} be judged according to the answers. You can
%submit your answers in a txt-file or as pdf.\bigskip
-\subsection*{Question 0 (Unmarked)}
-
-Please include in the PDF at the beginning your email address, your student
-number and whether you are BSc / MSci student and for the latter in which
-year your are in. Thanks!
+%\subsection*{Question 0 (Unmarked)}
+%
+%Please include in the PDF at the beginning your email address, your student
+%number and whether you are BSc / MSci student and for the latter in which
+%year your are in. Thanks!
\subsection*{Question 1}
@@ -233,16 +235,43 @@
Explain your decision and indicate what this program would
print out.
-\subsection*{Question 4 (Advanced)}
+\subsection*{Question 4}
+
+Extend the lexer and parser to add a \texttt{break} keyword. Modify
+the compiler such that when a \texttt{break}-statement is encountered
+the code should jump out of the ``enclosing'' for-loop, or in case it
+is not inside a for-loop to the end of the program. For example the
+program
+
+\begin{center}
+\begin{minipage}{12cm}
+\begin{lstlisting}[language=While, numbers=none]
+for i := 0 upto 10 do {
+ write i;
+ write "\n"
+};
-Extend the lexer and parser in order to add a \texttt{break} keyword.
-Modify the compiler such that when a \texttt{break} is encountered the
-code should jump out of the ``enclosing'' while loop, or in case it
-is not inside a while loop to the end of the
-program.
+for i := 0 upto 10 do {
+ if i > 4 then break else skip;
+ write i;
+ write "\n"
+};
-\bigskip\noindent
-\textcolor{red}{ADD EXAMPLE PROGRAMS}
+write "Should print\n";
+break;
+write "Should not print\n"
+\end{lstlisting}
+\end{minipage}
+\end{center}
+
+\noindent
+should print out 1 to 10 with the first for-loop, but only 1
+to 4 in the second. Similarly it should print out \code{"Should print"},
+but not \code{"Should not print"}. For this you need to add
+a label to the end of every for-loop and also to the end of the
+whole program just in case you need to jump to that label via a
+\code{break}.
+
\subsection*{Further Information}
--- a/langs.sty Sun Sep 17 19:12:57 2023 +0100
+++ b/langs.sty Tue Sep 19 09:54:41 2023 +0100
@@ -56,7 +56,7 @@
}[keywords,comments,strings]
\lstdefinelanguage{While}{
- morekeywords={if,then,else,while,do,true,false,write,upto,read,for,skip,new},
+ morekeywords={if,then,else,while,do,true,false,write,upto,read,for,skip,new,break},
morecomment=[l]{//},
morecomment=[n]{/*}{*/},
morestring=[b]",
--- a/solutions/cw2/collatz.while Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw2/collatz.while Tue Sep 19 09:54:41 2023 +0100
@@ -2,7 +2,7 @@
read n;
while n > 1 do {
if n % 2 == 0
- then n := n / 2
- else n := 3 * n + 1;
+ then n := n/2
+ else n := 3*n+1
};
-write "Yes\n";
+write "Yes\n"
--- a/solutions/cw2/factors.while Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw2/factors.while Tue Sep 19 09:54:41 2023 +0100
@@ -3,12 +3,9 @@
write "Input n please";
read n;
-write "The factors of n are\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
+while (f < n / 2 + 1) do {
+ if ((n / f) * f == n) then { write(f); write "\n" } else { skip };
+ f := f + 1
+}
--- a/solutions/cw2/fib.while Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw2/fib.while Tue Sep 19 09:54:41 2023 +0100
@@ -1,7 +1,7 @@
-write "Fib";
+write "Fib: ";
read n;
-minus1 := 1;
-minus2 := 0;
+minus1 := 0;
+minus2 := 1;
while n > 0 do {
temp := minus2;
minus2 := minus1 + minus2;
--- a/solutions/cw2/lexer.sc Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw2/lexer.sc Tue Sep 19 09:54:41 2023 +0100
@@ -1,5 +1,7 @@
-import scala.language.implicitConversions
-import scala.language.reflectiveCalls
+// CW 2
+//======
+
+
// Rexp
abstract class Rexp
@@ -27,7 +29,7 @@
case class Rec(x: String, v: Val) extends Val
-// Convenience typing
+// Convenience for typing
def charlist2rexp(s : List[Char]): Rexp = s match {
case Nil => ONE
case c::Nil => CHAR(c)
@@ -207,17 +209,18 @@
// 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 LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z').toSet)
+val SYM : Rexp = 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 STRING : Rexp = "\"" ~ (LET | DIGIT | SYM | PARENS | " " | "\\n").% ~ "\""
val ID : Rexp = LET ~ (LET | "_" | DIGIT).%
val NUM : Rexp = "0" | (DIGIT1 ~ DIGIT.%)
-val COMMENT : Rexp = "//" ~ (SYM | " " | DIGIT).% ~ ("\n" | "\r\n")
+val EOL : Rexp = "\n" | "\r\n"
+val COMMENT : Rexp = "//" ~ (LET | DIGIT | SYM | " ").% ~ EOL
val WHILE_REGS = (("k" $ KEYWORD) |
("o" $ OP) |
@@ -227,7 +230,7 @@
("w" $ WHITESPACE) |
("i" $ ID) |
("n" $ NUM) |
- ("c" $ COMMENT)).%
+ ("c" $ COMMENT)).%
def esc(raw: String): String = {
import scala.reflect.runtime.universe._
@@ -263,6 +266,7 @@
// Q2 Tests
+
lex_simp(NTIMES("a", 3), "aaa".toList)
lex_simp(NTIMES(("a" | ONE), 3), "aa".toList)
@@ -312,7 +316,7 @@
println(tokenise(prog3))
-println("MY TESTS")
+// More tests
println(lex_simp("x" $ OPTIONAL("a"), "a".toList))
println(lex_simp("x" $ OPTIONAL("a"), "".toList))
--- a/solutions/cw3/lexer.sc Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw3/lexer.sc Tue Sep 19 09:54:41 2023 +0100
@@ -1,5 +1,6 @@
-import scala.language.implicitConversions
-import scala.language.reflectiveCalls
+// Lexer from CW2
+//================
+
// Rexp
abstract class Rexp
@@ -27,7 +28,7 @@
case class Rec(x: String, v: Val) extends Val
-// Convenience typing
+// Convenience for typing
def charlist2rexp(s : List[Char]): Rexp = s match {
case Nil => ONE
case c::Nil => CHAR(c)
@@ -136,7 +137,7 @@
case (RANGE(_), Empty) => Chr(c)
case (PLUS(r), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
- case (OPTIONAL(r), Left(v1)) => Stars(List(inj(r, c, v1)))
+ case (OPTIONAL(r), v1) => Stars(List(inj(r, c, v1)))
case (NTIMES(r, n), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
}
--- a/solutions/cw4/compiler.sc Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw4/compiler.sc Tue Sep 19 09:54:41 2023 +0100
@@ -92,7 +92,7 @@
x ++ "_" ++ counter.toString()
}
-implicit def string_interpolations(sc: StringContext) = new {
+extension (sc: StringContext) {
def i(args: Any*): String = " " ++ sc.s(args:_*) ++ "\n"
def l(args: Any*): String = sc.s(args:_*) ++ ":\n"
}
@@ -138,7 +138,7 @@
compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ i"${compile_bop(op)} $jmp"
}
-def compile_stmt(s: Stmt, env: Env) : (String, Env) = s match {
+def compile_stmt(s: Stmt, env: Env, break: String) : (String, Env) = s match {
case Skip => ("", env)
case Assign(x, a) => {
val index = env.getOrElse(x, env.keys.size)
@@ -147,8 +147,8 @@
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)
+ val (instrs1, env1) = compile_block(bl1, env, break)
+ val (instrs2, env2) = compile_block(bl2, env1, break)
(compile_bexp(b, env, if_else) ++
instrs1 ++
i"goto $if_end" ++
@@ -159,7 +159,7 @@
case While(b, bl) => {
val loop_begin = Fresh("Loop_begin")
val loop_end = Fresh("Loop_end")
- val (instrs1, env1) = compile_block(bl, env)
+ val (instrs1, env1) = compile_block(bl, env, break)
(l"$loop_begin" ++
compile_bexp(b, env, loop_end) ++
instrs1 ++
@@ -167,17 +167,18 @@
l"$loop_end", env1)
}
case For(id, lower, upper, code) => {
- val (assignment_code, env1) = compile_stmt(Assign(id, lower), env) // id := lower;
+ val (assignment_code, env1) = compile_stmt(Assign(id, lower), env, break) // 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)
+ val new_break = Fresh("for_break")
+ val (while_code, env2) = compile_stmt(while_equivalent, env1, new_break)
+ (assignment_code ++ while_code ++ l"$new_break", env2)
}
+ case Break => (i"goto $break", env)
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" ++
@@ -189,38 +190,33 @@
}
}
-def compile_block(bl: Block, env: Env) : (String, Env) = bl match {
+def compile_block(bl: Block, env: Env, break: String) : (String, Env) = bl match {
case Nil => ("", env)
case s::bl => {
- val (instrs1, env1) = compile_stmt(s, env)
- val (instrs2, env2) = compile_block(bl, env1)
+ val (instrs1, env1) = compile_stmt(s, env, break)
+ val (instrs2, env2) = compile_block(bl, env1, break)
(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)
+ val prog_end = Fresh("Prog_end")
+ val instructions = compile_block(bl, Map.empty, prog_end)._1
+ (beginning ++ instructions ++ l"$prog_end" ++ 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_to_file(bl: Block, class_name: String) : Unit =
+ os.write.over(os.pwd / s"$class_name.j", compile(bl, class_name))
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 ")
+ println(s"Start of compilation")
+ compile_to_file(bl, class_name)
+ println(s"generated $class_name.j file")
+ os.proc("java", "-jar", "jasmin.jar", s"$class_name.j").call()
+ println(s"generated $class_name.class file ")
}
def time_needed[T](i: Int, code: => T) = {
@@ -231,10 +227,10 @@
}
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).!))
+ os.proc("java", s"${class_name}/${class_name}").call(stdout = os.Inherit)
+ println(s"done.")
}
// ---- Q1
@@ -280,4 +276,8 @@
}""")).head, "nestedloop") */
-compile_run(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head, "collatz2")
+//compile_run(Stmts.parse_all(tokenise(os.read(os.pwd / "collatz2.while"))).head, "collatz2")
+
+
+println(tokenise(os.read(os.pwd / "forloop.while")))
+compile_run(Stmts.parse_all(tokenise(os.read(os.pwd / "forloop.while"))).head, "forloop")
\ No newline at end of file
--- a/solutions/cw4/lexer.sc Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw4/lexer.sc Tue Sep 19 09:54:41 2023 +0100
@@ -1,5 +1,5 @@
-import scala.language.implicitConversions
-import scala.language.reflectiveCalls
+// Lexer from CW2 with additions to the rules
+//============================================
// Rexp
abstract class Rexp
@@ -27,7 +27,7 @@
case class Rec(x: String, v: Val) extends Val
-// Convenience typing
+// Convenience for typing
def charlist2rexp(s : List[Char]): Rexp = s match {
case Nil => ONE
case c::Nil => CHAR(c)
@@ -37,13 +37,13 @@
implicit def string2rexp(s : String) : Rexp =
charlist2rexp(s.toList)
-implicit def RexpOps(r: Rexp) = new {
+extension (r: Rexp) {
def | (s: Rexp) = ALT(r, s)
def % = STAR(r)
def ~ (s: Rexp) = SEQ(r, s)
}
-implicit def stringOps(s: String) = new {
+extension (s: String) {
def | (r: Rexp) = ALT(s, r)
def | (r: String) = ALT(s, r)
def % = STAR(s)
@@ -82,7 +82,7 @@
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 OPTIONAL(r1) => der(c, r1)
case NTIMES(r, i) =>
if (i == 0) ZERO else SEQ(der(c, r), NTIMES(r, i - 1))
}
@@ -119,7 +119,7 @@
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 OPTIONAL(r) => if (nullable(r)) Stars(List(mkeps(r))) else Stars(Nil)
case NTIMES(r, i) => Stars(List.fill(i)(mkeps(r)))
}
@@ -136,7 +136,7 @@
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 (OPTIONAL(r), v1) => Stars(List(inj(r, c, v1)))
case (NTIMES(r, n), Sequ(v1, Stars(vs))) => Stars(inj(r, c, v1)::vs)
}
@@ -199,20 +199,21 @@
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"
+// Language specific code
+val KEYWORD : Rexp = "while" | "if" | "then" | "else" | "do" | "for" | "upto" | "true" | "false" | "read" | "write" | "skip" | "break"
val OP : Rexp = "+" | "-" | "*" | "%" | "/" | "==" | "!=" | ">" | "<" | ">=" | "<=" | ":=" | "&&" | "||"
-val LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z'))
-val SYM : Rexp = LET | RANGE(Set('.', '_', '>', '<', '=', ';', ',', ':'))
+val LET: Rexp = RANGE(('A' to 'Z').toSet ++ ('a' to 'z').toSet)
+val SYM : Rexp = 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 STRING : Rexp = "\"" ~ (LET | SYM | DIGIT | " " | "\\n").% ~ "\""
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 COMMENT : Rexp = "//" ~ (LET | SYM | PARENS | " " | DIGIT).% ~ EOL
val WHILE_REGS = (("k" $ KEYWORD) |
("o" $ OP) |
@@ -224,6 +225,8 @@
("n" $ NUM) |
("c" $ COMMENT)).%
+
+
// Token
abstract class Token extends Serializable
case class T_KEYWORD(s: String) extends Token
--- a/solutions/cw4/parser.sc Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw4/parser.sc Tue Sep 19 09:54:41 2023 +0100
@@ -3,33 +3,65 @@
import $file.lexer
import lexer._
+case class ~[+A, +B](x: A, y: B)
-case class ~[+A, +B](_1: A, _2: B)
-type IsSeq[A] = A => Seq[_]
+// parser combinators
+
+abstract class Parser[I, T](using is: I => Seq[_]) {
+ def parse(in: I): Set[(T, I)]
+
+ def parse_all(in: I) : Set[T] =
+ for ((hd, tl) <- parse(in);
+ if is(tl).isEmpty) yield hd
+}
-abstract class Parser[I : IsSeq, T] {
- def parse(ts: I): Set[(T, I)]
+// alternative parser
+class AltParser[I, T](p: => Parser[I, T],
+ q: => Parser[I, T])(using I => Seq[_]) extends Parser[I, T] {
+ def parse(in: I) = p.parse(in) ++ q.parse(in)
+}
- def parse_all(ts: I) : Set[T] =
- for ((head, tail) <- parse(ts); if tail.isEmpty) yield head
+// sequence parser
+class SeqParser[I, T, S](p: => Parser[I, T],
+ q: => Parser[I, S])(using I => Seq[_]) extends Parser[I, ~[T, S]] {
+ def parse(in: I) =
+ for ((hd1, tl1) <- p.parse(in);
+ (hd2, tl2) <- q.parse(tl1)) yield (new ~(hd1, hd2), tl2)
}
-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)
+// map parser
+class MapParser[I, T, S](p: => Parser[I, T],
+ f: T => S)(using I => Seq[_]) extends Parser[I, S] {
+ def parse(in: I) = for ((hd, tl) <- p.parse(in)) yield (f(hd), tl)
+}
+
+// more convenient syntax for parser combinators
+extension [I, T](p: Parser[I, T])(using I => Seq[_]) {
+ def ||(q : => Parser[I, T]) = new AltParser[I, T](p, q)
+ def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
+ def map[S](f: => T => S) = new MapParser[I, T, S](p, f)
}
-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)
+/*
+// atomic parser for (particular) strings
+case class StrParser(s: String) extends Parser[String, String] {
+ def parse(sb: String) = {
+ val (prefix, suffix) = sb.splitAt(s.length)
+ if (prefix == s) Set((prefix, suffix)) else Set()
+ }
}
-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)
-}
+extension (sc: StringContext)
+ def p(args: Any*) = StrParser(sc.s(args:_*))
+*/
-// New parser that takes as input a list of tokens
+case class TokenParser(t: Token) extends Parser[List[Token], Token] {
+ def parse(in: List[Token]) = {
+ // an example of an atomic parser for characters
+ if (!in.isEmpty && in.head == t) Set((t, in.tail)) else Set()
+ }
+}
+
case class TokenListParser(ts: List[Token]) extends Parser[List[Token], List[Token]] {
def parse(tsb: List[Token]) = {
val (prefix, suffix) = tsb.splitAt(ts.length)
@@ -39,34 +71,17 @@
// 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 token2parser(t: Token) : Parser[List[Token], Token] =
+ TokenParser(t)
-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)
+extension (t: Token) {
+ def || (q : => Parser[List[Token], Token]) =
+ new AltParser[List[Token], Token](t, q)
+ def ~[S](q : => Parser[List[Token], S]) =
+ new SeqParser[List[Token], Token, S](t, 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
@@ -83,6 +98,7 @@
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 object Break extends Stmt
case class Var(s: String) extends AExp
@@ -117,46 +133,51 @@
}
// WHILE Language Parsing
+
+// 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
+ (Te ~ T_OP("+") ~ AExp).map{ case x ~ _ ~ z => Aop("+", x, z): AExp } ||
+ (Te ~ T_OP("-") ~ AExp).map{ 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
+ (Fa ~ T_OP("*") ~ Te).map{ case x ~ _ ~ z => Aop("*", x, z): AExp } ||
+ (Fa ~ T_OP("/") ~ Te).map{ case x ~ _ ~ z => Aop("/", x, z): AExp } ||
+ (Fa ~ T_OP("%") ~ Te).map{ 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
+ (T_PAREN("(") ~ AExp ~ T_PAREN(")")).map{ case _ ~ y ~ _ => y } ||
+ IdParser().map{Var(_)} ||
+ NumParser().map{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 }
+ (AExp ~ T_OP("==") ~ AExp).map{ case x ~ _ ~ z => Bop("==", x, z): BExp } ||
+ (AExp ~ T_OP("!=") ~ AExp).map{ case x ~ _ ~ z => Bop("!=", x, z): BExp } ||
+ (AExp ~ T_OP("<") ~ AExp).map{ case x ~ _ ~ z => Bop("<", x, z): BExp } ||
+ (AExp ~ T_OP(">") ~ AExp).map{ case x ~ _ ~ z => Bop(">", x, z): BExp } ||
+ (T_PAREN("(") ~ BExp ~ T_PAREN(")") ~ T_OP("&&") ~ BExp).map{ case _ ~ y ~ _ ~ _ ~ v => And(y, v): BExp } ||
+ (T_PAREN("(") ~ BExp ~ T_PAREN(")") ~ T_OP("||") ~ BExp).map{ case _ ~ y ~ _ ~ _ ~ v => Or(y, v): BExp } ||
+ (T_KEYWORD("true").map(_ => True: BExp )) ||
+ (T_KEYWORD("false").map(_ => False: BExp )) ||
+ (T_PAREN("(") ~ BExp ~ T_PAREN(")")).map{ 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
- }
+ T_KEYWORD("skip").map(_ => Skip: Stmt) ||
+ T_KEYWORD("break").map(_ => Break: Stmt) ||
+ (IdParser() ~ T_OP(":=") ~ AExp).map{ case id ~ _ ~ z => Assign(id, z): Stmt } ||
+ (T_KEYWORD("if") ~ BExp ~ T_KEYWORD("then") ~ Block ~ T_KEYWORD("else") ~ Block).map{ case _ ~ y ~ _ ~ u ~ _ ~ w => If(y, u, w): Stmt } ||
+ (T_KEYWORD("while") ~ BExp ~ T_KEYWORD("do") ~ Block).map{ case _ ~ y ~ _ ~ w => While(y, w) : Stmt } ||
+ (T_KEYWORD("for") ~ IdParser() ~ T_OP(":=") ~ AExp ~T_KEYWORD("upto") ~ AExp ~ T_KEYWORD("do") ~ Block).map{
+ case _ ~ id ~ _ ~ low ~ _ ~ high ~ _ ~ bl => For(id, low, high, bl) : Stmt } ||
+ (T_KEYWORD("read") ~ IdParser()).map{ case _ ~ id => Read(id): Stmt} ||
+ (T_KEYWORD("write") ~ IdParser()).map{ case _ ~ id => WriteId(id): Stmt} ||
+ (T_KEYWORD("write") ~ StringParser()).map{ case _ ~ s => WriteString(s): Stmt} ||
+ (T_KEYWORD("write") ~ T_PAREN("(") ~ IdParser() ~ T_PAREN(")")).map{ case _ ~ _ ~ id ~ _ => WriteId(id): Stmt} ||
+ (T_KEYWORD("write") ~ T_PAREN("(") ~ StringParser() ~ T_PAREN(")")).map{ 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))
+ (Stmt ~ T_SEMI ~ Stmts).map{ case x ~ _ ~ z => x :: z : Block } ||
+ (Stmt.map(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)))
+ (T_PAREN("{") ~ Stmts ~ T_PAREN("}")).map{ case x ~ y ~ z => y} ||
+ (Stmt.map(s => List(s)))
+
--- a/solutions/cw5/fun_llvm.sc Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw5/fun_llvm.sc Tue Sep 19 09:54:41 2023 +0100
@@ -195,10 +195,9 @@
// convenient string interpolations
// for instructions, labels and methods
-import scala.language.implicitConversions
-import scala.language.reflectiveCalls
+
-implicit def string_inters(sc: StringContext) = new {
+extension (sc: StringContext) {
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"
--- a/solutions/cw5/fun_parser.sc Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw5/fun_parser.sc Tue Sep 19 09:54:41 2023 +0100
@@ -15,46 +15,44 @@
// case x ~ y ~ z => ...
case class ~[+A, +B](x: A, y: B)
-// constraint for the input
-type IsSeq[A] = A => Seq[_]
-
-abstract class Parser[I : IsSeq, T]{
- def parse(in: I): Set[(T, I)]
+// parser combinators
+abstract class Parser[I, T](using is: I => Seq[_]) {
+ def parse(in: I): Set[(T, I)]
def parse_all(in: I) : Set[T] =
- for ((hd, tl) <- parse(in);
- if tl.isEmpty) yield hd
+ for ((hd, tl) <- parse(in);
+ if is(tl).isEmpty) yield hd
}
-// parser combinators
+// alternative parser
+class AltParser[I, T](p: => Parser[I, T],
+ q: => Parser[I, T])(using I => Seq[_]) extends Parser[I, T] {
+ def parse(in: I) = p.parse(in) ++ q.parse(in)
+}
// sequence parser
-class SeqParser[I : IsSeq, T, S](p: => Parser[I, T],
- q: => Parser[I, S]) extends Parser[I, ~[T, S]] {
- def parse(in: I) =
- for ((hd1, tl1) <- p.parse(in);
+class SeqParser[I, T, S](p: => Parser[I, T],
+ q: => Parser[I, S])(using I => Seq[_]) extends Parser[I, ~[T, S]] {
+ def parse(in: I) =
+ for ((hd1, tl1) <- p.parse(in);
(hd2, tl2) <- q.parse(tl1)) yield (new ~(hd1, hd2), tl2)
}
-// alternative parser
-class AltParser[I : IsSeq, T](p: => Parser[I, T],
- q: => Parser[I, T]) extends Parser[I, T] {
- def parse(in: I) = p.parse(in) ++ q.parse(in)
-}
-
// map parser
-class MapParser[I : IsSeq, T, S](p: => Parser[I, T],
- f: T => S) extends Parser[I, S] {
+class MapParser[I, T, S](p: => Parser[I, T],
+ f: T => S)(using I => Seq[_]) extends Parser[I, S] {
def parse(in: I) = for ((hd, tl) <- p.parse(in)) yield (f(hd), tl)
}
+
// more convenient syntax for parser combinators
-implicit def ParserOps[I : IsSeq, T](p: Parser[I, T]) = new {
+extension [I, T](p: Parser[I, T])(using I => Seq[_]) {
def ||(q : => Parser[I, T]) = new AltParser[I, T](p, q)
def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
def map[S](f: => T => S) = new MapParser[I, T, S](p, f)
}
+
// -------------------------------------------------
// atomic parsers
@@ -146,8 +144,8 @@
}
}
-implicit def parser_interpolation(sc: StringContext) = new {
- def p(args: Any*) = StrParser(sc.s(args:_*))
+extension (sc: StringContext) {
+ def p(args: Any*) = StrParser(sc.s(args:_*))
}
--- a/solutions/cw5/fun_tokens.sc Sun Sep 17 19:12:57 2023 +0100
+++ b/solutions/cw5/fun_tokens.sc Tue Sep 19 09:54:41 2023 +0100
@@ -33,9 +33,6 @@
case class Left(v: Val) extends Val
case class Right(v: Val) extends Val
case class Stars(vs: List[Val]) extends Val
-case class Opt(v: Val) extends Val
-case class Pls(vs: List[Val]) extends Val
-case class Nt(vs: List[Val]) extends Val
case class Rec(x: String, v: Val) extends Val
// some convenience for typing in regular expressions
@@ -45,30 +42,26 @@
case c::vs => SEQ(CHAR(c), charlist2rexp(vs))
}
-implicit def string2rexp(s : String) : Rexp =
+implicit def string2rexp(s : String) : Rexp =
charlist2rexp(s.toList)
-implicit def RexpOps(r: Rexp) = new {
+extension (r: Rexp) {
def | (s: Rexp) = ALT(r, s)
def % = STAR(r)
- def ? = OPTIONAL(r)
def + = PLUS(r)
- def ^ (n: Int) = NTIMES(r, n)
def ~ (s: Rexp) = SEQ(r, s)
}
-implicit def stringOps(s: String) = new {
+extension (s: String) {
def | (r: Rexp) = ALT(s, r)
def | (r: String) = ALT(s, r)
def % = STAR(s)
- def ? = OPTIONAL(s)
- def + = PLUS(s)
- def ^ (n: Int) = NTIMES(s, n)
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
@@ -107,9 +100,6 @@
case Right(v) => flatten(v)
case Sequ(v1, v2) => flatten(v1) ++ flatten(v2)
case Stars(vs) => vs.map(flatten).mkString
- case Opt(v) => flatten(v)
- case Pls(vs) => vs.map(flatten).mkString
- case Nt(vs) => vs.map(flatten).mkString
case Rec(_, v) => flatten(v)
}
@@ -122,9 +112,6 @@
case Right(v) => env(v)
case Sequ(v1, v2) => env(v1) ::: env(v2)
case Stars(vs) => vs.flatMap(env)
- case Opt(v) => env(v)
- case Pls(vs) => vs.flatMap(env)
- case Nt(vs) => vs.flatMap(env)
case Rec(x, v) => (x, flatten(v))::env(v)
}
@@ -132,20 +119,21 @@
// The injection and mkeps part of the lexer
//===========================================
+// Mkeps
def mkeps(r: Rexp) : Val = r match {
case ONE => Empty
- case RANGE(chars) => throw new Exception("lexing error") // this will never be called but the coursework asks for it so...
- case ALT(r1, r2) =>
+ 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 OPTIONAL(r) => Opt(Empty)
- case PLUS(r) => Pls(List(mkeps(r))) // scala define a list with one element
- case NTIMES(r, n) => if (n == 0) Nt(Nil) else Nt(List.fill(n)(mkeps(r))) // wrong
case RECD(x, r) => Rec(x, mkeps(r))
- case _ => throw new Exception("lexing error")
+
+ 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)
@@ -153,14 +141,16 @@
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 (RANGE(chars), Empty) => Chr(c)
- case (OPTIONAL(r1), v) => Opt(inj(r1, c, v))
- case (PLUS(r1), Sequ(v1, Stars(vs))) => Pls(inj(r1, c, v1)::vs)
- case (NTIMES(r1, n), Sequ(v1, Nt(vs))) => Nt(inj(r1, c, v1)::vs)
+ 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)
}
+
// some "rectification" functions for simplification
def F_ID(v: Val): Val = v
def F_RIGHT(f: Val => Val) = (v:Val) => Right(f(v))