<commit_before>namespace Parsley;

public class ErrorMessageList
{
    public static readonly ErrorMessageList Empty = new();

    readonly ErrorMessage? head;
    readonly ErrorMessageList? tail;

    ErrorMessageList()
    {
        head = null;
        tail = null;
    }

    ErrorMessageList(ErrorMessage head, ErrorMessageList tail)
    {
        this.head = head;
        this.tail = tail;
    }

    public ErrorMessageList With(ErrorMessage errorMessage)
    {
        return new ErrorMessageList(errorMessage, this);
    }

    public ErrorMessageList Merge(ErrorMessageList errors)
    {
        var result = this;

        foreach (var error in errors.All<ErrorMessage>())
            result = result.With(error);

        return result;
    }

    public override string ToString()
    {
        var expectationErrors = new List<string>(All<ExpectedErrorMessage>()
            .Select(error => error.Expectation)
            .Distinct()
            .OrderBy(expectation => expectation));

        var backtrackErrors = All<BacktrackErrorMessage>().ToArray();

        if (!expectationErrors.Any() && !backtrackErrors.Any())
            return All<UnknownErrorMessage>().Any() ? "Parse error." : "";

        var parts = new List<string>();

        if (expectationErrors.Any())
        {
            var suffixes = Separators(expectationErrors.Count - 1).Concat(new[] { " expected" });

            parts.Add(string.Join("", expectationErrors.Zip(suffixes, (error, suffix) => error + suffix)));
        }

        if (backtrackErrors.Any())
            parts.Add(string.Join(" ", backtrackErrors.Select(backtrack => $"[{backtrack.Position}: {backtrack.Errors}]")));

        return string.Join(" ", parts);
    }

    static IEnumerable<string> Separators(int count)
    {
        if (count <= 0)
            return Enumerable.Empty<string>();
        return Enumerable.Repeat(", ", count - 1).Concat(new[] { " or " });
    }

    IEnumerable<T> All<T>() where T : ErrorMessage
    {
        if (head is T match)
            yield return match;

        if (tail != null)
            foreach (var message in tail.All<T>())
                yield return message;
    }
}
<commit_msg>Reveal the previously obfuscated handling of ErrorMessageList.ToString() for the Empty instance.<commit_after>namespace Parsley;

public class ErrorMessageList
{
    public static readonly ErrorMessageList Empty = new();

    readonly ErrorMessage? head;
    readonly ErrorMessageList? tail;

    ErrorMessageList()
    {
        head = null;
        tail = null;
    }

    ErrorMessageList(ErrorMessage head, ErrorMessageList tail)
    {
        this.head = head;
        this.tail = tail;
    }

    public ErrorMessageList With(ErrorMessage errorMessage)
    {
        return new ErrorMessageList(errorMessage, this);
    }

    public ErrorMessageList Merge(ErrorMessageList errors)
    {
        var result = this;

        foreach (var error in errors.All<ErrorMessage>())
            result = result.With(error);

        return result;
    }

    public override string ToString()
    {
        if (this == Empty)
            return "";

        var expectationErrors = new List<string>(All<ExpectedErrorMessage>()
            .Select(error => error.Expectation)
            .Distinct()
            .OrderBy(expectation => expectation));

        var backtrackErrors = All<BacktrackErrorMessage>().ToArray();

        if (!expectationErrors.Any() && !backtrackErrors.Any())
            return "Parse error.";

        var parts = new List<string>();

        if (expectationErrors.Any())
        {
            var suffixes = Separators(expectationErrors.Count - 1).Concat(new[] { " expected" });

            parts.Add(string.Join("", expectationErrors.Zip(suffixes, (error, suffix) => error + suffix)));
        }

        if (backtrackErrors.Any())
            parts.Add(string.Join(" ", backtrackErrors.Select(backtrack => $"[{backtrack.Position}: {backtrack.Errors}]")));

        return string.Join(" ", parts);
    }

    static IEnumerable<string> Separators(int count)
    {
        if (count <= 0)
            return Enumerable.Empty<string>();
        return Enumerable.Repeat(", ", count - 1).Concat(new[] { " or " });
    }

    IEnumerable<T> All<T>() where T : ErrorMessage
    {
        if (head is T match)
            yield return match;

        if (tail != null)
            foreach (var message in tail.All<T>())
                yield return message;
    }
}

{'fraction_non_alphanumeric': 0.12003569834895136, 'fraction_numerical': 0.0013386880856760374, 'mean_word_length': 2.323202372127502, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5301717', 'n_tokens_mistral': 1248, 'n_tokens_neox': 1192, 'n_words': 295}