Arithmetic expression parser in C#

By Sep 02, 2015

Description:

Arithmetic expression parser in CSharp

Preferencesoft

In this page, we implement a parser for arithmetic expressions without parentheses. We limit ourselves to the four arithmetic operations +, *, -, / and real numbers. To represent real numbers, we use engineering notation. We authorize the presence of plus and minus signs in front of a real number.

Implementation

We want to split an expression on numbers and binary operators +, *, -, /, but first we must separate the plus and minus signs placed in front the operands or the exponents.

For example, consider -45 - 8.6 E + 12

The first symbol - is considered as a minus sign, the second - is a binary operator and the + after E is a plus sign.

We first look for possible signs after E, after binary operators or at the beginning of the expression and we use the rule of signs. We replace the minus sign by the letter M and remove plus sign.

We split the expression to get a list of strings made up of operands and operators. Then we replace in each operand, the possible letter M by -.

It remains to evaluate the expression taking into account the priorities of operators.

The program

public void Scan(string str)
 {
     error = false;
     //remove spaces
     string s = str.Replace(" ", "");
     if (s == "")
     {
         error = true;
         return;
     }
     //no other character than Ee,.+-*/0123456789
     // + cannot be followed by * or /
     // - cannot be followed by * or /
     if (Regex.IsMatch(s, "[^Ee,\\.+\\-*/0-9]|[+\\-][*/]"))
     {
         error = true;
         return;
     }
     s = s.Replace("e", "E");
     s = s.Replace("E+", "F");
     s = s.Replace("E-", "G");
     s = s.Replace("E", "F");
     //If there is a + or - at the beginning of the expression necessarily are unary operators.
     //as if there is an addition operation before
     s = "+" + s;
     s = SignsRule(s);
     //remove the + before
     s = s.Substring(1);
     //not two consecutive operators
     if (Regex.IsMatch(s, "[+\\-*/]{2,}"))
     {
          error = true;
          return;
     }
     //a trick to cut each side of operators
     //~ symbol is never used
     s = s.Replace("+", "~+~");
     s = s.Replace("-", "~-~");
     s = s.Replace("*", "~*~");
     s = s.Replace("/", "~/~");
     lex = (s.Split('~')).ToList();
     RestoreVerifyOperators();
 }
 
 public void RestoreVerifyOperators()
 {
     int i = 0;
     bool err = false;
     while (i < lex.Count)
     {
          if (lex[i].Length == 0)
          {
              error = true;
              return;
          }
          switch (lex[i][0])
          {
              case '+':
              case '-':
              case '/':
              case '*':
                  if (i % 2 == 0) err = true;
                  break;
              default:
                  string s = lex[i].Replace("F", "E");
                  s = s.Replace("G", "E-");
                  s = s.Replace("M", "-");
                  lex[i] = s;
                  if (i % 2 == 1) err = true;
                  break;
          }
          if (err)
          {
              error = true;
              return;
          }
          i++;
      }
  }
 public string Evaluate()
 {
     int i = 1;
     while (i + 1 < lex.Count)
     {
          switch (lex[i])
          {
              case "/":
                  double x = double.Parse(lex[i - 1]);
                  double y = double.Parse(lex[i + 1]);
                  lex.RemoveAt(i);
                  lex.RemoveAt(i);
                  lex[i - 1] = (x / y).ToString();
                  break;
              case "*":
                  x = double.Parse(lex[i - 1]);
                  y = double.Parse(lex[i + 1]);
                  lex.RemoveAt(i);
                  lex.RemoveAt(i);
                  lex[i - 1] = (x * y).ToString();
                  break;
              default:
                  i += 2;
                  break;
          }
     }
     i = 1;
     while (i + 1 < lex.Count)
     {
          switch (lex[i])
          {
              case "+":
                  double x = double.Parse(lex[i - 1]);
                  double y = double.Parse(lex[i + 1]);
                  lex.RemoveAt(i);
                  lex.RemoveAt(i);
                  lex[i - 1] = (x + y).ToString();
                  break;
              case "-":
                  x = double.Parse(lex[i - 1]);
                  y = double.Parse(lex[i + 1]);
                  lex.RemoveAt(i);
                  lex.RemoveAt(i);
                  lex[i - 1] = (x - y).ToString();
                  break;
              default:
                  i += 2;
                  break;
          }
      }
     return lex[0];
 }
}

and the call:

private async void button_Click(object sender, RoutedEventArgs e)
 {
     p.Scan("78*-89E+9+-8E-7-9*-7-8/6*8/4*2/255/3");
     bool b = p.error;
     if (!p.error)
     {
         textBlock1.Text = p.Evaluate();
     }
 }

CSharp

Categories

Share

Follow


KodFor Privacy Policy