//If the end of an expression has been reached reolve the current addition/subtraction and return the result. //If not resolve the current addition and subtraction if the next operation is of equal precedence. //Otherwise continue evaluating. private static float evaluate(float f1, IncrementalOp op, float f2, List<object> tail){ if(tail.Count==0) return MathResolver.resolve((MathOp)op, f1, f2); else if(tail[0].GetType().IsSubclassOf(typeof(ProportionalOp))) return evaluate(f1, op, f2, (ProportionalOp)tail[0], tail.GetRange(1, tail.Count-1)); else //we still expect an operator, so it must be an incremental return evaluate(MathResolver.resolve((MathOp)op, f1, f2), (IncrementalOp)tail[0], tail.GetRange(1, tail.Count-1)); }

//If the new operand is the start of a bracketed term, continue evaluating with an accumulator to collect the terms inside the brackets. //Otherwise, apply the multiplication/division operation now to ensure left evaluation and continue evaluation with the incomplete addition/subtraction in tow. private static float evaluate(float f1, IncrementalOp op1, float f2, ProportionalOp op2, List<object> tail){ if(tail[0].GetType()==typeof(OpenBracket)) return evaluate(f1, op1, f2, op2, new List<object>(), tail.GetRange(1, tail.Count-1)); return evaluate(f1, op1, MathResolver.resolve((MathOp)op2, f2, (float)tail[0]), tail.GetRange(1, tail.Count-1)); }

//Accumulate all terms in acc unless they are the matching bracket from the start of the accumulator. //If the matching bracket is found, continue the evaluation using the evaluation of the bracketed term now fully contained in acc //as the right operand of the open multiplcation/division. Resolving this now ensures left evaluation is maintained. public static float evaluate(float f1, IncrementalOp op1, float f2, ProportionalOp op2, List<object> acc, List<object> tail, int brackets = 0){ if(tail[0].GetType()==typeof(CloseBracket)){ if(brackets==0) return evaluate(f1, op1, MathResolver.resolve(op2, f2, evaluate(acc)), tail.GetRange(1, tail.Count-1)); else{ acc.Add(tail[0]); return evaluate(f1, op1, f2, op2, acc, tail.GetRange(1, tail.Count-1), brackets-1); } }else if(tail[0].GetType()==typeof(OpenBracket)){ acc.Add(tail[0]); return evaluate(f1, op1, f2, op2, acc, tail.GetRange(1, tail.Count-1), brackets+1); } acc.Add(tail[0]); return evaluate(f1, op1, f2, op2, acc, tail.GetRange(1, tail.Count-1), brackets); }

//If the new operand is the start of a bracketed term, continue evaluating with an accumulator to collect the terms inside the brackets. //Otherwise, peel of the second operand for the addition/subtraction operator and continue evaluating. private static float evaluate(float f, IncrementalOp op, List<object> tail){ if(tail[0].GetType()==typeof(OpenBracket)) return evaluate(f, op, new List<object>(), tail.GetRange(1, tail.Count-1)); return evaluate(f, op, (float)tail[0], tail.GetRange(1, tail.Count-1)); }

//Accumulate all terms in acc unless they are the matching bracket from the start of the accumulator. //If the matching bracket is found, continue the evaluation using the evaluation of the bracketed term now fully contained in acc //as the right operand of the open addition/subtraction. Resolving this now ensures left evaluation is maintained. public static float evaluate(float f, IncrementalOp op, List<object> acc, List<object> tail, int brackets = 0){ if(tail[0].GetType()==typeof(CloseBracket)){ if(brackets==0) return evaluate(f, op, evaluate(acc), tail.GetRange(1, tail.Count-1)); else{ acc.Add(tail[0]); return evaluate(f, op, acc, tail.GetRange(1, tail.Count-1), brackets-1); } }else if(tail[0].GetType()==typeof(OpenBracket)){ acc.Add(tail[0]); return evaluate(f, op, acc, tail.GetRange(1, tail.Count-1), brackets+1); } acc.Add(tail[0]); return evaluate(f, op, acc, tail.GetRange(1, tail.Count-1), brackets); }