package ru dgutov calcdsl private calcdsl abstract class Expr private

 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
package ru.dgutov.calcdsl
private[calcdsl] abstract class Expr
private[calcdsl] case class Number (value: Double) extends Expr
private[calcdsl] case class UnaryOp
(operator: String, arg: Expr) extends Expr
private[calcdsl] case class BinaryOp
(operator: String, left: Expr, right: Expr) extends Expr
private[calcdsl] case class SeqOp
(operator: String, list: List[Expr]) extends Expr
object Calc
{
def evaluate (e: Expr) : Double =
{
simplify(e) match {
case Number(x) => x
case UnaryOp("-", x) => -(evaluate(x))
case SeqOp(op, head :: Nil) => evaluate(head)
case SeqOp("+", list) => list.foldLeft(0.0) { _ + evaluate(_) }
case SeqOp("-", list) => evaluate(list.head) - evaluate(SeqOp("+", list.tail))
case SeqOp("*", list) => list.foldLeft(1.0) { _ * evaluate(_) }
case SeqOp("/", list) => evaluate(list.head) / evaluate(SeqOp("*", list.tail))
}
}
def simplify (e: Expr) : Expr =
{
val simpSubs = e match {
case SeqOp(op, list) => SeqOp(op, list.map { a => simplify(a) })
case UnaryOp(op, operand) => UnaryOp(op, simplify(operand))
case _ => e
}
def simplifyTop (ex: Expr) = ex match {
case UnaryOp("-", UnaryOp("-", x)) => x
case UnaryOp("+", x) => x
case SeqOp("+", list) => SeqOp("+", list.filter { a => a != 0.0 })
case SeqOp("-", list) => SeqOp("-", list.filter { a => a != 0.0 })
case SeqOp("*", list) => SeqOp("*", list.filter { a => a != 1.0 })
case SeqOp("/", list) => SeqOp("/", list.filter { a => a != 1.0 })
case _ => ex
}
simplifyTop(simpSubs)
}
import scala.util.parsing.combinator._
object ExprParser extends JavaTokenParsers {
def seqp (op: String, next: Parser[Expr]): Parser[Expr] = {
next ~ op ~ rep1sep(next, op) ^^ { case h ~ op ~ list => SeqOp(op, h :: list) } |
next
}
def expr = seqp("+", sub)
def sub = seqp("-", mul)
def mul = seqp("*", div)
def div = seqp("/", factor)
def factor: Parser[Expr] = "(" ~> expr <~ ")" |
floatingPointNumber ^^ { x => Number(x.toFloat) } |
(("-" | "+") ~ factor) ^^ { case op ~ token => UnaryOp(op, token) }
def parse (text: String) = parseAll(expr, text)
}
def parse (text: String) : Expr = ExprParser.parse(text).get
def evaluate (text: String) : Double = evaluate(parse(text))
}
object App extends Application {
println(Calc.evaluate(Number(3.5)))
println(Calc.parse("3 + 4"))
println(Calc.parse("3 + (5 - 7) / -(2 - 5) / 2 * 3"))
println(Calc.evaluate("3 + (5 - 7) / -3 / 2 * 3"))
}