|
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 |
|
72 // we use as an alphabet all lowercase letters |
|
73 val alphabet = "abcdefghijklmnopqrstuvwxyz".toSet |
|
74 |
|
75 def goto(q: State, c: Char, qs: States, delta: Transition) : (States, Transition) = { |
|
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) |
|
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 |
|
93 A.start |
|
94 A.states.toList.length |
|
95 |
|
96 println(A.accepts("bd")) |
|
97 println(A.accepts("ab")) |
|
98 println(A.accepts("ac")) |
|
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 |