Replace LINQ ordering with manual sorting for statement entries, replace Attributes form public API with normal data class

This commit is contained in:
Stone_Red
2026-06-10 23:35:20 +02:00
parent 3f595567e3
commit 8875f1ff99
6 changed files with 193 additions and 44 deletions
@@ -87,7 +87,6 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
_ = sb.AppendLine("#nullable enable"); _ = sb.AppendLine("#nullable enable");
_ = sb.AppendLine("using System;"); _ = sb.AppendLine("using System;");
_ = sb.AppendLine("using System.Collections.Generic;"); _ = sb.AppendLine("using System.Collections.Generic;");
_ = sb.AppendLine("using System.Linq;");
_ = sb.AppendLine(); _ = sb.AppendLine();
_ = sb.AppendLine("namespace YesNt.Interpreter.Runtime;"); _ = sb.AppendLine("namespace YesNt.Interpreter.Runtime;");
_ = sb.AppendLine(); _ = sb.AppendLine();
@@ -95,8 +94,8 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
_ = sb.AppendLine("{"); _ = sb.AppendLine("{");
_ = sb.AppendLine(" internal static void Register("); _ = sb.AppendLine(" internal static void Register(");
_ = sb.AppendLine(" RuntimeInformation runtimeInfo,"); _ = sb.AppendLine(" RuntimeInformation runtimeInfo,");
_ = sb.AppendLine(" out Dictionary<global::YesNt.Interpreter.Attributes.StatementAttribute, Action<string>> statements,"); _ = sb.AppendLine(" out Dictionary<global::YesNt.Interpreter.Attributes.StatementAttributeContainer, Action<string>> statements,");
_ = sb.AppendLine(" out List<KeyValuePair<global::YesNt.Interpreter.Attributes.StaticStatementAttribute, Action>> staticStatements)"); _ = sb.AppendLine(" out List<KeyValuePair<global::YesNt.Interpreter.Attributes.StaticStatementAttributeContainer, Action>> staticStatements)");
_ = sb.AppendLine(" {"); _ = sb.AppendLine(" {");
List<INamedTypeSymbol> allTypes = statementMethods List<INamedTypeSymbol> allTypes = statementMethods
@@ -117,36 +116,39 @@ public sealed class StatementRegistryGenerator : IIncrementalGenerator
_ = sb.AppendLine($" {instanceName}.RuntimeInfo = runtimeInfo;"); _ = sb.AppendLine($" {instanceName}.RuntimeInfo = runtimeInfo;");
} }
_ = sb.AppendLine(" var statementEntries = new List<KeyValuePair<global::YesNt.Interpreter.Attributes.StatementAttribute, Action<string>>>();"); _ = sb.AppendLine(" var statementEntries = new List<KeyValuePair<global::YesNt.Interpreter.Attributes.StatementAttributeContainer, Action<string>>>();");
foreach (MethodRegistration method in statementMethods foreach (MethodRegistration method in statementMethods
.OrderBy(x => x.ContainingType.ToDisplayString()) .OrderBy(x => x.ContainingType.ToDisplayString())
.ThenBy(x => x.Method.Name)) .ThenBy(x => x.Method.Name))
{ {
string instanceName = instanceNames[method.ContainingType]; string instanceName = instanceNames[method.ContainingType];
string attributeCreation = BuildAttributeCreation("global::YesNt.Interpreter.Attributes.StatementAttribute", method.Attribute); string attributeCreation = BuildAttributeCreation("global::YesNt.Interpreter.Attributes.StatementAttributeContainer", method.Attribute);
_ = sb.AppendLine($" statementEntries.Add(new({attributeCreation}, {instanceName}.{method.Method.Name}));"); _ = sb.AppendLine($" statementEntries.Add(new({attributeCreation}, {instanceName}.{method.Method.Name}));");
} }
_ = sb.AppendLine(" var staticEntries = new List<KeyValuePair<global::YesNt.Interpreter.Attributes.StaticStatementAttribute, Action>>();"); _ = sb.AppendLine(" var staticEntries = new List<KeyValuePair<global::YesNt.Interpreter.Attributes.StaticStatementAttributeContainer, Action>>();");
foreach (MethodRegistration method in staticStatementMethods foreach (MethodRegistration method in staticStatementMethods
.OrderBy(x => x.ContainingType.ToDisplayString()) .OrderBy(x => x.ContainingType.ToDisplayString())
.ThenBy(x => x.Method.Name)) .ThenBy(x => x.Method.Name))
{ {
string instanceName = instanceNames[method.ContainingType]; string instanceName = instanceNames[method.ContainingType];
string attributeCreation = BuildAttributeCreation("global::YesNt.Interpreter.Attributes.StaticStatementAttribute", method.Attribute); string attributeCreation = BuildAttributeCreation("global::YesNt.Interpreter.Attributes.StaticStatementAttributeContainer", method.Attribute);
_ = sb.AppendLine($" staticEntries.Add(new({attributeCreation}, {instanceName}.{method.Method.Name}));"); _ = sb.AppendLine($" staticEntries.Add(new({attributeCreation}, {instanceName}.{method.Method.Name}));");
} }
_ = sb.AppendLine(" statements = statementEntries"); _ = sb.AppendLine(" statementEntries.Sort((a, b) =>");
_ = sb.AppendLine(" .OrderBy(s => s.Key.Priority)"); _ = sb.AppendLine(" {");
_ = sb.AppendLine(" .ThenByDescending(s => s.Key.Name.Length)"); _ = sb.AppendLine(" int cmp = a.Key.Priority.CompareTo(b.Key.Priority);");
_ = sb.AppendLine(" .ToDictionary(x => x.Key, x => x.Value);"); _ = sb.AppendLine(" return cmp != 0 ? cmp : b.Key.Name.Length.CompareTo(a.Key.Name.Length);");
_ = sb.AppendLine(" });");
_ = sb.AppendLine(" statements = new Dictionary<global::YesNt.Interpreter.Attributes.StatementAttributeContainer, Action<string>>();");
_ = sb.AppendLine(" foreach (var entry in statementEntries)");
_ = sb.AppendLine(" statements.Add(entry.Key, entry.Value);");
_ = sb.AppendLine(); _ = sb.AppendLine();
_ = sb.AppendLine(" staticStatements = staticEntries"); _ = sb.AppendLine(" staticEntries.Sort((a, b) => a.Key.Priority.CompareTo(b.Key.Priority));");
_ = sb.AppendLine(" .OrderBy(s => s.Key.Priority)"); _ = sb.AppendLine(" staticStatements = staticEntries;");
_ = sb.AppendLine(" .ToList();");
_ = sb.AppendLine(" }"); _ = sb.AppendLine(" }");
_ = sb.AppendLine("}"); _ = sb.AppendLine("}");
@@ -4,7 +4,6 @@ using System.Collections.Generic;
using YesNt.Interpreter.Attributes; using YesNt.Interpreter.Attributes;
using YesNt.Interpreter.Enums; using YesNt.Interpreter.Enums;
using YesNt.Interpreter.Runtime;
namespace YesNt.Interpreter.Tests; namespace YesNt.Interpreter.Tests;
@@ -65,7 +64,7 @@ public class AddStatementTests
_ = YesNtAssert.GetLastLineWithSetup(lines, interpreter => _ = YesNtAssert.GetLastLineWithSetup(lines, interpreter =>
{ {
StatementAttribute attr = new StatementAttribute("attr_cmd", SearchMode.StartOfLine, SpaceAround.End); StatementAttributeContainer attr = new StatementAttributeContainer("attr_cmd", SearchMode.StartOfLine, SpaceAround.End);
interpreter.AddStatement(attr, _ => interpreter.AddStatement(attr, _ =>
{ {
handlerCalled = true; handlerCalled = true;
@@ -282,11 +281,11 @@ public class AddStatementTests
_ = YesNtAssert.GetLastLineWithSetup(lines, interpreter => _ = YesNtAssert.GetLastLineWithSetup(lines, interpreter =>
{ {
interpreter.AddStatement( interpreter.AddStatement(
new StatementAttribute("priority_cmd", SearchMode.StartOfLine, SpaceAround.End) { Priority = Priority.High }, new StatementAttributeContainer("priority_cmd", SearchMode.StartOfLine, SpaceAround.End) { Priority = Priority.High },
_ => highPriorityOrder = callOrder++); _ => highPriorityOrder = callOrder++);
interpreter.AddStatement( interpreter.AddStatement(
new StatementAttribute("priority_cmd", SearchMode.StartOfLine, SpaceAround.End) { Priority = Priority.Normal }, new StatementAttributeContainer("priority_cmd", SearchMode.StartOfLine, SpaceAround.End) { Priority = Priority.Normal },
_ => normalPriorityOrder = callOrder++); _ => normalPriorityOrder = callOrder++);
}); });
@@ -364,7 +363,7 @@ public class AddStatementTests
string? captured = null; string? captured = null;
YesNtAssert.GetLastLineWithSetup(lines, interpreter => _ = YesNtAssert.GetLastLineWithSetup(lines, interpreter =>
{ {
interpreter.AddStatement("echo_var", SearchMode.StartOfLine, SpaceAround.End, (args, rt) => interpreter.AddStatement("echo_var", SearchMode.StartOfLine, SpaceAround.End, (args, rt) =>
{ {
@@ -0,0 +1,109 @@
using System;
using YesNt.Interpreter.Enums;
namespace YesNt.Interpreter.Attributes;
/// <summary>
/// Marks a method as a YesNt statement handler.
/// The interpreter matches source lines against the <see cref="Name"/> keyword according to
/// <see cref="SearchMode"/> and <see cref="SpaceAround"/> rules, then invokes the decorated method
/// with the remaining argument text.
/// </summary>
/// <remarks>
/// Methods decorated with this attribute must be instance methods on a class that inherits
/// <see cref="Runtime.StatementRuntimeInformation"/> and must accept a single <see cref="string"/> parameter.
/// </remarks>
public class StatementAttributeContainer
{
/// <summary>Gets the keyword that identifies this statement in source code.</summary>
public string Name { get; }
/// <summary>Gets where in the line the keyword is searched for.</summary>
public SearchMode SearchMode { get; }
/// <summary>Gets which sides of the keyword must be padded with a space.</summary>
public SpaceAround SpaceAround { get; }
/// <summary>Gets or sets the syntax-highlight color used by the code editor.</summary>
public ConsoleColor Color { get; set; }
/// <summary>
/// Gets or sets the execution priority. Statements with a lower <see cref="Priority"/> value
/// run before those with a higher value. Defaults to <see cref="Priority.Normal"/>.
/// </summary>
public Priority Priority { get; set; } = Priority.Normal;
/// <summary>
/// Gets or sets a value indicating whether this statement is still invoked while the interpreter
/// is in search mode (scanning for a label or function definition). Defaults to <see langword="false"/>.
/// </summary>
public bool ExecuteInSearchMode { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the full current line (including the keyword itself)
/// is passed as the argument, rather than stripping the keyword prefix/suffix first.
/// Defaults to <see langword="false"/>.
/// </summary>
public bool KeepStatementInArgs { get; set; }
/// <summary>
/// Gets a value indicating whether this statement should be excluded from syntax highlighting.
/// Set to <see langword="true"/> when no <see cref="Color"/> is provided.
/// </summary>
public bool IgnoreSyntaxHighlighting { get; }
/// <summary>
/// Gets or sets an optional sub-string that must also be present in the line for this statement
/// to match. Used to differentiate overloaded keywords (e.g. <c>call</c> vs <c>call … with …</c>).
/// </summary>
public string Separator { get; set; }
/// <summary>
/// Gets or sets the name of the statement that marks the end of this block.
/// Used for block boundary caching (e.g., "while" has BlockPair = "end_while").
/// </summary>
public string BlockPair { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this statement is the end of a block.
/// Used for block boundary caching (e.g., "end_while" has IsBlockEnd = true).
/// </summary>
public bool IsBlockEnd { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this statement is an intermediate part of a block
/// (e.g., "else:" between "if" and "end_if").
/// </summary>
public bool IsBlockIntermediate { get; set; }
/// <summary>
/// Initializes a new <see cref="StatementAttribute"/> with a syntax-highlight color.
/// </summary>
/// <param name="name">The keyword that identifies this statement.</param>
/// <param name="searchMode">Where in the line the keyword is matched.</param>
/// <param name="spaceAround">Which sides of the keyword require a surrounding space.</param>
/// <param name="color">The color used for syntax highlighting in the code editor.</param>
public StatementAttributeContainer(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor color)
{
Name = name;
SearchMode = searchMode;
SpaceAround = spaceAround;
Color = color;
}
/// <summary>
/// Initializes a new <see cref="StatementAttribute"/> without a syntax-highlight color.
/// The statement will be excluded from syntax highlighting.
/// </summary>
/// <param name="name">The keyword that identifies this statement.</param>
/// <param name="searchMode">Where in the line the keyword is matched.</param>
/// <param name="spaceAround">Which sides of the keyword require a surrounding space.</param>
public StatementAttributeContainer(string name, SearchMode searchMode, SpaceAround spaceAround)
{
Name = name;
SearchMode = searchMode;
SpaceAround = spaceAround;
IgnoreSyntaxHighlighting = true;
}
}
@@ -0,0 +1,28 @@
using YesNt.Interpreter.Enums;
namespace YesNt.Interpreter.Attributes;
/// <summary>
/// Marks a parameterless method as a YesNt static statement handler.
/// Static statements are invoked once per line before regular statement matching begins,
/// regardless of whether the line matches any keyword. They are typically used for
/// pre-processing tasks such as transforming the current line before other statements run.
/// </summary>
/// <remarks>
/// Methods decorated with this attribute must be instance methods on a class that inherits
/// <see cref="Runtime.StatementRuntimeInformation"/> and must have no parameters.
/// </remarks>
public class StaticStatementAttributeContainer
{
/// <summary>
/// Gets or sets a value indicating whether this handler is still invoked while the interpreter
/// is in search mode (scanning for a label or function definition). Defaults to <see langword="false"/>.
/// </summary>
public bool ExecuteInSearchMode { get; set; }
/// <summary>
/// Gets or sets the execution priority relative to other static statements.
/// Defaults to <see cref="Priority.Normal"/>.
/// </summary>
public Priority Priority { get; set; } = Priority.Normal;
}
@@ -7,4 +7,4 @@ namespace YesNt.Interpreter.Runtime;
/// <summary> /// <summary>
/// Pre-calculated statement handler information for faster matching. /// Pre-calculated statement handler information for faster matching.
/// </summary> /// </summary>
internal record StatementHandler(StatementAttribute Attribute, Action<string> Handler, string FullName); internal record StatementHandler(StatementAttributeContainer Attribute, Action<string> Handler, string FullName);
@@ -28,11 +28,11 @@ public class YesNtInterpreter
public event Action<string> OnDebugOutput; public event Action<string> OnDebugOutput;
private readonly RuntimeInformation runtimeInfo = new RuntimeInformation(); private readonly RuntimeInformation runtimeInfo = new RuntimeInformation();
private Dictionary<StatementAttribute, Action<string>> statements; private Dictionary<StatementAttributeContainer, Action<string>> statements;
private List<StatementHandler> statementHandlers; private List<StatementHandler> statementHandlers;
private List<List<StatementHandler>> lineMatchingHandlers = []; private List<List<StatementHandler>> lineMatchingHandlers = [];
private readonly List<KeyValuePair<StaticStatementAttribute, Action>> staticStatements; private readonly List<KeyValuePair<StaticStatementAttributeContainer, Action>> staticStatements;
private readonly Dictionary<string, List<KeyValuePair<StatementAttribute, Action<string>>>> disabledStatements = []; private readonly Dictionary<string, List<KeyValuePair<StatementAttributeContainer, Action<string>>>> disabledStatements = [];
/// <summary> /// <summary>
/// Gets a read-only snapshot of all currently registered statements. /// Gets a read-only snapshot of all currently registered statements.
@@ -88,7 +88,7 @@ public class YesNtInterpreter
} }
/// <summary> /// <summary>
/// Registers a custom statement using a pre-built <see cref="StatementAttribute"/>. /// Registers a custom statement using a pre-built <see cref="StatementAttributeContainer"/>.
/// If a statement with the same attribute key (identical field values) already exists it will be replaced; /// If a statement with the same attribute key (identical field values) already exists it will be replaced;
/// otherwise a new entry is added. Built-in statements use distinct attribute instances, so passing a /// otherwise a new entry is added. Built-in statements use distinct attribute instances, so passing a
/// newly constructed attribute with the same name will <b>add</b> a second handler rather than replacing /// newly constructed attribute with the same name will <b>add</b> a second handler rather than replacing
@@ -98,15 +98,26 @@ public class YesNtInterpreter
/// <param name="attribute">The attribute describing the keyword, search mode, and priority.</param> /// <param name="attribute">The attribute describing the keyword, search mode, and priority.</param>
/// <param name="handler"> /// <param name="handler">
/// The delegate invoked when the statement matches. Receives the argument text /// The delegate invoked when the statement matches. Receives the argument text
/// (the part of the line after the keyword, unless <see cref="StatementAttribute.KeepStatementInArgs"/> is set). /// (the part of the line after the keyword, unless <see cref="StatementAttributeContainer.KeepStatementInArgs"/> is set).
/// </param> /// </param>
public void AddStatement(StatementAttribute attribute, Action<string> handler) public void AddStatement(StatementAttributeContainer attribute, Action<string> handler)
{ {
statements[attribute] = handler; statements[attribute] = handler;
statements = statements
.OrderBy(s => s.Key.Priority) List<KeyValuePair<StatementAttributeContainer, Action<string>>> entries = [.. statements];
.ThenByDescending(s => s.Key.Name.Length) entries.Sort((a, b) =>
.ToDictionary(x => x.Key, x => x.Value); {
int cmp = a.Key.Priority.CompareTo(b.Key.Priority);
return cmp != 0 ? cmp : b.Key.Name.Length.CompareTo(a.Key.Name.Length);
});
statements = [];
foreach (KeyValuePair<StatementAttributeContainer, Action<string>> entry in entries)
{
statements.Add(entry.Key, entry.Value);
}
UpdateStatementHandlers(); UpdateStatementHandlers();
PreScanLines(); PreScanLines();
} }
@@ -120,7 +131,7 @@ public class YesNtInterpreter
/// The delegate invoked when the statement matches. Receives the argument text and the current /// The delegate invoked when the statement matches. Receives the argument text and the current
/// <see cref="IStatementContext"/> for reading/writing script state. /// <see cref="IStatementContext"/> for reading/writing script state.
/// </param> /// </param>
public void AddStatement(StatementAttribute attribute, Action<string, IStatementContext> handler) public void AddStatement(StatementAttributeContainer attribute, Action<string, IStatementContext> handler)
{ {
AddStatement(attribute, args => handler(args, runtimeInfo)); AddStatement(attribute, args => handler(args, runtimeInfo));
} }
@@ -134,7 +145,7 @@ public class YesNtInterpreter
/// <param name="handler">The delegate invoked when the statement matches.</param> /// <param name="handler">The delegate invoked when the statement matches.</param>
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, Action<string> handler) public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, Action<string> handler)
{ {
AddStatement(new StatementAttribute(name, searchMode, spaceAround), handler); AddStatement(new StatementAttributeContainer(name, searchMode, spaceAround), handler);
} }
/// <summary> /// <summary>
@@ -150,7 +161,7 @@ public class YesNtInterpreter
/// </param> /// </param>
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, Action<string, IStatementContext> handler) public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, Action<string, IStatementContext> handler)
{ {
AddStatement(new StatementAttribute(name, searchMode, spaceAround), handler); AddStatement(new StatementAttributeContainer(name, searchMode, spaceAround), handler);
} }
/// <summary> /// <summary>
@@ -163,7 +174,7 @@ public class YesNtInterpreter
/// <param name="handler">The delegate invoked when the statement matches.</param> /// <param name="handler">The delegate invoked when the statement matches.</param>
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor consoleColor, Action<string> handler) public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor consoleColor, Action<string> handler)
{ {
AddStatement(new StatementAttribute(name, searchMode, spaceAround, consoleColor), handler); AddStatement(new StatementAttributeContainer(name, searchMode, spaceAround, consoleColor), handler);
} }
/// <summary> /// <summary>
@@ -180,7 +191,7 @@ public class YesNtInterpreter
/// </param> /// </param>
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor consoleColor, Action<string, IStatementContext> handler) public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor consoleColor, Action<string, IStatementContext> handler)
{ {
AddStatement(new StatementAttribute(name, searchMode, spaceAround, consoleColor), handler); AddStatement(new StatementAttributeContainer(name, searchMode, spaceAround, consoleColor), handler);
} }
/// <summary> /// <summary>
@@ -189,7 +200,7 @@ public class YesNtInterpreter
/// <param name="name">The keyword to remove.</param> /// <param name="name">The keyword to remove.</param>
public void RemoveStatement(string name) public void RemoveStatement(string name)
{ {
foreach (StatementAttribute key in statements.Keys.Where(k => k.Name == name).ToList()) foreach (StatementAttributeContainer key in statements.Keys.Where(k => k.Name == name).ToList())
{ {
_ = statements.Remove(key); _ = statements.Remove(key);
} }
@@ -212,7 +223,7 @@ public class YesNtInterpreter
return; return;
} }
List<KeyValuePair<StatementAttribute, Action<string>>> matching = List<KeyValuePair<StatementAttributeContainer, Action<string>>> matching =
statements.Where(kv => kv.Key.Name == name).ToList(); statements.Where(kv => kv.Key.Name == name).ToList();
if (matching.Count == 0) if (matching.Count == 0)
@@ -222,7 +233,7 @@ public class YesNtInterpreter
disabledStatements[name] = matching; disabledStatements[name] = matching;
foreach (KeyValuePair<StatementAttribute, Action<string>> kv in matching) foreach (KeyValuePair<StatementAttributeContainer, Action<string>> kv in matching)
{ {
statements[kv.Key] = _ => { }; statements[kv.Key] = _ => { };
} }
@@ -238,12 +249,12 @@ public class YesNtInterpreter
/// <param name="name">The keyword of the statement(s) to re-enable.</param> /// <param name="name">The keyword of the statement(s) to re-enable.</param>
public void EnableStatement(string name) public void EnableStatement(string name)
{ {
if (!disabledStatements.TryGetValue(name, out List<KeyValuePair<StatementAttribute, Action<string>>> saved)) if (!disabledStatements.TryGetValue(name, out List<KeyValuePair<StatementAttributeContainer, Action<string>>> saved))
{ {
return; return;
} }
foreach (KeyValuePair<StatementAttribute, Action<string>> kv in saved) foreach (KeyValuePair<StatementAttributeContainer, Action<string>> kv in saved)
{ {
statements[kv.Key] = kv.Value; statements[kv.Key] = kv.Value;
} }
@@ -349,9 +360,9 @@ public class YesNtInterpreter
}; };
} }
foreach (KeyValuePair<StaticStatementAttribute, Action> staticStatement in staticStatements) foreach (KeyValuePair<StaticStatementAttributeContainer, Action> staticStatement in staticStatements)
{ {
StaticStatementAttribute staticStatementAttribute = staticStatement.Key; StaticStatementAttributeContainer staticStatementAttribute = staticStatement.Key;
if (!staticStatementAttribute.ExecuteInSearchMode && runtimeInfo.IsSearching) if (!staticStatementAttribute.ExecuteInSearchMode && runtimeInfo.IsSearching)
{ {
continue; continue;
@@ -367,7 +378,7 @@ public class YesNtInterpreter
foreach (StatementHandler handler in handlers) foreach (StatementHandler handler in handlers)
{ {
StatementAttribute statementAttribute = handler.Attribute; StatementAttributeContainer statementAttribute = handler.Attribute;
if (!statementAttribute.ExecuteInSearchMode && runtimeInfo.IsSearching) if (!statementAttribute.ExecuteInSearchMode && runtimeInfo.IsSearching)
{ {
@@ -534,7 +545,7 @@ public class YesNtInterpreter
private static bool IsPossibleMatch(string content, StatementHandler handler) private static bool IsPossibleMatch(string content, StatementHandler handler)
{ {
StatementAttribute attr = handler.Attribute; StatementAttributeContainer attr = handler.Attribute;
string fullName = handler.FullName; string fullName = handler.FullName;
return attr.SearchMode switch return attr.SearchMode switch