Free Web Hosting Provider - Web Hosting - E-commerce - High Speed Internet - Free Web Page
Search the Web

vbProgramming | Tutorials | Advanced Calculator
    vbProgramming 
The Advanced Calculator
 
     

vbProgramming Home :: vbProgramming Forums :: Tutorials :: Contact :: Links 

 

 

This tutorial is aimed for those of you who want to perform complex string operations and parsing them. It gives you a good idea of how to put together
a nice parsing system. Obviously, this tutorial will parse text from a calculator

What's the difference between the other calculator and this one?
As you know, my other calculator tutorial is a basic calculator, similar to MS Calculator. For example:
1
<Press the + button>
2
<Press the = button>
<Output: 3>

This new calculator, however, will put your skills on the line, because by the end of this tutorial, we'll be looking at a calculator where you can type in anything you want into the text area, and then press enter. It'll be able to perform complex operations like this (with order of operations) :

((1 + 2) / (14.5 * 10 ^ (2 * Sin (Cos (12 + 99) + 14) - 2))) ^ 3 * 3

Heh.  Hell yeah.

Rules
Rules? Yep. Rules.

Since we're parsing the text, the text will need to come in exactly the way we want it. For example in VB.NET you can't really say EndIf
You have to have spaces in between End and If. Similarly, our calculator will have to have spaces in between the numbers.

1+2 <--- Not valid
1 + 2 <--- Valid

Sin(1+2) <-- Not valid
Sin(1 + 2) <-- STILL not valid
Sin (1 + 2) <-- Valid

This is the reason I opted to use a Label as the calculator display as opposed a Textbox. If we had a textbox the user would type in something silly such as "The Answer to Life The Universe and Everything Else." Which, for the misinformed, is 42. Ask Google, search "Answer to Life The Universe And Everything Else =" and it'll tell you.

Got off track a bit, sorry.

The Basic Code
Ok, as you may (or may not) know, I'm not very good when it comes to aesthetics. I'm more of a practical kind of guy, I'm pretty bad when it comes to graphics. Create an interface similar to this one (Doesn't matter how it looks or how everything's arranged, just create the buttons)



I named all the buttons like so:

Left side of the screen:
btn0........btn9
btnDot
btnPlusOrMinus <-- makes the number negative
btnToggleMode <--- this toggles between Degrees and Radians
btnEquals

Right side of the screen:
btnPlus ............. btnDivide
btnOpenParentheseese....btnCloseParentheseese
btnSin.......btnATan
btnStore.....btnRecall
btnClear...btnDelete
btnSquare, btnPower, btnSqrt, btnXRt

Output
lblDisplay - The big thing at the top
lblStore     - The thing to the right of it
lblHelp      - The thing at the bottom

Pretty basic, and pretty practical.

One quick announcement: You can download Part I of the source code, which contains the layout already made, and the buttons displaying things on lblDisplay.  Part II is the finished calculator, and that will be up for download as well.


Alright, now it's time to make the buttons display things on the screen. I'm assuming you have a solid knowledge of Visual Basic and you should be able to do the following. This is the code for the click events of all the buttons:

For buttons 0 - 9, and btnDot use the following code:
lblDisplay.Text += "0" (replace 0 with the number, or a dot(period))

For btnPi:
lblDisplay.Text += Math.PI.ToString

btnPlusOrMinus (this button will make a number negative)
lblDisplay.Text += "-"

For all the operators:
lblDisplay.Text += " + " (Replace this with the appropriate symbol and NOTE THE SPACING)

Opening and closing parentheseese:
lblDisplay.Text += "("
lblDisplay.Text += ")"


Trignometry Functions:
lblDisplay.Text += "Sin ("
lblDisplay.Text += "ArcSin ("

Replace the above with the appropriate function name and NOTE THE SPACING)

btnSquare:
lblDisplay.Text += " ^ 2"

btnPower:
lblDisplay.Text += " ^ "

btnSqrt:
lblDisplay.Text += "Sqrt ("

btnXRt:
lblDisplay.Text += " ^ (1 / "        'Ex - Cube root of 4: 4 ^ (1 / 3) <-- just a little algebra

btnClear:
lblDisplay.Text = ""

btnDelete:
lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1) 'Remove 1 character

Again. You don't have to type all this in. You can download Part I of the source code for everything we've done up to this point (see end of tutorial for links to source code).

The ExpressionParser Class: v1.0 (Sort between Numbers and Operators)
The program is already ~700 lines of code in Form1 because of all the buttons and the code (although it doesn't accomplish much).

The most useful bulk of code for this tutorial is the ExpressionParser class. This class will parse (read and understand) what you type in, and it'll give back the answer. In my mind, this class cannot be done in '1 try'. I originally programmed this class in Java for school (we had an entire week to do a calculator, and since I've done it several times and written a tutorial on it for vb, I finished in about a day..... so then I decided to make an "Advanced Expression Parsing Calculator" which takes in the equation all at once.... and so I did! Now I'm writing a tutorial for it in VB).

That "1 week" that I programmed this ExpressionParser class in was full of trial and error, and I believe that Trial and Error is the best way to learn. Although in this tutorial you're generally doing what I tell you.... I'll give you older versions of my class (that have fatal mistakes in them) on purpose, so that when we do the next version, it gets revised and we figure out what we're doing wrong, and as you get to the end of this tutorial you'll have a pretty good sense of the development process. In my mind this calculator can't be done perfectly in one try. What we're going to do is work on a basic calculator, and then start adding features to it instead of doing it all at once.

Alright. The first thing I decided to work on was BASIC operations, without Order of Operations. By basic operations I mean 1 + 2 - 3 type. If you type in something like 4 + 4 / 8 you'd get 1 instead of 4.5 (because Order of Operations isn't implemented in this version).

Create a new class called ExpressionParser.

Imports System.IO
Public Class ExpressionParser

End Class

Since our expressionparser will return an answer back to the user, we'll have to ... well... return a value! Create a new function
 Public Function ParseEquation(ByVal Equation As String) As Double

 End Function

First things first. We'll need to distinguish between the numbers and the operators.
1 + 2 - 3

1st item is a number(1)
2nd item is an operator(+)
3rd item is a number(2)
4th item is an operator(-)
5th item is a number(3)

Basically every other item is an operator, and every other item is a number.  The thing that's in between the numbers and the operators is a space. We're going to use String.Split(" ") to split up the numbers.

Brief oveview of String.Split:

Dim String1 as String
Dim String2() as String 'note: String2 is an array

String1 = "1,99,3"
String2 = String1.Split(",") 'The splitter char is a comma

MessageBox.Show(String2(0)) 'Messageboxes "1"
MessageBox.Show(String2(1)) 'Messageboxes "99"
MessageBox.Show(String2(2)) 'Messageboxes "2"
MessageBox.Show(String2(3)) 'Error. There is no 4th element in the array.

Alright, so we're basically doing the same thing, except splitting by spaces instead of commas. Every other item is an operator, and every other item is a number. If you remember from the TicTacToe tutorial, we use Mod 2 to do "Every other one". For example:

0 Mod 2 <-- this is 0
1 Mod 2 <---this is 1
2 Mod 2 <-- this is 0
3 Mod 2 <-- this is 1
4 Mod 2 <-- this is 0
5 Mod 2 <-- this is 1

Mod just returns the remainder (ex: 5/2, the remainder is 1). So we can use it to check for "every other one" in this case.

Your ParseEquation sub should look like this:
 

    Public Function ParseEquation(ByVal Equation As String) As Double
        Dim Numbers As ArrayList
        Dim Operators As ArrayList
        Dim SplitEquation() As String
        Dim x As Integer
        SplitEquation = Equation.Split(" ")
        For x = 0 To SplitEquation.Length - 1
            If x Mod 2 = 0 Then
                MessageBox.Show("Number: " & SplitEquation(x))
                Numbers.Add(SplitEquation(x))
            End If
            If x Mod 2 = 1 Then
                MessageBox.Show("Operator: " & SplitEquation(x))
                Operators.Add(SplitEquation(x))
            End If
        Next
    End Function

Look over the code. It's pretty straightforward. If you're not sure what an ArrayList is, please check the Alternative To Arrays tutorial.

Now in your Form1: Dim Parser As New ExpressionParser
In the Click event for the btnEquals: Parser.ParseEquation(lblDisplay.Text)

Run it. Type in "1 + 2 - 3" and it should tell you what's a number and what's an operator. Go back to the EquationParser class, and delete the MessageBox lines (they're annoying anyways).

The ExpressionParser Class: v2.0 (Perform Multiple Calculations)

Now it's time to take the given numbers, and the given operators, and actually make it calculate something!

Take my example again:
1 + 2 - 3
Since we're not doing order of operations now, obviously we'll have to read the expression from left to right.
Number   1:   1
Operator 1:   +
Number   2:   2
Operator 2:   -
Number   3:   3

As you know, calculations are performed by grouping. In this case: Groups of 2 numbers. Instead of "Doing it all at once", we're going to split it up.

We're going to do it from left to right in groups of 2 numbers:
1 + 2 first
3 - 3  last

Take a look at my numbers/operators list again. In between number X and number (X + 1), lies the operator (X), where X is the current operation. Sounded confusing. Let's do an example.

For the first operation (X = 1):
Number   1:   1
Operator 1:   +
Number   2:   2

In between Number X (which is Number 1... which is equal to 1) and number (X + 1) (which is number 2.... which is equal to 2) lies the operator X (which is Operator 1... which is +). So for Number 1 and Number 2, the operation you're going to perform is Operator 1.

Create a new sub:

 

    Private Function CalculateAnswer(ByVal Numbers As ArrayList, ByVal Operators As ArrayList) As Double
        Dim x As Integer
        Dim answer As Double
        For x = 0 To Operators.Count - 1
            If Operators(x) = "+" Then
                answer = Numbers(x) + Numbers(x + 1)
            End If
            If Operators(x) = "-" Then
                answer = Numbers(x) - Numbers(x + 1)
            End If
            If Operators(x) = "*" Then
                answer = Numbers(x) * Numbers(x + 1)
            End If
            If Operators(x) = "/" Then
                answer = Numbers(x) / Numbers(x + 1)
            End If
            If Operators(x) = "^" Then
                answer = Numbers(x) ^ Numbers(x + 1)
            End If
            MessageBox.Show("Performing Operation: " & Numbers(x) & Operators(x) & Numbers(x + 1) & " = " & answer)
        Next
    End Function

Now in your ParseEquation Sub, say CalculateAnswer(numbers,Operators).

Run the program: Type in 1 + 2 
You get 12???

Why? The problem lies in this line
               answer = Numbers(x) + Numbers(x + 1)
It's concatenating the two STRINGS. This problem is easy to fix:
                              answer = Convert.ToDouble(Numbers(x)) + Convert.ToDouble(Numbers(x + 1))

Do that for all the operations.

Now type in 1 + 2 and you'll get 3.
But type in 1 + 2 - 3
You'll get;  "1 + 2 = 3"
And         :  "2 - 3 = -1"

Obviously. The reason being is that we haven't done anything with the answer. We have to put the answer back into the equation.

Explanation time:
1 + 2 - 3 ------------> 3 - 3 -------------> 0

So we know that Numbers(x), numbers (x + 1) and Operators (x) must be deleted. In this case:
1 + 2 - 3

Numbers(0) = 1
Operators(0) = +
Numbers(1) = 2

We delete all the above, and replace them with 3. 

Although this might seem weird, ArrayLists are automatically bumped down. 
Take this example:
You have an arraylist with 3 elements
myAList(0) = 99
myAList(1) = "Red"
myAList(2) = "Balloons"

Got it? (ArrayLists can store anything: numbers, strings, objects, even arraylists... you name it)
Say you delete myAList(0). The arraylist now looks like this:
myAList(0) = "Red"
myAList(1) = 'Balloons"

By deleting myAList(0), it does NOT look like this:
myAList(0) = <Nothing/Null/Nada>
myAList(1) = "Red"
myAList(2) = "Balloons"
The ArrayList is automatically bumped down. 

This is important. Because when we delete number(0), to delete the next number, we delete number(0) AGAIN.

For example:
1 + 2 - 3

To delete the "1", the "+", and the "2" we'd do:
Numbers.RemoveAt(0)
Operators.RemoveAt(0)
Numbers.RemoveAt(0)
And to add the answer back into the array:
Numbers.Insert(0, answer) 'The 0 argument makes it get inserted in the beginning of the arraylist.

In terms of X:
Numbers.RemoveAt(X)
Operators.RemoveAt(X)
Numbers.RemoveAt(X)
Numbers.Insert(X, answer) 'The X argument makes it get inserted in the X Position of the arraylist.

Great. Here's your new code:
    Private Function CalculateAnswer(ByVal Numbers As ArrayList, ByVal Operators As ArrayList) As Double
        Dim x As Integer
        Dim answer As Double

        For x = 0 To Operators.Count - 1
            'If there are no more operators...
            If Operators.Count = 0 Then Return answer 'Then exit! We've got the answer!
            If Operators(x) = "+" Then
                answer = Convert.ToDouble(Numbers(x)) + Convert.ToDouble(Numbers(x + 1))
            End If
            If Operators(x) = "-" Then
                answer = Convert.ToDouble(Numbers(x)) - Convert.ToDouble(Numbers(x + 1))
            End If
            If Operators(x) = "*" Then
                answer = Convert.ToDouble(Numbers(x)) * Convert.ToDouble(Numbers(x + 1))
            End If
            If Operators(x) = "/" Then
                answer = Convert.ToDouble(Numbers(x)) / Convert.ToDouble(Numbers(x + 1))
            End If
            If Operators(x) = "^" Then
                answer = Convert.ToDouble(Numbers(x)) ^ Convert.ToDouble(Numbers(x + 1))
            End If
            MessageBox.Show("Performing Operation: " & Numbers(x) & Operators(x) & Numbers(x + 1) & " = " & answer)
            'Remove 2 numbers and an operator...
            Numbers.RemoveAt(x)
            Operators.RemoveAt(x)
            Numbers.RemoveAt(x)
            '... and replace them with an answer
            Numbers.Insert(x, answer)
            'Back up a bit. We just added a number, we need to backtrack
            x -= 1
        Next
    End Function

One line that I added  was
x -= 1

I'll explain why you have to do that, with an example
1 + 2 - 3 'Our starting equation
'First loop. X = 0
Numbers(X)       = 1
Numbers(X + 1) = 2

1 + 2 - 3 -------------> 3 - 3
'Second loop. X = 1
Numbers(X) = 3 (the second 3)
Numbers(X + 1) = <Nothing>

If we backtrack, our 2nd loop would look like this: 
1 + 2 - 3 -------------> 3 - 3
'Second loop. X = 0
Numbers(X) = 3 (the first 3)
Numbers(X + 1) = 3 (the second 3)
That's the reasoning behind that.

Go back to your ParseEquation sub. The very last line should be:
Return CalculateAnswer(Numbers, Operators)

I added the return statement. If you have an existing "CalculateAnswer(Numbers, Operators)" line, just delete it.

Go back to form1, and in the Equals click button:
lblDisplay.Text = Parser.ParseEquation(lblDisplay.Text)

Run your program! You can type in whatever you want in the display (besides the Parantheseese, Square Root, and the Trig functions).
Just note 1 thing. If you wanted to say -1 + 2, you'd type it in like so:

<PlusOrMinus key>   1    <Plus Key>  2
The PlusOrMinus is used to make something negative 

Another interesting feature: 
1 + 2 - 5
<Press Equals, it'll give you -2>

Without pressing clear, type this in:
* 5

<Press Equals, It'll give you -10>

Pretty awesome huh?  Multiple operations work! 

Now, do 4 + 4 / 8. It gives you 1 instead of 4.5.......... so it's time for ORDER OF OPERATIONS! 
The ExpressionParser Class: v3.0 (Order of Operations)
Order of operations is actually pretty simple. Remember your PEMDAS:

P    - Parentheseese
E    - Exponents  
M   - Multiplication  
D    - Division  
A    - Addition
S    - Subtraction

v3.0 of this class won't do Parentheseese yet. So we're stuck with EMDAS for now =).

It's actually pretty simple. The logic:
1) Sort through the operators and find what we're looking for
2) Find what position it's at and record it
3) Perform the required operation
4) Insert the answer back into the arraylist.
5) Repeat


4 + 4 / 8 
Number(0) = 4
Number(1) = 4
Number(2) = 8

Operator(0) = +
Operator(1) = /
The first operation we have to do is 4 / 8  ..... which is Number(1) Operator(1) Number(2).


Step 1: Sort through the opeartors and find what we're looking for
This is a simple for loop:
For x = 0 to Operators.Count - 1
   If Operators(x) = "/" Then ..... <Do Something>
Next

Step 2: Find what position it's at and record it
In the for loop:
   If Operators(x) = "/" Then 
       Index = X
   End If

Step 3: Perform the required operation
So if we're searching through the operator and we come across the index of the operator "/", we know it's (in this case) 1. Call 1 "X" since everything will be in
terms of X for loops.

The operands (numbers) are Numbers(X), and Numbers(X + 1).  

So our code would look like this:

    If Operators(x) = "/" Then 
       Index = X
       Answer = answer = Numbers(x) / Numbers(x + 1) 'The exact same line we have!
    End If

Step 4: Insert the answer back into the arraylist.
'Remove 2 numbers and an operator...
Numbers.RemoveAt(x)
Operators.RemoveAt(x)
Numbers.RemoveAt(x)

'... and replace them with an answer
Numbers.Insert(x, answer)

Step 5: Repeat
That's what the loop is for.

HELLO? This is EXACTLY what we've been doing all along! The ONLY difference is that now, we're SEARCHING for an operator and performing an operation instead of going from left to right!
 
 

    Private Function CalculateAnswer(ByVal Numbers As ArrayList, ByVal Operators As ArrayList, ByVal OperationToPerform As String) As Double
        Dim x As Integer
        Dim answer As Double
        For x = 0 To Operators.Count - 1
            'If there are no more operators...
            If Operators.Count = 0 Then Return answer 'Then exit! We've got the answer!
            'For some odd reason, EVEN THOUGH the loop goes 
            'from 0 to operators.count - 1... it ends up BEING operators.count
            If x = Operators.Count Then Exit Function
            If Operators(x) = OperationToPerform Then
                If Operators(x) = "+" Then
                    answer = Convert.ToDouble(Numbers(x)) + Convert.ToDouble(Numbers(x + 1))
                End If
                If Operators(x) = "-" Then
                    answer = Numbers(x) - Numbers(x + 1)
                End If
                If Operators(x) = "*" Then
                    answer = Numbers(x) * Numbers(x + 1)
                End If
                If Operators(x) = "/" Then
                    answer = Numbers(x) / Numbers(x + 1)
                End If
                If Operators(x) = "^" Then
                    answer = Numbers(x) ^ Numbers(x + 1)
                End If
                MessageBox.Show("Performing Operation: " & Numbers(x) & Operators(x) & Numbers(x + 1) & " = " & answer)
                'Remove 2 numbers and an operator...
                Numbers.RemoveAt(x)
                Operators.RemoveAt(x)
                Numbers.RemoveAt(x)
                '... and replace them with an answer
                Numbers.Insert(x, answer)
                'Back up a bit. We just added a number, we need to backtrack
                x -= -1
            End If
        Next
    End Function


Note the OperationToPerform argument.

You should be aware of this new line:
If x = Operators.Count Then Exit Function

I don't know why, but even though the loop says:
       
For x = 0 To Operators.Count - 1
X still gets set to Operators.Count - Not too sure why though.

Alright.
Now go back to ParseEquation, and delete your Return CalculateAnswer(Numbers,Operators) line.
Replace it with:

'PEMDAS.... or in this case: EMDAS

CalculateAnswer(Numbers, Operators, "^")
CalculateAnswer(Numbers, Operators, "*")
CalculateAnswer(Numbers, Operators, "/")
CalculateAnswer(Numbers, Operators, "+")
CalculateAnswer(Numbers, Operators, "-")


What do we return? Well there's going to be only 1 number remaining in the Numbers ArrayList, so that's the answer of course!
Return Numbers(0)

Well there you have it: Order of operations.
4 + 4 / 8 will give you 4.5. Give it a shot.

Sweet. Order of Operations wasn't so hard was it? I thought it was the hardest thing in the world when I programmed this last month. I stopped to pseudocode, and planned things out (stepwise) and ended up realizing that it's the exact same thing as left-to-right, with a few modifications involved.

Now it's time to get Parentheseese working!

The ExpressionParser Class: v4.0 (Parentheseese)
Actually, at first I told myself I wouldn't be doing parentheses (The calculator was for my Java final exam). But I ended up sitting down for 3 hours and figuring it out.
The concept is WAY easier than I had imagined.

(I learned this the hard way): BEFORE JUMPING TO CODE. BE SURE TO PSEUDOCODE!!


As you know, Parentheses work from "Innermost to Outermost".

(4 + (2 - 3)) + 1
What is the innermost expression? "2 - 3" of course. So we'd do that first. The equation would then be:

(4 + -1) + 1
The innermost expression would then be "4 + -1" and the equation would be

3 + 1
Which would be 4.

In our MINDS we can figure out what the innermost expression is. But in CODE how will the compiler know what it is? We have to point it in the right direction.

(4 + (2 - 3)) + 1
In this case, the innermost expression is between the LAST OPENING Parentheses and the FIRST CLOSING PARENTHESES.
And from here on, we'd simply parse the 2 - 3 (with the existing code we have).

Alright, I learned through trial and error (it took me at least an hour to figure this out) that I was wrong.
Take this expression:

(1 + 2) / (3 - 2)
Between the last opening parentheses and the first closing parentheses...... well........ it doesn't really exist (if you're reading right to left it would be the division sign, but obviously you don't do anything right to left in math).

I had these crazy ideas in my head about parentheses grouping, parentheses counting, how many parentheses there are, asking the user what the innermost parentheses was..... WOAH I got confused.

After an hour, I was about to shoot myself. The answer was extremely simple.

Get this:
The innermost expression is the expression that contains the FIRST CLOSING parentheses. It has nothing to do with the last opening parentheses.
Once you close the parentheses, you're out of the innermost block.

What we have to do is loop through the equation and find the parentheses, and record the indices.

    Private Function EvaluateAndSimplyParentheticalExpressions(ByVal Equation As String) As String
        Dim PositionOfInnerMostOpen As Integer
        Dim PositionOfInnerMostClosed As Integer
        Dim x As Integer
        'Find and retreive the innermost parenthesese
        For x = 0 To Equation.Length - 1
            If Equation.Chars(x) = "(" Then
                PositionOfInnerMostOpen = x
            End If
            If Equation.Chars(x) = ")" Then
                PositionOfInnerMostClosed = x
                Exit For
            End If
        Next
        MsgBox(PositionOfInnerMostOpen & "," & PositionOfInnerMostClosed)
    End Function

That method works every time.

Do this
 Equation = EvaluateAndSimplyParentheticalExpressions(Equation)
in ParseEquation BEFORE SplitEquation = Equation.Split(" ")

Give it a shot by typing in a complex parenthetical expression:
(4 + (2 - 3)) + 1
and then
(1 + 2) / (3 - 2)         <-- "Innermost" from Left to Right though

Obviously after giving you the position of the parentheses it'll error out because it doesn't understand parentheses yet, but we'll fix that :P.

Type in an expression without parentheses. It'll tell you that the parentheses are at position 0, 0.
--------

--------
Here's what we're going to do next:

-After getting the position of the parentheses that contain the innermost expression, we're going to store the innermost expression in a string.
-we'll take that string, and call ParseEquation to simplify that string (sounds very confusing I know)

Just remember that the inner most parenthetical expression is an expression WITHOUT parentheses.

Instead of sounding so cryptic, let me give you the answer directly.
Create a new sub:
 

    Private Sub TokenizeEquation(ByVal Numbers As ArrayList, ByVal Operators As ArrayList, ByVal Equation As String)
        Dim SplitEquation() As String
        Dim x As Integer
        SplitEquation = Equation.Split(" ")
        For x = 0 To SplitEquation.Length - 1
            If x Mod 2 = 0 Then
                ' MessageBox.Show("Number: " & SplitEquation(x))
                Numbers.Add(SplitEquation(x))
            End If
            If x Mod 2 = 1 Then
                ' MessageBox.Show("Operator: " & SplitEquation(x))
                Operators.Add(SplitEquation(x))
            End If
        Next
    End Sub

And change your ParseEquation like so:

    Public Function ParseEquation(ByVal Equation As String) As Double
        Dim Numbers As New ArrayList
        Dim Operators As New ArrayList
        Dim answer As Double
        Equation = EvaluateAndSimplyParentheticalExpressions(Equation)
        TokenizeEquation(Numbers, Operators, Equation)
        CalculateAnswer(Numbers, Operators, "^")
        CalculateAnswer(Numbers, Operators, "*")
        CalculateAnswer(Numbers, Operators, "/")
        CalculateAnswer(Numbers, Operators, "+")
        CalculateAnswer(Numbers, Operators, "-")
        answer = Numbers(0)
        Return answer
    End Function

All I've done is moved some code from ParseEquation to TokenizeEquation, and called TokenizeEquation from the ParseEquation sub.
You'll understand why I'm doing so, in a minute.

Meanwhile, edit your EvaluateAndSimplifyParentheticalExpressions like so:

    Private Function EvaluateAndSimplyParentheticalExpressions(ByVal Equation As String) As String
        Dim PositionOfInnerMostOpen As Integer
        Dim PositionOfInnerMostClosed As Integer
        Dim x As Integer
        Dim ParRemaining As Boolean = True
        Dim Numbers As ArrayList
        Dim Operators As ArrayList

        'Expression in the parentheses
        Dim ExpressionInPar As String
        While ParRemaining
            'Reset it, becuase this is a loop 
            'and we don't want any existing values from the previous iteration
            x = 0
            PositionOfInnerMostOpen = 0
            PositionOfInnerMostClosed = 0
            ExpressionInPar = ""
            Numbers = New ArrayList
            Operators = New ArrayList
            'Find and retreive the innermost parenthesese
            For x = 0 To Equation.Length - 1
                If Equation.Chars(x) = "(" Then
                    PositionOfInnerMostOpen = x
                End If
                If Equation.Chars(x) = ")" Then
                    PositionOfInnerMostClosed = x
                    Exit For
                End If
            Next
            
            'If there's no parentheses, Then exit.
            If PositionOfInnerMostOpen = 0 And PositionOfInnerMostClosed = 0 Then
                ParRemaining = False
                Return Equation
            End If
        End While
    End Function
I hope you understand what's going on. All I've really done is added a loop, since we'll be checking for parentheses until there are none left.

What we're going to do next is simply evaluate the expression in parentheses. 
Right now we've got the position of the opening and the closing parentheses. How do we get the numbers in between?

Right after that last If statement, add this:
            For x = PositionOfInnerMostOpen + 1 To PositionOfInnerMostClosed - 1
                ExpressionInPar += Equation.Chars(x)
            Next
            MessageBox.Show("Expression in Parentheses: " & ExpressionInPar)

Run the program with the equation 
(4 + (2 - 3 + 1)) + 1

It'll tell you that the Expression in Parentheses is "2 - 3 + 1" (Except, it'll be in a loop. Just exit out of the program using the Stop button in VS.NET to quit).

How do we evaluate that? Heh. We already have the code to evaluate this!
TokenizeEquation(Numbers, Operators, Equation)
CalculateAnswer(Numbers, Operators, "^")
CalculateAnswer(Numbers, Operators, "*")
CalculateAnswer(Numbers, Operators, "/")
CalculateAnswer(Numbers, Operators, "+")
CalculateAnswer(Numbers, Operators, "-")

There you go! Now it's time to:
1) Delete the expression that was in parentheses
2) Put the answer back into the equation

Pretty simple:

    Private Function EvaluateAndSimplyParentheticalExpressions(ByVal Equation As String) As String
        Dim PositionOfInnerMostOpen As Integer
        Dim PositionOfInnerMostClosed As Integer
        Dim x As Integer
        Dim ParRemaining As Boolean = True
        Dim Numbers As ArrayList
        Dim Operators As ArrayList
        'Expression in the parentheses
        Dim ExpressionInPar As String
        While ParRemaining
            'Reset it, becuase this is a loop 
            'and we don't want any existing values.
            x = 0
            PositionOfInnerMostOpen = 0
            PositionOfInnerMostClosed = 0
            ExpressionInPar = ""
            Numbers = New ArrayList
            Operators = New ArrayList
            'Find and retreive the innermost parenthesese
            For x = 0 To Equation.Length - 1
                If Equation.Chars(x) = "(" Then
                    PositionOfInnerMostOpen = x
                End If
                If Equation.Chars(x) = ")" Then
                    PositionOfInnerMostClosed = x
                    Exit For
                End If
            Next
            If PositionOfInnerMostOpen = 0 And PositionOfInnerMostClosed = 0 Then
                ParRemaining = False
                Return Equation
            End If
            For x = PositionOfInnerMostOpen + 1 To PositionOfInnerMostClosed - 1
                ExpressionInPar += Equation.Chars(x)
            Next
            MessageBox.Show(ExpressionInPar)
            TokenizeEquation(Numbers, Operators, ExpressionInPar)
            CalculateAnswer(Numbers, Operators, "^")
            CalculateAnswer(Numbers, Operators, "*")
            CalculateAnswer(Numbers, Operators, "/")
            CalculateAnswer(Numbers, Operators, "+")
            CalculateAnswer(Numbers, Operators, "-")
            Dim Answer As Double = Numbers(0)
            Dim AlreadyAddedAnswer As Boolean = False
            Dim TemporaryEquation As String = ""
            For x = 0 To Equation.Length - 1
                If x >= PositionOfInnerMostOpen And x <= PositionOfInnerMostClosed Then
                    If Not AlreadyAddedAnswer Then
                        TemporaryEquation += Answer.ToString
                        AlreadyAddedAnswer = True
                    End If
                Else
                    TemporaryEquation += Equation.Chars(x)
                End If
            Next
            MessageBox.Show("New Equation: " & TemporaryEquation)
            Equation = TemporaryEquation
        End While
    End Function

Everything in blue represents the changes made. 

Explanation of the For Loop:
2 - (3 + 4) + 1
The parentheses, in this case, are in positions 5 and 10. 
What the loop will do is write "2 - ".... and skip the part with the parentheses and only write the answer, and then continue on to do " + 1"
So it'll turn into 2 - 7 + 1
Run the program, type in:
(4 + ((8 - 3) + 2)) / 11

It'll do this:
(4 + ((8 - 3) + 2)) / 11 ----> (4 + (5 + 2)) / 11  ----> (4 + 7) / 11 ---> 11 / 11 ---> 1

Be sure you enter the parentheses correctly, or it'll come up with some weird answer.
The ExpressionParser Class: v5.0 (Bug Fixes and Quirks)
Every now and then you have to start stress-testing. There's a few bugs in our program.

Take the equation (4 + (2 - 3 + 1)) + 1

It'll do the 2 - 3 + 1 first. But since we told it to do Addition before Subtraction.... it'll do this
2 - 3 + 1 -----> 2 - 4 ------> -2

Whereas Addition and Subtraction should be performed at the same time so the answer should be 0
2 - 3 + 1 ---> -1 + 1 ----> 0

We'll fix that, same with Multiplication and Division (although I've never seen a case where it actually matters).

It's quite a simple fix:
    Private Function CalculateAnswer(ByVal Numbers As ArrayList, ByVal Operators As ArrayList, ByVal OperationToPerform As String) As Double
        Dim x As Integer
        Dim answer As Double
        For x = 0 To Operators.Count - 1
            Dim FoundOperationToDo As Boolean = False
            'If there are no more operators...
            If Operators.Count = 0 Then Return answer 'Then exit! We've got the answer!
            'For some odd reason, EVEN THOUGH the loop goes 
            'from 0 to operators.count - 1... it ends up BEING operators.count
            If x = Operators.Count Then Exit Function
            If OperationToPerform = "Exponent" Then
                If Operators(x) = "^" Then
                    answer = Convert.ToDouble(Numbers(x)) ^ Convert.ToDouble(Numbers(x + 1))
                    FoundOperationToDo = True
                End If
            End If
            If OperationToPerform = "MultiplicationAndDivision" Then
                If Operators(x) = "*" Then
                    answer = Convert.ToDouble(Numbers(x)) * Convert.ToDouble(Numbers(x + 1))
                    FoundOperationToDo = True
                End If
                If Operators(x) = "/" Then
                    answer = Convert.ToDouble(Numbers(x)) / Convert.ToDouble(Numbers(x + 1))
                    FoundOperationToDo = True
                End If
            End If
            If OperationToPerform = "AdditionAndSubtraction" Then
                If Operators(x) = "+" Then
                    answer = Convert.ToDouble(Numbers(x)) + Convert.ToDouble(Numbers(x + 1))
                    FoundOperationToDo = True
                End If
                If Operators(x) = "-" Then
                    answer = Convert.ToDouble(Numbers(x)) - Convert.ToDouble(Numbers(x + 1))
                    FoundOperationToDo = True
                End If
            End If
            If FoundOperationToDo Then
                MessageBox.Show("Performing Operation: " & Numbers(x) & Operators(x) & Numbers(x + 1) & " = " & answer)
                'Remove 2 numbers and an operator...
                Numbers.RemoveAt(x)
                Operators.RemoveAt(x)
                Numbers.RemoveAt(x)
                '... and replace them with an answer
                Numbers.Insert(x, answer)
                'Back up a bit. We just added a number, we need to backtrack
                x -= 1
            End If
        Next
    End Function

Instead of giving it individual operators, we'll just say "MultiplicationAndDivision" or "AdditionAndSubtraction" or "Exponent". Pretty simple. 

Go back to your code.
In both ParseEquation and EvaluateAndSimplyParentheticalExpressions, change the lines to:
CalculateAnswer(Numbers, Operators, "Exponent")
CalculateAnswer(Numbers, Operators, "MultiplicationAndDivision")
CalculateAnswer(Numbers, Operators, "AdditionAndSubtraction")

Now run your code.
(4 + (2 - 3 + 1)) + 1 ----> (4 + 0) + 1 ----> 4 + 1 -----> 5

One more problem. Take the delete key for example. It deletes 1 character.
1 + 2

If you push delete, the 2 will be deleted. If you push it again, the space before the two will be deleted. This is kind of a quirky thing, the end user won't really be able to comprehend that, So, we'll just make it so that if you hit delete, and there's a space.... delete that space as well :P.

In form1, the Click event of btnDelete:
 lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1) 'Remove 1 character
'while there are still spaces left...
While lblDisplay.Text.Chars(lblDisplay.Text.Length - 1) = " "
     '... keep deleting them
     lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1)
End While

Great, now type in "1 + 2" and hit delete. It'll delete the two and the space before the two.

One more quirk:
Type in "1 + ". If you hit delete, it'll only delete the space. Obviously we have to fix that.

I'm sure there's a better way of doing this. I guess a bit of laziness kicked in:

        'while there are still spaces left...
        While lblDisplay.Text.Chars(lblDisplay.Text.Length - 1) = " "
            '... keep deleting them
            lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1)
        End While
        lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1) 'Remove 1 character
        'while there are still spaces left...
        While lblDisplay.Text.Chars(lblDisplay.Text.Length - 1) = " "
            '... keep deleting them
            lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1)
        End While

Heh. Whatever. So long as it works.
The ExpressionParser Class: v6.0 (Functions - Trig Functions and Sqrt)
Now we have to work on the trig functions and the Sqrt function.

If you run your program and push the keys <1> <+> <Tan>, the Display will look like this: "1 + Tan("
What the user of this program will have to do is simply use it like an argument:

1 + Tan (30 / 2)

Since everything in Parentheses is automatically simplified, the code ends up looking like so:
1 + Tan 15 - 1

Here we go (again)

With our current code:
1st item = Number
2nd item = Operator
3rd item = Number
4th item = Operator 
5th item = Number
6th item = Operator

That would mean that Tan is a number and 15 is an operator, and - is a number and 1 is an operator. No way.
Simply evaluate the Tangent of 15, and convert it to a number (.2679 or so), delete "Tangent" and "15" and replace it with .2679.
A bit harder than you think. It took me a few tries to write this function (I had this crazy idea of doing it all at once), but if you break down the code, it's easy to understand:

    Private Function TokenizeEquation(ByVal Numbers As ArrayList, ByVal Operators As ArrayList, ByVal Equation As String)
        Dim SplitEquation() As String
        Dim x As Integer
        SplitEquation = Equation.Split(" ")
        Dim NumbersAndOperators As New ArrayList
        'This will store all the numbers and operators in an arraylist
        For x = 0 To SplitEquation.Length - 1
            Dim CurrentItem As String = SplitEquation(x)
            NumbersAndOperators.Add(CurrentItem)
            If CurrentItem = "Sin" Or CurrentItem = "Cos" Or CurrentItem = "Tan" _
            Or CurrentItem = "ArcSin" Or CurrentItem = "ArcCos" Or CurrentItem = "ArcTan" Or _
            CurrentItem = "Sqrt" Then
                'This will calculate the Sin of whatever number....etc
                'The reason for SplitEquation(x + 1) is becuase the 
                'number is always follwed AFTER the function
                'Ex: Sin 30
                CurrentItem = PerformOperation(CurrentItem, SplitEquation(x + 1))
                'Remove the original thing ("Sin" or "Cos"...etc)
                NumbersAndOperators.RemoveAt(x)
                'And add the actual number
                NumbersAndOperators.Add(CurrentItem)
                'We're goin ahead, because we already evaluated
                'splitEquation(x) and splitequation(x + 1)
                x += 1
            End If
        Next
        For x = 0 To NumbersAndOperators.Count - 1
            If x Mod 2 = 0 Then
                Numbers.Add(NumbersAndOperators(x))
            End If
            If x Mod 2 = 1 Then
                Operators.Add(NumbersAndOperators(x))
            End If
        Next
    End Function
    Private Function PerformOperation(ByVal theFunction As String, ByVal Number As Double) As Double
        Dim answer As Double
        Select Case theFunction
            Case "Sin"
                answer = Math.Sin(Number)
            Case "Cos"
                answer = Math.Cos(Number)
            Case "Tan"
                answer = Math.Tan(Number)
            Case "ArcSin"
                answer = Math.Asin(Number)
            Case "ArcCos"
                answer = Math.Acos(Number)
            Case "ArcTan"
                answer = Math.Atan(Number)
            Case "Sqrt"
                answer = Math.Sqrt(Number)
        End Select
        Return answer
    End Function
The PerformOperation function is self explanatory. However the changes to TokenizeEquation should be stressed. Basically I added all the numbers and operators
to 1 big arraylist. I then looped through that arraylist and looked for any function (sin cos tan .... etc), and then evaluated (for example) Sin <next number>. The 
rest is all the same.
Hey guess what? Run your code... guess how sweet this is:
Sin (Sin (Sin (Sin (Sin (30)))))  = about -.6250

Nope. There's still 1 problem. Even though we cna do the above equation we can't do this:

Sin (3) + Cos (5)

It bugs out at this line NumbersAndOperators.RemoveAt(x) saying Index out of bounds.

Note: This bug took me about an hour to fix.... ugh! And the fix was so simple too!

Let's break down the reason for this:
Cos (3) + Sin (5) ---------> Cos 3 + Cos 5 
This is what it does initially (after evaluating the parentheses)

We then loop through the equation. Normally it would be

Cos  = NumbersAndOperators(0)
3     = NumbersAndOperators(1)
+ = NumbersAndOperators(2)
Sin = NumbersAndOperators(3)
5     = NumbersAndOperators(4)

But we're dealing with 'X' here. In either case, X goes from 0 to 4. In our case, though, Sin 3 gets evaluated, and X gets incremented.  
Alright, if our program worked it would do this:

<X = 0>
Add "Cos" to NumbersAndOperators <-- NumbersAndOperators(0)
CurrentItem = PerformOperation (NumbersAndOperators(X), and NumberOfOperators(X + 1)) <-- this is the same as PerformOpreation("Cos", 3)
Remove NumbersAndOperators(X) <-- NumbersAndOperators(0)
Add CurrentItem to position X (Position 0)
Increment X by 1 because we want to skip the "3"

Because it's a loop, X gets incremented by 1 again

<X = 2>
Add "+" to NumbersAndOperators <-- NumbersAndOperators(1)
Do nothing else

<X = 3>
Add "Sin" to NumbersAndOperators <-- NumbersAndOperators(2)
CurrentItem = PerformOperation (NumbersAndOperators(X), and NumberOfOperators(X + 1)) <-- this is the same as PerformOpreation("Sin", 5)
Remove NumbersAndOperators(X) <-- Remove NumbersAndOperators 3 *ERROR: There IS no NumbersAndOperators 3
Add CurrentItem to position X (Position 3)
Increment X by 1 

See the error? This can be fixed:

In the TokenizeEquation sub, Dim Offset as Integer. Here's your new code. Blue represents the changes I made

        'This will store all the numbers and operators in an arraylist
        For x = 0 To SplitEquation.Length - 1
            Dim CurrentItem As String = SplitEquation(x)
            NumbersAndOperators.Add(CurrentItem)
            If CurrentItem = "Sin" Or CurrentItem = "Cos" Or CurrentItem = "Tan" _
            Or CurrentItem = "ArcSin" Or CurrentItem = "ArcCos" Or CurrentItem = "ArcTan" Or _
            CurrentItem = "Sqrt" Then
                'This will calculate the Sin of whatever number....etc
                'The reason for SplitEquation(x + 1) is becuase the 
                'number is always follwed AFTER the function
                'Ex: Sin 30
                CurrentItem = PerformOperation(CurrentItem, SplitEquation(x + 1))
                'Remove the original thing ("Sin" or "Cos"...etc)
                NumbersAndOperators.RemoveAt(x - offset)
                'And add the actual number
                NumbersAndOperators.Add(CurrentItem)
                'We're goin ahead, because we already evaluated
                'splitEquation(x) and splitquation(x + 1)
                x += 1
                offset += 1
            End If
        Next
There's the new code. This fixes the error with the removal thing. If you go through the third iteration again:
<X = 3>
Add "Sin" to NumbersAndOperators <-- NumbersAndOperators(2)
CurrentItem = PerformOperation (NumbersAndOperators(X), and NumberOfOperators(X + 1)) <-- this is the same as PerformOpreation("Sin", 5)
Remove NumbersAndOperators(X - offset) <-- Remove NumbersAndOperators 2 
Add CurrentItem to position X (Position 3)
Increment X by 1
Increment offset by 1

These small error are really annoying, but there you go, all patched up. Nice and straightforward.

That's it! We're DONE with the Equation Parser class (except for a few tweaks that we'll add)

More Features to your Program
The only 3 buttons in our program that don't work:
-Store
-Recall
-ToggleMode

Store and recall are fairly straightforward.
Here's the thing. We don't want the user storing something like 1 + 2. We want him storing something such as ... 1 or.... 10.5 or something. So, what I think the best 
plan is to do is to allow the user to store a number only AFTER they press the equal sign. Upon pushing any other button, the store button should be disabled.
This will require about 35 more lines of code.

At the beginning of EVERY button click event, EXCEPT for btnEquals, type in the following line:
btnStore.Enabled = False
Do this also in form1_Load.

At the END of the btnEquals click event, type in the following line
btnStore.Enabled = True
Now to actually make the thing store a number:
-Declare a Global variable called StoredNumber
-In the btnStore click event:
StoredNumber = lblDisplay.Text
lblStore.Text = StoredNumber

To recall a number, in btnRecall:
lblDisplay.Text &= Convert.ToDouble(lblDisplay.Text)

Not too shabby.

ToggleMode is pretty simple as well. In btnToggleMode's click event:
        If Parser.Mode = "Radians" Then
            btnToggleMode.Text = "Rad."
            Parser.Mode = "Degrees"
        ElseIf Parser.Mode = "Degrees" Then
            btnToggleMode.Text = "Deg."
            Parser.Mode = "Radians"
        End If
In ExpressionParser's global variables:
 Public Mode As String = "Radians"

You should change the PerformOperation function like so:
    Private Function PerformOperation(ByVal theFunction As String, ByVal Number As Double) As Double
        Dim answer As Double
        'No such thing as Degrees/Radians in Sqrt... so get that out of the way first
        If theFunction = "Sqrt" Then answer = Math.Sqrt(Number)
        'If it's degrees, then convert to radians
        If Mode = "Degrees" Then Number *= Math.PI / 180
        Select Case theFunction
            Case "Sin"
                answer = Math.Sin(Number)
            Case "Cos"
                answer = Math.Cos(Number)
            Case "Tan"
                answer = Math.Tan(Number)
            Case "ArcSin"
                answer = Math.Asin(Number)
            Case "ArcCos"
                answer = Math.Acos(Number)
            Case "ArcTan"
                answer = Math.Atan(Number)
        End Select
        Return answer
    End Function

Basic.

Now run your program, change to Degrees mode and do Sin (90) and you'll get 1 as expected.

Fine tuning and bug fixes
Type in the number 12. Now hit delete twice. Error!
Start over. This time make sure your lblDisplay shows nothing. Now hit delete. Error!

Let's fix that. Change btnDelete's click event like so:
        btnStore.Enabled = False
        If lblDisplay.Text.Length = 0 Then Exit Sub
        'while there are still spaces left...
        While lblDisplay.Text.Chars(lblDisplay.Text.Length - 1) = " "
            '... keep deleting them
            lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1)
        End While
        lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1) 'Remove 1 character
        If lblDisplay.Text.Length = 0 Then Exit Sub
        'while there are still spaces left...
        While lblDisplay.Text.Chars(lblDisplay.Text.Length - 1) = " "
            '... keep deleting them
            lblDisplay.Text = lblDisplay.Text.Remove(lblDisplay.Text.Length - 1, 1)
        End While

Blue represents the changes I made.

Now. There's one last non-functional feature in our program! The lblHelp. It's just sitting there uselessly. Let's make it do something!

Add the following code at the end of the respective button's Click event

btnOpenParentheses:
lblHelp.Text = "Be sure you close the parentheses! Implied multiplication is not valid: Use 3 * Cos (30) rather than 3 Cos (30)"

btnCloseParentheses
lblHelp.Text = "lblHelp.Text = "Be sure you've closed all of them!"

btnSin, btnCos, btnTan, btnArcSin, btnArcCos, btnArcTan, btnSqrt:
lblHelp.Text = "Be sure you close the parentheses! Implied multiplication is not valid:  3 * Sin (30) rather than 3 Sin (30)

btnXRt:
lblHelp.Text = "If you wanted to do the Cube root of 4, it would be: 4 ^ (1 / 3)."

btnClear:
lblHelp.Text = "The display is now clear!"

btnDelete:
lblHelp.Text = "You just deleted 1 character"

btnEquals:
lblHelp.Text = "Voila! The answer!"
btnStore:
lblHelp.Text = "You just stored a number"

btnRecall:
lblHelp.Text = "You just recalled a number"

btnToggleMode should look like this:
        If Parser.Mode = "Radians" Then
            btnToggleMode.Text = "Rad."
            Parser.Mode = "Degrees"
            lblHelp.Text = "The mode is now set to Degrees"
        ElseIf Parser.Mode = "Degrees" Then
            btnToggleMode.Text = "Deg."
            Parser.Mode = "Radians"
            lblHelp.Text = "The mode is now set to Radians"
        End If

Now go back and delete any MessageBoxes lines you have (the ones that say like "Peforming Operation:  "....etc) and replace them with Console.Writelines if 
you wish.
That's it for this tutorial. Enjoy your calculator that can handle insanely complex equations such as:
1 / Sin (90 * Cos (30 + Tan (3) / 2 ^ 2))  = 1.022 (In Degrees mode)

I hope you enjoyed this long tutorial. Tune in next time for more tutorials!

The Source 
            Code for this tutorial is located here:

You can also locate this by logging in to vbProgramming Forums and going to:
Tutorials > Tutorial Source Code > Source Code