Интерпретатор

Описание
Интерпретатор — поведенческий шаблон проектирования. Описывает компонент, который обрабатывает структурированные тестовые данные, превращая их в отдельные лексические токены, а затем интерпретируя последовательности указанных токенов.
пример
Опишем пример создания программы интерпретатора. Программа позволяет интерпретировать математический пример, записанный в строку в математическое выражение.

Первый метод lex позволяет создать список объектов типа Token, который хранит математические знаки и математических чисел.

Второй метод parse возвращает объект BinaryOperation, который позволяет вывести ответ математического выражения в методе value

from dataclasses import dataclass
from enum import Enum
from typing import ClassVar, List


@dataclass
class Token:
    class Type(Enum):
        INTEGER = 0
        PLUS = 1
        MINUS = 2
        LPAREN = 3
        RPAREN = 4

    type: Type
    text: str

    def __str__(self):
        return f'`{self.text}`'


@dataclass
class Integer:
    value: int


@dataclass
class BinaryOperation:
    class Type(Enum):
        ADDITION = 0
        SUBTRACTION = 1

    type: ClassVar = None
    left: ClassVar = None
    right: ClassVar = None

    @property
    def value(self) -> int:
        if self.type == self.Type.ADDITION:
            return self.left.value + self.right.value
        elif self.type == self.Type.SUBTRACTION:
            return self.left.value - self.right.value


def lex(input: str) -> List[str]:
    result = []
    set_type = {
        '+': Token.Type.PLUS,
        '-': Token.Type.MINUS,
        '(': Token.Type.LPAREN,
        ')': Token.Type.RPAREN
    }
    i = 0
    while i < len(input):
        if input[i] in set_type.keys():
            result.append(Token(set_type[input[i]], input[i]))
        else:
            digits = [input[i]]
            while input[i+1].isdigit():
                digits.append(input[i+1])
                i += 1
            result.append(Token(Token.Type.INTEGER, ''.join(digits)))
        i += 1

    return result


def parse(tokens: List[str]) -> BinaryOperation:
    result = BinaryOperation()
    have_lhs = False
    i = 0
    while i < len(tokens):
        token = tokens[i]

        if token.type == Token.Type.INTEGER:
            integer = Integer(int(token.text))
            if not have_lhs:
                result.left = integer
                have_lhs = True
            else:
                result.right = integer
        elif token.type == Token.Type.PLUS:
            result.type = BinaryOperation.Type.ADDITION
        elif token.type == Token.Type.MINUS:
            result.type = BinaryOperation.Type.SUBTRACTION
        elif token.type == Token.Type.LPAREN:
            j = i
            while j < len(tokens):
                if tokens[j].type == Token.Type.RPAREN:
                    break
                j += 1
            subexpression = tokens[i + 1:j]
            element = parse(subexpression)
            if not have_lhs:
                result.left = element
                have_lhs = True
            else:
                result.right = element
            i = j
        i += 1
    return result


def eval(input: str) -> None:
    tokens = lex(input)
    print(' '.join(map(str, tokens)))

    parsed = parse(tokens)
    print(f'{input} = {parsed.value}')


if __name__ == '__main__':
    eval('(14+7)-(11+4)')
    eval('12+(13-4)')