calculator: add an evaluation visitor

The `calculator.eval` module defines an `Evaluator` visitor for the AST
hierarchy.
This commit is contained in:
Bruno BELANYI 2019-11-30 15:21:42 +01:00
parent 5ea82de840
commit f103adddac
4 changed files with 71 additions and 0 deletions

View file

@ -1 +1,2 @@
import calculator.ast
import calculator.eval

View file

@ -0,0 +1 @@
from .evaluator import Evaluator

View 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)

View 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