private IEnumerable <LessonParagraph> GetParagraphs(StringWithIndex text) { var paragaphParts = text.SplitWithoutModificationRegex(@"\r\n(?:\s*\r\n)+"); var paragraphs = paragaphParts.Select(p => ParseParagraph(p)); return(paragraphs.ToList()); }
private LessonDocument ParseDocument(StringWithIndex text) { var sections = text.SplitWithoutModification(new string[] { "\n# STEP = " }); var header = sections[0]; var steps = sections.Skip(1).ToList(); var document = new LessonDocument(text); // Get the header metadata var headerLines = header.GetLines(); var title = headerLines.GetFirstLineValue("% TITLE = ").Trim(); document.Children.Add(new LessonDocumentTitle(title)); // Process the steps foreach (var s in steps) { document.Children.Add(ParseStep(s)); } // Add an end of file span (to catch pretext that was not processed) document.Children.Add(new LessonEnd(new StringWithIndex(text.Text, text.Length, 0))); DecorateNodesWithParent(document); DecorateSpansWithSkippedText(document); return(document); }
public static List<StringWithIndex> SplitWithoutModificationRegex(this StringWithIndex text, string regexStr) { var parts = new List<StringWithIndex>(); var regex = new Regex(regexStr); var area = text.Text; var prevIndex = 0; var m = regex.Match(area); while (m.Success) { var index = m.Index; parts.Add(text.Substring(prevIndex, index - prevIndex)); prevIndex = index; m = regex.Match(area, index + 1); } // Add last part parts.Add(text.Substring(prevIndex, area.Length - prevIndex)); return parts; }
public static StringWithIndex TrimStart(this StringWithIndex text, params char[] chars) { var trimmed = text.Text.TrimStart(chars); var lengthDiff = text.Length - trimmed.Length; return text.Substring(lengthDiff, text.Length - lengthDiff); }
public static List<StringWithIndex> Split(this StringWithIndex text, params string[] separators) { var parts = SplitWithoutModification(text, separators); var trimmed = parts.Select(p => p.TrimStart(separators)).ToList(); return trimmed; }
public static List<StringWithIndex> SplitAfterFirstLine(this StringWithIndex text) { var area = text.Text; var lineBreakIndex = area.IndexOf("\r\n"); return new List<StringWithIndex>() { text.Substring(0, lineBreakIndex), text.Substring(lineBreakIndex+2) }; }
public static StringWithIndex GetValueAfter(this StringWithIndex text, string lineStartMatch) { if (!text.Text.StartsWith(lineStartMatch)) { return null; } else { return text.ReplaceStart(lineStartMatch, ""); } }
public static StringWithIndex ReplaceStart(this StringWithIndex text, string match, string replacement) { if (text.Text.StartsWith(match)) { return text.Substring(match.Length); } else { return text; } }
public static StringWithIndex ReplaceEnd(this StringWithIndex text, string match, string replacement) { if (text.Text.EndsWith(match)) { return text.Substring(0, text.Text.Length - match.Length); } else { return text; } }
private LessonInstructions ParseInstructions(StringWithIndex text) { text = text.Trim(); var instructions = new LessonInstructions(text); var paragraphs = GetParagraphs(text); // Remove blank paragraphs paragraphs = paragraphs.Where(p => p.Phrases.Count > 0); instructions.AddChildren(paragraphs); return(instructions); }
public static StringWithIndex TrimStart(this StringWithIndex text, params string[] starts) { var trimmed = text.Text; foreach (var s in starts.OrderByDescending(s => s.Length)) { if (trimmed.StartsWith(s)) { trimmed = trimmed.ReplaceStart(s, ""); break; } } var lengthDiff = text.Length - trimmed.Length; return text.Substring(lengthDiff, text.Length - lengthDiff); }
private LessonTest ParseTest(StringWithIndex text) { text = text.Trim(); var test = new LessonTest(text); var parts = text.SplitAfterFirstLine(); // Parse the title var titlePart = parts[0]; test.Children.Add(new LessonBlankTitlePlaceholder(titlePart, "## TEST", "\r\n")); var codePart = parts[1]; test.Children.Add(new LessonCode(codePart)); return(test); }
private LessonFile ParseFile(StringWithIndex text) { text = text.Trim(); var file = new LessonFile(text); var parts = text.SplitAfterFirstLine(); // Parse the title var titlePart = parts[0]; file.Children.Add(new LessonFileMethodReference(titlePart)); var codePart = parts[1]; file.Children.Add(new LessonCode(codePart)); return(file); }
private LessonCodeExplanation ParseCodeExplanation(StringWithIndex text) { text = text.Trim(); var codeExp = new LessonCodeExplanation(text); var parts = text.SplitAfterFirstLine(); // Parse the title var titlePart = parts[0]; codeExp.Children.Add(new LessonCodeExplanationQuote(titlePart)); // Parse the phrases var phraseTexts = parts[1].SplitWithoutModification("-"); var phrases = phraseTexts.Select(t => new LessonPhrase(t.Trim())); codeExp.AddChildren(phrases); return(codeExp); }
private LessonGoal ParseGoal(StringWithIndex text) { text = text.Trim(); var goal = new LessonGoal(text); var parts = text.SplitAfterFirstLine(); // Parse the title var titlePart = parts[0]; goal.Children.Add(new LessonBlankTitlePlaceholder(titlePart, "## GOAL", "\r\n")); var paragraphs = GetParagraphs(parts[1]); // Remove blank paragraphs paragraphs = paragraphs.Where(p => p.Phrases.Count > 0 || p.Code != null); goal.AddChildren(paragraphs); return(goal); }
private LessonSummary ParseSummary(StringWithIndex text) { text = text.Trim(); var summary = new LessonSummary(text); var parts = text.SplitAfterFirstLine(); // Parse the title var titlePart = parts[0]; summary.Children.Add(new LessonBlankTitlePlaceholder(titlePart, "## SUMMARY", "\r\n")); var paragraphs = GetParagraphs(parts[1]); // Remove blank paragraphs paragraphs = paragraphs.Where(p => p.Phrases.Count > 0 || p.Code != null); summary.AddChildren(paragraphs); return(summary); }
private LessonExplanation ParseExplanation(StringWithIndex text) { text = text.Trim(); var explanation = new LessonExplanation(text); var parts = text.SplitAfterFirstLine(); // Parse the title var titlePart = parts[0]; explanation.Children.Add(new LessonBlankTitlePlaceholder(titlePart, "## EXPLANATION", "\r\n")); // Parse explanation // Divide the parts var codeExplanationTexts = parts[1].SplitWithoutModification("\n*"); var codeExplanations = codeExplanationTexts.Where(t => !StringHelper.IsNullOrWhiteSpace(t.Text)).Select(t => ParseCodeExplanation(t)); explanation.AddChildren(codeExplanations); return(explanation); }
private static void DecorateSpansWithSkippedText(LessonDocument document) { // Decorate the LessonSpans with skipped parts var spans = document.FlattenSpans(); // Find gaps between the spans LessonSpan lastSpan = null; foreach (var span in spans) { var nextExpectedIndex = lastSpan != null?lastSpan.Content.GetIndexAfter() : 0; if (span.Content.Index > nextExpectedIndex) { // Get skipped content var skipLength = span.Content.Index - nextExpectedIndex; var skipContent = new StringWithIndex(document.Content.Source, nextExpectedIndex, skipLength); span.SkippedPreText = skipContent; } else if (span.Content.Index == nextExpectedIndex) { // Blank Skipped span.SkippedPreText = new StringWithIndex(document.Content.Source, nextExpectedIndex, 0); } // TODO: move to testing else { var lastSpanContent = lastSpan.Content; var skipped = span.SkippedPreText; var spanContent = span.Content; throw new ArgumentException("The document is malformed and has overlapping spans"); } lastSpan = span; } }
private LessonParagraph ParseParagraph(StringWithIndex text) { //text = text.Trim(); var paragraph = new LessonParagraph(text); var lines = text.SplitLines(); // Remove comments and blank lines lines = lines.Where(l => !l.Text.StartsWith("//") && !l.Text.StartsWith("---") && !StringHelper.IsNullOrWhiteSpace(l.Text) ).ToList(); if (lines.Count == 0) { return(paragraph); } if (lines.All(l => l.Text.StartsWith("-"))) { var phrases = lines.Where(l => l.Text.StartsWith("-")).Select(l => new LessonPhrase(l)); paragraph.AddChildren(phrases); } else if (lines.All(l => l.Text.StartsWith("\t") || l.Text.StartsWith(" "))) { var codeText = text; paragraph.Children.Add(new LessonCode(text)); } else { throw new ArgumentException("All lines in a paragraph must be the same type: " + text); } return(paragraph); }
public static List<StringWithIndex> SplitWithoutModification(this StringWithIndex text, params string[] separatorStarts) { var parts = new List<StringWithIndex>(); var area = text.Text; var prevIndex = 0; var nextIndex = -1; nextIndex = area.IndexOfAny(separatorStarts); while (nextIndex >= 0) { // Add previous part parts.Add(new StringWithIndex(text.Source, text.Index + prevIndex, nextIndex - prevIndex)); prevIndex = nextIndex; nextIndex = area.IndexOfAny(separatorStarts, nextIndex + 1); } // Add last part parts.Add(new StringWithIndex(text.Source, text.Index + prevIndex, text.Length - prevIndex)); return parts; }
public static StringWithIndex TrimEnd(this StringWithIndex text) { return TrimEnd(text, new char[0]); }
public static StringWithIndex Trim(this StringWithIndex text, params char[] chars) { return text.TrimStart(chars).TrimEnd(chars); }
public static int GetIndexAfter(this StringWithIndex text) { return text.Index + text.Length; }
public static List<StringWithIndex> GetLines(this StringWithIndex text) { return Split(text, "\n").Where(l => !StringHelper.IsNullOrWhiteSpace(l.Text)).Select(l => l.TrimEnd('\r')).ToList(); }
public static List<StringWithIndex> SplitLines(this StringWithIndex text) { return text.Split("\r\n"); }
private LessonNode ParseStep(StringWithIndex text) { text = text.Trim(); var step = new LessonStep(text); // Divide the parts var parts = text.SplitWithoutModification("\n#"); // The header includes the title and the instructions var header = parts[0]; var headerParts = header.SplitAfterFirstLine(); // Parse the step title var titlePart = headerParts[0]; step.Children.Add(new LessonStepTitle(titlePart.TrimStart("# STEP = "))); // Add instructions - the first section (no header) var instructionsPart = headerParts[1]; step.Children.Add(ParseInstructions(instructionsPart)); // Add Goal var goalPart = parts.First(p => p.Text.StartsWith("\n## GOAL")); step.Children.Add(ParseGoal(goalPart)); // Add Summary var summaryPart = parts.First(p => p.Text.StartsWith("\n## SUMMARY")); step.Children.Add(ParseSummary(summaryPart)); // Add Test var testPart = parts.FirstOrDefault(p => p.Text.StartsWith("\n## TEST")); if (testPart != null) { step.Children.Add(ParseTest(testPart)); } // Add Explanation var explanationPart = parts.FirstOrDefault(p => p.Text.StartsWith("\n## EXPLANATION")); if (explanationPart != null) { step.Children.Add(ParseExplanation(explanationPart)); } // Add File var filePart = parts.FirstOrDefault(p => p.Text.StartsWith("\n## FILE = ")); if (filePart != null) { step.Children.Add(ParseFile(filePart)); } // TODO: Add other parts // Order children var ordered = step.Children.OrderBy(c => c.Content.Index).ToList(); step.Children.Clear(); step.AddChildren(ordered); return(step); }