Defines the attributes of a custom tag.
 /// <summary>
 /// Registers the given tag definition with the parser.
 /// </summary>
 /// <param name="definition">The tag definition to register.</param>
 /// <param name="isTopLevel">Specifies whether the tag is immediately in scope.</param>
 public void RegisterTag(TagDefinition definition, bool isTopLevel)
 {
     if (definition == null)
     {
         throw new ArgumentNullException("definition");
     }
     if (_tagLookup.ContainsKey(definition.Name))
     {
         string message = String.Format(Resources.DuplicateTagDefinition, definition.Name);
         throw new ArgumentException(message, "definition");
     }
     _tagLookup.Add(definition.Name, definition);
 }
 /// <summary>
 /// Adds the given generator, determining whether the generator should
 /// be part of the primary generators or added as an secondary generator.
 /// </summary>
 /// <param name="definition">The tag that the generator is generating text for.</param>
 /// <param name="generator">The generator to add.</param>
 public void AddGenerator(TagDefinition definition, IGenerator generator)
 {
     bool isSubGenerator = _definition.ShouldCreateSecondaryGroup(definition);
     addGenerator(generator, isSubGenerator);
 }
 /// <summary>
 /// Initializes a new instance of a CompoundGenerator.
 /// </summary>
 /// <param name="definition">The tag that the text is being generated for.</param>
 /// <param name="arguments">The arguments that were passed to the tag.</param>
 public CompoundGenerator(TagDefinition definition, ArgumentCollection arguments)
 {
     _definition = definition;
     _arguments = arguments;
     _primaryGenerators = new LinkedList<IGenerator>();
 }
 private static ArgumentCollection getArguments(TagDefinition definition, Match match)
 {
     ArgumentCollection collection = new ArgumentCollection();
     List<Capture> captures = match.Groups["argument"].Captures.Cast<Capture>().ToList();
     List<TagParameter> parameters = definition.Parameters.ToList();
     if (captures.Count > parameters.Count)
     {
         string message = String.Format(Resources.WrongNumberOfArguments, definition.Name);
         throw new FormatException(message);
     }
     if (captures.Count < parameters.Count)
     {
         captures.AddRange(Enumerable.Repeat((Capture)null, parameters.Count - captures.Count));
     }
     foreach (var pair in parameters.Zip(captures, (p, c) => new { Capture = c, Parameter = p }))
     {
         if (pair.Capture == null)
         {
             if (pair.Parameter.IsRequired)
             {
                 string message = String.Format(Resources.WrongNumberOfArguments, definition.Name);
                 throw new FormatException(message);
             }
             collection.AddArgument(pair.Parameter, null);
         }
         else
         {
             collection.AddArgument(pair.Parameter, pair.Capture.Value);
         }
     }
     return collection;
 }
 private Match findNextTag(TagDefinition definition, string format, int formatIndex)
 {
     Regex regex = prepareRegex(definition);
     return regex.Match(format, formatIndex);
 }
 private Regex prepareRegex(TagDefinition definition)
 {
     Regex regex;
     if (!_regexLookup.TryGetValue(definition.Name, out regex))
     {
         List<string> matches = new List<string>();
         matches.Add(getKeyRegex());
         matches.Add(getCommentTagRegex());
         foreach (string closingTag in definition.ClosingTags)
         {
             matches.Add(getClosingTagRegex(closingTag));
         }
         foreach (TagDefinition globalDefinition in _tagLookup.Values)
         {
             if (!globalDefinition.IsContextSensitive)
             {
                 matches.Add(getTagRegex(globalDefinition));
             }
         }
         foreach (string childTag in definition.ChildTags)
         {
             TagDefinition childDefinition = _tagLookup[childTag];
             matches.Add(getTagRegex(childDefinition));
         }
         matches.Add(getUnknownTagRegex());
         string match = "{{(" + String.Join("|", matches) + ")}}";
         regex = new Regex(match, RegexOptions.Compiled);
         _regexLookup.Add(definition.Name, regex);
     }
     return regex;
 }
        private int buildCompoundGenerator(
            TagDefinition tagDefinition,
            CompoundGenerator generator,
            Trimmer trimmer,
            string format, int formatIndex)
        {
            while (true)
            {
                Match match = findNextTag(tagDefinition, format, formatIndex);

                if (!match.Success)
                {
                    if (tagDefinition.ClosingTags.Any())
                    {
                        string message = String.Format(Resources.MissingClosingTag, tagDefinition.Name);
                        throw new FormatException(message);
                    }
                    break;
                }

                string leading = format.Substring(formatIndex, match.Index - formatIndex);

                if (match.Groups["key"].Success)
                {
                    generator.AddStaticGenerators(trimmer.RecordText(leading, true, true));
                    formatIndex = match.Index + match.Length;
                    string key = match.Groups["key"].Value;
                    string alignment = match.Groups["alignment"].Value;
                    string formatting = match.Groups["format"].Value;
                    KeyGenerator keyGenerator = new KeyGenerator(key, alignment, formatting);
                    generator.AddGenerator(keyGenerator);
                }
                else if (match.Groups["open"].Success)
                {
                    formatIndex = match.Index + match.Length;
                    string tagName = match.Groups["name"].Value;
                    TagDefinition nextDefinition = _tagLookup[tagName];
                    if (nextDefinition == null)
                    {
                        string message = String.Format(Resources.UnknownTag, tagName);
                        throw new FormatException(message);
                    }
                    if (nextDefinition.HasContent)
                    {
                        generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
                        ArgumentCollection arguments = getArguments(nextDefinition, match);
                        CompoundGenerator compoundGenerator = new CompoundGenerator(nextDefinition, arguments);
                        formatIndex = buildCompoundGenerator(nextDefinition, compoundGenerator, trimmer, format, formatIndex);
                        generator.AddGenerator(nextDefinition, compoundGenerator);
                    }
                    else
                    {
                        generator.AddStaticGenerators(trimmer.RecordText(leading, true, true));
                        Match nextMatch = findNextTag(nextDefinition, format, formatIndex);
                        ArgumentCollection arguments = getArguments(nextDefinition, nextMatch);
                        InlineGenerator inlineGenerator = new InlineGenerator(nextDefinition, arguments);
                        generator.AddGenerator(inlineGenerator);
                    }
                }
                else if (match.Groups["close"].Success)
                {
                    generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
                    string tagName = match.Groups["name"].Value;
                    TagDefinition nextDefinition = _tagLookup[tagName];
                    formatIndex = match.Index;
                    if (tagName == tagDefinition.Name)
                    {
                        formatIndex += match.Length;
                    }
                    break;
                }
                else if (match.Groups["comment"].Success)
                {
                    generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
                    formatIndex = match.Index + match.Length;
                }
                else if (match.Groups["unknown"].Success)
                {
                    throw new FormatException(Resources.UnknownTag);
                }
            }
            return formatIndex;
        }
 private static string getTagRegex(TagDefinition definition)
 {
     StringBuilder regexBuilder = new StringBuilder();
     regexBuilder.Append(@"(?<open>(#(?<name>");
     regexBuilder.Append(definition.Name);
     regexBuilder.Append(@")");
     foreach (TagParameter parameter in definition.Parameters)
     {
         regexBuilder.Append(@"(\s+?");
         regexBuilder.Append(@"(?<argument>");
         regexBuilder.Append(RegexHelper.CompoundKey);
         regexBuilder.Append(@"))");
         if (!parameter.IsRequired)
         {
             regexBuilder.Append("?");
         }
     }
     regexBuilder.Append(@"\s*?))");
     return regexBuilder.ToString();
 }
 /// <summary>
 /// Gets whether the given tag's generator should be used for a secondary (or substitute) text block.
 /// </summary>
 /// <param name="definition">The tag to inspect.</param>
 /// <returns>True if the tag's generator should be used as a secondary generator.</returns>
 public override bool ShouldCreateSecondaryGroup(TagDefinition definition)
 {
     return new string[] { "elif", "else" }.Contains(definition.Name);
 }
 /// <summary>
 /// Initializes a new instance of an InlineGenerator.
 /// </summary>
 /// <param name="definition">The tag to render the text for.</param>
 /// <param name="arguments">The arguments passed to the tag.</param>
 public InlineGenerator(TagDefinition definition, ArgumentCollection arguments)
 {
     _definition = definition;
     _arguments = arguments;
 }
 /// <summary>
 /// Requests which generator group to associate the given tag type.
 /// </summary>
 /// <param name="definition">The child tag definition being grouped.</param>
 /// <returns>The name of the group to associate the given tag with.</returns>
 public virtual bool ShouldCreateSecondaryGroup(TagDefinition definition)
 {
     return false;
 }