progs/re0.scala
author Christian Urban <urbanc@in.tum.de>
Thu, 21 Nov 2019 00:49:21 +0000
changeset 696 3a5a7908517f
parent 499 dfd0f41f8668
permissions -rw-r--r--
added krakatau version and made the mandel program work

import scala.annotation.tailrec
import scala.language.implicitConversions

abstract class Rexp

case object NULL extends Rexp
case object EMPTY extends Rexp
case object ALLCHAR extends Rexp
case class CHAR(c: Char) extends Rexp
case class STR(s: String) 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 
case class NOT(r: Rexp) extends Rexp
case class REP(r: Rexp, n: Int) extends Rexp 

// some convenience for typing in regular expressions
implicit def string2rexp(s : String) : Rexp = STR(s)

implicit def RexpOps(r: Rexp) : Rexp = new {
  def | (s: Rexp) = ALT(r, s)
  def % = STAR(r)
  def %(n: Int) = REP(r, n) 
  def %%(n: Int) = SEQ(REP(r, n), STAR(r)) 
  def ? = ALT(EMPTY, r)
  def unary_! = NOT(r)
  def ~ (s: Rexp) = SEQ(r, s)
}

implicit def stringOps(s: String) : Rexp = new {
  def | (r: Rexp) = ALT(s, r)
  def | (r: String) = ALT(s, r)
  def % = STAR(s)
  def %(n: Int) = REP(s, n)
  def %%(n: Int) = SEQ(REP(s, n), STAR(s)) 
  def ? = ALT(EMPTY, s)
  def unary_! = NOT(s)
  def ~ (r: Rexp) = SEQ(s, r)
  def ~ (r: String) = SEQ(s, r)
}


// nullable function: tests whether the regular 
// expression can recognise the empty string

def nullable (r: Rexp) : Boolean = r match {
  case NULL => false
  case EMPTY => true
  case ALLCHAR => false
  case CHAR(_) => false
  case STR(s) => s.isEmpty
  case ALT(r1, r2) => nullable(r1) || nullable(r2)
  case SEQ(r1, r2) => nullable(r1) && nullable(r2)
  case STAR(_) => true
  case NOT(r) => !(nullable(r))
  case REP(r, i) => if (i == 0) true else nullable(r)
}

// derivative of a regular expression w.r.t. a character
def der (c: Char, r: Rexp) : Rexp = r match {
  case NULL => NULL
  case EMPTY => NULL
  case ALLCHAR => EMPTY
  case CHAR(d) => if (c == d) EMPTY else NULL
  case STR(s) => if (s.isEmpty || s.head != c) NULL else STR(s.tail)
  case ALT(r1, r2) => ALT(der(c, r1), der(c, r2))
  case SEQ(r1, r2) => 
    if (nullable(r1)) ALT(SEQ(der(c, r1), r2), der(c, r2))
    else SEQ(der(c, r1), r2)
  case STAR(r) => SEQ(der(c, r), STAR(r))
  case NOT(r) => NOT(der (c, r))
  case REP(r, i) => 
    if (i == 0) NULL else SEQ(der(c, r), REP(r, i - 1))
}


// derivative w.r.t. a string (iterates der)
@tailrec
def ders (s: List[Char], r: Rexp) : Rexp = s match {
  case Nil => r
  case c::s => ders(s, der(c, r))
}

// main matcher function
def matcher(r: Rexp, s: String) : Boolean = nullable(ders(s.toList, r))

//examples
val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
val int = ("+" | "-").? ~ digit.%%(1)
val real = ("+" | "-").? ~ digit.%%(1) ~ ("." ~ digit.%%(1)).? ~ (("e" | "E") ~ ("+" | "-").? ~ digit.%%(1)).?

val ints = List("0", "-4534", "+049", "99")
val reals = List("0.9", "-12.8", "+91.0", "9e12", "+9.21E-12", "-512E+01")
val errs = List("", "-", "+", "+-1", "-+2", "2-")

ints.map(s => matcher(int, s))
reals.map(s => matcher(int, s))
errs.map(s => matcher(int, s))

ints.map(s => matcher(real, s))
reals.map(s => matcher(real, s))
errs.map(s => matcher(real, s))



def RTEST(n: Int) = ("a".? %(n)) ~ ("a" %(n))

def time_needed[T](i: Int, code: => T) = {
  val start = System.nanoTime()
  for (j <- 1 to i) code
  val end = System.nanoTime()
  (end - start)/(i * 1.0e9)
}

for (i <- 1 to 12000 by 500) {
  println(i + ": " + "%.5f".format(time_needed(1, matcher(RTEST(i), "a" * i))))
}