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