Evaluator.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. namespace ExpressionLib
  7. {
  8. public class Evaluator<T>
  9. {
  10. private IContext<T> context;
  11. public IContext<T> Context
  12. {
  13. get { return context; }
  14. }
  15. private string expression;
  16. public string Expression
  17. {
  18. get { return expression; }
  19. set { expression = ToPostfix(value, variables); }
  20. }
  21. private List<string> variables = new List<string>();
  22. public List<string> Variables
  23. {
  24. get { return variables; }
  25. }
  26. public Evaluator(IContext<T> context)
  27. {
  28. this.context = context;
  29. }
  30. public Evaluator(string expression, IContext<T> context)
  31. {
  32. this.context = context;
  33. Expression = expression;
  34. }
  35. public T Eval(Dictionary<string, T> variables = null)
  36. {
  37. if (Expression.Length == 0)
  38. throw new Exception("No expression set!");
  39. return EvalPostfix(Expression, variables);
  40. }
  41. public T EvalInfix(string infix, Dictionary<string, T> variables = null)
  42. {
  43. return EvalPostfix(ToPostfix(infix), variables);
  44. }
  45. public T EvalPostfix(string postfix, Dictionary<string, T> variables = null)
  46. {
  47. bool useVariables = (variables != null);
  48. Stack<T> nums = new Stack<T>();
  49. string[] expr = postfix.Split(' ');
  50. T val;
  51. List<T> tempVals = new List<T>();
  52. foreach (string o in expr)
  53. {
  54. if (o.Length == 0) continue;
  55. if (useVariables && IsValidIdentificator(o) && variables.ContainsKey(o))
  56. nums.Push(variables[o]);
  57. else if (Context.IsValue(o, out val))
  58. nums.Push(val);
  59. else
  60. {
  61. tempVals.Clear();
  62. // Operator
  63. for (int i = 1; i <= Context.NumParams(o); i++)
  64. tempVals.Add(nums.Pop());
  65. // Fix the position of the elements
  66. tempVals.Reverse();
  67. nums.Push(Context.EvalOperator(o, tempVals));
  68. }
  69. }
  70. if ( nums.Count != 1 )
  71. throw new ParsingException("Something is wrong! We should've had one element left in the stack...");
  72. return nums.Pop();
  73. }
  74. public string ToPostfix(string infix, List<string> variables = null)
  75. {
  76. string[] expr = splitExpression(infix);
  77. string output = "";
  78. Stack<string> oprtr = new Stack<string>();
  79. int openedBrackets = 0;
  80. foreach (string o in expr)
  81. {
  82. if (Context.IsValue(o))
  83. output += o + " ";
  84. else if (!Context.IsOperator(o) && IsValidIdentificator(o))
  85. {
  86. if ( variables != null && !variables.Contains(o) )
  87. variables.Add(o);
  88. output += o + " ";
  89. }
  90. else if (o == ",")
  91. {
  92. // Pop stack to output until we find the opening bracket
  93. while (oprtr.Count > 0 && (!oprtr.Peek().Contains('(')))
  94. output += oprtr.Pop() + " ";
  95. if (oprtr.Count == 0)
  96. throw new ParsingException("Too many closing brackets!");
  97. }
  98. else if (o == ")")
  99. {
  100. // Pop stack to output until we find the opening bracket
  101. while (oprtr.Count > 0 && (!oprtr.Peek().Contains('(')))
  102. output += oprtr.Pop() + " ";
  103. // Discard the bracket
  104. if (oprtr.Count == 0)
  105. throw new ParsingException("Too many closing brackets!");
  106. openedBrackets--;
  107. if (oprtr.Peek() != "(" && oprtr.Peek().Contains('('))
  108. {
  109. output += "(" + oprtr.Pop().Replace('(', ')') + " ";
  110. }
  111. else
  112. {
  113. oprtr.Pop();
  114. }
  115. }
  116. else
  117. {
  118. if (!o.Contains('(') && oprtr.Count > 0 && (Context.PriorityOf(o) < Context.PriorityOf(oprtr.Peek()) || (Context.AssociativityOf(o) == Associativity.Left && Context.PriorityOf(o) == Context.PriorityOf(oprtr.Peek()))))
  119. output += oprtr.Pop() + " ";
  120. if (o.Contains('('))
  121. openedBrackets++;
  122. oprtr.Push(o);
  123. }
  124. }
  125. if (openedBrackets != 0)
  126. throw new ParsingException("Too many opening brackets!");
  127. // Pop entire stack to output
  128. while (oprtr.Count > 0)
  129. output += oprtr.Pop() + " ";
  130. return output;
  131. }
  132. private string[] splitExpression(string expr)
  133. {
  134. char[] input = expr.ToCharArray();
  135. List<string> output = new List<string>();
  136. string last = "";
  137. foreach (char c in input)
  138. {
  139. if (c == ' ')
  140. {
  141. last = "space";
  142. continue;
  143. }
  144. if (Context.IsValue(c.ToString()) || (last == "value" && c == '.') || ((last == "(" || last == ")" || last == "") && c == '-'))
  145. {
  146. if (last != "value")
  147. output.Add(c.ToString());
  148. else
  149. output[output.Count - 1] += c.ToString();
  150. last = "value";
  151. }
  152. else if (Context.IsOperator(c.ToString()))
  153. {
  154. output.Add(c.ToString());
  155. last = "operator";
  156. }
  157. else if (IsValidIdentificator(c.ToString()))
  158. {
  159. if (last != "identificator")
  160. output.Add(c.ToString());
  161. else
  162. output[output.Count - 1] += c.ToString();
  163. last = "identificator";
  164. }
  165. else if (c == '(')
  166. {
  167. if (last == "identificator")
  168. output[output.Count - 1] += c.ToString();
  169. else
  170. output.Add(c.ToString());
  171. last = "(";
  172. }
  173. else if (c == ')')
  174. {
  175. output.Add(c.ToString());
  176. last = ")";
  177. }
  178. else if (c == ',')
  179. last = ",";
  180. }
  181. return output.ToArray();
  182. }
  183. public bool IsValidIdentificator(string c)
  184. {
  185. return Regex.IsMatch(c, @"^[a-zA-Z_]([a-zA-Z0-9_]+)?$");
  186. }
  187. }
  188. public class ParsingException : Exception
  189. {
  190. public ParsingException(string msg) : base(msg)
  191. {
  192. }
  193. }
  194. }