private NFA regex2NFA( String regex ) {
        // Given regex is in infix form. To calculate NFA for regex, we convert regex into postfix form and start calculating.
        ArrayList<String> postfix = infix2Postfix( regex ); // Converts infix regex definition into postfix. The postfix form is an array of regex elements
        Stack<NFA> stack = new Stack<NFA>(); // Stack for NFA calculation

        for ( String element : postfix ) { // For each element in postfix form
            NFA nfa1, nfa2;
            if ( element.equals( "*" ) ) { // If element is "*" then closure top of stack
                nfa1 = stack.pop(); // Pop the stack and put it into nfa1
                State start = new State( nfa1 ); // Create start state for nfa
                State fin = new State( nfa1 ); // Create final state for nfa
                try {
                    nfa1.addTransition( start, "\0", nfa1.getStartState() ); // Add epsilon transition from start to nfa1 start state
                    nfa1.addTransition( start, "\0", fin ); // Add epsilon transition from start to final state
                    nfa1.addTransition( nfa1.getFinalState(), "\0", start ); // Add epsilon transition from nfa1 final to start state
                    nfa1.addTransition( nfa1.getFinalState(), "\0", fin ); // Add epsilon transition from nfa1 final to final state
                }catch( StateNotBelongsToFAException e ) {
                    System.err.println( "In * regex2NFA NFA: " + e.getMessage() );
                }
                nfa1.setStartState( start ); // Change start state for nfa1
                nfa1.getFinalState().setFinal( false ); // Set finality of final state of nfa1 to false
                fin.setFinal( true ); // Set finality of final state to true
                nfa1.setFinalState( fin ); // Set final as final state for nfa1
                stack.push( nfa1 ); // Push nfa1 into the stack

            } else if ( element.equals( "." ) ) { // If element is "." then concatenate two NFAs above the stack
                nfa2 = stack.pop(); // Pop the stack and put it into nfa1
                nfa1 = stack.pop(); // Pop the stack and put it into nfa2

                nfa1.mergeNFA( nfa2 ); // Merge nfa2 into nfa1
                try {
                    nfa1.addTransition( nfa1.getFinalState(), "\0", nfa2.getStartState() ); // Add transition from nfa1 final state to nfa2 start state
                } catch ( StateNotBelongsToFAException e ) {
                    System.err.println( "In . regex2NFA NFA: " + e.getMessage() );
                }
                nfa1.setStartState( nfa1.getStartState() ); // Change start state for nfa1
                nfa1.getFinalState().setFinal( false ); // Set finality of final state of nfa1 to false
                nfa1.setFinalState( nfa2.getFinalState() ); // Set nfa2 final state as final state for nfa1
                stack.push( nfa1 ); // Push nfa1 into the stack
            } else if ( element.equals( "+" ) ) { // If element is "+" then
                nfa2 = stack.pop(); // Pop the stack and put it into nfa1
                nfa1 = stack.pop(); // Pop the stack and put it into nfa2

                State start = new State( nfa1 ); // Create start state for nfa
                State fin = new State( nfa1 ); // Create final state for nfa

                nfa1.mergeNFA( nfa2 ); // Merge nfa2 into nfa1
                try {
                    nfa1.addTransition( start, "\0", nfa1.getStartState() ); // Add epsilon transition from start to nfa1 start state
                    nfa1.addTransition( start, "\0", nfa2.getStartState() ); // Add epsilon transition from start to nfa2 start state

                    nfa1.addTransition( nfa1.getFinalState(), "\0", fin ); // Add epsilon transition from nfa1 final to final state
                    nfa1.addTransition( nfa2.getFinalState(), "\0", fin ); // Add epsilon transition from nfa2 final to final state
                } catch ( StateNotBelongsToFAException e ) {
                    System.err.println( "In + regex2NFA NFA: " + e.getMessage() );
                }
                nfa1.getFinalState().setFinal( false ); // Set finality of final state of nfa1 to false
                nfa2.getFinalState().setFinal( false ); // Set finality of final state of nfa2 to false
                fin.setFinal( true ); // Set finality of final state to true
                nfa1.setStartState( start ); // Change start state for nfa1
                nfa1.setFinalState( fin ); // Change final state for nfa1
                stack.push( nfa1 ); // Push nfa1 into the stack
            } else { // If element is a character then make it's NFA and push it into the stack. If the element is a NFA label then make a clone of that NFA from NFA list and push it into the stack
                stack.push( element.charAt( 0 ) == '~' ? nfas.get( stringToNFAIndex.get( element.substring( 1, element.length() - 1 ) ) ).clone() : string2Nfa( element.substring( 1, element.length() - 1 ) ) );
            }

        }
        NFA nfa = stack.pop(); // The nfa at top of stack is the result
        nfa.getFinalState().setFinal( true ); // Make sure that it's final state is true
        return nfa; // Return that NFA
    }