Minifying math statements The Challenge

You are the owner of an amazing service called Coyote Beta, which magically answers math questions its users send to it over the internet.

But it turns out, bandwidth is expensive.  You have two choices, either create a "Coyote Beta Pro" or find some way to solve this.  Just recently, someone queried (x + 2). Couldn't the client send x+2, and the user would see no difference?

The Task

Your task is to "minify" math expressions.  Given an input expression, you must get rid of whitespace and parentheses until it gives a minimal representation of the same input.  The parentheses around associative operations need not be preserved.

The only operators given here are +, -, *, /, and ^ (exponentiation), with standard mathematical associativity and precedence.  The only whitespace given in the input will be actual space characters.

Sample Input/Output

Input       | Output
------------|--------------
(2+x) + 3   | 2+x+3
((4+5))*x   | (4+5)*x
z^(x+42)    | z^(x+42)
x - ((y)+2) | x-(y+2)
(z - y) - x | z-y-x
x^(y^2)     | x^y^2
x^2 / z     | x^2/z
- (x + 5)+3 | -(x+5)+3


Scoring

Input/output can use any preferred method.  The smallest program in bytes wins.

Exact bits

Exponentiation is right associative and also follows standard math precedence (being the highest).  A valid numeric literal is /[0-9]+/, and a valid variable literal is /[a-z]+/.  A single variable literal represents a single value even when its character length is longer than 1.

What is meant by "the parentheses around associative operations need not be preserved" is that the output should consist of an expression that results in an identical parse tree, with the exception that associative operations can be rearranged.
C#, 523 519 504 bytes

Check the in-code comments to see how it works!



Golfed

using System;using System.Collections.Generic;namespace n{class p{static void Main(string[]a){foreach(String s in a){String r=s.Replace(" ","");List&lt;int&gt;l=new List&lt;int&gt;();for(int i=0;i&lt;r.Length;i++){if(r[i]=='('){l.Add(i);continue;}if(r[i]==')'){switch(r[Math.Max(l[l.Count-1]-1,0)]){case'+':case'(':switch(r[Math.Min(i+1,r.Length-1)]){case'+':case'-':case')':r=r.Remove(Math.Max(l[l.Count-1],0),1);r=r.Remove(Math.Min(i,r.Length)-1,1);i-=2;break;}break;}l.RemoveAt(l.Count-1);}}Console.WriteLine(r);}}}}




Ungolfed

using System;
using System.Collections.Generic;

namespace n {
    class p {
        static void Main( string[] a ) {
            // Loop every String given for the program
            foreach (String s in a) {
                // Get rid of the spaces
                String r = s.Replace( " ", "" );

                // A little helper that will have the indexes of the '('
                List&lt;int&gt; l = new List&lt;int&gt;();

                // Begin the optimizatio process
                for (int i = 0; i &lt; r.Length; i++) {
                    // If char is an '(', add the index to the helper list and continue
                    if (r[ i ] == '(') {
                        l.Add( i );
                        continue;
                    }

                    // If the char is an ')', validate the group
                    if (r[ i ] == ')') {
                        // If the char before the last '(' is an '+' or '(' ...
                        switch (r[ Math.Max( l[ l.Count - 1 ] - 1, 0 ) ]) {
                            case '+':
                            case '(':
                                // ... and the char after the ')' we're checking now is an '+', '-' or ')' ...
                                switch (r[ Math.Min( i + 1, r.Length - 1 ) ]) {
                                    case '+':
                                    case '-':
                                    case ')':
                                        // Remove the '()' since they're most likely desnecessary.
                                        r = r.Remove( Math.Max( l[ l.Count - 1 ], 0 ), 1 );
                                        r = r.Remove( Math.Min( i, r.Length ) - 1, 1 );

                                        // Go two steps back in the loop since we removed 2 chars from the String,
                                        //   otherwise we would miss some invalid inputs
                                        i -= 2;
                                        break;
                                }

                                break;
                        }

                        // Remove the last inserted index of '(' from the list,
                        //   since we matched an ')' for it.
                        l.RemoveAt( l.Count - 1 );
                    }
                }

                // Print the result
                Console.WriteLine( r );
            }
        }
    }
}




Side notes


Fixed some typos and renamed some vars.
Nested an switch to get rid of an unnecessary variable. Also, fixed a bug that would render some solutions invalid, reported by Anders Kaseorg.




P.S.: If you have a tip or found a bug, please let me know in the comments and I'll try to fix it ( I'll then add a note about the bug fix with your name ;) )