Created
March 13, 2012 13:13
-
-
Save robcd/2028683 to your computer and use it in GitHub Desktop.
A solution to SftI Ex 19.9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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