Home > database >  How can I parse a string that represents a chain of generic methods?
How can I parse a string that represents a chain of generic methods?

Time:04-21

In my app I need to parse a string like this, ".Add(20).Subtract(10).Add(2)" in a generic way into a series of method calls. In code I will supply the user with a value of T, and then expect the user to type an expression of the above format to calculate a new T from the expression. In the above example, I show the user an int and they typed the above string.

I need to aggregate any number of these chained string-representation of method calls into one cache-able property (delegate? Func<T,T>?) so that whenever a new value of T comes along it can be passed through the cached expression.

I initially thought there would be a way to aggregate these like a functional-programming pipeline, the outcome being a Func<T,T> that could represent the pipeline of methods. I'm guaranteed to know typeof(T) beforehand.

I'm hitting issues. Here's where I'm at:

I can regex the string with

\.(?<expName>[A-Z,a-z] )\((?<expValue>[^)] )\)

To get these matches:

expName expValue
"Add" "20"
"Subtract" "10"
"Add" "2"

I was expecting to use a TypeConverter to parse all expValue matches but I realized that given an arbitrary method T Foo(object arg) the arg can be any type to be determined by the specific method. The only guarantee is that a T input should always result in a T output.

We already know what type T is so we can theoretically map typeof(T) to a set of strings representing method names. I tried creating Dictionaries like this:

public static readonly Dictionary<string, Func<double, double, double>> DoubleMethods = new Dictionary<string, Func<double, double, double>>()
{
     {"Add",(d,v)=>d v },
     {"Subtract",(d,v)=>d-v },
     {"Multiply",(d,v)=>d*v },
     {"Divide",(d,v)=>d/v }
};
        
public static Dictionary<string, Func<T, T, T>> TypeMethods<T>(Type t)
{
    if(t.GetType() == typeof(double)) { return DoubleMethods; }
}

This won't compile, as I can't mix generics like this.

How do I create a linking structure that maps strings of predefined method names to a method, and then pass it the arg?

I also see that I will incur a bunch of boxing/unboxing penalties for arguments that happen to be primitive types, as in the example int.Add(int addedVal) method.

I believe I'm delving into parser/lexer territory without much familiarity.

Can you give an example of some code to point me in the right direction?

CodePudding user response:

I'm not sure I see the need for the generics part:

var ops = "Add(20).Subtract(10).Divide(2).Multiply(5)";//25
    
var res = ops
    .Split("().".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
    .Chunk(2)
    .Aggregate(0.0,(r,arr)=> r = DoubleMethods[arr[0]](r, double.Parse(arr[1])));

All those inputs parse as double, so let's just break the input string into chunks of 2 after splitting on the punctuation:

Add 20
Subtract 10
Divide 2
Multiply 5

Then run an agg op where we start from 0 (I wanted to start from 20 actually so that is what the add 20 is for)

The agg op looks up the method to call in the dictionary using the first element of the chunk

DoubleMethods[arr[0]]

And calls it passing in the current accumulator value r and the double parsing of the second element of the chunk:

(r, double.Parse(arr[1]))

and store the result into the accumulator for passing into the next op

I commented "do it in decimal" because it doesn't have floating point imprecision, but I used double just because your code did; you could swap to using decimal if you like, main point being that I can't see why you're worried about generics when decimal/double can store values one would encounter in ints too.

CodePudding user response:

//basevalue is the value of the code your applying the change to.

soo... lets pretend the class is called number

Number ect = new Number(startingAmount)

in number we would have startingAmount = this.baseValue

public static T add(T baseValue, T change){
return baseValue change;
}
public static T subtract(T baseValue, T change){
return baseValue-change;
}
public static T multiply(T baseValue, T change){
return baseValue*change;
}
public static T divide(T baseValue, T change){
return baseValue/change;
}

This should work... At least I hope it does

Here is a video on generics https://www.youtube.com/watch?v=K1iu1kXkVoA&t=1s

Java == c# so everything should be almost exactly the same

  •  Tags:  
  • c#
  • Related