add single ir generator methods
This commit is contained in:
@@ -5,11 +5,11 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
using System;
|
global using System;
|
||||||
using System.Collections.Generic;
|
global using System.Collections.Generic;
|
||||||
using System.Linq;
|
global using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
global using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
global using System.Text;
|
||||||
using System.Threading.Tasks;
|
global using System.Threading.Tasks;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("StgSharpDebug")]
|
[assembly: InternalsVisibleTo("StgSharpDebug")]
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
|
|
||||||
public static TextRegexSource Analyze(
|
public static TextRegexSource Analyze(
|
||||||
string regex
|
string regex,
|
||||||
|
bool require_ast = false,
|
||||||
|
bool require_origin_ir = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
#pragma warning disable CA1031
|
#pragma warning disable CA1031
|
||||||
@@ -81,17 +83,19 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new TextRegexSource(regex, tree, null, null, info, ex);
|
return new TextRegexSource(regex, require_ast ? tree : null!, null, null, info, ex);
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ScanAndOptimizeIR(base_ir, out List<RegexIR> opt_ir);
|
ScanAndOptimizeIR(base_ir, out List<RegexIR> opt_ir);
|
||||||
List<RegexIR> prefix_ir = EmitPrefix(opt_ir, ref info);
|
List<RegexIR> prefix_ir = EmitPrefix(opt_ir, ref info);
|
||||||
return new TextRegexSource(regex, tree, base_ir, prefix_ir, info);
|
return new TextRegexSource(regex, require_ast ? tree : null!,
|
||||||
|
require_origin_ir ? base_ir : null!, prefix_ir, info);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new TextRegexSource(regex, tree, base_ir, null, info, ex);
|
return new TextRegexSource(regex, require_ast ? tree : null!,
|
||||||
|
require_origin_ir ? base_ir : null!, null!, info, ex);
|
||||||
}
|
}
|
||||||
#pragma warning restore CA1031
|
#pragma warning restore CA1031
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
* [011] 0003, Sccs, DEF , LINE, 0010, PACK SUCCESS GROUP: "name"
|
* [011] 0003, Sccs, DEF , LINE, 0010, PACK SUCCESS GROUP: "name"
|
||||||
* [012] 0000, DEF , DEF , NONE, 0000, MATCH_SEQ "234"
|
* [012] 0000, DEF , DEF , NONE, 0000, MATCH_SEQ "234"
|
||||||
*/
|
*/
|
||||||
if (ir_span[0] is not RegexCountSetIR { IsDotStar:true} ||
|
if (ir_span[0] is not RegexCountSetIR { IsDotStar: true } ||
|
||||||
ir_span[1] is not RegexPackIR pack)
|
ir_span[1] is not RegexPackIR pack)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -5,11 +5,44 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using StgSharp.RegularAnalysis.Abstraction;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace StgSharp.RegularAnalysis.Text
|
namespace StgSharp.RegularAnalysis.Text
|
||||||
{
|
{
|
||||||
internal partial class TextRegexSourceGen
|
internal partial class TextRegexSourceGen
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private static int TryGenerateCountSeq(
|
||||||
|
int outer_level,
|
||||||
|
ReadOnlySpan<RegexIR> ir_span,
|
||||||
|
SourceGenContext sc,
|
||||||
|
in SequenceEmitter<string> code,
|
||||||
|
in List<SequenceEmitter<string>> 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
|
* L4, one instruction
|
||||||
@@ -70,5 +103,98 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
* - PACK decides nothing, so it should leave __ok untouched. Whether it runs at all is
|
* - PACK decides nothing, so it should leave __ok untouched. Whether it runs at all is
|
||||||
* already expressed by its execution condition.
|
* already expressed by its execution condition.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private static int TryGenerateMatchSeq(
|
||||||
|
int outer_level,
|
||||||
|
ReadOnlySpan<RegexIR> ir_span,
|
||||||
|
SourceGenContext sc,
|
||||||
|
in SequenceEmitter<string> code,
|
||||||
|
in List<SequenceEmitter<string>> local_func
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (ir_span[0] is not RegexMatchSeqIR match_seq) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
string seq = match_seq.Pattern;
|
||||||
|
string literal = FormatStringLiteral(seq);
|
||||||
|
_ = code.AppendLine(@$"if({sc.InputSpan}[{sc.Cursor}..].StartWith(""{literal}""))")
|
||||||
|
.AppendLine("{")
|
||||||
|
.AppendLine($"{sc.StepResult} = true;")
|
||||||
|
.AppendLine($@"{sc.Cursor} += {seq.Length};")
|
||||||
|
.AppendLine("}");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int TryGenerateMatchSet(
|
||||||
|
int outer_level,
|
||||||
|
ReadOnlySpan<RegexIR> ir_span,
|
||||||
|
SourceGenContext sc,
|
||||||
|
in SequenceEmitter<string> code,
|
||||||
|
in List<SequenceEmitter<string>> local_func
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (ir_span[0] is not RegexMatchSetIR match_set) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
string set_source = match_set.Charset;
|
||||||
|
RegexCharSet[] set_arr = FigureCharSetType(set_source, out bool accept);
|
||||||
|
|
||||||
|
_ = code.AppendLine(@$"if(");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
* lives in test/SingleFile/regex.cs.
|
* lives in test/SingleFile/regex.cs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private const string current_ptr = "cur_ptr_global";
|
// private const string current_ptr = "cur_ptr_global"; // global match cursor
|
||||||
private const string offset = "offset";
|
// private const string offset = "offset"; //
|
||||||
private const string orig_text = "text";
|
// private const string orig_text = "text"; //
|
||||||
|
|
||||||
#region L0 method level
|
#region L0 method level
|
||||||
|
|
||||||
@@ -155,10 +155,11 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
.AppendLine($@"int region_{i}_end = -1;")
|
.AppendLine($@"int region_{i}_end = -1;")
|
||||||
.AppendLine($@"bool region_{i}_success;");
|
.AppendLine($@"bool region_{i}_success;");
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
_ = se.AppendLine($@"int {current_ptr} = 0;")
|
_ = se.AppendLine($@"int {current_ptr} = 0;")
|
||||||
.AppendLine($@"int {offset} = 0;")
|
.AppendLine($@"int {offset} = 0;")
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
|
/**/
|
||||||
|
|
||||||
// _ = sc.GenVarDefine(se);
|
// _ = sc.GenVarDefine(se);
|
||||||
|
|
||||||
@@ -177,6 +178,14 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private enum RegionExitMethod
|
||||||
|
{
|
||||||
|
|
||||||
|
Break,
|
||||||
|
Continue
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#region shared helpers
|
#region shared helpers
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -224,14 +233,14 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal record struct RegexCharSet(
|
internal record class RegexCharSet(
|
||||||
string Value,
|
string Value,
|
||||||
CharSetType Type,
|
CharSetType Type,
|
||||||
bool Accept
|
bool Accept
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
public override readonly string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{{value:{Value}, type:{Type}, accept:{Accept}}}";
|
return $"{{value:{Value}, type:{Type}, accept:{Accept}}}";
|
||||||
}
|
}
|
||||||
@@ -244,34 +253,45 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// range or single
|
// range or single
|
||||||
|
HashSet<char> single_char = [];
|
||||||
if (source[0] == '[')
|
if (source[0] == '[')
|
||||||
{
|
{
|
||||||
int idx;
|
int idx;
|
||||||
accept = source[1] != '^';
|
accept = source[1] != '^';
|
||||||
idx = accept ? 1 : 2;
|
idx = accept ? 1 : 2;
|
||||||
|
List<RegexCharSet> list = [];
|
||||||
ReadOnlySpan<char> seq = source.Slice(idx, source.Length - idx - 1);
|
ReadOnlySpan<char> seq = source.Slice(idx, source.Length - idx - 1);
|
||||||
if (seq.IndexOf('-') == -1)
|
if (seq.IndexOf('-') == -1)
|
||||||
{
|
{
|
||||||
return [new RegexCharSet(seq.ToString(), CharSetType.Single, true)];
|
SplitSet(single_char, seq, list);
|
||||||
|
StringBuilder sb = new();
|
||||||
|
foreach (char c in single_char) {
|
||||||
|
_ = sb.Append(c);
|
||||||
|
}
|
||||||
|
list.Add(new(sb.ToString(), CharSetType.Single, true));
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
List<RegexCharSet> list = [];
|
|
||||||
for (; seq.Length > 0; )
|
for (; seq.Length > 0; )
|
||||||
{
|
{
|
||||||
int pos = seq.IndexOf('-');
|
int pos = seq.IndexOf('-');
|
||||||
if (pos == -1)
|
if (pos == -1)
|
||||||
{
|
{
|
||||||
// has no range
|
// has no range
|
||||||
SplitSet(seq, list);
|
SplitSet(single_char, seq, list);
|
||||||
break;
|
break;
|
||||||
} else if (pos > 1)
|
} else if (pos > 1)
|
||||||
{
|
{
|
||||||
// has other sequence before range
|
// has other sequence before range
|
||||||
SplitSet(seq[0..(pos - 1)], list);
|
SplitSet(single_char, seq[0..(pos - 1)], list);
|
||||||
}
|
}
|
||||||
list.Add(new($"{seq[pos - 1]}{seq[pos + 1]}", CharSetType.Range, true));
|
list.Add(new($"{seq[pos - 1]}{seq[pos + 1]}", CharSetType.Range, true));
|
||||||
seq = seq[(pos + 2)..];
|
seq = seq[(pos + 2)..];
|
||||||
}
|
}
|
||||||
|
StringBuilder sb = new();
|
||||||
|
foreach (char c in single_char) {
|
||||||
|
_ = sb.Append(c);
|
||||||
|
}
|
||||||
|
list.Add(new(sb.ToString(), CharSetType.Single, true));
|
||||||
return [.. list];
|
return [.. list];
|
||||||
}
|
}
|
||||||
} else if (source[0] == '\\')
|
} else if (source[0] == '\\')
|
||||||
@@ -293,6 +313,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void SplitSet(
|
static void SplitSet(
|
||||||
|
HashSet<char> single_char,
|
||||||
ReadOnlySpan<char> seq,
|
ReadOnlySpan<char> seq,
|
||||||
List<RegexCharSet> sets
|
List<RegexCharSet> sets
|
||||||
)
|
)
|
||||||
@@ -310,19 +331,31 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
sets.Add(new RegexCharSet(singles.ToString(), CharSetType.Single, true));
|
sets.Add(new RegexCharSet(singles.ToString(), CharSetType.Single, true));
|
||||||
}
|
}
|
||||||
bool accept = char.IsAsciiLetterUpper(remain[pos + 1]);
|
bool accept = char.IsAsciiLetterUpper(remain[pos + 1]);
|
||||||
RegexCharSet set_set = char.ToLower(remain[pos + 1], CultureInfo.InvariantCulture)
|
|
||||||
switch
|
switch (char.ToLower(remain[pos + 1], CultureInfo.InvariantCulture))
|
||||||
{
|
{
|
||||||
's' => new("s", CharSetType.Set, accept),
|
case 's':
|
||||||
'w' => new("w", CharSetType.Set, accept),
|
sets.Add(new("s", CharSetType.Set, accept));
|
||||||
'd' => new("d", CharSetType.Set, accept),
|
break;
|
||||||
_ => throw new NotSupportedException()
|
case 'w':
|
||||||
};
|
sets.Add(new("w", CharSetType.Set, accept));
|
||||||
sets.Add(set_set);
|
break;
|
||||||
|
case 'd':
|
||||||
|
sets.Add(new("d", CharSetType.Set, accept));
|
||||||
|
break;
|
||||||
|
case '['or'('or')'or']'or'\\'or'\"':
|
||||||
|
_ = single_char.Add(remain[pos + 1]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidCastException($"Unknown char or charset {remain[0..1]}");
|
||||||
|
}
|
||||||
remain = remain[(pos + 2)..];
|
remain = remain[(pos + 2)..];
|
||||||
}
|
}
|
||||||
if (remain.Length > 0) {
|
if (remain.Length > 0)
|
||||||
sets.Add(new RegexCharSet(remain.ToString(), CharSetType.Single, true));
|
{
|
||||||
|
foreach (char c in remain) {
|
||||||
|
_ = single_char.Add(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
internal partial class TextRegexSourceGen
|
internal partial class TextRegexSourceGen
|
||||||
{
|
{
|
||||||
|
|
||||||
#region var naming
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tracks variable naming and declaration demand while generating one match method.
|
/// Tracks variable naming and declaration demand while generating one match method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -34,35 +32,79 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Active pointer. Indexes the next unconsumed element of the input.
|
/// Active pointer. Indexes the next unconsumed element of the input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Cursor = "__cur";
|
public const string _cursor = "__cur";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the match method parameter holding the input sequence.
|
/// Name of the match method parameter holding the input sequence.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string InputSpan = "text";
|
public const string _inputSpan = "text";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Result of the instruction emitted immediately before the current one.
|
/// Result of the instruction emitted immediately before the current one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string StepResult = "__ok";
|
public const string _stepResult = "__ok";
|
||||||
|
|
||||||
private readonly Dictionary<string, int> _groupIndex = [];
|
private readonly Dictionary<string, int> _groupIndex = [];
|
||||||
private readonly SortedSet<int> _lineSlots = [];
|
|
||||||
private readonly HashSet<int> _observedLines = [];
|
private readonly HashSet<int> _observedLines = [];
|
||||||
private readonly SortedSet<int> _regionSlots = [];
|
|
||||||
|
|
||||||
private int _anonymousGroup;
|
private int _anonymousGroup;
|
||||||
private int _scratchIndex;
|
private int _scratchIndex;
|
||||||
|
private readonly SortedSet<int> _lineSlots = [];
|
||||||
|
private readonly SortedSet<int> _regionSlots = [];
|
||||||
|
|
||||||
|
public string Cursor { get; } = _cursor;
|
||||||
|
|
||||||
|
public string InputSpan { get; } = _inputSpan;
|
||||||
|
|
||||||
|
public string StepResult { get; } = _stepResult;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Records that some prefix observes the result of the instruction at line
|
/// Emits declarations for every slot requested during body generation.
|
||||||
/// <paramref name="line" />.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void MarkLineObserved(
|
/// <remarks>
|
||||||
int line
|
/// Must be called after the method body has been generated, but the produced lines
|
||||||
|
/// have to be placed before it.
|
||||||
|
/// </remarks>
|
||||||
|
public SequenceEmitter<string> EmitDeclarationsTo(
|
||||||
|
SequenceEmitter<string> emitter
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_ = _observedLines.Add(line);
|
if (emitter is null) {
|
||||||
|
return null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = emitter.AppendLine($@"int {_cursor} = 0;")
|
||||||
|
.AppendLine($@"bool {_stepResult} = true;");
|
||||||
|
|
||||||
|
foreach (int region in _regionSlots) {
|
||||||
|
_ = emitter.AppendLine($@"int {RegionAnchor(region)} = 0;")
|
||||||
|
.AppendLine($@"bool {RegionResult(region)} = false;");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (int line in _lineSlots) {
|
||||||
|
_ = emitter.AppendLine($@"bool {LineResult(line)} = false;");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (int group in _groupIndex.Values) {
|
||||||
|
_ = emitter.AppendLine($@"int __g{group}_begin = -1;")
|
||||||
|
.AppendLine($@"int __g{group}_end = -1;");
|
||||||
|
}
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GroupBegin(
|
||||||
|
string name
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return $"__g{ResolveGroup(name)}_begin";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GroupEnd(
|
||||||
|
string name
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return $"__g{ResolveGroup(name)}_end";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -76,6 +118,30 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
return _observedLines.Contains(line);
|
return _observedLines.Contains(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the slot holding the match result of the instruction at line <paramref
|
||||||
|
/// name="line" />. Only lines actually referenced by an <see
|
||||||
|
/// cref="RegexIRPrefixMask.Observe_Line" /> prefix get a slot.
|
||||||
|
/// </summary>
|
||||||
|
public string LineResult(
|
||||||
|
int line
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_ = _lineSlots.Add(line);
|
||||||
|
return $"__l{line}_ok";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Records that some prefix observes the result of the instruction at line <paramref
|
||||||
|
/// name="line" />.
|
||||||
|
/// </summary>
|
||||||
|
public void MarkLineObserved(
|
||||||
|
int line
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_ = _observedLines.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allocates a fresh block scoped scratch variable name.
|
/// Allocates a fresh block scoped scratch variable name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -91,8 +157,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the slot holding the cursor value captured when region
|
/// Name of the slot holding the cursor value captured when region <paramref
|
||||||
/// <paramref name="region" /> was entered.
|
/// name="region" /> was entered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string RegionAnchor(
|
public string RegionAnchor(
|
||||||
int region
|
int region
|
||||||
@@ -113,77 +179,11 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
return $"__r{region}_ok";
|
return $"__r{region}_ok";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the slot holding the match result of the instruction at line
|
|
||||||
/// <paramref name="line" />. Only lines actually referenced by an
|
|
||||||
/// <see cref="RegexIRPrefixMask.Observe_Line" /> prefix get a slot.
|
|
||||||
/// </summary>
|
|
||||||
public string LineResult(
|
|
||||||
int line
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_ = _lineSlots.Add(line);
|
|
||||||
return $"__l{line}_ok";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GroupBegin(
|
|
||||||
string name
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return $"__g{ResolveGroup(name)}_begin";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GroupEnd(
|
|
||||||
string name
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return $"__g{ResolveGroup(name)}_end";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Emits declarations for every slot requested during body generation.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Must be called after the method body has been generated, but the produced lines
|
|
||||||
/// have to be placed before it.
|
|
||||||
/// </remarks>
|
|
||||||
public SequenceEmitter<string> EmitDeclarations(
|
|
||||||
SequenceEmitter<string> emitter
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (emitter is null) {
|
|
||||||
return null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = emitter.AppendLine($@"int {Cursor} = 0;")
|
|
||||||
.AppendLine($@"bool {StepResult} = true;");
|
|
||||||
|
|
||||||
foreach (int region in _regionSlots)
|
|
||||||
{
|
|
||||||
_ = emitter.AppendLine($@"int {RegionAnchor(region)} = 0;")
|
|
||||||
.AppendLine($@"bool {RegionResult(region)} = false;");
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int line in _lineSlots) {
|
|
||||||
_ = emitter.AppendLine($@"bool {LineResult(line)} = false;");
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int group in _groupIndex.Values)
|
|
||||||
{
|
|
||||||
_ = emitter.AppendLine($@"int __g{group}_begin = -1;")
|
|
||||||
.AppendLine($@"int __g{group}_end = -1;");
|
|
||||||
}
|
|
||||||
|
|
||||||
return emitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int ResolveGroup(
|
private int ResolveGroup(
|
||||||
string name
|
string name
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
string key = string.IsNullOrEmpty(name) ?
|
string key = string.IsNullOrEmpty(name) ? $"#{_anonymousGroup++}" : name;
|
||||||
$"#{_anonymousGroup++}" :
|
|
||||||
name;
|
|
||||||
if (_groupIndex.TryGetValue(key, out int index)) {
|
if (_groupIndex.TryGetValue(key, out int index)) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
@@ -194,6 +194,5 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace StgSharp
|
|||||||
{
|
{
|
||||||
foreach (string regex in regexSource)
|
foreach (string regex in regexSource)
|
||||||
{
|
{
|
||||||
TextRegexSource interpreterSource = RegexAnalyzer.Analyze(regex);
|
TextRegexSource interpreterSource = RegexAnalyzer.Analyze(regex, true, true);
|
||||||
AbstractSyntaxTree<RegexAstNode, RegexElementLabel> tree = interpreterSource.Ast;
|
AbstractSyntaxTree<RegexAstNode, RegexElementLabel> tree = interpreterSource.Ast;
|
||||||
Console.WriteLine($"result of \"{regex}\"");
|
Console.WriteLine($"result of \"{regex}\"");
|
||||||
TreeEnumerator<RegexAstNode, RegexElementLabel> enumerator = new(tree);
|
TreeEnumerator<RegexAstNode, RegexElementLabel> enumerator = new(tree);
|
||||||
|
|||||||
Reference in New Issue
Block a user