Soham Dasgupta
Soham Dasgupta

Reputation: 5199

What is the simplest way to parse an expression and retrieve a parse tree?

I just want to parse simple expressions like -

IIF(FVAL(PFC) = TRUE, (IIF((ORGVAL(BAS, "2012/12/31") + ORGVAL(DA)) < 6500, (FVAL(BAS)  + FVAL(DA)) * 12%, 780)), 0)`

After parsing this I should be able to know what functions contains what parameters.

|-FVAL
       |-PFC
|-ORGVAL 
       |-BAS
       |-"2012/12/31"

I'm stuck with .Net Framework 2.0, so no Linq or lambda expression goodies for me. Also I want to include the code in my custom library and not just reference it. Can anyone point me to some good library or code.

I just need to parse and not evaluate the expression and find what tokens are in use. After finding the tokens I need to change the expression string before parsing, like if the function ORGVAL is used then the parameter passed has has to be prefixed by an underscore. Like ORGVAL(BAS) will transform to ORGVAL(_BAS). Some functions can have tow parameters like ORGVAL(BAS, "2012/12/31") and this will transform to ORGVAL(_BAS, "2012/12/31")

NOTE: IF THERE ARE OTHER WAYS OF DOING IT PLEASE LET ME KNOW. I WOULD LOVE TO AVOID A PARSER AND LEXER.

Upvotes: 1

Views: 998

Answers (3)

user545725
user545725

Reputation: 11

What you want to do sounds a lot like parsing, so I don't think you'll have much luck finding a solution that doesn't involve parsing. If what you are trying to say is you don't want to program a parser yourself then there are several math expression parsing libraries available.

I am one of the authors of Jep.Net (http://www.singularsys.com/jep.net) which is an expression parsing component that would likely suit your needs. It is well documented, highly customizable and would definitely let you skip the tedious and error-prone process of implementing your own custom parser. And if it's not a fit for you, Googling ".net expression parsing library" will get you other libraries.

Good luck!

Upvotes: 1

Steven Doggart
Steven Doggart

Reputation: 43743

If you don't mind using one of the .NET languages for the code, you can use CodeDom to compile and the code on the fly and then execute it as an in-memory-only assembly. For instance, this would be the closest approximation to the example expression you showed:

private abstract class Logic
{
    protected double FVAL(object arg)
    {
        // put code here
        return 0;
    }

    protected double ORGVAL(object arg)
    {
        // put code here
        return 0;
    }

    protected double ORGVAL(object arg, string date)
    {
        // put code here
        return 0;
    }

    public abstract double GetValue(object PFC, object BAS, object DA);
}

private class DynamicLogic : Logic
{
    public override double GetValue(object PFC, object BAS, object DA)
    {
        return (FVAL(PFC) = true ? ((ORGVAL(BAS, "2012/12/31") + ORGVAL(DA)) < 6500 ? (FVAL(BAS) + FVAL(DA)) * .12 : 780) : 0);
    }
}


private Logic GenerateLogic(string code)
{
    using (CSharpCodeProvider provider = new CSharpCodeProvider())
    {
        StringBuilder classCode = new StringBuilder();
        classCode.AppendLine("private class DynamicLogic : Logic");
        classCode.AppendLine("    {");
        classCode.AppendLine("        public override int GetValue(object PFC, object BAS, object DA)");
        classCode.AppendLine("        {");
        classCode.AppendLine("            return (" + code + ");");
        classCode.AppendLine("        }");
        classCode.AppendLine("    }");
        CompilerParameters p = new CompilerParameters();
        p.GenerateInMemory = true;
        p.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
        CompilerResults results = provider.CompileAssemblyFromSource(p, code);
        return (Logic)Activator.CreateInstance(type);
        if (results.Errors.HasErrors)
        {
            throw new Exception("Failed to compile DynamicLogic class");
        }
        return (Logic)results.CompiledAssembly.CreateInstance("DynamicLogic");
    }
}

private double evaluate(object PFC, object BAS, object DA)
{
    Logic logic = GenerateLogic("FVAL(PFC) = true ? ((ORGVAL(BAS, \"2012/12/31\") + ORGVAL(DA)) < 6500 ? (FVAL(BAS) + FVAL(DA)) * .12  : 780) : 0");
    return logic.GetValue(PFC, BAS, DA);
}

EDIT: I know you said you need to actually get the expression three, itself, not just evaluate it, but I worked up the code, so I figured I'd just go ahead and post it for future passers-by.

Upvotes: 1

Alex
Alex

Reputation: 23310

This looks like an unpolished but complete expression parser (haven't tested it, but might be a starting point).

http://gbacon.blogspot.it/2005/09/simple-expression-parser-in-c.html

It's old but you mentioned C# 2.0 so it might be alright anyway. I'm not sure which version of C# it targets though.

Upvotes: 1

Related Questions