#region Copyright & License Information /* * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. * This file is part of OpenRA. * * OpenRA is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenRA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenRA. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; namespace OpenRA.FileFormats { public static class Evaluator { public static int Evaluate(string expr) { return Evaluate(expr, new Dictionary()); } public static int Evaluate(string expr, Dictionary syms) { var toks = Tokens(expr, "+-*/()"); var postfix = ToPostfix(toks, syms); var s = new Stack(); foreach (var t in postfix) { switch (t[0]) { case '+': ApplyBinop(s, (x, y) => y + x); break; case '-': ApplyBinop(s, (x, y) => y - x); break; case '*': ApplyBinop(s, (x, y) => y * x); break; case '/': ApplyBinop(s, (x, y) => y / x); break; default: s.Push(int.Parse(t)); break; } } return s.Pop(); } static void ApplyBinop( Stack s, Func f ) { var x = s.Pop(); var y = s.Pop(); s.Push( f(x,y) ); } static IEnumerable ToPostfix(IEnumerable toks, Dictionary syms) { var s = new Stack(); foreach (var t in toks) { if (t[0] == '(') s.Push(t); else if (t[0] == ')') { var temp = ""; while ((temp = s.Pop()) != "(") yield return temp; } else if (char.IsNumber(t[0])) yield return t; else if (char.IsLetter(t[0])) { if (!syms.ContainsKey(t)) throw new InvalidOperationException("Substitution `{0}` undefined".F(t)); yield return syms[t].ToString();; } else { while (s.Count > 0 && Prec[t] <= Prec[s.Peek()]) yield return s.Pop(); s.Push(t); } } while (s.Count > 0) yield return s.Pop(); } static readonly Dictionary Prec = new Dictionary { { "+", 0 }, { "-", 0 }, { "*", 1 }, { "/", 1 }, { "(", -1 } }; static IEnumerable Tokens(string expr, string ops) { var s = ""; foreach (var c in expr) { if (char.IsWhiteSpace(c)) { if (s != "") yield return s; s = ""; } else if (ops.Contains(c)) { if (s != "") yield return s; s = ""; yield return "" + c; } else s += c; } if (s != "") yield return s; } } }