calculator: add an evaluation visitor
The `calculator.eval` module defines an `Evaluator` visitor for the AST hierarchy.
This commit is contained in:
parent
5ea82de840
commit
f103adddac
|
@ -1 +1,2 @@
|
||||||
import calculator.ast
|
import calculator.ast
|
||||||
|
import calculator.eval
|
||||||
|
|
1
calculator/calculator/eval/__init__.py
Normal file
1
calculator/calculator/eval/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from .evaluator import Evaluator
|
35
calculator/calculator/eval/evaluator.py
Normal file
35
calculator/calculator/eval/evaluator.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from calculator.ast.visit import Visitor
|
||||||
|
from pydantic.dataclasses import dataclass
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from calculator.ast import BinOp, Constant, Node, UnaryOp
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Evaluator(Visitor):
|
||||||
|
"""
|
||||||
|
Evaluate a Tree and retrieve the calculated value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
value: int = dataclasses.field(default=0, init=False)
|
||||||
|
|
||||||
|
def visit_and_get_value(self, n: Node) -> int:
|
||||||
|
n.accept(self)
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def visit_constant(self, c: Constant) -> None:
|
||||||
|
self.value = c.value
|
||||||
|
|
||||||
|
def visit_binop(self, b: BinOp) -> None:
|
||||||
|
lhs_val = self.visit_and_get_value(b.lhs)
|
||||||
|
rhs_val = self.visit_and_get_value(b.rhs)
|
||||||
|
self.value = b.op(lhs_val, rhs_val)
|
||||||
|
|
||||||
|
def visit_unaryop(self, b: UnaryOp) -> None:
|
||||||
|
rhs_val = self.visit_and_get_value(b.rhs)
|
||||||
|
self.value = b.op(rhs_val)
|
34
calculator/calculator/eval/test_evaluator.py
Normal file
34
calculator/calculator/eval/test_evaluator.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import pytest
|
||||||
|
from calculator.ast import BinOp, Constant, UnaryOp
|
||||||
|
|
||||||
|
from .evaluator import Evaluator
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def vis():
|
||||||
|
return Evaluator()
|
||||||
|
|
||||||
|
|
||||||
|
def test_evaluate_constant(vis):
|
||||||
|
c = Constant(42)
|
||||||
|
assert vis.visit_and_get_value(c) == 42
|
||||||
|
|
||||||
|
|
||||||
|
def test_evaluate_unaryop_identity(vis):
|
||||||
|
op = UnaryOp(lambda x: x, Constant(42))
|
||||||
|
assert vis.visit_and_get_value(op) == 42
|
||||||
|
|
||||||
|
|
||||||
|
def test_evaluate_unaryop_negate(vis):
|
||||||
|
op = UnaryOp(lambda x: -x, Constant(-42))
|
||||||
|
assert vis.visit_and_get_value(op) == 42
|
||||||
|
|
||||||
|
|
||||||
|
def test_evaluate_binop_plus(vis):
|
||||||
|
op = BinOp(lambda x, y: x + y, Constant(12), Constant(27))
|
||||||
|
assert vis.visit_and_get_value(op) == 39
|
||||||
|
|
||||||
|
|
||||||
|
def test_evaluate_binop_minus(vis):
|
||||||
|
op = BinOp(lambda x, y: x - y, Constant(42), Constant(51))
|
||||||
|
assert vis.visit_and_get_value(op) == -9
|
Loading…
Reference in a new issue