Skip to content

Instantly share code, notes, and snippets.

@robcd
Created March 13, 2012 13:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robcd/2028683 to your computer and use it in GitHub Desktop.
Save robcd/2028683 to your computer and use it in GitHub Desktop.
A solution to SftI Ex 19.9
import util.parsing.combinator.RegexParsers
class RegexProgRunner extends RegexParsers {
val number = "-?[0-9]+".r
val varName = "[a-zA-Z_][a-zA-Z_0-9]*".r
var vars = Map[String, Double]()
def start: Parser[Any] = prog(true)
def prog(b: Boolean): Parser[Any] = rep1(statement(b))
def statement(b: Boolean): Parser[Any] = assignment(b) | iF(b) | wHile(b)
def assignment(execute: Boolean): Parser[Any] = (varName <~ "=") ~ expr ^^ {
case varName ~ value =>
if (execute) {
if (varName == "out") println(value)
else {
vars += varName -> value
println(varName +" set to "+ value)
}
}
}
def iF(execute: Boolean): Parser[Any] = "if" ~> boolExpr into { b =>
statementOrBlock(execute && b) ~ eLse(execute && !b)
}
def boolExpr: Parser[Boolean] = "(" ~> atom ~ boolOp ~ atom <~ ")" ^^ {
case a ~ bop ~ b => bop match {
case "<" => a < b
case "<=" => a <= b
case "==" => a == b
case ">=" => a >= b
case ">" => a > b
}
}
def statementOrBlock(b: Boolean): Parser[Any] = statement(b) | block(b)
def eLse(b: Boolean): Parser[Any] =
opt("else" ~> (iF(b) | statementOrBlock(b))) ^^ (_.getOrElse(()))
def wHile(execute: Boolean): Parser[Any] =
if (execute) iterate into wHile
else "while" ~> boolExpr ~ block(execute)
def iterate: Parser[Boolean] = guard("while" ~> boolExpr into { b => block(b) ^^ (_ => b) })
def block(b: Boolean): Parser[Any] = "{" ~> prog(b) <~ "}"
def boolOp: Parser[String] = "<" | "<=" | "==" | ">=" | ">"
def expr: Parser[Double] = term ~ rep(("+" | "-") ~ term) ^^ { case term0 ~ parsers =>
parsers.foldLeft(term0) { (left, parser) => parser._1 match {
case "+" => left + parser._2
case "-" => left - parser._2
}
}
}
def term: Parser[Double] = factor ~ rep(("*" | "/" | "%") ~ factor) ^^ {
case a0 ~ pairs => pairs.foldLeft(a0) { (a, pair) => pair._1 match {
case "*" => a*pair._2
case "/" => a/pair._2
case "%" => a%pair._2
}
}
}
def factor: Parser[Double] = atom ~ rep("^" ~> atom) ^^ {
case a ~ bs => // a^b0^b1
a::bs reduceRight { (x, y) => math.pow(x, y) } // b = b0^b1; b = a^b
}
def atom: Parser[Double] = number^^(_.toDouble) | "(" ~> expr <~ ")" | varName ^^ {
s => vars.getOrElse(s, 0)
}
}
object test extends App {
val prog = ("""
| a = """+ (if (args isEmpty) 0 else args(0).toInt) +"""
| if (a < 0) b = 1
| else if (a == 0) b = 2
| else b = 3
| while (b > 0) {
| b = b - 1
| }""").stripMargin
println(prog)
val progRunner = new RegexProgRunner
val result = progRunner.parseAll(progRunner.start, prog)
if (!result.successful) println(result)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment