public static ParserAction Parse(ExpressionWalker ew, string code, LineBuilder output) { // multiline: // %foreach expr% // code #1 // % // singleline: // %foreach expr // code #1 ew.IsCurrentOrThrow(ExpressionToken.Foreach); ew.NextOrThrow(); //parse the arguments for the foreach var args = ArgumentsExpression.Parse(ew, token => token.Token == ExpressionToken.NewLine || token.Token == ExpressionToken.Mod, false, typeof(ForeachExpression)); //ew.Back(); //agrumentsExpression skips the closer token and we need it to identify if this is a singleline or multiline if (output == null) { output = new LineBuilder(code); } string content; var relatedLines = new List <Line>(); relatedLines.AddRange(output.GetLinesRelated(args.Matches())); if (ew.PeakBack.Token == ExpressionToken.Mod) { //the content is % to % block var leftBorder = ew.Current.Match; var nextMod = ForeachExpression.FindCloser(leftBorder.Index, code); //handle implicit end block (when % is not existing) if (nextMod == -1) { nextMod = code.Length - 1; } ew.SkipForwardWhile(token => token.Match.Index < nextMod); ew.Next(); //skip % itself var l1 = output.GetLineAt(leftBorder.Index) ?? output.Lines.First(); relatedLines.AddRange(new Range(l1.LineNumber, output.GetLineAt(nextMod).LineNumber).EnumerateIndexes().Select(output.GetLineByLineNumber)); content = null; } else { //the content is only next line var leftMod = ew.Current.Match; var nextMod = code.IndexOf('\n', leftMod.Index); relatedLines.Add(output.GetLineByLineNumber(relatedLines.Last().LineNumber + 1)); //next line. content = code.Substring(leftMod.Index, nextMod == -1 ? (code.Length - leftMod.Index) : nextMod - leftMod.Index); } relatedLines = relatedLines.Distinct().OrderBy(l => l.StartIndex).ToList(); if (relatedLines.Count(l => l.Content.Contains("%foreach")) <= relatedLines.Count(l => l.CleanContent() == "%") && relatedLines.Last().CleanContent() == "%") { relatedLines[relatedLines.Count - 1].MarkedForDeletion = true; relatedLines.RemoveAt(relatedLines.Count - 1); } //make sure to clean out % at the end if (content == null) { content = relatedLines.Select(l => l.Content).StringJoin(); } //all lines of the foreach are destined to deletion foreach (var line in relatedLines) { line.MarkedForDeletion = true; } return(new ParserAction(ParserToken.ForeachLoop, relatedLines, new ForeachExpression() { Content = content, Arguments = args })); }