public static IEnumerable <ITemplateToken> Tokenize([CanBeNull] string template, [NotNull] INamedTokenFactory namedTokenFactory) { if (string.IsNullOrEmpty(template)) { yield break; } var nextIndex = 0; while (true) { // (iloktionov): Consume text until we find a beginning of a named token: var text = ConsumeText(template, nextIndex, out nextIndex); if (text != null) { yield return(text); } if (nextIndex == template.Length) { yield break; } // (iloktionov): Try to consume a named token. If it's incorrect, consume it as text: var namedToken = ParseNamedToken(template, nextIndex, out nextIndex, namedTokenFactory); if (namedToken != null) { yield return(namedToken); } if (nextIndex == template.Length) { yield break; } } }
private static ITemplateToken ParseNamedToken(string template, int offset, out int next, INamedTokenFactory factory) { var beginning = offset++; // (iloktionov): Just move on until we encounter something that should not be in a named token: while (offset < template.Length && IsValidInNamedToken(template[offset])) { offset++; } // (iloktionov): If we reached the end of template or didn't stop on a closing brace, there will be no named token: if (offset == template.Length || template[offset] != ClosingBrace) { next = offset; return(CreateTextToken(template, beginning, offset - beginning)); } next = offset + 1; // (iloktionov): Raw content is token with braces included, like '{prop:format}'. var rawOffset = beginning; var rawLength = next - rawOffset; // (iloktionov): Token content is token without braces, like 'prop:format'. var tokenOffset = rawOffset + 1; var tokenLength = rawLength - 2; if (TryParseNamedToken(template, tokenOffset, tokenLength, out var name, out var format)) { return(factory.Create(name, format)); } return(CreateTextToken(template, rawOffset, rawLength)); }