| 
34
 | 
     1  | 
  | 
| 
 | 
     2  | 
// a class for deterministic finite automata,
  | 
| 
 | 
     3  | 
// the type of states is kept polymorphic
  | 
| 
 | 
     4  | 
  | 
| 
 | 
     5  | 
case class Automaton[A](start: A, states: Set[A], delta: Map[(A, Char), A], fins: Set[A]) {
 | 
| 
 | 
     6  | 
  | 
| 
 | 
     7  | 
  // the transition function lifted to list of characters
  | 
| 
 | 
     8  | 
  def deltas(q: A, cs: List[Char]) : Either[A, String] = 
  | 
| 
 | 
     9  | 
    if (states.contains(q)) cs match {
 | 
| 
 | 
    10  | 
      case Nil => Left(q)
  | 
| 
 | 
    11  | 
      case c::cs => 
  | 
| 
 | 
    12  | 
        if (delta.isDefinedAt(q, c)) deltas(delta(q, c), cs)
  | 
| 
 | 
    13  | 
        else Right(q + " does not have a transition for " + c)
  | 
| 
 | 
    14  | 
    }
  | 
| 
 | 
    15  | 
    else Right(q + " is not a state of the automaton")
  | 
| 
 | 
    16  | 
  | 
| 
 | 
    17  | 
  // wether a string is accepted by the automaton
  | 
| 
 | 
    18  | 
  def accepts(s: String) = deltas(start, s.toList) match {
 | 
| 
 | 
    19  | 
    case Left(q) => fins.contains(q)
  | 
| 
 | 
    20  | 
    case _ => false
  | 
| 
 | 
    21  | 
  } 
  | 
| 
 | 
    22  | 
}
  | 
| 
 | 
    23  | 
  | 
| 
 | 
    24  | 
  | 
| 
 | 
    25  | 
// translating a regular expression into a finite
  | 
| 
 | 
    26  | 
// automaton
  | 
| 
 | 
    27  | 
  | 
| 
 | 
    28  | 
abstract class Rexp
  | 
| 
 | 
    29  | 
  | 
| 
 | 
    30  | 
case object NULL extends Rexp
  | 
| 
 | 
    31  | 
case object EMPTY extends Rexp
  | 
| 
 | 
    32  | 
case class CHAR(c: Char) extends Rexp 
  | 
| 
 | 
    33  | 
case class ALT(r1: Rexp, r2: Rexp) extends Rexp
  | 
| 
 | 
    34  | 
case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
  | 
| 
 | 
    35  | 
case class STAR(r: Rexp) extends Rexp
  | 
| 
 | 
    36  | 
  | 
| 
 | 
    37  | 
implicit def string2rexp(s : String) = { 
 | 
| 
 | 
    38  | 
  def chars2rexp (cs: List[Char]) : Rexp = cs match {
 | 
| 
 | 
    39  | 
    case Nil => EMPTY
  | 
| 
 | 
    40  | 
    case c::Nil => CHAR(c)
  | 
| 
 | 
    41  | 
    case c::cs => SEQ(CHAR(c), chars2rexp(cs))
  | 
| 
 | 
    42  | 
  }
  | 
| 
 | 
    43  | 
  chars2rexp(s.toList)
  | 
| 
 | 
    44  | 
}
  | 
| 
 | 
    45  | 
  | 
| 
 | 
    46  | 
def nullable (r: Rexp) : Boolean = r match {
 | 
| 
 | 
    47  | 
  case NULL => false
  | 
| 
 | 
    48  | 
  case EMPTY => true
  | 
| 
 | 
    49  | 
  case CHAR(_) => false
  | 
| 
 | 
    50  | 
  case ALT(r1, r2) => nullable(r1) || nullable(r2)
  | 
| 
 | 
    51  | 
  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
  | 
| 
 | 
    52  | 
  case STAR(_) => true
  | 
| 
 | 
    53  | 
}
  | 
| 
 | 
    54  | 
  | 
| 
 | 
    55  | 
def der (r: Rexp, c: Char) : Rexp = r match {
 | 
| 
 | 
    56  | 
  case NULL => NULL
  | 
| 
 | 
    57  | 
  case EMPTY => NULL
  | 
| 
 | 
    58  | 
  case CHAR(d) => if (c == d) EMPTY else NULL
  | 
| 
 | 
    59  | 
  case ALT(r1, r2) => ALT(der(r1, c), der(r2, c))
  | 
| 
 | 
    60  | 
  case SEQ(r1, r2) => if (nullable(r1)) ALT(SEQ(der(r1, c), r2), der(r2, c))
  | 
| 
 | 
    61  | 
                      else SEQ(der(r1, c), r2)
  | 
| 
 | 
    62  | 
  case STAR(r) => SEQ(der(r, c), STAR(r))
  | 
| 
 | 
    63  | 
}
  | 
| 
 | 
    64  | 
  | 
| 
 | 
    65  | 
  | 
| 
 | 
    66  | 
// Here we construct an automaton whose
  | 
| 
 | 
    67  | 
// states are regular expressions
  | 
| 
 | 
    68  | 
type State = Rexp
  | 
| 
 | 
    69  | 
type States = Set[State]
  | 
| 
 | 
    70  | 
type Transition =  Map[(State, Char), State]
  | 
| 
 | 
    71  | 
  | 
| 
35
 | 
    72  | 
// we use as an alphabet all lowercase letters
  | 
| 
 | 
    73  | 
val alphabet = "abcdefghijklmnopqrstuvwxyz".toSet
  | 
| 
 | 
    74  | 
  | 
| 
34
 | 
    75  | 
def goto(q: State, c: Char, qs: States, delta: Transition) : (States, Transition) = {
 | 
| 
35
 | 
    76  | 
  val q_der : State = der(q, c)
  | 
| 
 | 
    77  | 
  if (qs.contains(q_der)) (qs, delta + ((q, c) -> q))
  | 
| 
 | 
    78  | 
  else explore(qs + q_der, delta + ((q, c) -> q_der), q_der)
  | 
| 
34
 | 
    79  | 
}
  | 
| 
 | 
    80  | 
  | 
| 
 | 
    81  | 
def explore (qs: States, delta: Transition, q: State) : (States, Transition) =
  | 
| 
 | 
    82  | 
  alphabet.foldRight[(States, Transition)] (qs, delta) ((c, qsd) => goto(q, c, qsd._1, qsd._2)) 
  | 
| 
 | 
    83  | 
  | 
| 
 | 
    84  | 
  | 
| 
 | 
    85  | 
def mk_automaton (r: Rexp) : Automaton[Rexp] = {
 | 
| 
 | 
    86  | 
  val (qs, delta) = explore(Set(r), Map(), r);
  | 
| 
 | 
    87  | 
  val fins = for (q <- qs if nullable(q)) yield q;
  | 
| 
 | 
    88  | 
  Automaton[Rexp](r, qs, delta, fins)
  | 
| 
 | 
    89  | 
}
  | 
| 
 | 
    90  | 
  | 
| 
 | 
    91  | 
val A = mk_automaton(ALT("ab","ac"))
 | 
| 
 | 
    92  | 
  | 
| 
43
 | 
    93  | 
A.start
  | 
| 
 | 
    94  | 
A.states.toList.length
  | 
| 
 | 
    95  | 
  | 
| 
34
 | 
    96  | 
println(A.accepts("bd"))
 | 
| 
 | 
    97  | 
println(A.accepts("ab"))
 | 
| 
 | 
    98  | 
println(A.accepts("ac"))
 | 
| 
43
 | 
    99  | 
  | 
| 
 | 
   100  | 
val r1 = STAR(ALT("a","b"))
 | 
| 
 | 
   101  | 
val r2 = SEQ("b","b")
 | 
| 
 | 
   102  | 
val r3 = SEQ(SEQ(SEQ(r1, r2), r1), "a")
  | 
| 
 | 
   103  | 
val B = mk_automaton(r3)
  | 
| 
 | 
   104  | 
  | 
| 
 | 
   105  | 
B.start
  | 
| 
 | 
   106  | 
B.states.toList.length
  |