yog-script-analyze 0.4.0

Yog Script (.ys, .yns) analyzed: its lexer, parser and syntax tree, names resolved, the class index model — what the build and the editors read a source with.
Documentation
namespace System.Logging;

using System.Collections.Generic;
using System.Threading;

/// A log entry's state made of a message template and its values (.NET's
/// `FormattedLogValues`): each value by its hole's name, then the
/// template as `{OriginalFormat}`; written, the message.
public readonly sealed class FormattedLogValues : IReadOnlyList<KeyValuePair<string, object?>> {
    private readonly LogValuesFormatter? _formatter;
    private readonly object?[] _values;
    private readonly string _original;

    /// How many templates are kept read (.NET's): past it, one is read at each entry.
    private const i32 MaxCachedFormatters = 1024;
    /// The templates read so far, by their text: each read once, not at each entry.
    private static readonly Lock<Dictionary<string, LogValuesFormatter>> _formatters = new Lock<Dictionary<string, LogValuesFormatter>>(new Dictionary<string, LogValuesFormatter>());

    public FormattedLogValues(string? format, object?[] values) {
        _original = format ?? LogValuesFormatter.NullValue;
        _formatter = values.Length > 0 && format != null ? Formatter(format) : null;
        _values = move values;
    }

    /// A state of a template read already (`LoggerMessage.Define`'s).
    public FormattedLogValues(LogValuesFormatter formatter, object?[] values) {
        _original = formatter.Template;
        _formatter = values.Length > 0 ? formatter : null;
        _values = move values;
    }

    /// Template `format` read: the one read before, else read now (and
    /// kept, while there's room).
    private static LogValuesFormatter Formatter(string format) {
        let mut found: LogValuesFormatter? = null;
        lock (_formatters, ref let cache) {
            found = cache.TryGetValue(format);
            if (found == null) {
                let made = new LogValuesFormatter(format);
                if (cache.Count < MaxCachedFormatters) {
                    cache[format] = made;
                }
                found = made;
            }
        }
        return found ?? new LogValuesFormatter(format);
    }

    public i32 Count => _formatter == null ? 1 : _formatter.ValueNames.Count + 1;

    public KeyValuePair<string, object?> this[i32 index] {
        get {
            if (index == Count - 1) {
                return new KeyValuePair<string, object?>(LogValuesFormatter.OriginalFormat, _original);
            }
            let name = _formatter == null ? "" : _formatter.ValueNames[index];
            let value = index < _values.Length ? _values[index] : null;
            return new KeyValuePair<string, object?>(name, value);
        }
    }

    public IEnumerator<KeyValuePair<string, object?>> GetEnumerator() {
        for (let mut i = 0; i < Count; i += 1) {
            yield return this[i];
        }
    }

    public override string ToString() => _formatter == null ? _original : _formatter.Format(_values);

    /// What makes an entry's message of its state: the state written.
    public static string Message(FormattedLogValues state, Error? error) => state.ToString();
}