using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; namespace mustache { /// /// Parses a format string and returns the text generator. /// internal sealed class FormatParser { private const string key = @"[_\w][_\w\d]*"; private const string compoundKey = key + @"(\." + key + ")*"; /// /// Initializes a new instance of a FormatParser. /// public FormatParser() { } /// /// Builds a text generator based on the given format. /// /// The format to parse. /// The text generator. public IGenerator Build(string format) { TagDefinition definition = new TagDefinition("builtins"); definition.HasBody = true; CompoundGenerator generator = new CompoundGenerator(); TagScope tagScope = new TagScope(); registerTags(definition, tagScope); Match match = findNextTag(definition, format, 0); buildCompoundGenerator(definition, tagScope, generator, format, 0, match); return generator; } private static void registerTags(TagDefinition definition, TagScope scope) { foreach (TagDefinition childTag in definition.ChildTags) { scope.AddTag(childTag); } } private static Match findNextTag(TagDefinition definition, string format, int formatIndex) { List matches = new List(); matches.Add(getKeyRegex()); matches.Add(getClosingTagRegex(definition)); matches.Add(getCommentTagRegex()); foreach (TagDefinition childTag in definition.ChildTags) { matches.Add(getTagRegex(childTag)); } string match = "{{(" + String.Join("|", matches) + ")}}"; Regex regex = new Regex(match); return regex.Match(format, formatIndex); } private static string getClosingTagRegex(TagDefinition definition) { StringBuilder regexBuilder = new StringBuilder(); regexBuilder.Append(@"(?(/"); regexBuilder.Append(definition.Name); regexBuilder.Append(@"\s*?))"); return regexBuilder.ToString(); } private static string getCommentTagRegex() { return @"(?#!.*?)"; } private static string getKeyRegex() { return @"((?" + compoundKey + @")(,(?(-)?[\d]+))?(:(?.*?))?)"; } private static string getTagRegex(TagDefinition definition) { StringBuilder regexBuilder = new StringBuilder(); regexBuilder.Append(@"(?(#(?"); regexBuilder.Append(definition.Name); regexBuilder.Append(@")"); foreach (TagParameter parameter in definition.Parameters) { regexBuilder.Append(@"\s+?"); regexBuilder.Append(@"(?"); regexBuilder.Append(compoundKey); regexBuilder.Append(@")"); } regexBuilder.Append(@"\s*?))"); return regexBuilder.ToString(); } private static int buildCompoundGenerator(TagDefinition tagDefinition, TagScope scope, CompoundGenerator generator, string format, int formatIndex, Match match) { bool done = false; while (!done) { string leading = format.Substring(formatIndex, match.Index - formatIndex); formatIndex = match.Index + match.Length; if (match.Groups["comment"].Success) { // TODO - process comment } else if (match.Groups["close"].Success) { // TODO - process closing tag done = true; } else if (match.Groups["open"].Success) { string tagName = match.Groups["name"].Value; TagDefinition nextDefinition = scope.Find(tagName); if (nextDefinition == null) { // TODO - handle missing tag definition } if (nextDefinition.HasBody) { CompoundGenerator nextGenerator = new CompoundGenerator(); TagScope nextScope = new TagScope(scope); registerTags(nextDefinition, nextScope); Match nextMatch = findNextTag(nextDefinition, format, formatIndex); formatIndex = buildCompoundGenerator(nextDefinition, nextScope, nextGenerator, format, formatIndex, nextMatch); // TODO - grab the generated text and parameters and pass it to the tag's processor // TODO - a parameter can be a key or a default value } else { // TODO - grab all of the parameters and pass them to the tag's generator // TODO - a parameter can be a key or a default value } } else if (match.Groups["key"].Success) { string alignment = match.Groups["alignment"].Value; string formatting = match.Groups["format"].Value; // TODO - create a key generator } } return formatIndex; } } }