diff -r 53f08d873e09 -r 7af2eea19646 solutions/cw5/fun_tokens.sc --- 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))