progs/automata1.scala
author cu
Thu, 28 Sep 2017 11:04:11 +0100
changeset 510 25580bf89ac0
parent 93 4794759139ea
permissions -rw-r--r--
typos


// a class for deterministic finite automata,
// the type of states is kept polymorphic

case class Automaton[A](start: A, states: Set[A], delta: Map[(A, Char), A], fins: Set[A]) {

  // the transition function lifted to list of characters
  def deltas(q: A, cs: List[Char]) : A = 
    if (states.contains(q)) cs match {
      case Nil => q
      case c::cs => 
        if (delta.isDefinedAt(q, c)) deltas(delta(q, c), cs)
        else throw new RuntimeException(q + " does not have a transition for " + c)
    }
    else throw new RuntimeException(q + " is not a state of the automaton")

  // wether a string is accepted by the automaton
  def accepts(s: String) = 
    try {
      fins.contains(deltas(start, s.toList))
    } catch {
      case e:RuntimeException => false
    }
}



abstract class Rexp

case object NULL extends Rexp
case object EMPTY extends Rexp
case class CHAR(c: Char) extends Rexp 
case class ALT(r1: Rexp, r2: Rexp) extends Rexp
case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
case class STAR(r: Rexp) extends Rexp

implicit def string2rexp(s : String) = { 
  def chars2rexp (cs: List[Char]) : Rexp = cs match {
    case Nil => EMPTY
    case c::Nil => CHAR(c)
    case c::cs => SEQ(CHAR(c), chars2rexp(cs))
  }
  chars2rexp(s.toList)
}

def nullable (r: Rexp) : Boolean = r match {
  case NULL => false
  case EMPTY => true
  case CHAR(_) => false
  case ALT(r1, r2) => nullable(r1) || nullable(r2)
  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
  case STAR(_) => true
}

def der (r: Rexp, c: Char) : Rexp = r match {
  case NULL => NULL
  case EMPTY => NULL
  case CHAR(d) => if (c == d) EMPTY else NULL
  case ALT(r1, r2) => ALT(der(r1, c), der(r2, c))
  case SEQ(r1, r2) => if (nullable(r1)) ALT(SEQ(der(r1, c), r2), der(r2, c))
                      else SEQ(der(r1, c), r2)
  case STAR(r) => SEQ(der(r, c), STAR(r))
}


// Here we construct an automaton whose
// states are regular expressions
type State = Rexp
type States = Set[State]
type Transition =  Map[(State, Char), State]

def goto(q: State, c: Char, qs: States, delta: Transition) : (States, Transition) = {
  val qc : State = der(q, c)
  if (qs.contains(qc)) (qs, delta + ((q, c) -> q))
  else explore(qs + qc, delta + ((q, c) -> qc), qc)
}

// we use as alphabet all lowercase letters
val alphabet = "abcdefghijklmnopqrstuvwxyz".toSet
   
def explore (qs: States, delta: Transition, q: State) : (States, Transition) =
  alphabet.foldRight[(States, Transition)] (qs, delta) ((c, qsd) => goto(q, c, qsd._1, qsd._2)) 


def mk_automaton (r: Rexp) : Automaton[Rexp] = {
  val (qs, delta) = explore(Set(r), Map(), r);
  val fins = for (q <- qs if nullable(q)) yield q;
  Automaton[Rexp](r, qs, delta, fins)
}

val A = mk_automaton(ALT("ab","ac"))

println(A.accepts("bd"))
println(A.accepts("ab"))
println(A.accepts("ac"))