terça-feira, dezembro 14, 2010

Eval para C#

Por acaso nunca tinha precisado de avaliar uma string em C# para calcular o valor numérico dela, e fiquei surpreendido quando me apercebi que o C# não tinha de framework uma função equivalente ao Eval do javascript. Como a minha necessidade era só para contas simples somas, multiplicações, etc resolvi perguntar ao meu amigo Google se ele tinha alguma coisa que me resolvesse o meu problema.

Apesar de não ter encontrado nenhum código facilmente 'digerível' em meia dúzia de minutos e que não fosse uma black-box, encontrei um código que sabia fazer umas continhas, mas só funcionava para números com um dígito e para ter noção de precedência tinham que forçosamente serem colocados parêntesis. Após alterar esse código cheguei ao seguinte resultado:
public static double Evaluate(String input){
String expr = input.Substring(0, 1) == "(" ? input : "(" + input + ")";
expr = expr.Replace("+", ")+(");
expr = expr.Replace("-", ")-(");
Stack<string> ops = new Stack<string>();
Stack<double> vals = new Stack<double>();
  for (int i = 0; i < expr.Length; ++i){
    String s = expr.Substring(i, 1);
    if (s.Equals("(")) { }
    else if (s.Equals("+")) ops.Push(s);
    else if (s.Equals("-")) ops.Push(s);
    else if (s.Equals("*")) ops.Push(s);
    else if (s.Equals("/")) ops.Push(s);
    else if (s.Equals(")")){
      int count = ops.Count;
      while (count > 0){
        String op = ops.Pop();
        double v = vals.Pop();
        if (op.Equals("+")) v = vals.Pop() + v;
        else if (op.Equals("-")) v = vals.Pop() - v;
        else if (op.Equals("*")) v = vals.Pop() * v;
        else if (op.Equals("/")) v = vals.Pop() / v;
        vals.Push(v);
        count--;
      }
    }else{
      int idx = expr.IndexOfAny(new char[] { '+', '-', '/', '*', ')' }, i);
      vals.Push(Double.Parse(expr.Substring(i, idx - i)));
      i += (idx - i - 1);
    }
  }
  return vals.Pop();
}

Sem comentários: