regex ir emitter is working

This commit is contained in:
Youmu Liu
2026-06-18 19:10:30 +08:00
parent 5570c6f338
commit eeca5721fb
5 changed files with 164 additions and 88 deletions
+64 -44
View File
@@ -40,13 +40,13 @@ namespace StgSharp.RegularAnalysis.TextRegex
Search_Seq = 0,
Search_Set = 1,
SearchChar = 2,
Jump = 3,
ConfigFail = 3,
Condition = 4,
Try = 5,
Pop = 6,
Count = 7,
Count_Seq = 8,
Count_Set = 9
Count = 6,
Count_Seq = 7,
Count_Set = 8,
Pop = 10
}
@@ -55,30 +55,16 @@ namespace StgSharp.RegularAnalysis.TextRegex
internal abstract RegexIRCommand Command { get; }
internal abstract void Execute();
}
internal sealed class RegexPopIR(
bool isSuccess
) : RegexIR
{
public bool IsSuccess { get; } = isSuccess;
internal override RegexIRCommand Command => RegexIRCommand.Pop;
internal override void Execute()
{
throw new NotImplementedException();
}
internal abstract string AsCommand(
int lineNum
);
}
/// <summary>
/// Searches for a certain string pattern or charset from current position.
/// </summary>
internal sealed class RegexSearchSeqIR(
internal sealed class RegexMatchSeqIR(
string pattern
) : RegexIR
{
@@ -87,9 +73,11 @@ namespace StgSharp.RegularAnalysis.TextRegex
internal override RegexIRCommand Command => RegexIRCommand.Search_Seq;
internal override void Execute()
internal override string AsCommand(
int lineNum
)
{
throw new NotImplementedException();
return $"[{lineNum}]\t SEARCH_SEQ \"{(string.IsNullOrEmpty(Pattern) ? "<EPSILON>" : Pattern)}\"";
}
}
@@ -103,31 +91,35 @@ namespace StgSharp.RegularAnalysis.TextRegex
internal override RegexIRCommand Command => RegexIRCommand.Search_Set;
internal override void Execute()
internal override string AsCommand(
int lineNum
)
{
throw new NotImplementedException();
return $"[{lineNum}]\t SEARCH_SET \"{Charset}\"";
}
}
/// <summary>
/// JUMP to a certain instruction index.
/// Configures the failure jump of current position. The failMod ranges 0-2
/// </summary>
/// <param name="target">
///
/// </param>
internal sealed class RegexJumpIR(
int target
internal sealed class RegexConfigFailIR(
int failMod
) : RegexIR
{
public int TargetIndex { get; } = target;
public int FailMod { get; } = failMod & 0x3;
internal override RegexIRCommand Command => RegexIRCommand.Jump;
internal override RegexIRCommand Command => RegexIRCommand.ConfigFail;
internal override void Execute()
internal override string AsCommand(
int lineNum
)
{
throw new NotImplementedException();
return $"[{lineNum}]\t CONFIG_FAIL {FailMod}";
}
}
@@ -144,9 +136,11 @@ namespace StgSharp.RegularAnalysis.TextRegex
internal override RegexIRCommand Command => RegexIRCommand.Condition;
internal override void Execute()
internal override string AsCommand(
int lineNum
)
{
throw new NotImplementedException();
return $"[{lineNum}]\t CONDITION SUCCESS: {SuccessCase} FAIL: {FailCase}";
}
}
@@ -169,9 +163,11 @@ namespace StgSharp.RegularAnalysis.TextRegex
internal override RegexIRCommand Command { get; } = RegexIRCommand.Count;
internal override void Execute()
internal override string AsCommand(
int lineNum
)
{
throw new NotImplementedException();
return $"[{lineNum}]\t COUNT {(IsGreedy ? "GREEDY" : "NON_GREEDY")} MIN: {Min} MAX: {Max} OPERATION: {OperationIndex}";
}
}
@@ -194,9 +190,11 @@ namespace StgSharp.RegularAnalysis.TextRegex
internal override RegexIRCommand Command { get; } = RegexIRCommand.Count_Seq;
internal override void Execute()
internal override string AsCommand(
int lineNum
)
{
throw new NotImplementedException();
return $"[{lineNum}]\t COUNT_SEQ {(IsGreedy ? "GREEDY" : "NON_GREEDY")} MIN: {Min} MAX: {Max} SEQ: \"{Sequence}\"";
}
}
@@ -219,9 +217,11 @@ namespace StgSharp.RegularAnalysis.TextRegex
internal override RegexIRCommand Command => RegexIRCommand.Count_Set;
internal override void Execute()
internal override string AsCommand(
int lineNum
)
{
throw new NotImplementedException();
return $"[{lineNum}]\t COUNT_SET {(IsGreedy ? "GREEDY" : "NON_GREEDY")} MIN: {Min} MAX: {Max} SET: \"{CharSet}\"";
}
}
@@ -235,9 +235,29 @@ namespace StgSharp.RegularAnalysis.TextRegex
internal override RegexIRCommand Command => RegexIRCommand.Try;
internal override void Execute()
internal override string AsCommand(
int lineNum
)
{
throw new NotImplementedException();
return $"[{lineNum}]\t TRY {CountOfCommand}";
}
}
internal sealed class RegexPopIR(
bool isSuccess
) : RegexIR
{
public bool IsSuccess { get; } = isSuccess;
internal override RegexIRCommand Command => RegexIRCommand.Try;
internal override string AsCommand(
int lineNum
)
{
return $"[{lineNum}]\t POP {(IsSuccess ? "SUCCESS" : "FAIL")}";
}
}
@@ -56,6 +56,14 @@ namespace StgSharp.RegularAnalysis.TextRegex
Count++;
}
public void EmitConfigFail(
int fail
)
{
_tail.Add(new RegexConfigFailIR(fail));
Count++;
}
public void EmitCount(
int min,
int max,
@@ -101,14 +109,6 @@ namespace StgSharp.RegularAnalysis.TextRegex
Count += stream.Count;
}
public void EmitMatchSet(
string unit
)
{
_tail.Add(new RegexMatchSetIR(unit));
Count++;
}
public void EmitMatchSeq(
string unit
)
@@ -117,7 +117,15 @@ namespace StgSharp.RegularAnalysis.TextRegex
Count++;
}
public void EmitPopLevel(
public void EmitMatchSet(
string unit
)
{
_tail.Add(new RegexMatchSetIR(unit));
Count++;
}
public void EmitPop(
bool isSuccess
)
{
@@ -43,23 +43,21 @@ namespace StgSharp.RegularAnalysis.TextRegex
public partial class RegexInterpreter
{
private List<RegexIR> EmptyIR { get; } = [];
internal List<RegexIR> GenerateIR(
AbstractSyntaxTree<RegexAstNode, RegexElementLabel> tree
internal static List<RegexIR> GenerateIR(
AbstractSyntaxTree<RegexAstNode, RegexElementLabel> tree
)
{
return GenerateIRRecursion(tree.Root, 0).ToList();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EmitCount(
int curDepth,
RegexIRGenerator ir,
int min,
int max,
bool isGreedy,
RegexAstNode right
private static void EmitCount(
int curDepth,
RegexIRGenerator ir,
int min,
int max,
bool isGreedy,
RegexAstNode right
)
{
bool isRightOperator = !RegexAstNode.IsNullOrEmpty(right) &&
@@ -83,7 +81,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
ir.EmitCountSet(min, max, isGreedy, right.Source.Value);
break;
case RegexElementLabel.UNIT_SPAN:
ir.EmitCountSet(min, max, isGreedy, right.Source.Value);
ir.EmitCountSeq(min, max, isGreedy, right.Source.Value);
break;
default:
throw new InvalidOperationException($"Unexpected regex element type for count operator: {count_source_mask}");
@@ -91,9 +89,9 @@ namespace StgSharp.RegularAnalysis.TextRegex
}
}
private RegexIRGenerator GenerateIRRecursion(
RegexAstNode node,
int depth
private static RegexIRGenerator GenerateIRRecursion(
RegexAstNode node,
int depth
)
{
#pragma warning disable IDE0010
@@ -107,11 +105,6 @@ namespace StgSharp.RegularAnalysis.TextRegex
RegexIRGenerator leftIR;
RegexIRGenerator rightIR;
bool isLeftOperator = !RegexAstNode.IsNullOrEmpty(left) &&
(left.Source.Flag & RegexElementLabel.VAST_OPERATOR) != 0;
bool isRightOperator = !RegexAstNode.IsNullOrEmpty(right) &&
(right.Source.Flag & RegexElementLabel.VAST_OPERATOR) != 0;
Token<RegexElementLabel> token = node.Source;
switch (node.Source.Flag)
@@ -131,26 +124,41 @@ namespace StgSharp.RegularAnalysis.TextRegex
groupIR.EmitTry(rightIR.Count);
groupIR.EmitIRStream(rightIR);
groupIR.EmitCondition(2, 1);
groupIR.EmitPopLevel(false);
// a grouping op needed here, before popping
groupIR.EmitPopLevel(true);
/*generate pack ir here*/
return groupIR;
case RegexElementLabel.COUNT:
string count_source = token.Value;
bool isGreedy = !(count_source.Length > 1 && !count_source.EndsWith('?'));
bool isGreedy = !(count_source.Length > 1 && count_source.EndsWith('?'));
int seq_mask = (RegexAstNode.IsNullOrEmpty(left) ? 0 : 2) + (RegexAstNode.IsNullOrEmpty(right) ?
0 :
1);
RegexAstNode seq = seq_mask switch
{
// no seq, invalid count operator
0 => throw new InvalidOperationException("Count operator must have a sequence to operate on"),
// right seq
1 => right,
// left seq
2 => left,
// both sides, invalid count operator
3 => throw new InvalidOperationException("Count operator cannot have two sequences to operate on"),
_ => throw new InvalidOperationException("Unexpected sequence mask for count operator"),
};
ReadOnlySpan<char> count_source_span = count_source;
RegexIRGenerator countIR = new();
if (count_source_span[0] == '*') // infinite count
{
EmitCount(curDepth, countIR, 0, int.MaxValue, isGreedy, right);
EmitCount(curDepth, countIR, 0, int.MaxValue, isGreedy, seq);
} else if (count_source_span[0] == '+') // at least one
{
EmitCount(curDepth, countIR, 1, int.MaxValue, isGreedy, right);
EmitCount(curDepth, countIR, 1, int.MaxValue, isGreedy, seq);
} else if (count_source_span[0] == '?') // zero or one
{
EmitCount(curDepth, countIR, 0, 1, isGreedy, right);
EmitCount(curDepth, countIR, 0, 1, isGreedy, seq);
} else if (count_source_span[0] == '{') // zero or one, non-greedy
{
int comma_pos = count_source_span.IndexOf(',');
@@ -158,7 +166,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
{
if (int.TryParse(count_source[1..(isGreedy ? ^3 : ^2)], out int count))
{
EmitCount(curDepth, countIR, count, count, isGreedy, right);
EmitCount(curDepth, countIR, count, count, isGreedy, seq);
} else
{
throw new InvalidCastException($"Invalid count format: {count_source}");
@@ -168,7 +176,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
if (int.TryParse(count_source[1..comma_pos], out int min) &&
int.TryParse(count_source[(comma_pos + 1)..(isGreedy ? ^3 : ^2)], out int max))
{
EmitCount(curDepth, countIR, min, max, isGreedy, right);
EmitCount(curDepth, countIR, min, max, isGreedy, seq);
} else
{
throw new InvalidCastException($"Invalid count format: {count_source}");
@@ -192,7 +200,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
switch (left.Source.Flag)
{
case RegexElementLabel.UNIT_SPAN:
goto case RegexElementLabel.UNIT_SPAN;
goto case RegexElementLabel.UNIT;
case RegexElementLabel.UNIT:
concat.EmitMatchSeq(left.Source.Value);
break;
@@ -234,7 +242,40 @@ namespace StgSharp.RegularAnalysis.TextRegex
}
return concat;
case RegexElementLabel.ALT:
return null!;
RegexIRGenerator alt = new();
List<RegexIRGenerator> casesIRList = [];
RegexAstNode cur_node = right;
int ir_count = 0;
while (!RegexAstNode.IsNullOrEmpty(cur_node))
{
RegexIRGenerator _case = GenerateIRRecursion(cur_node, curDepth + 1);
casesIRList.Add(_case);
ir_count += _case.Count;
cur_node = cur_node.Next;
}
int case_count = casesIRList.Count;
while (case_count != 0)
{
RegexIRGenerator cur_ir = casesIRList[^case_count];
ir_count -= cur_ir.Count;
case_count--;
alt.EmitTry(cur_ir.Count);
alt.EmitIRStream(cur_ir);
alt.EmitCondition(1/* need to calc here */, 2);
alt.EmitPop(true);
}
alt.EmitPop(false);
return alt;
case RegexElementLabel.UNIT_SPAN:
goto case RegexElementLabel.UNIT;
case RegexElementLabel.UNIT:
RegexIRGenerator unit_seq = new();
unit_seq.EmitMatchSeq(node.Source.Value);
return unit_seq;
case RegexElementLabel.UNIT_SET:
RegexIRGenerator unit_set = new();
unit_set.EmitMatchSet(node.Source.Value);
return unit_set;
default:
return null!;
}