diff --git a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.SingleIR.cs b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.SingleIR.cs index c15af0d2..25fb9c57 100644 --- a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.SingleIR.cs +++ b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.SingleIR.cs @@ -13,36 +13,6 @@ namespace StgSharp.RegularAnalysis.Text { internal partial class TextRegexSourceGen { - - private static int TryGenerateCountSeq( - int outer_level, - ReadOnlySpan ir_span, - SourceGenContext sc, - in SequenceEmitter code, - in List> local_func - ) - { - if (ir_span[0] is not RegexCountSeqIR count_seq) { - return -1; - } - string seq = count_seq.Pattern; - string literal = FormatStringLiteral(seq); - bool is_greedy = count_seq.IsGreedy; - int min = count_seq.Min; - int max = count_seq.Max; - int leave = is_greedy ? min : max; - - // TODO cannot check real count outside the cycle - _ = code.AppendLine(@$"for(int count = 0; count<{leave}; count++)") - .AppendLine("{") - .AppendLine(@$"if({sc.InputSpan}[{sc.Cursor}..].StartWith(""{literal}""))") - .AppendLine("{") - .AppendLine(@$"{sc.Cursor}+={seq.Length};") - .AppendLine("}") - .AppendLine("}"); - return 1; - } - /* * ======================================================================================= * L4, one instruction @@ -104,6 +74,75 @@ namespace StgSharp.RegularAnalysis.Text * already expressed by its execution condition. */ + #region code + + private static int TryGenerateCountSet( + int outer_level, + ReadOnlySpan ir_span, + SourceGenContext sc, + in SequenceEmitter code, + in List> local_func + ) + { + if (ir_span[0] is not RegexCountSetIR count_set) { + return -1; + } + string set_source = count_set.CharSet; + RegexCharSet[] set_arr = FigureCharSetType(set_source, out bool accept); + + _ = code.AppendLine(accept ? @$"if(" : @$"if(!("); + string _case_code; + RegexCharSet set; + for (int i = 1; i < set_arr.Length - 1; i++) + { + set = set_arr[i]; + _case_code = GenLine(set, sc, "||"); + _ = code.AppendLine(_case_code); + } + set = set_arr[^1]; + _case_code = GenLine(set, sc, ")"); + _ = code.AppendLine(_case_code) + .AppendLine("{") + .AppendLine("-------------------------------------")// TODO case success + .AppendLine("}"); + return 1; + } + + private static int TryGenerateCountSeq( + int outer_level, + ReadOnlySpan ir_span, + SourceGenContext sc, + in SequenceEmitter code, + in List> local_func + ) + { + if (ir_span[0] is not RegexCountSeqIR count_seq) { + return -1; + } + string seq = count_seq.Pattern; + string literal = FormatStringLiteral(seq); + bool is_greedy = count_seq.IsGreedy; + int min = count_seq.Min; + int max = count_seq.Max; + int leave = is_greedy ? max : min; + string counter = sc.RequireNewCounter(); + + // TODO cannot check real count outside the cycle + _ = code.AppendLine($@"int {counter} = 0;") + .AppendLine(@$"for(; {counter}<{leave}; {counter}++)") + .AppendLine("{") + .AppendLine(@$"if({sc.InputSpan}[{sc.Position}..].StartWith({literal}))") + .AppendLine("{") + .AppendLine(@$"{sc.Position}+={seq.Length};") + .AppendLine("}") + .AppendLine("}") + .AppendLine() + .AppendLine(@$"if({counter}>{min})") // TODO match here + + .AppendLine($@"else"); // TODO fail here + return 1; + } + private static int TryGenerateMatchSeq( int outer_level, ReadOnlySpan ir_span, @@ -117,10 +156,10 @@ namespace StgSharp.RegularAnalysis.Text } string seq = match_seq.Pattern; string literal = FormatStringLiteral(seq); - _ = code.AppendLine(@$"if({sc.InputSpan}[{sc.Cursor}..].StartWith(""{literal}""))") + _ = code.AppendLine(@$"if({sc.InputSpan}[{sc.Position}..].StartWith({literal}))") .AppendLine("{") .AppendLine($"{sc.StepResult} = true;") - .AppendLine($@"{sc.Cursor} += {seq.Length};") + .AppendLine($@"{sc.Position} += {seq.Length};") .AppendLine("}"); return 1; } @@ -139,62 +178,76 @@ namespace StgSharp.RegularAnalysis.Text string set_source = match_set.Charset; RegexCharSet[] set_arr = FigureCharSetType(set_source, out bool accept); - _ = code.AppendLine(@$"if("); + _ = code.AppendLine(accept ? @$"if(" : @$"if(!("); + string _case_code; + RegexCharSet set; for (int i = 1; i < set_arr.Length - 1; i++) { - RegexCharSet set = set_arr[i]; - switch (set.Type) - { - case CharSetType.Single: - string cases = set.Value; - if (cases.Length > 8) - { - // more than 8 different chars, using string match - string literal = FormatStringLiteral(cases); - _ = code.AppendLine($@"{literal}.Contains({sc.InputSpan}[{sc.Cursor}])"); - } else - { - StringBuilder sb = new(); - _ = sb.Append('('); - for (int j = 0; j < cases.Length - 1; j++) { - _ = sb.Append(CultureInfo.InvariantCulture, $@"{sc.InputSpan}[{sc.Cursor}] == {cases[j]} ||"); - } - _ = sb.Append(CultureInfo.InvariantCulture, $@"{sc.InputSpan}[{sc.Cursor}] == {cases[^1]}") - .Append(")||"); - _ = code.AppendLine(sb.ToString()); - } - break; - case CharSetType.Range: - string rule = set.Value; - char lo = rule[0]; - char hi = rule[1]; - _ = code.AppendLine($@"({sc.InputSpan}[{sc.Cursor}] >= '{lo}' && {sc.InputSpan}[{sc.Cursor}] <= '{hi}') ||"); - break; - case CharSetType.Set: - string accept_mark = set.Accept ? string.Empty : "!"; - switch (set.Value) - { - case "s": - _ = code.AppendLine($@"{accept_mark}char.IsWhiteSpace({sc.InputSpan}[{sc.Cursor}]) ||"); - break; - case "d": - _ = code.AppendLine($@"{accept_mark}char.IsDigit({sc.InputSpan}[{sc.Cursor}]) ||"); - break; - case "w": - _ = code .AppendLine($@"{accept_mark}IsCharWord({sc.InputSpan}[{sc.Cursor}]) ||"); - break; - default: - break; - } - break; - case CharSetType.None: - break; - default: - break; - } + set = set_arr[i]; + _case_code = GenLine(set, sc, "||"); + _ = code.AppendLine(_case_code); } + set = set_arr[^1]; + _case_code = GenLine(set, sc, ")"); + _ = code.AppendLine(_case_code) + .AppendLine("{") + .AppendLine("-------------------------------------")// TODO case success + .AppendLine("}"); return 1; } + private static string GenLine( + RegexCharSet set, + SourceGenContext sc, + string end_of_line + ) + { + switch (set.Type) + { + case CharSetType.Single: + string cases = set.Value; + if (cases.Length > 8) + { + // more than 8 different chars, using string match + string literal = FormatStringLiteral(cases); + return $@"{literal}.Contains({sc.InputSpan}[{sc.Position}]) {end_of_line}"; + } else + { + StringBuilder sb = new(); + string literal; + _ = sb.Append('('); + for (int j = 0; j < cases.Length - 1; j++) + { + literal = FormatCharLiteral(cases[j]); + _ = sb.Append(CultureInfo.InvariantCulture, $@"{sc.InputSpan}[{sc.Position}] == {literal} ||"); + } + literal = FormatCharLiteral(cases[^1]); + _ = sb.Append(CultureInfo.InvariantCulture, $@"{sc.InputSpan}[{sc.Position}] == {literal}") + .Append(CultureInfo.InvariantCulture, @$") {end_of_line}"); + return sb.ToString(); + } + case CharSetType.Range: + string rule = set.Value; + char lo = rule[0]; + char hi = rule[1]; + return $@"({sc.InputSpan}[{sc.Position}] >= '{lo}' && {sc.InputSpan}[{sc.Position}] <= '{hi}') {end_of_line}"; + + case CharSetType.Set: + string accept_mark = set.Accept ? string.Empty : "!"; + return set.Value switch + { + "s" => $@"{accept_mark}char.IsWhiteSpace({sc.InputSpan}[{sc.Position}]) {end_of_line}", + "d" => $@"{accept_mark}char.IsDigit({sc.InputSpan}[{sc.Position}]) {end_of_line}", + "w" => $@"{accept_mark}IsCharWord({sc.InputSpan}[{sc.Position}]) {end_of_line}", + _ => string.Empty + }; + case CharSetType.None: + return string.Empty; + default: + return string.Empty; + } + } + + #endregion } } diff --git a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs index dc604f8b..5cf3f20f 100644 --- a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs +++ b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs @@ -48,11 +48,13 @@ namespace StgSharp.RegularAnalysis.Text private readonly HashSet _observedLines = []; private int _anonymousGroup; + + private int _count_idx = 0; private int _scratchIndex; private readonly SortedSet _lineSlots = []; private readonly SortedSet _regionSlots = []; - public string Cursor { get; } = _cursor; + public string Position { get; } = _cursor; public string InputSpan { get; } = _inputSpan; @@ -179,6 +181,13 @@ namespace StgSharp.RegularAnalysis.Text return $"__r{region}_ok"; } + public string RequireNewCounter() + { + string counter_name = $"__count{_count_idx}"; + _count_idx++; + return counter_name; + } + private int ResolveGroup( string name )