-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathchapter20-8.scala
96 lines (81 loc) · 2.9 KB
/
chapter20-8.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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// 8
import scala.collection.mutable
import scala.util.parsing.combinator.syntactical.StandardTokenParsers
class Context {
val vars = new mutable.HashMap[String,Int]()
def getVariable(name: String): Int =
vars.getOrElse(name, 0)
def setVariable(name: String, value: Int): Unit =
vars.put(name, value)
}
abstract class Expr {
def eval(ctx: Context): Int
}
case class Number(value: Int) extends Expr {
override def eval(ctx: Context) = value
}
case class Operator(op: String, left: Expr, right: Expr) extends Expr {
override def eval(ctx: Context) = op match {
case "+" => left.eval(ctx) + right.eval(ctx)
case "-" => left.eval(ctx) - right.eval(ctx)
case "*" => left.eval(ctx) * right.eval(ctx)
case "/" => left.eval(ctx) / right.eval(ctx)
case "%" => left.eval(ctx) % right.eval(ctx)
case "^" => Math.pow(left.eval(ctx), right.eval(ctx)).toInt
}
}
case class Ref(name: String) extends Expr {
override def eval(ctx: Context) = ctx.getVariable(name)
}
case class Assign(name: String, expr: Expr) extends Expr {
override def eval(ctx: Context) = {
val value = expr.eval(ctx)
if (name == "out")
println(value)
else
ctx.setVariable(name, value);
value
}
}
class ExprTreeParser extends StandardTokenParsers {
lexical.delimiters += ("+", "-", "*", "/", "%", "^", "=", "(", ")")
// left-associative binary expression
def binOpLeft(op: Parser[String], next: Parser[Expr]): Parser[Expr] =
next ~ rep(op ~ next) ^^ {
case i ~ rep => rep.foldLeft(i)((acc, r) => r match {
case op ~ t => Operator(op, acc, t)
})
}
// right-associative binary expression
def binOpRight(op: Parser[String], next: Parser[Expr]): Parser[Expr] =
next ~ opt(op ~ expr) ^^ {
case l ~ None => l
case l ~ Some(op ~ r) => Operator(op, l, r)
}
def expr = binOpLeft(("+" | "-"), term)
def term = binOpLeft(("*"|"/"|"%"), factor)
def factor = binOpRight("^", primary)
def primary: Parser[Expr] = {
ident ~ "=" ~ expr ^^ { case id ~ "=" ~ e => Assign(id, e) } |
ident ^^ { id => new Ref(id) } |
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
val ctx = new Context()
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(ctx)
assert(actual == expected, s"parse result of $input should be $expected, but is $actual")
println(s"Ok: $input -> $actual")
}
test("1 + n", 1 + 0)
test("n = 3 - 4 - 5", 3 - 4 - 5) // -6
test("n * 2 + 12", -6 * 2 + 12)
test("(4 ^ 2) ^ 3", Math.pow(Math.pow(4, 2), 3).toInt)
test("out = 4 ^ 2 ^ 3", Math.pow(4, Math.pow(2, 3)).toInt)