public bool TryParse <T>(Template template, string input, out int matches, out T result) where T : class, new() { log.Debug($"Start: Processing: {template.Name}"); Token current = null; matches = 0; var value = new T(); var enumerator = new TokenEnumerator(input); var replacement = new StringBuilder(); var matchIds = new List <int>(); while (enumerator.IsEmpty == false) { var next = enumerator.Peek(); // Handle Windows new lines (normalize to Unix) if (next == "\r" && enumerator.Peek(1) == "\n") { enumerator.Next(); next = "\n"; } // Check for repeated current token if (current != null && enumerator.Match(current.Preamble)) { // Can't assign, so clear current context and move to next match if (current.CanAssign(replacement.ToString()) == false) { replacement.Clear(); enumerator.Advance(current.Preamble.Length); continue; } } // Check for next token if (enumerator.Match(template.TokensExcluding(matchIds), out var match)) { if (current == null) { current = match; replacement.Clear(); enumerator.Advance(match.Preamble.Length); matchIds.AddRange(template.GetTokenIdsUpTo(match)); } else if (replacement.Length > 0 && current.Assign(value, replacement.ToString(), template.Options, log)) { matches++; current = match; replacement.Clear(); enumerator.Advance(match.Preamble.Length); matchIds.AddRange(template.GetTokenIdsUpTo(match)); } else { replacement.Append(next); enumerator.Next(); } } // Append to replacement else { replacement.Append(next); enumerator.Next(); } } if (current != null && replacement.Length > 0 && !string.IsNullOrEmpty(current.Name)) { current.Assign(value, replacement.ToString(), template.Options, log); matches++; } result = value; log.Debug($" Found {matches} matches."); log.Debug($"Finished: Processing: {template.Name}"); return(matches > 0); }