Skip to content

Commit 0cbb8ce

Browse files
committed
evaluator
1 parent 90ee476 commit 0cbb8ce

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.github.murzagalin.evaluator.ast
2+
3+
import com.github.murzagalin.evaluator.Token
4+
import kotlin.math.pow
5+
6+
class AstEvaluator(private val values: Map<String, Any> = emptyMap()): AstVisitor {
7+
8+
fun evaluate(expression: Expression) = expression.visit(this)
9+
10+
override fun visitTerminal(terminal: Expression.Terminal) = when(val operand = terminal.token) {
11+
is Token.Operand.Number -> operand.value
12+
is Token.Operand.Boolean -> operand.value
13+
is Token.Operand.Variable -> requireNotNull(values[operand.value]) {
14+
"Could not resolve variable '${operand.value}'"
15+
}
16+
}
17+
18+
override fun visitUnary(unary: Expression.Unary): Any {
19+
val literal = evaluate(unary.expression)
20+
21+
return when (unary.token) {
22+
Token.Operator.UnaryPlus -> {
23+
require(literal is Number) { "A Number is expected after a unary plus" }
24+
literal
25+
}
26+
Token.Operator.UnaryMinus -> {
27+
require(literal is Number) { "A Number is expected after a unary plus" }
28+
-literal.toDouble()
29+
}
30+
Token.Operator.Not -> {
31+
require(literal is Boolean) { "A Number is expected after a unary plus" }
32+
!literal
33+
}
34+
else -> {
35+
error("${unary.token} was incorrectly parsed as a unary operator")
36+
}
37+
}
38+
}
39+
40+
override fun visitBinary(binary: Expression.Binary): Any {
41+
return when (binary.token) {
42+
Token.Operator.LessThan -> binaryOnNumbers(binary,"<") { left, right -> left < right }
43+
Token.Operator.LessEqualThan -> binaryOnNumbers(binary,"<=") { left, right -> left <= right }
44+
Token.Operator.GreaterThan -> binaryOnNumbers(binary,">") { left, right -> left > right }
45+
Token.Operator.GreaterEqualThan -> binaryOnNumbers(binary,">=") { left, right -> left >= right }
46+
Token.Operator.Equal -> {
47+
val left = evaluate(binary.leftExpression)
48+
val right = evaluate(binary.rightExpression)
49+
left == right
50+
}
51+
Token.Operator.NotEqual -> {
52+
val left = evaluate(binary.leftExpression)
53+
val right = evaluate(binary.rightExpression)
54+
left != right
55+
}
56+
Token.Operator.And -> binaryOnBooleans(binary, "&&") { left, right -> left && right}
57+
Token.Operator.Or -> binaryOnBooleans(binary, "||") { left, right -> left || right}
58+
Token.Operator.Plus -> binaryOnNumbers(binary, "+") { left, right -> left + right }
59+
Token.Operator.Minus -> binaryOnNumbers(binary,"-") { left, right -> left - right }
60+
Token.Operator.Modulo -> binaryOnNumbers(binary,"%") { left, right -> left % right }
61+
Token.Operator.Multiplication -> binaryOnNumbers(binary,"*") { left, right -> left * right }
62+
Token.Operator.Division -> binaryOnNumbers(binary,"/") { left, right -> left / right }
63+
Token.Operator.Power -> binaryOnNumbers(binary,"^") { left, right -> left.pow(right) }
64+
else -> {
65+
error("${binary.token} was incorrectly parsed as a binary operator")
66+
}
67+
}
68+
}
69+
70+
private fun binaryOnBooleans(binary: Expression.Binary, strRep: String, eval: (Boolean, Boolean) -> Any): Any {
71+
val left = evaluate(binary.leftExpression)
72+
val right = evaluate(binary.rightExpression)
73+
require(left is Boolean && right is Boolean) { "'$strRep' must be called with boolean operands" }
74+
75+
return eval(left, right)
76+
}
77+
78+
private fun binaryOnNumbers(binary: Expression.Binary, strRep: String, eval: (Double, Double) -> Any): Any {
79+
val left = evaluate(binary.leftExpression)
80+
val right = evaluate(binary.rightExpression)
81+
require(left is Number && right is Number) { "'$strRep' must be called with number operands" }
82+
83+
return eval(left.toDouble(), right.toDouble())
84+
}
85+
86+
override fun visitTernary(ternary: Expression.Ternary): Any {
87+
if (ternary.token is Token.Operator.TernaryIfElse) {
88+
val left = evaluate(ternary.firstExpression)
89+
require(left is Boolean) {
90+
"Ternary <condition> ? <expression1> : <expression2> must be called with a boolean value as a condition"
91+
}
92+
return if (left) {
93+
evaluate(ternary.secondExpression)
94+
} else {
95+
evaluate(ternary.thirdExpression)
96+
}
97+
} else {
98+
error("${ternary.token} was incorrectly parsed as a ternary operator")
99+
}
100+
}
101+
102+
override fun visitFunctionCall(functionCall: Expression.FunctionCall): Any {
103+
val arguments = mutableListOf<Any>()
104+
105+
for (arg in functionCall.arguments) {
106+
arguments.add(evaluate(arg))
107+
}
108+
109+
return functionCall.token(arguments)
110+
}
111+
112+
}

0 commit comments

Comments
 (0)