I want to create algebra evaluator but I don't want to write my own parser because it takes much time to learn.
I don't like algebra expression in C# because it does not look natural compared to VB counterpart. For example, "x^2+1" in VB must be written as "Math.Pow(x,2)+1" in C#.
I am still a newbie in the world of programming and always interested to learn the best practices. I am always not confident with my code.
Minimal Working Example
Based on the 3 constraint given above, I tried to implement it as follows.
First, I create a template named template.vb
as follows:
Imports System
Imports System.Math
Namespace AnyNamespace
Public Class AnyClass
Public Function AnyMethod(ByVal x As Double) As Double
Return expression-in-x
End Function
End Class
End Namespace
where expression-in-x
is just a place holder that will be replaced. See the next code how I replace it.
Second, I create a class named Parser.cs
as follows:
using Microsoft.VisualBasic;
using System;
using System.CodeDom.Compiler;
using System.IO;
using System.Reflection;
using System.Text;
namespace Mathematics
{
public static class Parser
{
static string expression;
static object obj;
static MethodInfo mi;
public static string Expression
{
get
{
return expression;
}
set
{
expression = value;
string source;
using (StreamReader sr = new StreamReader("template.vb"))
{
source = sr.ReadToEnd().Replace("expression-in-x", expression);
}
CompilerParameters cps = new CompilerParameters();
cps.GenerateExecutable = false;
cps.GenerateInMemory = true;
CompilerResults crs = (new VBCodeProvider()).CompileAssemblyFromSource(cps, source);
if (crs.Errors.Count > 0)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError ce in crs.Errors)
{
sb.Append(string.Format("Error Number : {0}, Line : {1}, Message : {2}.\n", ce.ErrorNumber, ce.Line, ce.ErrorText));
}
throw new Exception(sb.ToString());
}
else
{
Assembly a = crs.CompiledAssembly;
obj = a.CreateInstance("AnyNamespace.AnyClass");
mi = obj.GetType().GetMethod("AnyMethod");
}
}
}
public static double Evaluate(double x)
{
if (expression == null)
throw new Exception("Unitialized expression.");
try
{
return (double)mi.Invoke(obj, new object[] { x });
}
catch (TargetInvocationException error)
{
throw error;
}
catch (Exception error)
{
throw error;
}
}
}
}
Last, I test the code as follows:
using Mathematics;
using System;
namespace Tester
{
class Program
{
static void Main(string[] args)
{
Parser.Expression = "x^2+1";
for (int x = 0; x < 10; x++)
Console.WriteLine(Parser.Evaluate(x));
Console.ReadKey();
}
}
}
Questions
Is what I did above recommended? If not, could you give me a direction or suggestion what should I improve it or whatever to make it much better?