//holds operands and alwasys both //however if exactly unary function - meaningfull just right //the left will be empty public Operation(string name, FormulaObject _left, FormulaObject _right) { if (!OperationDictionary.ContainsKey(name)) { throw new Exception("Unacceptable formula : " + name); } index = OperationDictionary[name]; left = _left; right = _right; wasActivated = false; }
//this class reshape the text of formula to //it's binary tree of computing //so any node (which roly is being playing by FormulaObject - it's just node container) //contains either operation and forks (operands) or constant //so we need FormulaObject just for these purpose - unified Container public Function(string formula) { if (formula == null) { throw new Exception("Empty Formula"); } formula = formula.ToLower().Trim(' '); //there the formula is being prePared for parsing and transforms onto simplified form List <FormulaObject> parsedFormula = new List <FormulaObject>(); parsedFormula = new List <FormulaObject>(); //this list will hold the sequence of recognized FormulaObjects int parenthesesCounter = 0; //if we need to properly parse formula we have to notify if parentheses are positioned correctly //therefore keep the count of still opened parentheses for (int i = 0; i < formula.Length;) //and going through the text formula { if (formula[i] == '(') //if we meet the Opening Parenthese - we are looking for it's end //if there is no end - "parenthesesCounter" will let us know { parenthesesCounter++; string inParenteses = ""; //here will be the part which stay in parentheses ++i; while (i < formula.Length && parenthesesCounter > 0) //going down and seek the end //by the way recording the internal part { if (formula[i] == '(') { parenthesesCounter++; } if (formula[i] == ')') { parenthesesCounter--; } if (parenthesesCounter > 0) { inParenteses += formula[i]; } i++; } Function FuncinParentheses = new Function(inParenteses); //we need to calculate the part in parentheses earlier than any another operation //so let's assume this part is independent formula // and put on this place already parsed tree of it parsedFormula.Add(FuncinParentheses.root); i++; } else if (Char.IsDigit(formula[i])) //here we check if numeric value was began parsing //and in the same way recognise it's value and create constant, adding it to formula { double value = 0; while (i < formula.Length && Char.IsDigit(formula[i])) { value = value * 10 + formula[i++] - '0'; } if (i < formula.Length && (formula[i] == '.' || formula[i] == ',')) { ++i; double divider = 10; while (i < formula.Length && Char.IsDigit(formula[i])) { value += (formula[i++] - '0') / divider; divider *= 10; } } parsedFormula.Add(new Const(value)); } else if (Operation.CharIsOperatorPart(formula[i])) //if char is operator part - we need to read only one it's symbol //there is impossible to let the operator have two symbols by the definition //try to define it and push back to the parsed formula { if (formula[i] == '-' && (parsedFormula.Count == 0 || (parsedFormula[parsedFormula.Count - 1] is Operation) && (parsedFormula[parsedFormula.Count - 1] as Operation).index < 13)) { parsedFormula.Add(new Operation("opposite", new FormulaObject(), new FormulaObject())); i++; } else { parsedFormula.Add(new Operation("" + formula[i++], new FormulaObject(), new FormulaObject())); } //here is solving the problem of '-' definition //it can be the unary and equally likely to be binary //so we need to look it's predecessor //if const - only binary, else - only unary because of standart math syntax } else if (Operation.CharIsMethodPart(formula[i])) { string formulaName = ""; while (i < formula.Length && Operation.CharIsMethodPart(formula[i])) { formulaName += formula[i++]; } parsedFormula.Add(new Operation(formulaName, new FormulaObject(), new FormulaObject())); //here we read the full operation (Method) name and push it at the end of formula } else if (formula[i++] != ' ') { throw new Exception("Unacceptable symbol : " + formula[i]); } } if (parenthesesCounter != 0) { throw new Exception("Uncorrect parentheses arrangement"); } //and we should check if all parentheses are correct //if not - stop working - the result has no matter for (int priority = 10; priority > 0; --priority) //looking for the most priority operations in funtion //and assign them their operands //there we must use field wasActivated not to let operators be assigned twice { for (int i = parsedFormula.Count - 1; i >= 0; --i) { if (parsedFormula[i] is Operation && (parsedFormula[i] as Operation).Priority() == priority && (parsedFormula[i] as Operation).ArgumentsCount() == 1 && (parsedFormula[i] as Operation).wasActivated == false) { (parsedFormula[i] as Operation).right = parsedFormula[i + 1]; (parsedFormula[i] as Operation).wasActivated = true; parsedFormula.RemoveAt(i + 1); } } //check the unary functions from right to left //note: to let cos cos cos cos x be computed witho for (int i = 0; i < parsedFormula.Count; ++i) { if (parsedFormula[i] is Operation && (parsedFormula[i] as Operation).Priority() == priority && (parsedFormula[i] as Operation).ArgumentsCount() == 2 && (parsedFormula[i] as Operation).wasActivated == false) { (parsedFormula[i] as Operation).right = parsedFormula[i + 1]; (parsedFormula[i] as Operation).left = parsedFormula[i - 1]; (parsedFormula[i] as Operation).wasActivated = true; parsedFormula.RemoveAt(i + 1); parsedFormula.RemoveAt(i - 1); i = 0; } } //and the binary from left to right } if (parsedFormula.Count != 1) { throw new Exception("Unacceptable formula"); } //check all formula was reduced up to singular tree root = parsedFormula[0]; }