Skip to content

Commit 90ee476

Browse files
committed
tests pass with ast
1 parent 0299977 commit 90ee476

File tree

7 files changed

+119
-20
lines changed

7 files changed

+119
-20
lines changed
Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package com.github.murzagalin.evaluator
22

3+
import com.github.murzagalin.evaluator.ast.BooleanAstEvaluator
4+
import com.github.murzagalin.evaluator.ast.DoubleAstEvaluator
5+
import com.github.murzagalin.evaluator.ast.Expression
6+
import com.github.murzagalin.evaluator.ast.Parser
7+
38
class Evaluator(
49
functions: List<Function> = DefaultFunctions.ALL,
510
constants: List<Constant> = DefaultConstants.ALL,
611
doubleDelimiter: Char = '.',
712
argumentsDelimiter: Char = ','
813
) {
914

10-
private val booleanEvaluator = BooleanEvaluator()
11-
private val doubleEvaluator = DoubleEvaluator()
12-
private val converter = Converter()
15+
private val booleanEvaluator = BooleanAstEvaluator()
16+
private val doubleEvaluator = DoubleAstEvaluator()
17+
private val parser = Parser()
1318
private val tokenizer = Tokenizer(
1419
functions = functions,
1520
constants = constants,
@@ -22,34 +27,34 @@ class Evaluator(
2227
values: Map<String, Any> = emptyMap()
2328
): Double {
2429
val tokenized = tokenizer.tokenize(expression)
25-
val converted = converter.convert(tokenized)
30+
val parsed = parser.parse(tokenized)
2631

27-
return doubleEvaluator.evaluate(converted, values)
32+
return doubleEvaluator.evaluate(parsed, values)
2833
}
2934

3035
fun evaluateBoolean(
3136
expression: String,
3237
values: Map<String, Any> = emptyMap()
3338
): Boolean {
3439
val tokenized = tokenizer.tokenize(expression)
35-
val converted = converter.convert(tokenized)
40+
val parsed = parser.parse(tokenized)
3641

37-
return booleanEvaluator.evaluate(converted, values)
42+
return booleanEvaluator.evaluate(parsed, values)
3843
}
3944

4045
fun evaluateDouble(
41-
expression: PreprocessedExpression,
46+
expression: Expression,
4247
values: Map<String, Any> = emptyMap()
4348
) = doubleEvaluator.evaluate(expression, values)
4449

4550
fun evaluateBoolean(
46-
expression: PreprocessedExpression,
51+
expression: Expression,
4752
values: Map<String, Any> = emptyMap()
4853
) = booleanEvaluator.evaluate(expression, values)
4954

50-
fun preprocessExpression(expression: String): PreprocessedExpression {
55+
fun preprocessExpression(expression: String): Expression {
5156
val tokenized = tokenizer.tokenize(expression)
5257

53-
return converter.convert(tokenized)
58+
return parser.parse(tokenized)
5459
}
5560
}

src/commonMain/kotlin/com/github/murzagalin/evaluator/Token.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ sealed interface Token {
6565
}
6666
}
6767

68+
operator fun invoke(args: List<Any>) = function(*args.toTypedArray())
69+
6870
object Delimiter: Token
6971
}
7072

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.github.murzagalin.evaluator.ast
2+
3+
class BooleanAstEvaluator {
4+
5+
fun evaluate(expression: Expression, values: Map<String, Any> = emptyMap()): Boolean {
6+
val baseEvaluator = AstEvaluator(values)
7+
val evaluated = baseEvaluator.evaluate(expression)
8+
9+
require(evaluated is Boolean)
10+
11+
return evaluated
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.github.murzagalin.evaluator.ast
2+
3+
class DoubleAstEvaluator {
4+
5+
fun evaluate(expression: Expression, values: Map<String, Any> = emptyMap()): Double {
6+
val baseEvaluator = AstEvaluator(values)
7+
val evaluated = baseEvaluator.evaluate(expression)
8+
9+
require(evaluated is Number)
10+
11+
return evaluated.toDouble()
12+
}
13+
}

src/commonMain/kotlin/com/github/murzagalin/evaluator/ast/Expression.kt

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,52 @@ package com.github.murzagalin.evaluator.ast
33
import com.github.murzagalin.evaluator.Token
44

55
sealed class Expression {
6+
7+
abstract fun visit(visitor: AstVisitor): Any
8+
69
data class Unary(
710
val token: Token.Operator,
811
val expression: Expression
9-
) : Expression()
12+
) : Expression() {
13+
override fun visit(visitor: AstVisitor) = visitor.visitUnary(this)
14+
}
1015

1116
data class Binary(
1217
val token: Token.Operator,
1318
val leftExpression: Expression,
1419
val rightExpression: Expression
15-
) : Expression()
20+
) : Expression() {
21+
override fun visit(visitor: AstVisitor) = visitor.visitBinary(this)
22+
}
1623

1724
data class Ternary(
1825
val token: Token.Operator,
1926
val firstExpression: Expression,
2027
val secondExpression: Expression,
2128
val thirdExpression: Expression
22-
) : Expression()
29+
) : Expression() {
30+
override fun visit(visitor: AstVisitor) = visitor.visitTernary(this)
31+
}
2332

2433
data class FunctionCall(
2534
val token: Token.FunctionCall,
2635
val arguments: List<Expression>
27-
) : Expression()
36+
) : Expression() {
37+
override fun visit(visitor: AstVisitor) = visitor.visitFunctionCall(this)
38+
}
2839

2940
data class Terminal(
3041
val token: Token.Operand
31-
) : Expression()
42+
) : Expression() {
43+
override fun visit(visitor: AstVisitor) = visitor.visitTerminal(this)
44+
}
45+
}
46+
47+
48+
interface AstVisitor {
49+
fun visitUnary(unary: Expression.Unary): Any
50+
fun visitBinary(binary: Expression.Binary): Any
51+
fun visitTernary(ternary: Expression.Ternary): Any
52+
fun visitFunctionCall(functionCall: Expression.FunctionCall): Any
53+
fun visitTerminal(terminal: Expression.Terminal): Any
3254
}

src/commonMain/kotlin/com/github/murzagalin/evaluator/ast/Parser.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ package com.github.murzagalin.evaluator.ast
22

33
import com.github.murzagalin.evaluator.Token
44

5-
6-
/* the parser uses these rules
7-
5+
/*
86
expression -> ternary_if
97
ternary_if -> logic_or ( "?" ternary_if ":" ternary_if )?
108
logic_or -> logic_and ( ( "or" | "||" ) logic_and )*
@@ -173,7 +171,7 @@ class Parser {
173171
arguments += expression(tokens)
174172
if (tokens[ix] is Token.FunctionCall.Delimiter) ix++
175173
}
176-
require(tokens[ix] is Token.Bracket.Right) { "expected ')' after a function call" }
174+
require(tokens[ix++] is Token.Bracket.Right) { "expected ')' after a function call" }
177175

178176
return Expression.FunctionCall(token, arguments)
179177
}

src/commonTest/kotlin/com/github/murzagalin/evaluator/ast/ParserFunctionsTest.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,50 @@ class ParserFunctionsTest {
7878
subject.parse(expression)
7979
)
8080
}
81+
82+
@Test
83+
fun expression_with_functions() {
84+
val variable = Token.Operand.Variable("var")
85+
val sin = Token.FunctionCall(1, DefaultFunctions.SIN)
86+
val cos = Token.FunctionCall(1, DefaultFunctions.COS)
87+
88+
val expression = listOf(
89+
sin,
90+
Token.Bracket.Left,
91+
variable,
92+
Token.Bracket.Right,
93+
Token.Operator.Power,
94+
Token.Operand.Number(2),
95+
Token.Operator.Plus,
96+
cos,
97+
Token.Bracket.Left,
98+
variable,
99+
Token.Bracket.Right,
100+
Token.Operator.Power,
101+
Token.Operand.Number(2),
102+
)
103+
104+
assertEquals(
105+
Expression.Binary(
106+
Token.Operator.Plus,
107+
Expression.Binary(
108+
Token.Operator.Power,
109+
Expression.FunctionCall(
110+
sin,
111+
arguments = listOf(Expression.Terminal(variable))
112+
),
113+
Expression.Terminal(Token.Operand.Number(2))
114+
),
115+
Expression.Binary(
116+
Token.Operator.Power,
117+
Expression.FunctionCall(
118+
cos,
119+
arguments = listOf(Expression.Terminal(variable))
120+
),
121+
Expression.Terminal(Token.Operand.Number(2))
122+
)
123+
),
124+
subject.parse(expression)
125+
)
126+
}
81127
}

0 commit comments

Comments
 (0)