Files
stgsharp/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenSpecial.cs
T

112 lines
4.3 KiB
C#

// -----------------------------------------------------------------------------
// file="TextRegexSourceGen.GenSpecial"
// Project: Nitload
// Copyright (c) Nitload.
// SPDX-License-Identifier: MIT
// -----------------------------------------------------------------------------
using Nitload.RegularAnalysis.Abstraction;
namespace Nitload.RegularAnalysis.Text
{
internal partial class TextRegexSourceGen
{
private static bool TryGenerateSpecialSource(
RegexAstNode node,
in SequenceEmitter<string> sc,
in List<SequenceEmitter<string>> func_list,
in SourceGenContext context,
in ExitLabel exit_label
)
{
// A special rule owns the entire matched subtree. Returning false leaves emission
// to the general path; returning true prevents that subtree from being emitted twice.
return node.Label switch
{
RegexElementLabel.CONCAT => TryGenerateFindConcat(node, sc, func_list, context, exit_label),
_ => false,
};
}
#region FIND concatenation
private static bool TryGenerateFindConcat(
RegexAstNode node,
in SequenceEmitter<string> sc,
in List<SequenceEmitter<string>> func_list,
in SourceGenContext context,
in ExitLabel exit_label
)
{
RegexAstNode left = node.Left;
RegexAstNode right = node.Right;
if (RegexAstNode.IsNullOrEmpty(left)) {
return false;
}
// Existing FIND dispatch scaffold; candidate search and retries are not implemented.
// TODO(NGRA): Use the COUNT payload's IsGreedy flag to order FIND candidates.
if (RegexAstNode.IsDotStar(left) && !RegexAstNode.IsNullOrEmpty(right))
{
// TODO(NGRA): Emit the unnamed FIND candidate loop and retry the right subtree
// until it succeeds or no anchor candidate remains.
GenerateMethodSource(right, sc, func_list, context, exit_label, true);
return true;
}
if (IsNamedFind(node))
{
if (!RegexAstNode.IsNullOrEmpty(right))
{
// TODO(NGRA): Emit named FIND retry and capture the text preceding the
// successful anchor candidate.
GenerateMethodSource(right, sc, func_list, context, exit_label, true);
} else
{
// TODO(NGRA): Capture the remaining input for a terminal named dot-star.
}
return true;
}
return false;
}
private static bool IsNamedFind(
RegexAstNode node
)
{
// (?<name>.*)abc
if (RegexAstNode.IsNullOrEmpty(node) || node.Label != RegexElementLabel.CONCAT) {
return false;
}
RegexAstNode group = node.Left;
if (RegexAstNode.IsNullOrEmpty(group) ||
group.Payload is not RegexGroupPayload { IsCapturing: true } config ||
string.IsNullOrEmpty(config.Name)) {
return false;
}
bool left_empty = RegexAstNode.IsNullOrEmpty(group.Left);
bool right_empty = RegexAstNode.IsNullOrEmpty(group.Right);
return left_empty != right_empty &&
RegexAstNode.IsDotStar(left_empty ? group.Right : group.Left);
}
#endregion
#region Repeated capture rules
// TODO(NGRA): Recognize and implement repeated captures such as (?<name>.*){m,n}
// and (?<name>.*a){m,n}. Keep these distinct from (?<name>.*)a FIND concatenation.
// A later repetition may need to reopen an earlier repetition's inner search, restoring
// its cursor, candidate position, and capture state. Do not claim these subtrees in
// TryGenerateSpecialSource until the complete retry/rollback strategy is implemented.
// An escaped star is a literal and must not be classified as dot-star repetition.
#endregion
}
}