--- 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))