/// <summary> /// Parses the given template string into a <see cref="TemplateTree"/> using standard technique /// of preprocessing then lexical analysis followed by a syntactic analysis /// </summary> /// <param name="template">The template string to parse</param> /// <returns>A <see cref="TemplateTree"/> which is the abstract representation of the given string</returns> public static TemplateTree Parse(string template) { string preprocessedTemplate = Preprocess(template); if (preprocessedTemplate == null) { return(null); } IEnumerable <string> tokenStream = Tokenize(preprocessedTemplate); TemplateTree result = ParseTokenStream(tokenStream); return(result); }
private static TemplateTree ParseTokenStream(IEnumerable <string> tokenStream) { // Structure expressions (like *if and *foreach) cause the state to be pushed in this stack var stack = new Stack <(StructureBase, TemplateTree)>(); // State var currentTemplate = new TemplateTree(); var currentStruct = default(StructureBase); bool insideCurlies = false; foreach (var token in tokenStream) { if (token == "{{") { insideCurlies = true; } else if (token == "}}") { insideCurlies = false; } else if (insideCurlies) { var tokenTrim = token.Trim(); if (tokenTrim.StartsWith("*")) // Inside the curlies, starts with an asterisk *: Structure component { // If it's an *end component, pop the state stack if (tokenTrim.ToLower() == StructureBase._end || tokenTrim.ToLower().StartsWith(StructureBase._end + " ")) { if (stack.Count == 0) { throw new TemplateException("Unexpected expression {{" + token + "}}"); } // Pop the previous state (currentStruct, currentTemplate) = stack.Pop(); } else // If it's anything other then *end, push the state in the stack { // Parse the token and add it to the current template var templateStructure = StructureBase.Parse(tokenTrim); currentTemplate.Contents.Add(templateStructure); // Start of a structural block // Push the current state into the stack stack.Push((currentStruct, currentTemplate)); // Start a fresh state currentStruct = templateStructure; currentTemplate = new TemplateTree(); currentStruct.Template = currentTemplate; } } else // Inside the curlies, does not start with an asterisk *: An expression { currentTemplate.Contents.Add(TemplexBase.Parse(token)); } } else // Outside the curlies: Plain markup { currentTemplate.Contents.Add(TemplateMarkup.Make(token)); } } // All scopes that weren't explicitly closed, do so implicitly at the end of the template // Keep popping until you're back at the root while (stack.Count > 0) { (_, currentTemplate) = stack.Pop(); } return(currentTemplate); }