private static ISet <string> ValidateReferencedMatchersList(
            IReadOnlyList <IReferencedMatchers> referencedMatchersList)
        {
            var knownReferences = new HashSet <string>();

            foreach (var referencedMatchers in referencedMatchersList)
            {
                var           referenceName = referencedMatchers.ReferenceName;
                List <string> regexes       = referencedMatchers.Data.Select(d => d.Regex).ToList();
                ValidateGroupNames(regexes);

                if (regexes.Any(s => s.Contains(ValuePlaceholder)))
                {
                    throw new ParseException(
                              $"A regex of reference {referenceName} contains values");
                }

                if (regexes.Any(s => ReferencePlaceholderRegex.IsMatch(s)))
                {
                    throw new ParseException(
                              $"A regex of reference {referenceName} contains references");
                }

                if (!knownReferences.Add(referenceName))
                {
                    throw new ParseException(
                              $"The reference name {referenceName} is not unique between the IReferencedMatchers");
                }
            }

            return(knownReferences);
        }
        private static IReadOnlyDictionary <string, ISet <string> > ValidateStatMatchersList(
            IReadOnlyList <IStatMatchers> statMatchersList, ISet <string> knownReferences)
        {
            var recursiveReferences = new Dictionary <string, ISet <string> >();

            foreach (var statMatchers in statMatchersList)
            {
                var           referenceNames = statMatchers.ReferenceNames;
                List <string> regexes        = statMatchers.Data.Select(d => d.Regex).ToList();
                ValidateGroupNames(regexes);

                if (referenceNames.IsEmpty())
                {
                    continue;
                }

                if (regexes.Any(s => s.Contains(ValuePlaceholder)))
                {
                    throw new ParseException(
                              $"A regex of reference {string.Join(",", referenceNames)} contains values");
                }

                foreach (var referenceName in referenceNames)
                {
                    if (knownReferences.Contains(referenceName))
                    {
                        throw new ParseException(
                                  $"The reference name {referenceName} is used by both an IReferencedMatchers and an IStatMatchers");
                    }

                    var containedReferences = regexes
                                              .SelectMany(r => ReferencePlaceholderRegex.Matches(r).Cast <Match>())
                                              .Select(m => m.Groups[1].Value);
                    recursiveReferences.GetOrAdd(referenceName, _ => new HashSet <string>())
                    .UnionWith(containedReferences);
                }
            }

            knownReferences.UnionWith(recursiveReferences.Keys);
            return(recursiveReferences);
        }
        private string ExpandReferences(string regex, string referencePrefix)
        {
            var referenceIndex = 0;

            return(ReferencePlaceholderRegex.Replace(regex, match =>
            {
                var referenceName = match.Groups[1].Value;
                var prefix = _regexGroupFactory.CombineGroupPrefixes(referencePrefix, referenceIndex.ToString());
                referenceIndex++;
                var indexedReferencedRegexes = _referencedRegexes.GetRegexes(referenceName)
                                               .Select((matcher, index) => (matcher, index));
                IEnumerable <string> regexes =
                    from t in indexedReferencedRegexes
                    // Without ordering, e.g. "Has a" in "(Has|Has a)" would never be matched
                    orderby t.matcher.Length descending
                    // Recursive expansion
                    let innerRegex = ExpandReferences(t.matcher, prefix)
                                     select _regexGroupFactory.CreateReferenceGroup(prefix, referenceName, t.index, innerRegex);
                var joinedRegex = string.Join("|", regexes);
                return $"({joinedRegex})";
            }));
        }