/// <summary>
        /// parse out one symbol expression matcher. leave the enumerator on the character directly following the matched symbol.
        ///     return the next symbol, and whether or not there is another symbol
        /// </summary>
        /// <param name="symbols"></param>
        /// <param name="validParameters"></param>
        /// <param name="currentIndexInStream"></param>
        /// <returns></returns>
        private static (ReplacementSymbolGenerator, bool) ParseOutSymbolExpression(
            Func <char, int> symbolRemapper,
            CharEnumerator symbols,
            string[] validParameters,
            ref int currentIndexInStream)
        {
            var nextSymbol = symbols.Current;

            if (nextSymbol == '(' || nextSymbol == ')')
            {
                throw new SyntaxException("Cannot use parentheses as a symbol", currentIndexInStream);
            }
            currentIndexInStream++;
            if (!symbols.MoveNext())
            {
                return(new ReplacementSymbolGenerator(symbolRemapper(nextSymbol)), false);
            }
            if (symbols.Current != '(')
            {
                return(new ReplacementSymbolGenerator(symbolRemapper(nextSymbol)), true);
            }
            var delegates = new List <DynamicExpressionData>();

            while (symbols.Current != ')')
            {
                AttemptMoveNext(symbols, ref currentIndexInStream);
                var indentationDepth = 0;
                var expressionString = new StringBuilder();
                while (symbols.Current != ',')
                {
                    switch (symbols.Current)
                    {
                    case '(':
                        indentationDepth++;
                        break;

                    case ')':
                        indentationDepth--;
                        break;

                    default:
                        break;
                    }
                    if (indentationDepth < 0)
                    {
                        break;
                    }
                    expressionString.Append(symbols.Current);
                    AttemptMoveNext(symbols, ref currentIndexInStream);
                }
                var expressionToParse = expressionString.ToString();
                try
                {
                    delegates.Add(ExpressionCompiler.CompileExpressionToDelegateWithParameters(
                                      "(" + expressionToParse + ")",
                                      validParameters));
                }
                catch (SyntaxException e)
                {
                    e.RecontextualizeIndex(currentIndexInStream - expressionToParse.Length - 1);
                    throw e;
                }
            }
            // reset to next char to stay consistent


            currentIndexInStream++;
            return(new ReplacementSymbolGenerator(symbolRemapper(nextSymbol), delegates), symbols.MoveNext());
        }