| 
     1 trait RegExp { | 
         | 
     2   def nullable: Boolean  | 
         | 
     3   def derive(c: Char): RegExp  | 
         | 
     4 }  | 
         | 
     5   | 
         | 
     6 case object Empty extends RegExp { | 
         | 
     7   def nullable = false  | 
         | 
     8   def derive(c: Char) = Empty  | 
         | 
     9 }  | 
         | 
    10   | 
         | 
    11 case object Eps extends RegExp { | 
         | 
    12   def nullable = true  | 
         | 
    13   def derive(c: Char) = Empty  | 
         | 
    14 }  | 
         | 
    15   | 
         | 
    16 case class Str(s: String) extends RegExp { | 
         | 
    17   def nullable = s.isEmpty  | 
         | 
    18   def derive(c: Char) =  | 
         | 
    19     if (s.isEmpty || s.head != c) Empty  | 
         | 
    20     else Str(s.tail)  | 
         | 
    21 }  | 
         | 
    22   | 
         | 
    23 case class Cat(r: RegExp, s: RegExp) extends RegExp { | 
         | 
    24   def nullable = r.nullable && s.nullable  | 
         | 
    25   def derive(c: Char) =  | 
         | 
    26     if (r.nullable) Or(Cat(r.derive(c), s), s.derive(c))   | 
         | 
    27     else Cat(r.derive(c), s)  | 
         | 
    28 }  | 
         | 
    29   | 
         | 
    30 case class Star(r: RegExp) extends RegExp { | 
         | 
    31   def nullable = true  | 
         | 
    32   def derive(c: Char) = Cat(r.derive(c), this)  | 
         | 
    33 }  | 
         | 
    34   | 
         | 
    35 case class Or(r: RegExp, s: RegExp) extends RegExp { | 
         | 
    36   def nullable = r.nullable || s.nullable  | 
         | 
    37   def derive(c: Char) = Or(r.derive(c), s.derive(c))  | 
         | 
    38 }  | 
         | 
    39   | 
         | 
    40 case class And(r: RegExp, s: RegExp) extends RegExp { | 
         | 
    41   def nullable = r.nullable && s.nullable  | 
         | 
    42   def derive(c: Char) = And(r.derive(c), s.derive(c))  | 
         | 
    43 }  | 
         | 
    44   | 
         | 
    45 case class Not(r: RegExp) extends RegExp { | 
         | 
    46   def nullable = !r.nullable  | 
         | 
    47   def derive(c: Char) = Not(r.derive(c))  | 
         | 
    48 }  | 
         | 
    49   | 
         | 
    50   | 
         | 
    51   | 
         | 
    52   | 
         | 
    53 object Matcher { | 
         | 
    54   def matches(r: RegExp, s: String): Boolean = { | 
         | 
    55     if (s.isEmpty) r.nullable  | 
         | 
    56     else matches(r.derive(s.head), s.tail)  | 
         | 
    57   }  | 
         | 
    58 }  | 
         | 
    59   | 
         | 
    60   | 
         | 
    61 object Pimps { | 
         | 
    62   implicit def string2RegExp(s: String) = Str(s)  | 
         | 
    63   | 
         | 
    64   implicit def regExpOps(r: RegExp) = new { | 
         | 
    65     def | (s: RegExp) = Or(r, s)  | 
         | 
    66     def & (s: RegExp) = And(r, s)  | 
         | 
    67     def % = Star(r)  | 
         | 
    68     def %(n: Int) = rep(r, n)  | 
         | 
    69     def ? = Or(Eps, r)  | 
         | 
    70     def ! = Not(r)  | 
         | 
    71     def ++ (s: RegExp) = Cat(r, s)  | 
         | 
    72     def ~ (s: String) = Matcher.matches(r, s)  | 
         | 
    73   }  | 
         | 
    74   | 
         | 
    75   implicit def stringOps(s: String) = new { | 
         | 
    76     def | (r: RegExp) = Or(s, r)  | 
         | 
    77     def | (r: String) = Or(s, r)  | 
         | 
    78     def & (r: RegExp) = And(s, r)  | 
         | 
    79     def & (r: String) = And(s, r)  | 
         | 
    80     def % = Star(s)  | 
         | 
    81     def % (n: Int) = rep(Str(s), n)  | 
         | 
    82     def ? = Or(Eps, s)  | 
         | 
    83     def ! = Not(s)  | 
         | 
    84     def ++ (r: RegExp) = Cat(s, r)  | 
         | 
    85     def ++ (r: String) = Cat(s, r)  | 
         | 
    86     def ~ (t: String) = Matcher.matches(s, t)  | 
         | 
    87   }  | 
         | 
    88   | 
         | 
    89   def rep(r: RegExp, n: Int): RegExp =  | 
         | 
    90     if (n <= 0) Star(r)  | 
         | 
    91     else Cat(r, rep(r, n - 1))  | 
         | 
    92 }  | 
         | 
    93   | 
         | 
    94   | 
         | 
    95 object Test { | 
         | 
    96   import Pimps._  | 
         | 
    97   | 
         | 
    98   val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"  | 
         | 
    99   val int = ("+" | "-").? ++ digit.%(1) | 
         | 
   100   val real = ("+" | "-").? ++ digit.%(1) ++ ("." ++ digit.%(1)).? ++ (("e" | "E") ++ ("+" | "-").? ++ digit.%(1)).? | 
         | 
   101   | 
         | 
   102   def main(args: Array[String]) { | 
         | 
   103     val ints = List("0", "-4534", "+049", "99") | 
         | 
   104     val reals = List("0.9", "-12.8", "+91.0", "9e12", "+9.21E-12", "-512E+01") | 
         | 
   105     val errs = List("", "-", "+", "+-1", "-+2", "2-") | 
         | 
   106   | 
         | 
   107     ints.foreach(s => assert(int ~ s))  | 
         | 
   108     reals.foreach(s => assert(!(int ~ s)))  | 
         | 
   109     errs.foreach(s => assert(!(int ~ s)))  | 
         | 
   110   | 
         | 
   111     ints.foreach(s => assert(real ~ s))  | 
         | 
   112     reals.foreach(s => assert(real ~ s))  | 
         | 
   113     errs.foreach(s => assert(!(real ~ s)))  | 
         | 
   114   }  | 
         | 
   115 }  |