Skip to content

Instantly share code, notes, and snippets.

@lyricallogical
Forked from kmizu/ReversePolishNotationParsers.scala
Created October 5, 2012 00:05
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 lyricallogical/3837249 to your computer and use it in GitHub Desktop.
Save lyricallogical/3837249 to your computer and use it in GitHub Desktop.
RPN expression parser, derived from http://d.hatena.ne.jp/hideshi_o/20120925/1348562081
$ scala rpnparser.scala
1 1 + = 2
3 1 - = -2
3 2 * = 6
8 2 / = 0
2 (2 2 +) + = 6
2 2 2 + + = 6
(2 2 +) 2 + = 6
(2 2 +) (4 2 +) * = 24
((2 2 +) (4 2 +) *) 2 / = 0
import scala.util.parsing.combinator._
class RPNParser extends JavaTokenParsers {
def parse(data: String) = parseAll(expression, data)
val operator: Parser[String] = "+" | "-" | "*" | "/"
val literal: Parser[Operand] = ("""[0-9]+""".r) ^^ { lit => Operand(lit.toInt) }
val inParenExpr: Parser[OperatorApplication] = "(" ~> expression <~ ")"
val factor: Parser[Ast] = literal | inParenExpr
val expression: Parser[OperatorApplication] = {
def consumeFactor(stack: List[Ast]): Parser[OperatorApplication] = factor into { expr => aux(expr :: stack) }
lazy val aux: List[Ast] => Parser[OperatorApplication] = {
case stack @ rhs :: lhs :: rest => {
val consumeOperands: Parser[OperatorApplication] = operator ^^ { op => OperatorApplication(op, lhs, rhs) } into { opapp =>
rest match {
case Nil => success(opapp)
case rest => aux(opapp :: rest)
}
}
consumeOperands | consumeFactor(stack)
}
case stack => consumeFactor(stack)
}
aux(Nil)
}
}
abstract sealed class Ast
case class OperatorApplication(operator: String, lhs: Ast, rhs: Ast) extends Ast
case class Operand(value: Int) extends Ast
val opToFun: String => (Int, Int) => Int = {
case "+" => _ + _
case "-" => _ - _
case "*" => _ * _
case "/" => _ / _
}
val eval: Ast => Int = {
case OperatorApplication(operator, lhs, rhs) => opToFun(operator)(eval(lhs), eval(rhs))
case Operand(value) => value
}
val inputs = List(
"1 1 +"
,"3 1 -"
,"3 2 *"
,"8 2 /"
,"2 (2 2 +) +"
,"2 2 2 + +"
,"(2 2 +) 2 +"
,"(2 2 +) (4 2 +) *"
,"((2 2 +) (4 2 +) *) 2 /"
)
val parsers = new RPNParser
inputs.map{i => (i, parsers.parse(i))}.filter{_._2.successful}.foreach { case (i, r) =>
printf("%s = %d%n", i, eval(r.get))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment