Parser rewrite

This commit is contained in:
2026-09-15 00:14:26 +02:00
parent 3dde5e5acf
commit 1d53f6930a
11 changed files with 889 additions and 667 deletions
+41 -22
View File
@@ -1,29 +1,37 @@
namespace ParadoxSaveParser.Lib;
public readonly record struct SearchArgs
/// <summary>
/// The node a search expression is tested against: its key inside the parent dictionary,
/// still in the raw bytes of the save, and its position inside the parent list.
/// Keys stay undecoded so that a key rejected by the query never becomes a string.
/// </summary>
public readonly ref struct MatchCandidate
{
public readonly string KeyStr;
public readonly StringBuilder? KeySB;
public readonly int LocalIndex;
public readonly ReadOnlySpan<byte> Key;
public readonly int Index;
public SearchArgs(int localIndex, string keyStr)
/// <summary>Encoding <see cref="Key" /> is written in.</summary>
public readonly Encoding Encoding;
/// <summary>A list item, which has a position but no key.</summary>
public MatchCandidate(int index)
{
KeyStr = keyStr;
KeySB = null;
LocalIndex = localIndex;
Key = default;
Index = index;
Encoding = Encoding.Latin1;
}
public SearchArgs(int localIndex, StringBuilder keySb)
public MatchCandidate(int index, ReadOnlySpan<byte> key, Encoding encoding)
{
KeyStr = string.Empty;
KeySB = keySb;
LocalIndex = localIndex;
Key = key;
Index = index;
Encoding = encoding;
}
}
public interface ISearchExpression
{
bool DoesMatch(SearchArgs args, out ISearchExpression? nextSearchExpression);
bool DoesMatch(MatchCandidate candidate, out ISearchExpression? nextSearchExpression);
}
public static class SearchExpressionCompiler
@@ -108,7 +116,7 @@ public static class SearchExpressionCompiler
private record AnyMatchExpression(ISearchExpression? next) : ISearchExpression
{
public bool DoesMatch(SearchArgs args, out ISearchExpression? nextSearchExpression)
public bool DoesMatch(MatchCandidate candidate, out ISearchExpression? nextSearchExpression)
{
nextSearchExpression = next;
return true;
@@ -117,7 +125,7 @@ public static class SearchExpressionCompiler
private record NoMatchExpression : ISearchExpression
{
public bool DoesMatch(SearchArgs args, out ISearchExpression? nextSearchExpression)
public bool DoesMatch(MatchCandidate candidate, out ISearchExpression? nextSearchExpression)
{
nextSearchExpression = null;
return false;
@@ -126,10 +134,10 @@ public static class SearchExpressionCompiler
private record MultipleMatchExpression(List<ISearchExpression> subExprs) : ISearchExpression
{
public bool DoesMatch(SearchArgs args, out ISearchExpression? nextSearchExpression)
public bool DoesMatch(MatchCandidate candidate, out ISearchExpression? nextSearchExpression)
{
foreach (var e in subExprs)
if (e.DoesMatch(args, out nextSearchExpression))
if (e.DoesMatch(candidate, out nextSearchExpression))
return true;
nextSearchExpression = null;
@@ -139,9 +147,9 @@ public static class SearchExpressionCompiler
private record IndexMatchExpression(int index, ISearchExpression? next) : ISearchExpression
{
public bool DoesMatch(SearchArgs args, out ISearchExpression? nextSearchExpression)
public bool DoesMatch(MatchCandidate candidate, out ISearchExpression? nextSearchExpression)
{
if (args.LocalIndex == index)
if (candidate.Index == index)
{
nextSearchExpression = next;
return true;
@@ -154,9 +162,20 @@ public static class SearchExpressionCompiler
private record ExactMatchExpression(string key, ISearchExpression? next) : ISearchExpression
{
public bool DoesMatch(SearchArgs args, out ISearchExpression? nextSearchExpression)
// the key is compared as bytes, so it is encoded once for whatever encoding the
// parser reads the save in
private byte[]? _keyBytes;
private Encoding? _keyEncoding;
public bool DoesMatch(MatchCandidate candidate, out ISearchExpression? nextSearchExpression)
{
if ((args.KeySB != null && args.KeySB.Equals(key)) || args.KeyStr == key)
if (!ReferenceEquals(_keyEncoding, candidate.Encoding))
{
_keyEncoding = candidate.Encoding;
_keyBytes = candidate.Encoding.GetBytes(key);
}
if (candidate.Key.SequenceEqual(_keyBytes))
{
nextSearchExpression = next;
return true;