class AST:

    def __init__(self, token=None):
        self.token = token
        self.line = token.line if token else None
        self.column = token.column if token else None


class Program(AST):

    def __init__(self, statements):
        super().__init__()
        self.statements = statements


class Block(AST):

    def __init__(self, statements):
        super().__init__()
        self.statements = statements


class BinOp(AST):

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


class UnaryOp(AST):

    def __init__(self, op, expr):
        super().__init__(op)
        self.op = op
        self.expr = expr


class Num(AST):

    def __init__(self, token):
        super().__init__(token)
        self.value = token.value


class String(AST):

    def __init__(self, token):
        super().__init__(token)
        self.value = token.value


class Boolean(AST):

    def __init__(self, token):
        super().__init__(token)
        self.value = token.value


class NoneValue(AST):

    def __init__(self, token):
        super().__init__(token)
        self.value = None


class List(AST):

    def __init__(self, elements, token=None):
        super().__init__(token)
        self.elements = elements


class Dict(AST):

    def __init__(self, pairs, token=None):
        super().__init__(token)
        self.pairs = pairs


class Set(AST):

    def __init__(self, elements, token=None):
        super().__init__(token)
        self.elements = elements


class Tuple(AST):

    def __init__(self, elements, token=None):
        super().__init__(token)
        self.elements = elements


class DictComp(AST):

    def __init__(
        self, key_expr, value_expr, var_name, iterable, condition=None, token=None
    ):
        super().__init__(token)
        self.key_expr = key_expr
        self.value_expr = value_expr
        self.var_name = var_name
        self.iterable = iterable
        self.condition = condition


class Var(AST):

    def __init__(self, token):
        super().__init__(token)
        self.name = token.value


class VarDecl(AST):

    def __init__(self, var_name, value, token=None, type_hint=None):
        super().__init__(token)
        self.var_name = var_name
        self.value = value
        self.type_hint = type_hint


class Assign(AST):

    def __init__(self, var, value, token=None):
        super().__init__(token)
        self.var = var
        self.value = value


class MultiVarDecl(AST):

    def __init__(self, var_names, values, token=None, type_hints=None):
        super().__init__(token)
        self.var_names = var_names
        self.values = values
        self.type_hints = type_hints or {}


class MultiAssign(AST):

    def __init__(self, vars, values, token=None):
        super().__init__(token)
        self.vars = vars
        self.values = values


class NoOp(AST):
    pass


class Print(AST):

    def __init__(self, expr, token=None):
        super().__init__(token)
        self.expr = expr


class Input(AST):

    def __init__(self, prompt, var_name=None, token=None):
        super().__init__(token)
        self.prompt = prompt
        self.var_name = var_name


class If(AST):

    def __init__(self, condition, if_body, else_body=None, token=None):
        super().__init__(token)
        self.condition = condition
        self.if_body = if_body
        self.else_body = else_body


class While(AST):

    def __init__(self, condition, body, token=None):
        super().__init__(token)
        self.condition = condition
        self.body = body


class For(AST):

    def __init__(self, var_name, start, end, body, token=None):
        super().__init__(token)
        self.var_name = var_name
        self.start = start
        self.end = end
        self.body = body


class ForEach(AST):

    def __init__(self, var_name, iterable, body, token=None):
        super().__init__(token)
        self.var_name = var_name
        self.iterable = iterable
        self.body = body


class Break(AST):

    def __init__(self, token=None):
        super().__init__(token)


class Continue(AST):

    def __init__(self, token=None):
        super().__init__(token)


class FuncDecl(AST):

    def __init__(
        self, name, params, body, token=None, return_type=None, param_types=None
    ):
        super().__init__(token)
        self.name = name
        self.params = params
        self.body = body
        self.return_type = return_type
        self.param_types = param_types


class FuncCall(AST):

    def __init__(self, name_or_expr, args, token=None, kwargs=None):
        super().__init__(token)
        if isinstance(name_or_expr, str):
            self.name = name_or_expr
            self.func_expr = None
        else:
            self.name = None
            self.func_expr = name_or_expr
        self.args = args
        self.kwargs = kwargs or {}


class Return(AST):

    def __init__(self, expr=None, token=None):
        super().__init__(token)
        self.expr = expr


class ClassDecl(AST):

    def __init__(self, name, methods, parent=None, token=None, class_vars=None):
        super().__init__(token)
        self.name = name
        self.methods = methods
        self.parent = parent
        self.class_vars = class_vars or []


class MethodDecl(AST):

    def __init__(
        self, name, params, body, token=None, return_type=None, param_types=None
    ):
        super().__init__(token)
        self.name = name
        self.params = params
        self.body = body
        self.return_type = return_type
        self.param_types = param_types


class Constructor(AST):

    def __init__(self, params, body, token=None, param_types=None):
        super().__init__(token)
        self.params = params
        self.body = body
        self.param_types = param_types


class AttributeRef(AST):

    def __init__(self, obj, attr, token=None):
        super().__init__(token)
        self.obj = obj
        self.attr = attr


class MethodCall(AST):

    def __init__(self, obj, method, args, token=None, kwargs=None):
        super().__init__(token)
        self.obj = obj
        self.method = method
        self.args = args
        self.kwargs = kwargs or {}


class Import(AST):

    def __init__(self, module, alias=None, token=None):
        super().__init__(token)
        self.module = module
        self.alias = alias


class PythonImport(AST):

    def __init__(self, module, alias=None, token=None):
        super().__init__(token)
        self.module = module
        self.alias = alias


class PythonCall(AST):

    def __init__(self, func_expr, args, token=None, kwargs=None):
        super().__init__(token)
        self.func_expr = func_expr
        self.args = args
        self.kwargs = kwargs if kwargs is not None else {}


class TryCatch(AST):

    def __init__(self, try_block, except_blocks, finally_block=None, token=None):
        super().__init__(token)
        self.try_block = try_block
        self.except_blocks = except_blocks
        self.finally_block = finally_block


class Raise(AST):

    def __init__(self, exception, token=None):
        super().__init__(token)
        self.exception = exception


class IndexAccess(AST):

    def __init__(self, obj, index, token=None):
        super().__init__(token)
        self.obj = obj
        self.index = index


class SliceAccess(AST):

    def __init__(self, obj, start, end=None, step=None, token=None):
        super().__init__(token)
        self.obj = obj
        self.start = start
        self.end = end
        self.step = step


class SelfVar(AST):

    def __init__(self, name, token=None):
        super().__init__(token)
        self.name = name


class Lambda(AST):

    def __init__(self, params, body, token=None, param_types=None, return_type=None):
        super().__init__(token)
        self.params = params
        self.body = body
        self.param_types = param_types
        self.return_type = return_type


class ListComp(AST):

    def __init__(self, expr, var_name, iterable, condition=None, token=None):
        super().__init__(token)
        self.expr = expr
        self.var_name = var_name
        self.iterable = iterable
        self.condition = condition


class SetComp(AST):

    def __init__(self, expr, var_name, iterable, condition=None, token=None):
        super().__init__(token)
        self.expr = expr
        self.var_name = var_name
        self.iterable = iterable
        self.condition = condition


class Generator(AST):

    def __init__(self, expr, var_name, iterable, condition=None, token=None):
        super().__init__(token)
        self.expr = expr
        self.var_name = var_name
        self.iterable = iterable
        self.condition = condition


class Yield(AST):

    def __init__(self, expr=None, token=None):
        super().__init__(token)
        self.expr = expr


class YieldFrom(AST):

    def __init__(self, expr, token=None):
        super().__init__(token)
        self.expr = expr


class Decorator(AST):

    def __init__(self, name, args, decorated, token=None):
        super().__init__(token)
        self.name = name
        self.args = args
        self.decorated = decorated


class AsyncFuncDecl(AST):

    def __init__(
        self, name, params, body, token=None, return_type=None, param_types=None
    ):
        super().__init__(token)
        self.name = name
        self.params = params
        self.body = body
        self.return_type = return_type
        self.param_types = param_types


class AsyncMethodDecl(AST):

    def __init__(
        self, name, params, body, token=None, return_type=None, param_types=None
    ):
        super().__init__(token)
        self.name = name
        self.params = params
        self.body = body
        self.return_type = return_type
        self.param_types = param_types


class Await(AST):

    def __init__(self, expr, token=None):
        super().__init__(token)
        self.expr = expr


class TypeHint(AST):

    def __init__(self, type_name, token=None):
        super().__init__(token)
        self.type_name = type_name


class FormatString(AST):

    def __init__(self, parts, token=None):
        super().__init__(token)
        self.parts = parts


class Ternary(AST):

    def __init__(self, condition, if_expr, else_expr, token=None):
        super().__init__(token)
        self.condition = condition
        self.if_expr = if_expr
        self.else_expr = else_expr


class Unpacking(AST):

    def __init__(self, expr, token=None):
        super().__init__(token)
        self.expr = expr


class WalrusOperator(AST):

    def __init__(self, var_name, value, token=None):
        super().__init__(token)
        self.var_name = var_name
        self.value = value


class CompoundAssign(AST):

    def __init__(self, var, op, value, token=None):
        super().__init__(token)
        self.var = var
        self.op = op
        self.value = value


class Switch(AST):

    def __init__(self, expr, cases, default_case=None, token=None):
        super().__init__(token)
        self.expr = expr
        self.cases = cases
        self.default_case = default_case


class Case(AST):

    def __init__(self, values, body, token=None):
        super().__init__(token)
        self.values = values
        self.body = body


class With(AST):

    def __init__(self, context_expr, var_name, body, token=None):
        super().__init__(token)
        self.context_expr = context_expr
        self.var_name = var_name
        self.body = body


class SliceAssign(AST):

    def __init__(self, target, start, end, step, value, token):
        self.target = target
        self.start = start
        self.end = end
        self.step = step
        self.value = value
        self.token = token


class ExtendedUnpacking(AST):

    def __init__(self, targets, value, token):
        self.targets = targets
        self.value = value
        self.token = token


class StarredExpr(AST):

    def __init__(self, expr, token):
        self.expr = expr
        self.token = token


class PropertyDecl(AST):

    def __init__(self, name, getter, setter=None, deleter=None, token=None):
        self.name = name
        self.getter = getter
        self.setter = setter
        self.deleter = deleter
        self.token = token


class StaticMethodDecl(AST):

    def __init__(self, name, func, token=None):
        self.name = name
        self.func = func
        self.token = token


class ClassMethodDecl(AST):

    def __init__(self, name, func, token=None):
        self.name = name
        self.func = func
        self.token = token


class TypeAlias(AST):

    def __init__(self, name, type_expr, token=None):
        super().__init__(token)
        self.name = name
        self.type_expr = type_expr


class LiteralType(AST):

    def __init__(self, values, token=None):
        super().__init__(token)
        self.values = values


class TypedDictType(AST):

    def __init__(self, fields, token=None):
        super().__init__(token)
        self.fields = fields
