/// <summary> /// Recalculate a cell's value from it's formula /// </summary> private ResultSet RefreshCell(int x, int y, bool formulaChanged) { var gc = bigGrid1.Items[x, y] as GridCell ?? new GridCell(); var rs = new ResultSet {Result = gc.Value}; if (formulaChanged) {// Clear out old refs and add new ones: rs = RecalculateFromCell(x, y); var data = new SheetDataTableAdapters.DependsAdapter(); ulong key = bigGrid1.Items.GetUniqueKey(x, y); data.ClearDependenciesForHash((long)key); foreach (var refr in rs.References) { data.AddDependency((long)key, (long)bigGrid1.Items.GetUniqueKey(refr.X, refr.Y)); } UpdateCell(gc, x, y); return rs; } Stack<string> rpn = null; try { rpn = calculator.InfixToPostfix(gc.Formula ?? ""); } catch (Exception ex) { gc.Value = "Formula Error: " + ex.Message; } if (rpn != null && rpn.Count > 0) { // formula is non-empty try { rs = calculator.EvaluatePostfix(rpn, y, x); gc.Value = rs.Result; } catch (Exception ex) { gc.Value = "Calculation Error: " + ex.Message; } } UpdateCell(gc,x,y); return rs; }
/// <summary> /// Evaluate the result of a postfix expression string. /// Expected to be space-delimited /// </summary> public ResultSet EvaluatePostfix(Stack<string> postfix, int row, int column) { // very simple for the moment, doesn't handle variables or functions var @out = new ResultSet(); var values = new Stack<CValue>(); values.Push(new CValue((decimal)0.0)); // quick hack to deal with leading uniary operators foreach (string token in postfix/*.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)*/) { if (String.IsNullOrEmpty(token)) continue; decimal value; if (decimal.TryParse(token, out value)) { values.Push(new CValue(value)); continue; } CValue l, r; switch (token.ToLowerInvariant()) { case "+": r = values.Pop(); l = values.Pop(); values.Push(l + r); break; case "-": r = values.Pop(); l = values.Pop(); values.Push(l - r); break; case "*": r = values.Pop(); l = values.Pop(); values.Push(l * r); break; case "/": r = values.Pop(); l = values.Pop(); values.Push(l / r); break; case "^": r = values.Pop(); l = values.Pop(); values.Push(CValue.Pow(l, r)); break; case "%": r = values.Pop(); l = values.Pop(); values.Push(l % r); break; case "!": l = CValue.Floor(values.Pop()); decimal f = 1; for (var c = (int)l.NumericValue; c > 0; c--) { f *= c; } values.Push(new CValue(f)); break; // not used in postfix notation: case ",": case "(": case ")": throw new Exception("Unexpected token in Postfix"); default: if (token.StartsWith("['") && token.EndsWith("']")) { // it's a string literal. Like this: ['String literal'] values.Push(new CValue(token.Substring(2, token.Length - 4))); } else { @out.References.AddRange( funcs.HandleFunction(token, values, column, row) ); } break; } } // end foreach @out.Result = values.Pop().ToString(); return @out; }