/// <summary>
        /// Resolves the ambiguity between <see cref="FormulaNodeSubtract"/> and <seealso cref="FormulaNodeNegativeSign"/>. 
        /// </summary>
        private IEnumerable<IFormulaToken> InterpretMinusTokenBackwards(IEnumerable<IFormulaToken> tokens)
        {
            foreach (var context in tokens.WithContext())
            {
                var nextToken = context[0];
                var token = context[1];

                // handle minus token
                var minusToken = nextToken as FormulaNodeSubtract;
                if (minusToken != null)
                {
                    nextToken = InterpretMinusToken(token, minusToken);
                    yield return nextToken;
                }

                // yield any non minus token
                if (token != null && !(token is FormulaNodeSubtract)) yield return token;
            }
        }
        /// <summary>
        /// Attaches the children of <see cref="FormulaNodePrefixOperator"/> and <see cref="FormulaNodeInfixOperator"/>. 
        /// </summary>
        private IEnumerable<IFormulaToken> InterpretOperators(IEnumerable<IFormulaToken> tokens, IEnumerable<IFormulaToken> lookAhead)
        {
            var pending = new Stack<FormulaTree>();
            lookAhead = InterpretMinusTokenForward(lookAhead);
            var lookAhead2 = lookAhead.GetEnumerator();
            foreach (var context in tokens.WithContext())
            {
                if (IsCancellationRequested) yield break;
                var previousToken = context[0];
                var token = context[1];
                if (token == null) yield break;

                // yield parameter separators
                if (token is FormulaTokenParameterSeparator)
                {
                    yield return token;
                    continue;
                }
                var token2 = (FormulaTree) token;
                var operatorToken = token2 as IFormulaOperator;

                // advance lookAhead parallel to tokens
                var nextIndex = GetOrigin(token).End;
                var nextToken = lookAhead2.FirstOrDefault(token3 => GetOrigin(token3).Start == nextIndex);

                // stash operators
                if (operatorToken != null)
                {
                    if ((previousToken == null || previousToken is FormulaTokenParameterSeparator) && operatorToken is FormulaNodeInfixOperator)
                    {
                        SetParsingError(
                            source: Range.Empty(GetOrigin(token2).Start),
                            message: AppResourcesHelper.Get("FormulaInterpreter_Operator_LeftEmptyInfixOperator"));
                        yield break;
                    }
                    if (nextToken == null || nextToken is FormulaTokenParameterSeparator || nextToken is FormulaNodeInfixOperator)
                    {
                        SetParsingError(
                            source: Range.Empty(GetOrigin(token2).End),
                            message: operatorToken is FormulaNodePrefixOperator
                                ? AppResourcesHelper.Get("FormulaInterpreter_Operator_EmptyPrefixOperator")
                                : AppResourcesHelper.Get("FormulaInterpreter_Operator_RightEmptyInfixOperator"));
                        yield break;
                    }

                    pending.Push(token2);
                    continue;
                }

                // merge with pending tokens (regarding operator order)
                var nextInfixOperatorToken = nextToken as FormulaNodeInfixOperator;
                if (nextInfixOperatorToken == null || (pending.Count != 0 && ((IFormulaOperator) pending.Peek()).Order >= nextInfixOperatorToken.Order))
                {
                    while (pending.Count != 0)
                    {
                        var pendingOperator = pending.Pop();

                        // attach token to prefix operator
                        var pendingPrefixOperator = pendingOperator as FormulaNodePrefixOperator;
                        if (pendingPrefixOperator != null)
                        {
                            var numberToken = token2 as FormulaNodeNumber;

                            // merge negative sign and number
                            if (numberToken != null && pendingPrefixOperator is FormulaNodeNegativeSign)
                            {
                                numberToken.Value *= -1;
                                SetOrigin(token2, new[] {pendingOperator, token2});
                            }
                            else
                            {
                                pendingPrefixOperator.Child = token2;
                                SetOrigin(pendingPrefixOperator, new[] {pendingOperator, token2});
                                token2 = pendingOperator;
                            }
                        }

                        // attach token to infix operator
                        var pendingInfixOperator = pendingOperator as FormulaNodeInfixOperator;
                        if (pendingInfixOperator != null)
                        {
                            pendingInfixOperator.LeftChild = pending.Pop();
                            pendingInfixOperator.RightChild = token2;
                            SetOrigin(pendingInfixOperator,
                                new[] {pendingInfixOperator.LeftChild, pendingOperator, token2});
                            token2 = pendingOperator;
                        }
                    }
                }

                // stash to infix operator attached tokens
                if (nextInfixOperatorToken != null)
                {
                    pending.Push(token2);
                    continue;
                }

                // yield finished or unattached tokens
                yield return token2;
            }
        }
        private IEnumerable<IFormulaToken> CompleteFunctionsBackwards(IEnumerable<IFormulaToken> tokens)
        {
            foreach (var context in tokens.WithContext())
            {
                var nextToken = context[0];
                var token = context[1];

                // attach parentheses to function
                if (token is IFormulaFunction)
                {
                    // missing function argument
                    if (!(nextToken is FormulaNodeParentheses)) yield break;

                    SetOrigin(token, new[] { token, nextToken });
                    yield return token;
                    continue;
                }

                // yield unattached parentheses
                if (nextToken is FormulaNodeParentheses) yield return nextToken;

                // yield any other token
                if (token != null && !(token is FormulaNodeParentheses)) yield return token;
            }
        }
        /// <summary>
        /// Resolves the ambiguity between <see cref="FormulaNodeSubtract"/> and <seealso cref="FormulaNodeNegativeSign"/>. 
        /// </summary>
        private IEnumerable<IFormulaToken> InterpretMinusTokenForward(IEnumerable<IFormulaToken> tokens)
        {
            foreach (var context in tokens.WithContext())
            {
                var previousToken = context[0];
                var token = context[1];

                // handle minus token
                var minusToken = token as FormulaNodeSubtract;
                if (minusToken != null) token = InterpretMinusToken(previousToken, minusToken);

                if (token != null) yield return token;
            }
        }
        /// <summary>
        /// Attaches the children of <see cref="FormulaNodeUnaryFunction"/> and <see cref="FormulaNodeBinaryFunction"/>. 
        /// </summary>
        private IEnumerable<IFormulaToken> InterpretFunctions(IEnumerable<IFormulaToken> tokens)
        {
            foreach (var context in tokens.WithContext())
            {
                if (IsCancellationRequested) yield break;
                var previousToken = context[0];
                var token = context[1];

                // attach argument to function
                if (previousToken  is IFormulaFunction)
                {
                    if (!(token is FormulaTokenParameter))
                    {
                        SetParsingError(
                            source: Range.Empty(GetOrigin(previousToken).End), 
                            message: AppResourcesHelper.Get("FormulaInterpreter_Function_Empty"));
                        yield break;
                    }

                    var unaryFunctionToken = previousToken as FormulaNodeUnaryFunction;
                    if (unaryFunctionToken != null)
                    {
                        var unaryParameterToken = (FormulaTokenUnaryParameter) token;
                        unaryFunctionToken.Child = unaryParameterToken.Parameter;
                    }
                    var binaryFunctionToken = previousToken as FormulaNodeBinaryFunction;
                    if (binaryFunctionToken != null)
                    {
                        var binaryParameterToken = (FormulaTokenBinaryParameter) token;
                        binaryFunctionToken.FirstChild = binaryParameterToken.FirstParameter;
                        binaryFunctionToken.SecondChild = binaryParameterToken.SecondParameter;
                    }

                    SetOrigin(previousToken, new[] { previousToken, token });
                    yield return previousToken;
                    continue;
                }

                // yield any other token
                if (token != null && !(token is IFormulaFunction))
                {
                    yield return token;
                }
            }
        }
        /// <summary>
        /// Ensures all parameters are separated by exactly one <see cref="FormulaTokenParameterSeparator"/> and suppresses all occurences of <see cref="FormulaTokenParameterSeparator"/>. 
        /// </summary>
        private IEnumerable<IFormulaToken> InterpretParameters(IEnumerable<IFormulaToken> tokens)
        {
            var expectSeparator = false;
            foreach (var context in tokens.WithContext())
            {
                if (IsCancellationRequested) yield break;
                var previousToken = context[0];
                var token = context[1];

                // parameter separator
                if (token is FormulaTokenParameterSeparator)
                {
                    if (!expectSeparator)
                    {
                        SetParsingError(
                            source: Range.Empty(GetOrigin(token).Start),
                            message: AppResourcesHelper.Get("FormulaInterpreter_Brackets_EmptyArgument"));
                        yield break;
                    }
                    expectSeparator = false;
                    continue;
                }

                //  any argument
                if (token != null)
                {
                    if (expectSeparator)
                    {
                        SetParsingError(
                            source: Range.Empty(GetOrigin(token).Start), 
                            message: AppResourcesHelper.Get("FormulaInterpreter_Brackets_ArgumentDoubleValue"));
                        yield break;
                    }
                    yield return token;
                    expectSeparator = true;
                    continue;
                }

                // last token
                if (token == null && !expectSeparator)
                {
                    SetParsingError(
                        source: Range.Empty(GetOrigin(previousToken).End), 
                        message: AppResourcesHelper.Get("FormulaInterpreter_Brackets_EmptyArgument"));
                    yield break;
                }
            }
        }
        /// <remarks>Compare <see cref="InterpretBrackets"/>. </remarks>
        private IEnumerable<IFormulaToken> CompleteBrackets(IEnumerable<IFormulaToken> tokens, bool forward = true)
        {
            var parenthesesTokens = new Stack<List<IFormulaToken>>();
            var parentheses = new Stack<IFormulaToken>();
            foreach (var context in tokens.WithContext())
            {
                var previousToken = context[0];
                var token = context[1];

                var parenthesisToken = token as FormulaTokenParenthesis;
                if (parenthesisToken != null)
                {
                    if (forward ? parenthesisToken.IsOpening : parenthesisToken.IsClosing)
                    {
                        parenthesesTokens.Push(new List<IFormulaToken>());
                        parenthesesTokens.Peek().Add(token);
                        parentheses.Push(FormulaTreeFactory.CreateParenthesesNode(null));
                    }
                    else
                    {
                        // unmatched bracket
                        if (parenthesesTokens.Count == 0) yield break;

                        parenthesesTokens.Peek().Add(token);

                        var commonToken = parentheses.Pop();
                        CompleteChildren(commonToken, parenthesesTokens.Pop());
                        if (parenthesesTokens.Count != 0)
                        {
                            parenthesesTokens.Peek().Add(commonToken);
                        }
                        else
                        {
                            yield return commonToken;
                        }
                    }
                    continue;
                }

                // complete incomplete parentheses
                if (token == null)
                {
                    while (parenthesesTokens.Count != 0)
                    {
                        parenthesesTokens.Peek().Add(previousToken);

                        var commonToken = parentheses.Pop();
                        CompleteChildren(commonToken, parenthesesTokens.Pop());
                        if (parenthesesTokens.Count != 0)
                        {
                            parenthesesTokens.Peek().Add(commonToken);
                        }
                        else
                        {
                            yield return commonToken;
                        }
                    }
                    continue;
                }

                // dismiss tokens inside parentheses
                if (parenthesesTokens.Count != 0) continue;

                // yield any token outside parentheses
                yield return token;
            }
        }
        /// <summary>
        /// Maps all opening and closing parentheses and packs them with their interpreted children into <see cref="FormulaTokenParameter"/>. 
        /// </summary>
        private IEnumerable<IFormulaToken> InterpretBrackets(IEnumerable<IFormulaToken> tokens)
        {
            var parenthesesTokens = new Stack<List<IFormulaToken>>();
            var parentheses = new Stack<IFormulaToken>();
            foreach (var context in tokens.WithContext())
            {
                if (IsCancellationRequested) yield break;
                var previousToken = context[0];
                var token = context[1];

                var parenthesisToken = token as FormulaTokenParenthesis;
                if (parenthesisToken != null)
                {
                    // handle opening parenthesis
                    if (parenthesisToken.IsOpening)
                    {
                        parenthesesTokens.Push(new List<IFormulaToken>());
                        parenthesesTokens.Peek().Add(token);
                        if (previousToken is FormulaNodeUnaryFunction)
                        {
                            parentheses.Push(FormulaTokenFactory.CreateUnaryParameterToken(null));
                        }
                        else if (previousToken is FormulaNodeBinaryFunction)
                        {
                            parentheses.Push(FormulaTokenFactory.CreateBinaryParameterToken(null, null));
                        }
                        else
                        {
                            parentheses.Push(FormulaTreeFactory.CreateParenthesesNode(null));
                        }
                    }

                    // handle closing parenthesis
                    else
                    {
                        if (parenthesesTokens.Count == 0)
                        {
                            if (previousToken is IFormulaFunction)
                            {
                                SetParsingError(Range.Empty(GetOrigin(parenthesisToken).Start), AppResourcesHelper.Get("FormulaInterpreter_Function_Empty"));
                            }
                            else
                            {
                                SetParsingError(parenthesisToken, AppResourcesHelper.Get("FormulaInterpreter_Brackets_UnmatchedClosingParenthesis"));
                            }
                            yield break;
                        }
                        parenthesesTokens.Peek().Add(token);

                        // interpret parentheses
                        var commonToken = parentheses.Pop();
                        InterpretChildren(commonToken, parenthesesTokens.Pop());
                        if (IsCancellationRequested) yield break;
                        if (parenthesesTokens.Count != 0)
                        {
                            parenthesesTokens.Peek().Add(commonToken);
                        }
                        else
                        {
                            yield return commonToken;
                        }
                    }
                    continue;
                }

                // last token
                if (token == null)
                {
                    if (parenthesesTokens.Count != 0)
                    {
                        SetParsingError(
                            source: Range.Empty(GetOrigin(parenthesesTokens.Peek().Last()).End),
                            message: AppResourcesHelper.Get("FormulaInterpreter_Brackets_UnmatchedOpeningParenthesis"));
                        yield break;
                    }
                    continue;
                }

                // stash tokens inside parentheses
                if (parenthesesTokens.Count != 0)
                {
                    parenthesesTokens.Peek().Add(token);
                    continue;
                }

                // yield any token outside parentheses
                yield return token;
            }

        }
        /// <remarks>Compare <see cref="InterpretNumbers"/>. </remarks>
        private IEnumerable<IFormulaToken> CompleteNumbers(IEnumerable<IFormulaToken> tokens)
        {
            var numberTokens = new List<IFormulaToken>();
            var decimalSeparators = 0;

            foreach (var token in tokens.WithContext().Select(context => context[1]))
            {
                // append digit
                if (token is FormulaNodeNumber)
                {
                    numberTokens.Add(token);
                    continue;
                }

                // append decimal separator
                if (token is FormulaTokenDecimalSeparator)
                {
                    if (decimalSeparators == 0)
                    {
                        numberTokens.Add(token);
                        decimalSeparators++;
                        continue;
                    }
                    decimalSeparators++;
                }

                // create common token of value
                if (numberTokens.Count != 0)
                {
                    IFormulaToken commonToken;
                    if (numberTokens.Count == 1)
                    {
                        if (decimalSeparators != 0) yield break;
                        commonToken = numberTokens[0];
                    }
                    else
                    {
                        commonToken = FormulaTreeFactory.CreateNumberNode(default(double));
                        SetOrigin(commonToken, GetOrigin(numberTokens));
                    }
                    yield return commonToken;
                    if (decimalSeparators > 1) yield break;
                    numberTokens.Clear();
                    decimalSeparators = 0;
                }

                // yield any non-number token
                yield return token;
            }
        }