-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathchapter20-6.scala
66 lines (57 loc) · 2.01 KB
/
chapter20-6.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 6, using StandardTokenParsers and with eval
import scala.util.parsing.combinator.syntactical.StandardTokenParsers
abstract class Expr {
def eval: Int
}
case class Number(value: Int) extends Expr {
override def eval = value
}
case class Operator(op: String, left: Expr, right: Expr) extends Expr {
override def eval = op match {
case "+" => left.eval + right.eval
case "-" => left.eval - right.eval
case "*" => left.eval * right.eval
case "/" => left.eval / right.eval
case "%" => left.eval % right.eval
case "^" => Math.pow(left.eval, right.eval).toInt
}
}
class ExprTreeParser extends StandardTokenParsers {
lexical.delimiters += ("+", "-", "*", "/", "%", "^", "(", ")")
def expr: Parser[Expr] = term ~ rep(("+" | "-") ~ term) ^^ {
case t ~ rep => rep.foldLeft(t)((acc, r) => r match {
case op ~ t => Operator(op, acc, t)
})
}
def term: Parser[Expr] = factor ~ rep(("*"|"/"|"%") ~ factor) ^^ {
case f ~ rep => rep.foldLeft(f)((acc, r) => r match {
case op ~ f => Operator(op, acc, f)
})
}
def factor: Parser[Expr] = primary ~ opt("^" ~ expr) ^^ {
case p ~ None => p
case p ~ Some(op ~ e) => Operator(op, p, e)
}
def primary: Parser[Expr] = {
numericLit ^^ { n => Number(n.toInt) } |
"(" ~ expr ~ ")" ^^ { case _ ~ e ~ _ => e }
}
def parseAll[T](p: Parser[T], in: String): ParseResult[T] =
phrase(p)(new lexical.Scanner(in))
}
val parser = new ExprTreeParser
def test(input: String, expected: Int): Unit = {
val result = parser.parseAll(parser.expr, input)
assert(result.successful, s"parse result of $input not succesful")
val expr = result.get
val actual = expr.eval
assert(actual == expected, s"parse result of $input should be $expected, but is $actual")
println(s"Ok: $input -> $actual")
}
test("1", 1)
test("1 + 2", 1 + 2)
test("3 - 4 - 5", 3 - 4 - 5)
test("3 - 4 * 2 + 5", 3 - 4 * 2 + 5)
test("3 + 4 * 5", 3 + 4 * 5)
test("(4 ^ 2) ^ 3", Math.pow(Math.pow(4, 2), 3).toInt)
test("4 ^ 2 ^ 3", Math.pow(4, Math.pow(2, 3)).toInt)