| author | Christian Urban <christian.urban@kcl.ac.uk> | 
| Fri, 24 Oct 2025 10:52:05 +0100 | |
| changeset 1018 | ab6c61f82c91 | 
| parent 93 | 4794759139ea | 
| permissions | -rw-r--r-- | 
| 92 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 1 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 2 | // a class for deterministic finite automata, | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 3 | // the type of states is kept polymorphic | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 4 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 5 | case class Automaton[A](start: A, states: Set[A], delta: Map[(A, Char), A], fins: Set[A]) {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 6 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 7 | // the transition function lifted to list of characters | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 8 | def deltas(q: A, cs: List[Char]) : A = | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 9 |     if (states.contains(q)) cs match {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 10 | case Nil => q | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 11 | case c::cs => | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 12 | if (delta.isDefinedAt(q, c)) deltas(delta(q, c), cs) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 13 | else throw new RuntimeException(q + " does not have a transition for " + c) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 14 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 15 | else throw new RuntimeException(q + " is not a state of the automaton") | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 16 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 17 | // wether a string is accepted by the automaton | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 18 | def accepts(s: String) = | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 19 |     try {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 20 | fins.contains(deltas(start, s.toList)) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 21 |     } catch {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 22 | case e:RuntimeException => false | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 23 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 24 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 25 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 26 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 27 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 28 | abstract class Rexp | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 29 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 30 | case object NULL extends Rexp | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 31 | case object EMPTY extends Rexp | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 32 | case class CHAR(c: Char) extends Rexp | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 33 | case class ALT(r1: Rexp, r2: Rexp) extends Rexp | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 34 | case class SEQ(r1: Rexp, r2: Rexp) extends Rexp | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 35 | case class STAR(r: Rexp) extends Rexp | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 36 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 37 | implicit def string2rexp(s : String) = { 
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 38 |   def chars2rexp (cs: List[Char]) : Rexp = cs match {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 39 | case Nil => EMPTY | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 40 | case c::Nil => CHAR(c) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 41 | case c::cs => SEQ(CHAR(c), chars2rexp(cs)) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 42 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 43 | chars2rexp(s.toList) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 44 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 45 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 46 | def nullable (r: Rexp) : Boolean = r match {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 47 | case NULL => false | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 48 | case EMPTY => true | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 49 | case CHAR(_) => false | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 50 | case ALT(r1, r2) => nullable(r1) || nullable(r2) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 51 | case SEQ(r1, r2) => nullable(r1) && nullable(r2) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 52 | case STAR(_) => true | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 53 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 54 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 55 | def der (r: Rexp, c: Char) : Rexp = r match {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 56 | case NULL => NULL | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 57 | case EMPTY => NULL | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 58 | case CHAR(d) => if (c == d) EMPTY else NULL | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 59 | case ALT(r1, r2) => ALT(der(r1, c), der(r2, c)) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 60 | case SEQ(r1, r2) => if (nullable(r1)) ALT(SEQ(der(r1, c), r2), der(r2, c)) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 61 | else SEQ(der(r1, c), r2) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 62 | case STAR(r) => SEQ(der(r, c), STAR(r)) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 63 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 64 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 65 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 66 | // Here we construct an automaton whose | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 67 | // states are regular expressions | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 68 | type State = Rexp | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 69 | type States = Set[State] | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 70 | type Transition = Map[(State, Char), State] | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 71 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 72 | def goto(q: State, c: Char, qs: States, delta: Transition) : (States, Transition) = {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 73 | val qc : State = der(q, c) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 74 | if (qs.contains(qc)) (qs, delta + ((q, c) -> q)) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 75 | else explore(qs + qc, delta + ((q, c) -> qc), qc) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 76 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 77 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 78 | // we use as alphabet all lowercase letters | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 79 | val alphabet = "abcdefghijklmnopqrstuvwxyz".toSet | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 80 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 81 | def explore (qs: States, delta: Transition, q: State) : (States, Transition) = | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 82 | alphabet.foldRight[(States, Transition)] (qs, delta) ((c, qsd) => goto(q, c, qsd._1, qsd._2)) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 83 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 84 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 85 | def mk_automaton (r: Rexp) : Automaton[Rexp] = {
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 86 | val (qs, delta) = explore(Set(r), Map(), r); | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 87 | val fins = for (q <- qs if nullable(q)) yield q; | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 88 | Automaton[Rexp](r, qs, delta, fins) | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 89 | } | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 90 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 91 | val A = mk_automaton(ALT("ab","ac"))
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 92 | |
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 93 | println(A.accepts("bd"))
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 94 | println(A.accepts("ab"))
 | 
| 
e85600529ca5
moved scala files
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: diff
changeset | 95 | println(A.accepts("ac"))
 |