-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathKeval.kt
More file actions
130 lines (108 loc) · 3.97 KB
/
Copy pathKeval.kt
File metadata and controls
130 lines (108 loc) · 3.97 KB
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
122
123
124
125
126
127
128
129
130
package com.notkamui.keval
import kotlin.jvm.JvmName
import kotlin.jvm.JvmStatic
/**
* Main class for evaluating mathematical expressions.
* It can be customized with additional operators, functions, and constants.
*/
class Keval<N> internal constructor(
private val number: KevalNumber<N>,
private val resources: Map<String, KevalOperator<N>>,
private val operators: Map<String, KevalOperator<N>>,
) {
internal constructor(
number: KevalNumber<N>,
resources: Map<String, KevalOperator<N>>,
) : this(
number = number,
resources = resources,
operators = resources + (
"*" to KevalBinaryOperator(3, true) { a, b -> number.multiply(a, b) }
),
)
fun withBinaryOperator(
symbol: Char,
precedence: Int,
isLeftAssociative: Boolean,
implementation: (N, N) -> N
): Keval<N> = KevalBuilder(number, resources)
.binaryOperator {
this.symbol = symbol
this.precedence = precedence
this.isLeftAssociative = isLeftAssociative
this.implementation = implementation
}
.build()
fun withUnaryOperator(
symbol: Char,
isPrefix: Boolean,
implementation: (N) -> N
): Keval<N> = KevalBuilder(number, resources)
.unaryOperator {
this.symbol = symbol
this.isPrefix = isPrefix
this.implementation = implementation
}
.build()
fun withFunction(
name: String,
arity: Int? = null,
implementation: (List<N>) -> N
): Keval<N> = KevalBuilder(number, resources)
.function {
this.name = name
this.arity = arity
this.implementation = implementation
}
.build()
fun withConstant(
name: String,
value: N
): Keval<N> = KevalBuilder(number, resources)
.constant {
this.name = name
this.value = value
}
.build()
fun withDefault(): Keval<N> = KevalBuilder(number, resources).includeDefault().build()
fun compile(mathExpression: String): CompiledExpression<N> {
val root = mathExpression.toAST(number, operators)
return CompiledExpression(root, root.collectVariables())
}
fun eval(mathExpression: String): N = eval(mathExpression, emptyMap())
fun eval(mathExpression: String, bindings: Map<String, N>): N =
compile(mathExpression).eval(bindings)
fun evalOrNull(mathExpression: String): N? = evalOrNull(mathExpression, emptyMap())
fun evalOrNull(mathExpression: String, bindings: Map<String, N>): N? = try {
eval(mathExpression, bindings)
} catch (_: KevalException) {
null
}
fun evalResult(mathExpression: String): Result<N> = evalResult(mathExpression, emptyMap())
fun evalResult(mathExpression: String, bindings: Map<String, N>): Result<N> = try {
Result.success(eval(mathExpression, bindings))
} catch (e: KevalException) {
Result.failure(e)
}
/**
* Returns the operator resources of this [Keval] instance, including the non-overridable `*` operator.
*/
fun resourcesView(): Map<String, KevalOperator<N>> = operators
companion object {
@JvmStatic
fun <N> create(
number: KevalNumber<N>,
generator: KevalBuilder<N>.() -> Unit = { includeDefault() }
): Keval<N> =
KevalBuilder(number).apply(generator).build()
@JvmName("evaluate")
@JvmStatic
fun eval(mathExpression: String): Double =
KevalNumbers.defaultRealKeval.eval(mathExpression)
}
}
fun String.keval(generator: KevalBuilder<Double>.() -> Unit): Double =
Keval.create(KevalNumbers.real, generator).eval(this)
fun String.keval(): Double = KevalNumbers.real.eval(this)
fun String.kevalOrNull(): Double? = KevalNumbers.defaultRealKeval.evalOrNull(this)
fun String.kevalResult(): Result<Double> = KevalNumbers.defaultRealKeval.evalResult(this)