简体   繁体   English

解析数学表达式

[英]Parsing a mathematical expression

Given a string containing a mathematical expression, given a set of functions/commands and given a set of assigned variables, are there tools that .NET provides to quickly build a parser? 给定一个包含数学表达式的字符串,给定一组函数/命令并给定一组赋值变量,.NET是否提供了快速构建解析器的工具?

I would like to build a simple parser that analyzes an expression and breaks it into its simplest components, for example: 我想构建一个简单的解析器来分析表达式并将其分解为最简单的组件,例如:

d*(abs(ab)+sqrt(c))

becomes

  1. f = abs(ab) and g = sqrt(c) f = abs(ab)g = sqrt(c)
  2. e = f + g
  3. d*e

Do you want to build a parser or just have the solution presented? 您想构建解析器还是只提供解决方案?

Either way, check out nCalc . 无论哪种方式,请查看nCalc If you just need to solve it, grab the binaries. 如果您只需要解决它,请抓住二进制文件。 If you need to see how they parse out the expression tree, grab the source. 如果您需要查看它们如何解析表达式树,请抓住源代码。

I've heard good things about the Grammatica parser generator. 我听说过有关Grammatica解析器生成器的好东西。 ANTLR is also widely used (especially in Java). ANTLR也被广泛使用(特别是在Java中)。

I assume you know how to define a BNF grammar and have learned about or built parsers in the past. 我假设您知道如何定义BNF语法并且过去已经了解或构建了解析器。

Check out veparser as well. 查看veparser Here is a sample code that shows how you can build an expression evaluator( the code parses the expression and directly calculates the output ). 下面是一个示例代码,演示如何构建表达式求值程序(代码解析表达式并直接计算输出)。 This sample can be modified to store the evaluation tree instead of running it. 可以修改此示例以存储评估树,而不是运行它。

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;
    }
}

Another Aproach 另一个Aproach

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;
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM