PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/Internal/ElementEvaluator.cs

https://bitbucket.org/zdebeer99/simple-parser
C# | 568 lines | 497 code | 66 blank | 5 comment | 67 complexity | 209e61e605e10da0e37fc659181db297 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Reflection;
  6. namespace codesticks.parser.Internal
  7. {
  8. public class ElementEvaluator
  9. {
  10. IDictionary<string, object> mvariables;
  11. EvalFunctionRepository mfunctions;
  12. public readonly static string[] mStandardFunctions = new string[] { "+", "-", "/", "*", "=", "==", ">=", "<=", "!=", ">", "<", "!", "if", "?", ":" };
  13. public enum EvalValue { noresult }
  14. public static readonly object resultvoid = EvalValue.noresult;
  15. public ElementEvaluator()
  16. {
  17. mvariables = new Dictionary<string, object>();
  18. }
  19. public EvalFunctionRepository Functions { get { return mfunctions; } set { mfunctions = value; } }
  20. public IDictionary<string, object> Variables { get { return mvariables; } set { mvariables = value; } }
  21. public object Eval(ParsedElement expression)
  22. {
  23. object o = BaseProxy(expression);
  24. return o;
  25. }
  26. protected virtual object BaseProxy(ParsedElement expr)
  27. {
  28. if (expr.IsCommandEmpty)
  29. throw ex("Command Empty", expr);
  30. switch (expr.ElementType)
  31. {
  32. case ElementType.Text:
  33. return evalText(expr);
  34. case ElementType.Function:
  35. return evalFunction(expr);
  36. case ElementType.Number:
  37. return evalNumber(expr);
  38. case ElementType.Bool:
  39. return evalBool(expr);
  40. case ElementType.None:
  41. return evalNonInstruction(expr);
  42. case ElementType.Variable:
  43. return evalVariableValue(expr);
  44. case ElementType.Collection:
  45. return evalGroup(expr);
  46. default:
  47. break;
  48. }
  49. throw ex("Not Supported Element Type:", expr);
  50. }
  51. #region Getters
  52. protected virtual IComparable getIComparable(ParsedElement expr)
  53. {
  54. object o = getObject(expr);
  55. if (o is IComparable)
  56. return (IComparable)o;
  57. throw ex("The Value returned by '{0}' cannot be compered, must be of type IComparable. value: ", expr, o);
  58. }
  59. protected virtual double getDouble(ParsedElement expr)
  60. {
  61. object o = getObject(expr);
  62. try
  63. {
  64. return Convert.ToDouble(o);
  65. }
  66. catch (Exception)
  67. {
  68. throw ex("The Value returned by '{0}' is not a valid number, value: {1} ", expr, o);
  69. }
  70. }
  71. protected virtual bool getBool(ParsedElement expr)
  72. {
  73. object o = getObject(expr);
  74. try
  75. {
  76. return Convert.ToBoolean(o);
  77. }
  78. catch (Exception)
  79. {
  80. throw ex("The Value returned by '{0}' is not a valid boolean, value: {1} ", expr, o);
  81. }
  82. }
  83. protected virtual object getObject(ParsedElement expr)
  84. {
  85. return BaseProxy(expr);
  86. }
  87. #endregion
  88. #region Standard Evaluators
  89. protected virtual object evalFunction(ParsedElement expr)
  90. {
  91. //Check Build in Function
  92. if (IsStandardFunction(expr))
  93. return evalStandardFunction(expr);
  94. return callFunction(expr);
  95. }
  96. protected virtual object evalStandardFunction(ParsedElement expr)
  97. {
  98. string cmd = expr.Command;
  99. switch (cmd)
  100. {
  101. case "+":
  102. Valid(expr, 2, 0);
  103. return math_plus(getDouble(expr.Get(0)), getDouble(expr.Get(1)));
  104. case "-":
  105. Valid(expr, 2, 0);
  106. return math_subtract(getDouble(expr.Get(0)), getDouble(expr.Get(1)));
  107. case "/":
  108. Valid(expr, 2, 0);
  109. return math_devide(getDouble(expr.Get(0)), getDouble(expr.Get(1)));
  110. case "*":
  111. Valid(expr, 2, 0);
  112. return math_multiply(getDouble(expr.Get(0)), getDouble(expr.Get(1)));
  113. case "=":
  114. Valid(expr, 2, 0);
  115. if (expr.Get(0).ElementType == ElementType.Variable)
  116. comp_set(expr.Get(0).Command, getObject(expr.Get(1)));
  117. else
  118. throw ex("Only variables values can be set. The first argument must be a variable name. Expression: {0}", expr);
  119. return resultvoid;
  120. case "==":
  121. Valid(expr, 2, 0);
  122. return comp_equal(getObject(expr.Get(0)), getObject(expr.Get(1)));
  123. case "!=":
  124. Valid(expr, 2, 0);
  125. return comp_notequal(getObject(expr.Get(0)), getObject(expr.Get(1)));
  126. case "<=":
  127. Valid(expr, 2, 0);
  128. return comp_smallerequal(getIComparable(expr.Get(0)), getIComparable(expr.Get(1)));
  129. case ">=":
  130. Valid(expr, 2, 0);
  131. return comp_largerequal(getIComparable(expr.Get(0)), getIComparable(expr.Get(1)));
  132. case "<":
  133. Valid(expr, 2, 0);
  134. return comp_smaller(getIComparable(expr.Get(0)), getIComparable(expr.Get(1)));
  135. case ">":
  136. Valid(expr, 2, 0);
  137. return comp_larger(getIComparable(expr.Get(0)), getIComparable(expr.Get(1)));
  138. case "&&":
  139. Valid(expr, 2, 0);
  140. return comp_opand(getBool(expr.Get(0)), getBool(expr.Get(1)));
  141. case "||":
  142. Valid(expr, 2, 0);
  143. return comp_opor(getBool(expr.Get(0)), getBool(expr.Get(1)));
  144. case "if":
  145. Valid(expr, 3, 0);
  146. return comp_branchif(getBool(expr.Get(0)), expr.Get(1), expr.Get(2));
  147. case "?":
  148. Valid(expr, 2, 0);
  149. return comp_branchif(getBool(expr.Get(0)), expr.Get(1));
  150. case "!":
  151. Valid(expr, 1, 0);
  152. return comp_opnot(getBool(expr.Get(0)));
  153. case ":":
  154. Valid(expr, 2, 0);
  155. return new KeyValuePair<object, object>(getObject(expr.Get(0)), getObject(expr.Get(1)));
  156. default:
  157. throw ex("{0} Is not a valid build in function.", expr.Command);
  158. }
  159. }
  160. protected virtual object evalNonInstruction(ParsedElement expr)
  161. {
  162. if (expr.IsGrouped && (string.Equals(expr.OpeningToken, "[") || string.Equals(expr.OpeningToken, "{")))
  163. {
  164. return evalGroup(expr);
  165. }
  166. List<object> values = new List<object>();
  167. foreach (var item in expr.Arguments)
  168. {
  169. object o = getObject(item);
  170. if (!IsResultVoid(o))
  171. values.Add(o);
  172. }
  173. if (values.Count == 0)
  174. return resultvoid;
  175. if (values.Count == 1)
  176. return values[0];
  177. return values.ToArray();
  178. }
  179. protected virtual object evalGroup(ParsedElement expr)
  180. {
  181. //List
  182. if (string.IsNullOrEmpty(expr.OpeningToken))
  183. return null;
  184. if (expr.OpeningToken.Equals("["))
  185. {
  186. List<object> values = new List<object>();
  187. foreach (var item in expr.Arguments)
  188. {
  189. object o = getObject(item);
  190. if (!IsResultVoid(o))
  191. values.Add(o);
  192. }
  193. return values;
  194. }
  195. if (expr.OpeningToken.Equals("{"))
  196. {
  197. Dictionary<object, object> dict = new Dictionary<object, object>();
  198. foreach (var item in expr.Arguments)
  199. {
  200. object o = getObject(item);
  201. if (o is KeyValuePair<object, object>)
  202. {
  203. KeyValuePair<object, object> kv = (KeyValuePair<object, object>)o;
  204. dict.Add(kv.Key, kv.Value);
  205. }
  206. else
  207. {
  208. throw ex("Dictionary Array expects key value pairs to be separated by ':'. {0}", item);
  209. }
  210. }
  211. return dict;
  212. }
  213. throw ex("'{0}' Invalid Grouping Item. {1}", expr.OpeningToken, expr);
  214. }
  215. protected virtual bool evalBool(ParsedElement expr)
  216. {
  217. bool r;
  218. if (bool.TryParse(expr.Command, out r))
  219. return r;
  220. throw ex("The Value '{0}' is not a valid boolean, booleans must either be true or false", expr);
  221. }
  222. protected virtual string evalText(ParsedElement expr)
  223. {
  224. string text = expr.Command;
  225. if (text.StartsWith("\"") && text.EndsWith("\"") || text.StartsWith("'") && text.EndsWith("'"))
  226. text = text.Substring(1, text.Length - 2);
  227. return text;
  228. }
  229. protected virtual double evalNumber(ParsedElement expr)
  230. {
  231. double val;
  232. if (Double.TryParse(expr.Command, out val))
  233. return val;
  234. throw ex("The Value '{0}' is not a valid number.", expr.Command);
  235. }
  236. protected virtual object evalVariableValue(ParsedElement expr)
  237. {
  238. string name = expr.Command;
  239. return Get(name);
  240. }
  241. #endregion
  242. #region Reflected Functions
  243. protected virtual object callFunction(ParsedElement expr)
  244. {
  245. if (Functions == null)
  246. ex("Function calls not allowed. Activate buy adding functions to the functionrepository.", expr);
  247. string cmd = expr.Command;
  248. EvalFunctionRepository.functionmeta meta = Functions.Get(cmd);
  249. //Get Methods
  250. MethodInfo m = meta.info;
  251. //Set Parameter values
  252. ParameterInfo[] ps = m.GetParameters();
  253. List<object> parametervalues = new List<object>();
  254. for (int i = 0; i < ps.Length; i++)
  255. {
  256. if (i < expr.Count)
  257. {
  258. parametervalues.Add(Convert.ChangeType(getObject(expr.Arguments[i]), ps[i].ParameterType));
  259. }
  260. else
  261. {
  262. if (ps[i].IsOptional)
  263. parametervalues.Add(ps[i].DefaultValue);
  264. else
  265. throw new EvaluatorException(string.Format("The Argument '{1}' of function '{0}' is not optional and must be set.", m.Name, ps[i].Name));
  266. }
  267. }
  268. //Call Function
  269. return m.Invoke(meta.instance, parametervalues.ToArray());
  270. }
  271. #endregion
  272. #region Boolean Operators
  273. protected virtual void comp_set(string varname, object value)
  274. {
  275. Set(varname, value);
  276. }
  277. protected virtual bool comp_equal(Object v1, Object v2)
  278. {
  279. return v1.Equals(v2);
  280. }
  281. protected virtual bool comp_notequal(Object v1, Object v2)
  282. {
  283. return !v1.Equals(v2);
  284. }
  285. protected virtual bool comp_smallerequal(IComparable v1, IComparable v2)
  286. {
  287. return v1.CompareTo(v2) <= 0;
  288. }
  289. protected virtual bool comp_largerequal(IComparable v1, IComparable v2)
  290. {
  291. return v1.CompareTo(v2) >= 0;
  292. }
  293. protected virtual bool comp_smaller(IComparable v1, IComparable v2)
  294. {
  295. return v1.CompareTo(v2) < 0;
  296. }
  297. protected virtual bool comp_larger(IComparable v1, IComparable v2)
  298. {
  299. return v1.CompareTo(v2) > 0;
  300. }
  301. protected virtual object comp_branchif(bool condition, ParsedElement KeyValueElement)
  302. {
  303. if (!KeyValueElement.CommandEqual(":"))
  304. throw ex("'?' statement expects ':' separation after condition. Example: condition?true:false");
  305. KeyValueElement = KeyValueElement.Get(":");
  306. Valid(KeyValueElement, 2, 0);
  307. if (condition)
  308. return getObject(KeyValueElement.Get(0));
  309. return getObject(KeyValueElement.Get(1));
  310. }
  311. protected virtual object comp_branchif(bool condition, ParsedElement path1, ParsedElement path2)
  312. {
  313. if (condition)
  314. return getObject(path1);
  315. return getObject(path2);
  316. }
  317. protected virtual bool comp_opand(bool b1, bool b2)
  318. {
  319. return b1 && b2;
  320. }
  321. protected virtual bool comp_opor(bool b1, bool b2)
  322. {
  323. return b1 || b2;
  324. }
  325. protected virtual bool comp_opnot(bool b1)
  326. {
  327. return !b1;
  328. }
  329. #endregion
  330. #region Maths
  331. protected virtual double math_plus(double v1, double v2)
  332. {
  333. return v1 + v2;
  334. }
  335. protected virtual double math_subtract(double v1, double v2)
  336. {
  337. return v1 - v2;
  338. }
  339. protected virtual double math_multiply(double v1, double v2)
  340. {
  341. return v1 * v2;
  342. }
  343. protected virtual double math_devide(double v1, double v2)
  344. {
  345. return v1 / v2;
  346. }
  347. #endregion
  348. #region Variable Management
  349. public void Set(string name, object value)
  350. {
  351. if (mvariables.ContainsKey(name))
  352. mvariables[name] = value;
  353. else
  354. mvariables.Add(name, value);
  355. }
  356. public object Get(string name)
  357. {
  358. if (mvariables.ContainsKey(name))
  359. return mvariables[name];
  360. throw ex("Variable '{0}' not defined!", name);
  361. }
  362. public object Get(string name, Type astype)
  363. {
  364. if (mvariables.ContainsKey(name))
  365. return Convert.ChangeType(mvariables[name], astype);
  366. throw ex("Variable '{0}' not defined!");
  367. }
  368. #endregion
  369. #region Function Management
  370. public void Valid(ParsedElement expr, int requiredparameters, int optionalparameters)
  371. {
  372. if (expr.Count < requiredparameters)
  373. throw ex("{0} Requers atleast {1} parameters.", expr.Command, requiredparameters);
  374. if (optionalparameters == -1)
  375. return;
  376. if (expr.Count > (requiredparameters + optionalparameters))
  377. throw ex("{0} has too many parameters specified, expecting no more than {1} parameters.", expr.Command, requiredparameters + optionalparameters);
  378. }
  379. public bool IsStandardFunction(ParsedElement expr)
  380. {
  381. if (mStandardFunctions.Contains(expr.Command))
  382. return true;
  383. return false;
  384. }
  385. #endregion
  386. #region helpers
  387. public bool IsResultVoid(object o)
  388. {
  389. if (object.ReferenceEquals(o, resultvoid))
  390. return true;
  391. return false;
  392. }
  393. Exception ex(string format, params object[] args)
  394. {
  395. return new EvaluatorException(string.Format(format, args));
  396. }
  397. #endregion
  398. }
  399. public class EvalFunctionRepository
  400. {
  401. Dictionary<string, functionmeta> mfunctions;
  402. public class functionmeta { public MethodInfo info; public object instance;}
  403. public EvalFunctionRepository()
  404. {
  405. mfunctions = new Dictionary<string, functionmeta>();
  406. }
  407. public void Clear()
  408. {
  409. mfunctions.Clear();
  410. }
  411. public void RegisterFunctions(object functionclass)
  412. {
  413. if (functionclass == null)
  414. throw new ArgumentNullException("functionclass");
  415. Type ctype = functionclass.GetType();
  416. MethodInfo[] funcs = ctype.GetMethods();
  417. foreach (var f in funcs)
  418. {
  419. RegisterFunction(f, functionclass);
  420. }
  421. }
  422. public void RegisterFunctions(Type functionclass)
  423. {
  424. if (functionclass == null)
  425. throw new ArgumentNullException("functionclass");
  426. MethodInfo[] funcs = functionclass.GetMethods();
  427. foreach (var f in funcs)
  428. {
  429. RegisterFunction(f, null);
  430. }
  431. }
  432. public void RegisterFunction(string functionname, object functionclass)
  433. {
  434. if (functionclass == null)
  435. throw new ArgumentNullException("functionclass");
  436. Type ctype = functionclass.GetType();
  437. MethodInfo func = ctype.GetMethod(functionname);
  438. if (func == null)
  439. ex("Function {0} coult not be found in type {1}", functionname, ctype);
  440. RegisterFunction(functionname, func, functionclass);
  441. }
  442. public void RegisterFunction(string functionname, Type functionclass)
  443. {
  444. if (functionclass == null)
  445. throw new ArgumentNullException("functionclass");
  446. MethodInfo func = functionclass.GetMethod(functionname);
  447. if (func == null)
  448. ex("Function {0} coult not be found in type {1}", functionname, functionclass);
  449. RegisterFunction(functionname, func, functionclass);
  450. }
  451. public void RegisterFunction(string name, string functionname, object functionclass)
  452. {
  453. if (functionclass == null)
  454. throw new ArgumentNullException("functionclass");
  455. Type ctype = functionclass.GetType();
  456. MethodInfo func = ctype.GetMethod(functionname);
  457. if (func == null)
  458. ex("Function {0} coult not be found in type {1}", functionname, ctype);
  459. RegisterFunction(name, func, functionclass);
  460. }
  461. public void RegisterFunction(string name, string functionname, Type functionclass)
  462. {
  463. if (functionclass == null)
  464. throw new ArgumentNullException("functionclass");
  465. MethodInfo func = functionclass.GetMethod(functionname);
  466. if (func == null)
  467. ex("Function {0} coult not be found in type {1}", functionname, functionclass);
  468. RegisterFunction(name, func, functionclass);
  469. }
  470. public void RegisterFunction(MethodInfo info, object instance)
  471. {
  472. RegisterFunction(info.Name, info, instance);
  473. }
  474. public void RegisterFunction(string name, MethodInfo info, object instance)
  475. {
  476. functionmeta meta = new functionmeta() { info = info, instance = instance };
  477. if (mfunctions.ContainsKey(name))
  478. ex("Function {0} is already registered.", name);
  479. mfunctions.Add(name, meta);
  480. }
  481. public void UnRegister(string name)
  482. {
  483. mfunctions.Remove(name);
  484. }
  485. public functionmeta Get(string name)
  486. {
  487. return getfunction(name);
  488. }
  489. functionmeta getfunction(string name)
  490. {
  491. if (mfunctions.ContainsKey(name))
  492. return mfunctions[name];
  493. throw ex("Function '{0}' not found, make sure spelling and case is correct.", name);
  494. }
  495. #region helpers
  496. Exception ex(string format, params object[] args)
  497. {
  498. return new EvaluatorException(string.Format(format, args));
  499. }
  500. #endregion
  501. }
  502. }