#!/usr/bin/python3
#
#  Naše objektové řešení obsahovalo spoustu duplicitního kódu: speciálně
#  všechny binární operátory měly identické metody __init__ a velmi podobné
#  metody __str__ a eval.
#
#  Zde se duplicit zbavíme tím, že je přesuneme do společného předka:
#  třídy BinaryNode, která popisuje chování obecného binárního operátoru.
#  Každý konkrétní operátor si pak pouze dodefinuje, jak se jmenuje a jaké
#  funkci odpovídá.
#

class Node:

    def eval(self):
        raise NotImplementedError()


class NumNode(Node):

    def __init__(self, x):
        self.value = x

    def __str__(self):
        return str(self.value)

    def eval(self):
        return self.value


class BinaryNode(Node):

    op_name = '?'

    def __init__(self, left, right):
        self.left = left
        self.right = right

    def __str__(self):
        return '(' + str(self.left) + self.op_name + str(self.right) + ')'

    def eval(self):
        return self.eval_op(self.left.eval(), self.right.eval())

    def eval_op(self, x, y):
        raise NotImplementedError()


class AddNode(BinaryNode):

    op_name = '+'

    def eval_op(self, x, y):
        return x + y


class SubNode(BinaryNode):

    op_name = '-'

    def eval_op(self, x, y):
        return x - y


class MulNode(BinaryNode):

    op_name = '*'

    def eval_op(self, x, y):
        return x * y


class DivNode(BinaryNode):

    op_name = '/'

    def eval_op(self, x, y):
        return x // y


x = AddNode(MulNode(NumNode(3), NumNode(4)), SubNode(NumNode(5), NumNode(6)))
print(x)
print(x.eval())