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.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