From 5ea82de840d3a36595bb53352d7b4b46e3e0b227 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sat, 30 Nov 2019 15:18:26 +0100 Subject: [PATCH] calculator: add initial AST implementation The AST's class hierarchy is defined inside the `calculator.ast` module. An abstract visitor is defined inside the `calculator.ast.visit` package. --- calculator/calculator/__init__.py | 1 + calculator/calculator/ast/__init__.py | 4 +++ calculator/calculator/ast/binop.py | 28 +++++++++++++++++++++ calculator/calculator/ast/constant.py | 26 +++++++++++++++++++ calculator/calculator/ast/node.py | 17 +++++++++++++ calculator/calculator/ast/unaryop.py | 27 ++++++++++++++++++++ calculator/calculator/ast/visit/__init__.py | 1 + calculator/calculator/ast/visit/visitor.py | 28 +++++++++++++++++++++ 8 files changed, 132 insertions(+) create mode 100644 calculator/calculator/__init__.py create mode 100644 calculator/calculator/ast/__init__.py create mode 100644 calculator/calculator/ast/binop.py create mode 100644 calculator/calculator/ast/constant.py create mode 100644 calculator/calculator/ast/node.py create mode 100644 calculator/calculator/ast/unaryop.py create mode 100644 calculator/calculator/ast/visit/__init__.py create mode 100644 calculator/calculator/ast/visit/visitor.py 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