142 lines
4.6 KiB
C#
142 lines
4.6 KiB
C#
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
namespace ParadoxSaveParser.Lib;
|
|
|
|
public record SearchArgs(string key, int currentDepth, int localIndex);
|
|
|
|
public interface ISearchExpression
|
|
{
|
|
bool DoesMatch(SearchArgs args);
|
|
}
|
|
|
|
public class SearchExpression : ISearchExpression
|
|
{
|
|
private List<ISearchExpression> _compiledExpression;
|
|
private int _expressionDepth;
|
|
|
|
private SearchExpression(List<ISearchExpression> compiledExpression, int expressionDepth)
|
|
{
|
|
_compiledExpression = compiledExpression;
|
|
_expressionDepth = expressionDepth;
|
|
}
|
|
|
|
|
|
public bool DoesMatch(SearchArgs args)
|
|
{
|
|
if (args.key is "c" or "d" or "e")
|
|
Console.WriteLine("UwU");
|
|
int index = args.currentDepth - _expressionDepth;
|
|
if (index < 0 || index >= _compiledExpression.Count)
|
|
return true;
|
|
|
|
return _compiledExpression[index].DoesMatch(args);
|
|
}
|
|
|
|
|
|
private static bool CharEqualsAndNotEscaped(char c, ReadOnlySpan<char> chars, int i) =>
|
|
chars[i] == c && (i < 1 || chars[i - 1] != '\\') && (i < 2 || chars[i - 2] != '\\');
|
|
|
|
public static SearchExpression Parse(string query) => ParseInternal(query, 0);
|
|
|
|
private static SearchExpression ParseInternal(ReadOnlySpan<char> query, int expressionDepth)
|
|
{
|
|
var compiledExpression = new List<ISearchExpression>();
|
|
ISearchExpression exprPart;
|
|
int partBegin = 0;
|
|
int bracketBalance = 0;
|
|
int expressionDepthIncrement = 0;
|
|
|
|
for (int i = 0; i < query.Length; i++)
|
|
{
|
|
if (CharEqualsAndNotEscaped('(', query, i))
|
|
bracketBalance++;
|
|
else if (CharEqualsAndNotEscaped(')', query, i))
|
|
bracketBalance--;
|
|
else if (bracketBalance == 0 && CharEqualsAndNotEscaped('.', query, i))
|
|
{
|
|
var part = query.Slice(partBegin, i - partBegin);
|
|
expressionDepthIncrement++;
|
|
exprPart = ParsePart(part, query, partBegin,
|
|
expressionDepth + expressionDepthIncrement);
|
|
compiledExpression.Add(exprPart);
|
|
partBegin = i + 1;
|
|
}
|
|
}
|
|
|
|
exprPart = ParsePart(query.Slice(partBegin), query, partBegin,
|
|
expressionDepth + expressionDepthIncrement);
|
|
compiledExpression.Add(exprPart);
|
|
|
|
return new SearchExpression(compiledExpression, expressionDepth);
|
|
}
|
|
|
|
private static ISearchExpression ParsePart(ReadOnlySpan<char> part,
|
|
ReadOnlySpan<char> query, int partBegin, int expressionDepth)
|
|
{
|
|
if (part is "*")
|
|
{
|
|
return new AnyMatchExpression();
|
|
}
|
|
|
|
if (CharEqualsAndNotEscaped('[', query, partBegin))
|
|
{
|
|
part = part.Slice(1, part.Length - 2);
|
|
return new IndexMatchExpression(int.Parse(part));
|
|
}
|
|
|
|
if(part[0] is '(')
|
|
{
|
|
var subExprs = new List<ISearchExpression>();
|
|
ISearchExpression subExpr;
|
|
part = part.Slice(1, part.Length - 2);
|
|
int supExprBegin = 0;
|
|
for (int j = 0; j < part.Length; j++)
|
|
{
|
|
if (CharEqualsAndNotEscaped('|', part, j))
|
|
{
|
|
subExpr = ParseInternal(part.Slice(supExprBegin, j - supExprBegin),
|
|
expressionDepth);
|
|
subExprs.Add(subExpr);
|
|
supExprBegin = j + 1;
|
|
}
|
|
}
|
|
|
|
subExpr = ParseInternal(part.Slice(supExprBegin), expressionDepth);
|
|
subExprs.Add(subExpr);
|
|
return new MultipleMatchExpression(subExprs);
|
|
}
|
|
|
|
return new ExactMatchExpression(part.ToString());
|
|
}
|
|
|
|
private record AnyMatchExpression : ISearchExpression
|
|
{
|
|
public bool DoesMatch(SearchArgs args) => true;
|
|
}
|
|
|
|
private record MultipleMatchExpression(List<ISearchExpression> subExprs) : ISearchExpression
|
|
{
|
|
public bool DoesMatch(SearchArgs args)
|
|
{
|
|
foreach (var e in subExprs)
|
|
{
|
|
if(e.DoesMatch(args))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private record IndexMatchExpression(int index) : ISearchExpression
|
|
{
|
|
public bool DoesMatch(SearchArgs args) => args.localIndex == index;
|
|
}
|
|
|
|
private record ExactMatchExpression(string key) : ISearchExpression
|
|
{
|
|
public bool DoesMatch(SearchArgs args) => args.key == key;
|
|
}
|
|
|
|
}
|