/// <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()); }