public List <TemplateSection> Parse(string text) { this.text = text; var stack = new Stack <ParserStackEntry>(); while (!IsEOF()) { if (LookingAt("{{")) { CaptureLiteralText(); if (LookingAt("{{#")) { position += 3; string key = ExtractKey(); stack.Push(new ParserStackEntry { key = key, sections = sections }); sections = new List <TemplateSection>(); } else if (LookingAt("{{/")) { position += 3; string key = ExtractKey(); if (stack.Count == 0) { throw Error($"Found a close for '{key}', but no section is open."); } ParserStackEntry top = stack.Pop(); if (top.key != key) { throw Error($"Mismatched close; expected a close for '{top.key}' but got '{key}'."); } BlockTemplateSection section = new BlockTemplateSection(key, sections); sections = top.sections; sections.Add(section); } else { position += 2; string key = ExtractKey(); sections.Add(new VariableReferenceTemplateSection(key)); } literalStart = position; } else { position++; } } CaptureLiteralText(); if (stack.Count > 0) { throw Error($"Unexpected end of template; did not find a close for section '{stack.Peek().key}'."); } return(sections); }
static List <TemplateSection> ParseTemplate(string text) { int position = 0; int literalStart = 0; Stack <ParserStackEntry> stack = new Stack <ParserStackEntry>(); var sections = new List <TemplateSection>(); while (!IsEOF()) { if (LookingAt("{{")) { CaptureLiteralText(); if (LookingAt("{{#")) { position += 3; string key = ExtractKey(); stack.Push(new ParserStackEntry { key = key, sections = sections }); sections = new List <TemplateSection>(); } else if (LookingAt("{{/")) { position += 3; string key = ExtractKey(); if (stack.Count == 0) { throw Error($"Found a close for '{key}', but no section is open."); } ParserStackEntry top = stack.Pop(); if (top.key != key) { throw Error($"Mismatched close; expected a close for '{top.key}' but got '{key}'."); } NestedSection section = new NestedSection(key, sections); sections = top.sections; sections.Add(section); } else { position += 2; string key = ExtractKey(); sections.Add(new VariableSection(key)); } literalStart = position; } else { position++; } } CaptureLiteralText(); if (stack.Count > 0) { throw Error($"Unexpected end of template; did not find a close for section '{stack.Peek().key}'."); } return(sections); void CaptureLiteralText() { if (position != literalStart) { string literal = text.Substring(literalStart, position - literalStart); sections.Add(new LiteralSection(literal)); } } bool IsEOF() { return(position >= text.Length); } Exception Error(string v) { throw new InvalidDataException($"{v} (at position {position}"); } string ExtractKey() { if (IsEOF()) { throw Error("Unexpected end of template; did not find '}}'"); } int end = text.IndexOf("}}", position); if (end < 0) { throw Error("Unexpected end of template; did not find '}}'"); } string key = text.Substring(position, end - position); position = end + 2; return(key); } bool LookingAt(string token) { int i = position; int j = 0; while (i < text.Length) { if (j == token.Length) { return(true); } if (text[i] != token[j]) { break; } i++; j++; } return(false); } }