CSC344 Assignment 3 (Scala)
Write a Scala program that performs pattern matching on strings, where
patterns are expressed using only the concatenation, alternation ("|")
and optional ("?") operators of regular expressions (no loops/"*", no
escape characters). Each run of the program should accept a pattern,
and then any number of strings, reporting only whether they match.
Your program should represent expressions (as trees) and evaluate on
the inputs, without using the Scala regular expression library. For
example:
pattern? ((h|j)ello worl?d)|(42)
string? hello world
match
string? jello word
match
string? 42
match
string? 24
no match
string? hello world42
no match
Bootstrap
A flat grammar for patterns is:
E -> c | EE | E'|'E | E'?' | '(' E ')'
Where c is any character other than "()|?", and where option('?') has
highest precedence, then concatentation, then alternation('|'). This
can be transformed into an ugly but deterministically parseable form:
S -> E$
E -> T E2
E2 -> '|' E3
E2 -> NIL
E3 -> T E2
T -> F T2
T2 -> F T2
T2 -> NIL
F -> A F2
F2 -> '?' F2
F2 -> NIL
A -> c
A -> '(' A2
A2 -> E ')'
where '$' is eof/end-of-string, and NIL means empty (which in these
productions means take the rhs only if others do not apply).
Some Scala classes represeting this might be:
abstract class S
case class E(left: T, right: Option[E2]) extends S
case class E2(left: E3) extends S
case class E3(left: T, right: Option[E2]) extends S
case class T(left: F, right: Option[T2]) extends S
case class T2(left: F, right: Option[T2]) extends S
case class F(left: A, right: Option[F2]) extends S
case class F2(left: Option[F2]) extends S
case class A(opt1: Option[C], opt2: Option[A2]) extends S
case class A2(left: E) extends S
case class C(content: Char) extends S
with ...
var input: String;
var inputIndex = 0;
...
def parseS(): E = { parseE() }
def parseE(): E = { E(parseT(),parseE2()) }
def parse2() : Option[E2] = {
if (inputIndex < input.length && input.charAt(inputIndex) == '|') {
++inputIndex;
Some(E2(parseE3));
}
else
None
}
...
and ...
val root: E;
var candidate : String;
var candidateIndex = 0;
...
def matchS() : Boolean = { matchE(root) && candidateIndex == candidate.length }
...
def matchT2(t: Option[T2]) = t match {
case Some(s) => matchF(s.left) && matchT2(s.right)
case None => true;
}
...
Doug Lea