Recursive Descent Parser in Scala
import scala.util.matching.Regex
// Unambiguous grammar
// S -> E$
// E -> T + E | T
// T -> Const | Var
// Easier to parse grammar
// S -> E$
// E -> Terminal E2
// E2 -> + E
// E2 -> NIL
// Terminal -> Const
// Terminal -> Var
sealed trait S {
def eval(env: Main.Environment): Int
}
sealed trait Terminal extends S
case class E(left: Terminal, right: Option[E2]) extends S {
def eval(env: Main.Environment): Int = {
val leftVal: Int = left.eval(env)
right match {
case Some(r) => leftVal + r.eval(env)
case None => leftVal
}
}
}
case class E2(l: E) extends S {
def eval(env: Main.Environment): Int = l.eval(env)
}
case class Var(n: String) extends Terminal {
def eval(env: Main.Environment): Int = env(n)
}
case class Const(v: Int) extends Terminal {
def eval(env: Main.Environment): Int = v
}
class RecursiveDescent(input: String) {
private val constRegex: Regex = "^[0-9]+".r
private val varRegex: Regex = "^[A-Za-z]+".r
private var index = 0
private def remainingInput: String =
input.substring(index)
def parseS(): S = {
val result = parseE()
if (index != input.length) {
throw new IllegalArgumentException(
s"Unexpected input at position $index: '${input.substring(index)}'"
)
}
result
}
private def parseE(): E = E(parseTerminal(), parseE2())
private def parseE2(): Option[E2] = {
if (index < input.length && input(index) == '+'){
index+=1; // Advance past +
Some(E2(parseE()))
}
else{
None
}
}
private def parseConst(): Option[Const] = {
constRegex.findFirstIn(remainingInput) match {
case Some(const) =>
index += const.length
Some(Const(const.toInt))
case None => None
}
}
private def parseVar(): Option[Var] = {
varRegex.findFirstIn(remainingInput) match {
case Some(varName) =>
index += varName.length
Some(Var(varName))
case None => None
}
}
private def parseTerminal(): Terminal =
parseConst() // Check for const
.orElse(parseVar()) // If not there, check for var
.getOrElse { // If not there, fail
throw new IllegalArgumentException(
s"Expected constant or variable at position $index in '$input'"
)
}
}
object Main {
type Environment = String => Int
def main(args: Array[String]): Unit = {
val env: Environment = { case "x" => 5 case "y" => 7 }
val rd = new RecursiveDescent("x+x+7+y")
val exp2rd: S = rd.parseS()
println(exp2rd)
println(exp2rd.eval(env))
}
}