Imports a NFA from a DOT file.

    Of .dot files are recognized the following attributes
      • nodeX   shape=doublecircle -> accepting node;
      • nodeX   root=true -> initial node;
      • edgeX   label="a" -> action in alphabet;
      • fakeX    style=invisible -> dummy invisible nodes pointing
                to initial state (it will be skipped);
      • fakeX->S [style=bold] -> dummy transitions to draw arrows
                pointing to initial states (they will be skipped).

    All invisible nodes are skipped.

    Forbidden names:
      • 'fake'  used for graphical purpose to drawn the arrow of
        the initial state
      • 'sink'  used as additional state when completing a NFA

    Forbidden characters:
      • "
      • '
      • (
      • )
      • spaces

    :param str input_file: Path to input DOT file;
    :return: *(dict)* representing a NFA.

def nfa_dot_importer(input_file: str) -> dict:
    """ Imports a NFA from a DOT file.

    Of .dot files are recognized the following attributes
      • nodeX   shape=doublecircle -> accepting node;
      • nodeX   root=true -> initial node;
      • edgeX   label="a" -> action in alphabet;
      • fakeX    style=invisible -> dummy invisible nodes pointing
                to initial state (it will be skipped);
      • fakeX->S [style=bold] -> dummy transitions to draw arrows
                pointing to initial states (they will be skipped).

    All invisible nodes are skipped.

    Forbidden names:
      • 'fake'  used for graphical purpose to drawn the arrow of
        the initial state
      • 'sink'  used as additional state when completing a NFA

    Forbidden characters:
      • "
      • '
      • (
      • )
      • spaces

    :param str input_file: Path to input DOT file;
    :return: *(dict)* representing a NFA.
    """

    # pyDot Object
    g = pydot.graph_from_dot_file(input_file)[0]

    states = set()
    initial_states = set()
    accepting_states = set()

    replacements = {'"': '', "'": '', '(': '', ')': '', ' ': ''}

    for node in g.get_nodes():
        attributes = node.get_attributes()
        if node.get_name() == 'fake' \
                or node.get_name() == 'None' \
                or node.get_name() == 'graph' \
                or node.get_name() == 'node':
            continue
        if 'style' in attributes \
                and attributes['style'] == 'invisible':
            continue

        node_reference = __replace_all(replacements,
                                       node.get_name()).split(',')
        if len(node_reference) > 1:
            node_reference = tuple(node_reference)
        else:
            node_reference = node_reference[0]
        states.add(node_reference)
        for attribute in attributes:
            if attribute == 'root':
                initial_states.add(node_reference)
            if attribute == 'shape' \
                    and attributes['shape'] == 'doublecircle':
                accepting_states.add(node_reference)

    alphabet = set()
    transitions = {}
    for edge in g.get_edges():
        source = __replace_all(replacements,
                               edge.get_source()).split(',')
        if len(source) > 1:
            source = tuple(source)
        else:
            source = source[0]
        destination = __replace_all(replacements,
                                    edge.get_destination()).split(',')
        if len(destination) > 1:
            destination = tuple(destination)
        else:
            destination = destination[0]

        if source not in states or destination not in states:
            continue

        label = __replace_all(replacements, edge.get_label())
        alphabet.add(label)

        transitions.setdefault((source, label), set()).add(
            destination)

    nfa = {
        'alphabet': alphabet,
        'states': states,
        'initial_states': initial_states,
        'accepting_states': accepting_states,
        'transitions': transitions
    }

    return nfa