progs/matcher/re2.sc
changeset 725 f345e89895f5
child 742 b5b5583a3a08
equal deleted inserted replaced
724:61a936be50c4 725:f345e89895f5
       
     1 // A Version with an explicit n-times regular expression;
       
     2 // this keeps the size of the regular expression in the
       
     3 // EVIL1 test-case quite small
       
     4 //
       
     5 // call the test cases with X = {1,2}
       
     6 //
       
     7 //   amm re2.sc testX
       
     8 //
       
     9 // or 
       
    10 //
       
    11 //   amm re2.sc all
       
    12 
       
    13 
       
    14 abstract class Rexp 
       
    15 case object ZERO extends Rexp
       
    16 case object ONE extends Rexp
       
    17 case class CHAR(c: Char) extends Rexp
       
    18 case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
       
    19 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
       
    20 case class STAR(r: Rexp) extends Rexp 
       
    21 case class NTIMES(r: Rexp, n: Int) extends Rexp   //explicit constructor for n-times
       
    22 
       
    23 
       
    24 def nullable (r: Rexp) : Boolean = r match {
       
    25   case ZERO => false
       
    26   case ONE => true
       
    27   case CHAR(_) => false
       
    28   case ALT(r1, r2) => nullable(r1) || nullable(r2)
       
    29   case SEQ(r1, r2) => nullable(r1) && nullable(r2)
       
    30   case STAR(_) => true
       
    31   case NTIMES(r, i) => if (i == 0) true else nullable(r)
       
    32 }
       
    33 
       
    34 
       
    35 def der (c: Char, r: Rexp) : Rexp = r match {
       
    36   case ZERO => ZERO
       
    37   case ONE => ZERO
       
    38   case CHAR(d) => if (c == d) ONE else ZERO
       
    39   case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
       
    40   case SEQ(r1, r2) => 
       
    41     if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
       
    42     else SEQ(der(c, r1), r2)
       
    43   case STAR(r1) => SEQ(der(c, r1), STAR(r1))
       
    44   case NTIMES(r1, i) => 
       
    45     if (i == 0) ZERO else SEQ(der(c, r1), NTIMES(r1, i - 1))
       
    46 }
       
    47 
       
    48 def ders (s: List[Char], r: Rexp) : Rexp = s match {
       
    49   case Nil => r
       
    50   case c::s => ders(s, der(c, r))
       
    51 }
       
    52 
       
    53 def matcher(r: Rexp, s: String) : Boolean = 
       
    54   nullable(ders(s.toList, r))
       
    55 
       
    56 
       
    57 // the optional regular expression: one or zero times
       
    58 // this regular expression is still defined in terms of ALT
       
    59 def OPT(r: Rexp) = ALT(r, ONE)
       
    60 
       
    61 
       
    62 // Test Cases
       
    63 
       
    64 // evil regular expressions
       
    65 def EVIL1(n: Int) = SEQ(NTIMES(OPT(CHAR('a')), n), NTIMES(CHAR('a'), n))
       
    66 val EVIL2 = SEQ(STAR(STAR(CHAR('a'))), CHAR('b'))
       
    67 
       
    68 def time_needed[T](i: Int, code: => T) = {
       
    69   val start = System.nanoTime()
       
    70   for (j <- 1 to i) code
       
    71   val end = System.nanoTime()
       
    72   (end - start) / (i * 1.0e9)
       
    73 }
       
    74 
       
    75 
       
    76 
       
    77 // test: (a?{n}) (a{n})
       
    78 @doc("Test (a?{n}) (a{n})")
       
    79 @main
       
    80 def test1() = {
       
    81   for (i <- 0 to 1000 by 100) {
       
    82     println(f"$i: ${time_needed(2, matcher(EVIL1(i), "a" * i))}%.5f")
       
    83   }
       
    84 }  
       
    85 
       
    86 // test: (a*)* b
       
    87 @doc("Test (a*)* b")
       
    88 @main
       
    89 def test2() = {
       
    90   for (i <- 0 to 20) {
       
    91     println(f"$i: ${time_needed(2, matcher(EVIL2, "a" * i))}%.5f")
       
    92   }
       
    93 }
       
    94 
       
    95 // the size of a regular expressions - for testing purposes 
       
    96 def size(r: Rexp) : Int = r match {
       
    97   case ZERO => 1
       
    98   case ONE => 1
       
    99   case CHAR(_) => 1
       
   100   case ALT(r1, r2) => 1 + size(r1) + size(r2)
       
   101   case SEQ(r1, r2) => 1 + size(r1) + size(r2)
       
   102   case STAR(r) => 1 + size(r)
       
   103   case NTIMES(r, _) => 1 + size(r)
       
   104 }
       
   105 
       
   106 // EVIL1(n) has now a constant size, no matter
       
   107 // what n is; also the derivative only grows 
       
   108 // moderately 
       
   109 
       
   110 size(EVIL1(1))  // 7
       
   111 size(EVIL1(3))  // 7
       
   112 size(EVIL1(5))  // 7
       
   113 size(EVIL1(7))  // 7
       
   114 size(EVIL1(20)) // 7
       
   115 
       
   116 size(ders("".toList, EVIL1(5)))       // 7
       
   117 size(ders("a".toList, EVIL1(5)))      // 16
       
   118 size(ders("aa".toList, EVIL1(5)))     // 35
       
   119 size(ders("aaa".toList, EVIL1(5)))    // 59
       
   120 size(ders("aaaa".toList, EVIL1(5)))   // 88
       
   121 size(ders("aaaaa".toList, EVIL1(5)))  // 122
       
   122 size(ders("aaaaaa".toList, EVIL1(5))) // 151
       
   123 
       
   124 // but the size of the derivatives can still grow 
       
   125 // quite dramatically in case of EVIL2
       
   126 
       
   127 size(ders("".toList, EVIL2))       // 5
       
   128 size(ders("a".toList, EVIL2))      // 12
       
   129 size(ders("aa".toList, EVIL2))     // 28
       
   130 size(ders("aaa".toList, EVIL2))    // 58
       
   131 size(ders("aaaa".toList, EVIL2))   // 116
       
   132 size(ders("aaaaa".toList, EVIL2))  // 230
       
   133 size(ders("aaaaaa".toList, EVIL2)) // 456
       
   134 
       
   135 size(ders(("a" * 20).toList, EVIL2)) // 7340068
       
   136 
       
   137 
       
   138 
       
   139 @doc("All tests.")
       
   140 @main
       
   141 def all() = { test1(); test2() }