Introduction
In Math Tutor Part 1, I showed you a program created for a math student I know to study mathematical functions. Four pre-defined math functions were provided. In this Part 2, you can define your own math functions, and they will be evaluated by a Recursive Descent Parser, or RDP.
Changes from Part 1
Before we get into the RDP, here are some of the changes from Part 1:
- Created
MathUtilLib
and moved MathFunction.cs, ExtensionMethods.cs and Options.cs to it. - Options now allows you to set the precision of the text box and numeric up-down controls, and the increment value of the numeric up-down controls.
- Added logging that utilizes the Windows Event Log; you will need admin access to your machine to use it. If you don't have admin access or don't want to use the Windows Event Log, you can modify Logger.cs in
MathUtilLib
to do something simple, like write logging info to a text file. - Created
RDPLib
containing the RDP and a unit test project (more on this below). - Changed the settings saving. Some reviewers thought using
%userprofile%\appdata
was too "Windows-centric." So, for simplicity and flexibility, the settings are saved in a text file. So now you can simply save and retrieve your settings from a regular text file using File->Save and File->Open. See the HelpFile.html available via the Menu Strip's Help->Instructions for details. - Modified the UI giving the ability to Edit functions, and to Define new functions.
The Recursive Descent Parser (RDP)
For this Part 2 of the Math Tutor, I've utilized Herb Shildt's Recursive Descent Parser. Mr. Shildt's books on C and C++ introduced me to the concept several years ago, and I've modified it to be able to recognize constants and math functions like cosine and sine.
An expression parser evaluates an algebraic expression like: 1 + 2 * 3
. The precedence is defined by rules known as the grammar. Thus, in the expression 1 + 2 * 3
, the 2 is multiplied by 3 first, then the 1 is added, giving 7. If we use parentheses, we can force the addition to happen first, so (1 + 2) * 3
evaluates to 9
. In these expressions, each component is called a token and is a string
that contains at least a single character. In the above expressions, the digits 1
, 2
, and 3
are TokenType NUMBER
, the +, - and parentheses are TokenType DELIMITER
. The magic of the RDP is in RecursiveDescentParser.cs. At first, the code may seem a little daunting. But creating your own Test Methods (methods annotated with [TestMethod]
) in the RDPUnitTestProject
and stepping through them with the debugger is an effective way to learn how the RDP works.
The math functions (sqrt, sin, cos, etc.) are defined in Functions.cs; the constants (pi, e, G) are defined in Constants.cs. The net result of all this somewhat complex code is that you can type an expression directly into the function parameter text box like this: sqrt(3 * pi / 2)
, and the RDP will arrive at the right answer, namely 2.171
. The RDP is not only used to evaluate expressions in the text box controls, it is also used to evaluate functions you define. Associated with each user defined function are four parameters called A
, B
, C
and D
. You set values of the parameters with the function parameter controls. So when you define your own function, you can include any of the parameters. For example, the pre-defined linear function is A * x + B
where x
is the value on the x axis. You can label the controls, so for the linear function, the controls associated with A
are labeled "Slope
", the controls associated with B
are labeled "Y-intercept
". Moving the track bar ("slider"), clicking the numeric up-down control, or typing a value into the text box will change the value of the respective parameter, causing the slope or y-intercept of the line to change accordingly.
Editing Functions
As with Math Tutor Part 1, there are four pre-defined math functions. However, in this Part 2, you can edit the formula and labels, and set the initial values of the parameters. See the HelpFile.html available via the Menu Strip's Help->Instructions for details.
Defining Your Own Functions
You can also define your own functions. See the HelpFile.html available via the Menu Strip's Help->Instructions for details. In brief, select Function->Manage from the Men2u Strip to display the Math Function Detail dialog, then click "Add New Function". After entering the name, formula, labels and initial values, clicking Save, then Close, the new MathFunction
will be added to the list of math functions, and the new MathFunctionUserControl
will be added to the FlowLayoutPanel
.
Using the Code
Download and open the MathTutor.sln solution with Visual Studio. It consists of five projects:
ControlLib
- a library that contains a .NET User Control MathTutor
- the main forms of the solution MathUtilLib
- a utility library that contains, among other things, the MathFunction
class RDPLib
- the Recursive Descent Parser (RDP) RDPUnitTestProject
- a Visual Studio Unit Test Project for the RDP
Build the solution by pressing F6.
The primary class is still MathFunction
, but it differs from the previous version in that it has an instance of the RDP instead of MyFunc
:
public class MathFunction
{
RecursiveDescentParser parser = new RecursiveDescentParser();
public String Name { get; set; }
public String Formula { get; set; }
...
}
Conclusion
With a little imagination, you can create some extraordinary functions. Here's one I came up with while I was testing the project: A * cos(x - B) + sqrt(abs(sin (C * x)))
. Start the parameter C
, "sin freq
", at 1.0
, then click the "Start Timer" button and watch what happens.
History