给定一个包含数学表达式的字符串,给定一组函数/命令和一组分配的变量,是否有.NET提供的工具可以快速构建解析器?我想构建一个简单的解析器来分析表达式并将其分解为其最简单的组成部分,例如:d *(abs(a-b)+ sqrt(c)),变成f = abs(a-b)和g = sqrt(c),e = f + g,d * e。
你是想构建一个解析器还是只需要解决方案呢?
无论哪种情况,都可以查看nCalc。如果只需要解决问题,请获取二进制文件。如果需要查看它们如何解析表达式树,请获取源代码。
同时也可以查看veparser。这里有一个示例代码,展示了如何构建表达式求值器(该代码解析表达式并直接计算输出)。可以修改此示例以存储评估树而不是运行它。
using System;
using VeParser;
public class MathEvaluator : CharParser
{
protected override Parser GetRootParser()
{
Func<double, double, double> productFunc = (value1, value2) => value1 * value2;
Func<double, double, double> divideFunc = (value1, value2) => value1 / value2;
Func<double, double, double> sumFunc = (value1, value2) => value1 + value2;
Func<double, double, double> subtractFunc = (value1, value2) => value1 - value2;
Func<double, double> negativeFunc = value => -value;
Func<double, double> posititveFunc = value => value;
var dot = token('.');
var op = token('(');
var cp = token(')');
var sumOp = create(sumFunc, token('+'));
var subtractOp = create(subtractFunc, token('-'));
var positiveOp = create(posititveFunc, token('+'));
var negativeOp = create(negativeFunc, token('-'));
var productOp = create(productFunc, token('*'));
var divideOp = create(divideFunc, token('/'));
// Numbers
var deciamlPlaceValue = 1M;
var decimalDot = run(() => { deciamlPlaceValue = 1; }, dot);
var digit = consume((n, d) => n * 10 + char.GetNumericValue(d), keep(Digit));
var decimalDigit = consume((n, d) => { deciamlPlaceValue = deciamlPlaceValue * 10; return (double)((decimal)n + ((decimal)char.GetNumericValue(d)) / deciamlPlaceValue); }, keep(Digit));
var number = any(
/* float */ create(0, seq(zeroOrMore(digit), decimalDot, oneOrMore(decimalDigit))),
/* int */ create(0, oneOrMore(digit))
);
var expression = createReference();
var simpleExpression = createReference();
// Unary
var unaryOp = any(positiveOp, negativeOp);
var unaryExpression = update(d => d.action(d.value),
createNew(seq(set("action", unaryOp), set("value", expression))));
// Binary
var binaryOp = any(sumOp, subtractOp, productOp, divideOp);
var binaryExpressinoTree = update(x => x.value1, createNew(
seq(
set("value1", simpleExpression),
zeroOrMore(
update(d => { var r = base.CreateDynamicObject(); r.value1 = d.action(d.value1, d.value2); return r; },
seq(
set("action", binaryOp),
set("value2", simpleExpression))))
)));
var privilegedExpressoin = seq(op, expression, cp);
setReference(simpleExpression, any(privilegedExpressoin, unaryExpression, number));
setReference(expression, any(binaryExpressinoTree, simpleExpression));
return seq(expression, endOfFile());
}
public static object Eval(string expression)
{
MathEvaluator me = new MathEvaluator();
var result = me.Parse(expression.ToCharArray());
return result;
}
}
另一种方法
class Program
{
static void Main(string[] args)
{
var a = 1;
var b = 2;
Console.WriteLine(FN_ParseSnippet($"{a} + {b} * 2"));
Console.ReadKey();
}
public static object FN_ParseSnippet(string snippet)
{
object ret = null;
var usingList = new List<string>();
usingList.Add("System");
usingList.Add("System.Collections.Generic");
usingList.Add("System.Text");
usingList.Add("Microsoft.CSharp");
//Create method
CodeMemberMethod pMethod = new CodeMemberMethod();
pMethod.Name = "Execute";
pMethod.Attributes = MemberAttributes.Public;
pMethod.ReturnType = new CodeTypeReference(typeof(object));
pMethod.Statements.Add(new CodeSnippetExpression(" return " + snippet));
//Create Class
CodeTypeDeclaration pClass = new System.CodeDom.CodeTypeDeclaration("Compilator");
pClass.Attributes = MemberAttributes.Public;
pClass.Members.Add(pMethod);
//Create Namespace
CodeNamespace pNamespace = new CodeNamespace("MyNamespace");
pNamespace.Types.Add(pClass);
foreach (string sUsing in usingList)
pNamespace.Imports.Add(new CodeNamespaceImport(sUsing));
//Create compile unit
CodeCompileUnit pUnit = new CodeCompileUnit();
pUnit.Namespaces.Add(pNamespace);
CompilerParameters param = new CompilerParameters();
param.GenerateInMemory = true;
List<AssemblyName> pReferencedAssemblys = new List<AssemblyName>();
pReferencedAssemblys = Assembly.GetExecutingAssembly().GetReferencedAssemblies().ToList();
pReferencedAssemblys.Add(Assembly.GetExecutingAssembly().GetName());
pReferencedAssemblys.Add(Assembly.GetCallingAssembly().GetName());
foreach (AssemblyName asmName in pReferencedAssemblys)
{
Assembly asm = Assembly.Load(asmName);
param.ReferencedAssemblies.Add(asm.Location);
}
//Compile
CompilerResults pResults = (new CSharpCodeProvider()).CreateCompiler().CompileAssemblyFromDom(param, pUnit);
if (pResults.Errors != null && pResults.Errors.Count > 0)
{
//foreach (CompilerError pError in pResults.Errors)
// MessageBox.Show(pError.ToString());
}
var instance = pResults.CompiledAssembly.CreateInstance("MyNamespace.Compilator");
ret = instance.GetType().InvokeMember("Execute", BindingFlags.InvokeMethod, null, instance, null);
return ret;
}
}