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