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;
     }
 }
Beispiel #10
0
        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);
        }
Beispiel #12
0
        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);
        }
Beispiel #13
0
        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);
        }
Beispiel #14
0
        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);
        }
Beispiel #15
0
        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);
        }
Beispiel #16
0
        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);
        }
Beispiel #17
0
        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);
        }
Beispiel #18
0
        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;
            }
        }
Beispiel #19
0
        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");
 }
Beispiel #26
0
        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);
        }