diff --git a/calculator/calculator/__init__.py b/calculator/calculator/__init__.py new file mode 100644 index 0000000..ba34e1e --- /dev/null +++ b/calculator/calculator/__init__.py @@ -0,0 +1 @@ +import calculator.ast diff --git a/calculator/calculator/ast/__init__.py b/calculator/calculator/ast/__init__.py new file mode 100644 index 0000000..aba748c --- /dev/null +++ b/calculator/calculator/ast/__init__.py @@ -0,0 +1,4 @@ +from .node import Node # isort:skip +from .binop import BinOp +from .constant import Constant +from .unaryop import UnaryOp diff --git a/calculator/calculator/ast/binop.py b/calculator/calculator/ast/binop.py new file mode 100644 index 0000000..a091bbd --- /dev/null +++ b/calculator/calculator/ast/binop.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable + +from pydantic.dataclasses import dataclass + +from .node import Node + +if TYPE_CHECKING: + from calculator.ast.visit import Visitor + + +class Config: + arbitrary_types_allowed = True + + +@dataclass(config=Config) +class BinOp(Node): + """ + Node to represent a binary operation + """ + + op: Callable[[int, int], int] + lhs: Node + rhs: Node + + def accept(self, v: Visitor) -> None: + v.visit_binop(self) diff --git a/calculator/calculator/ast/constant.py b/calculator/calculator/ast/constant.py new file mode 100644 index 0000000..833f507 --- /dev/null +++ b/calculator/calculator/ast/constant.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from pydantic.dataclasses import dataclass + +from .node import Node + +if TYPE_CHECKING: + from calculator.ast.visit import Visitor + + +class Config: + arbitrary_types_allowed = True + + +@dataclass(config=Config) +class Constant(Node): + """ + Node to represent a constant value. + """ + + value: int + + def accept(self, v: Visitor) -> None: + v.visit_constant(self) diff --git a/calculator/calculator/ast/node.py b/calculator/calculator/ast/node.py new file mode 100644 index 0000000..dfbdf85 --- /dev/null +++ b/calculator/calculator/ast/node.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from calculator.ast.visit import Visitor + + +class Node(ABC): + """ + Abstract Node class for calculator AST. + """ + + @abstractmethod + def accept(self, v: Visitor) -> None: + pass diff --git a/calculator/calculator/ast/unaryop.py b/calculator/calculator/ast/unaryop.py new file mode 100644 index 0000000..38f2877 --- /dev/null +++ b/calculator/calculator/ast/unaryop.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable + +from pydantic.dataclasses import dataclass + +from .node import Node + +if TYPE_CHECKING: + from calculator.ast.visit import Visitor + + +class Config: + arbitrary_types_allowed = True + + +@dataclass(config=Config) +class UnaryOp(Node): + """ + Node to represent a unary operation + """ + + op: Callable[[int], int] + rhs: Node + + def accept(self, v: Visitor) -> None: + v.visit_unaryop(self) diff --git a/calculator/calculator/ast/visit/__init__.py b/calculator/calculator/ast/visit/__init__.py new file mode 100644 index 0000000..8bf79fe --- /dev/null +++ b/calculator/calculator/ast/visit/__init__.py @@ -0,0 +1 @@ +from .visitor import Visitor # isort:skip diff --git a/calculator/calculator/ast/visit/visitor.py b/calculator/calculator/ast/visit/visitor.py new file mode 100644 index 0000000..c111215 --- /dev/null +++ b/calculator/calculator/ast/visit/visitor.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from calculator.ast import BinOp, Constant, Node, UnaryOp + + +class Visitor(ABC): + """ + Abstract Visitor class for the AST class hierarchy. + """ + + def visit(self, n: Node) -> None: + n.accept(self) + + @abstractmethod + def visit_constant(self, c: Constant) -> None: + pass + + @abstractmethod + def visit_binop(self, b: BinOp) -> None: + pass + + @abstractmethod + def visit_unaryop(self, b: UnaryOp) -> None: + pass