-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathArithmetic.kt
121 lines (99 loc) · 3.14 KB
/
Arithmetic.kt
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
@file:Suppress("MemberVisibilityCanBePrivate", "PackageDirectoryMismatch")
package me.alllex.parsus.demo.math
import me.alllex.parsus.parser.*
import me.alllex.parsus.token.literalToken
import me.alllex.parsus.token.regexToken
import kotlin.math.pow
sealed class Expr {
data class Con(val value: Int) : Expr()
data class Neg(val expr: Expr) : Expr()
data class Pow(val left: Expr, val right: Expr) : Expr()
data class Mul(val left: Expr, val right: Expr) : Expr()
data class Div(val left: Expr, val right: Expr) : Expr()
data class Add(val left: Expr, val right: Expr) : Expr()
data class Sub(val left: Expr, val right: Expr) : Expr()
}
abstract class AbstractArithmeticGrammar<T> : Grammar<T>() {
init { regexToken("\\s+", ignored = true) }
val lpar by literalToken("(")
val rpar by literalToken(")")
val pow by literalToken("^")
val times by literalToken("*")
val div by literalToken("/")
val plus by literalToken("+")
val minus by literalToken("-")
val int by regexToken("\\d+")
val number by parser { int() } map { it.text.toInt() }
val braced by parser { skip(lpar) * expr() * skip(rpar) }
abstract val expr: Parser<T>
override val root by parser { expr() }
}
object ExprParser : AbstractArithmeticGrammar<Expr>() {
val const by number map { Expr.Con(it) }
val term by parser {
val neg = has(minus)
val v = choose(const, braced)
if (neg) Expr.Neg(v) else v
}
val powExpr by parser {
reduce(term, pow) { l, _, r -> Expr.Pow(l, r) }
}
val mulExpr by parser {
reduce(powExpr, times or div) { l, o, r ->
if (o.token == times) Expr.Mul(l, r) else Expr.Div(l, r)
}
}
val addExpr by parser {
reduce(mulExpr, plus or minus) { l, o, r ->
if (o.token == plus) Expr.Add(l, r) else Expr.Sub(l, r)
}
}
override val expr by addExpr
}
object ExprCalculator : AbstractArithmeticGrammar<Int>() {
val const by number
val term by parser {
val neg = has(minus)
val v = choose(const, braced)
if (neg) -v else v
}
val powExpr by parser {
reduce(term, pow) { l, _, r ->
l.toDouble().pow(r.toDouble()).toInt()
}
}
val mulExpr by parser {
reduce(powExpr, times or div) { l, o, r ->
if (o.token == times) l * r else l / r
}
}
val addExpr by parser {
reduce(mulExpr, plus or minus) { l, o, r ->
if (o.token == plus) l + r else l - r
}
}
override val expr by addExpr
}
fun main() {
val exprs = listOf(
"1",
"-1 + 2 * 3 + 4 ^ 5",
buildString {
repeat(1000) { append("(") }
append("1")
repeat(1000) { append(")") }
}
)
for (expr in exprs) {
println("Parsing expr: $expr")
println()
val parsedExpr = ExprParser.parseOrThrow(expr)
println("Parsed tree:")
println(parsedExpr)
println()
val computedValue = ExprCalculator.parseOrThrow(expr)
println("Computed value:")
println(computedValue)
println()
}
}