我在 C# 中使用 Superpower 解析器库。我想创建解析器来解析类似于以下正则表达式的字符串:
(?'prefix'.*)(some\skeyword)
例如,从字符串“This is some prefix of some keywords”中,我想获取“This is some prefix of”作为前缀。
我的尝试如下:
public static readonly TokenListParser<StoryToken, string> Text =
Token.EqualTo(StoryToken.Text)
.Select(x => x.Span.ToStringValue());
public static readonly TokenListParser<StoryToken, string> Keyword =
from k1 in Token.EqualToValueIgnoreCase(StoryToken.Text, "some")
from k2 in Token.EqualToValueIgnoreCase(StoryToken.Text, "keyword")
select "some keyword";
public static readonly TokenListParser<StoryToken, string> TextWithKeyword =
from prefix in Text.Many()
from keyword in Keyword
select string.Join(" ", prefix) + " " + keyword;
但这行不通。问题在于该行:
from prefix in Text.Many()
消耗输入中的所有标记,包括“some”和“keyword”,解析器失败并显示错误“预期为“some”,但遇到输入结尾”。
我尝试在该行中的 .Many() 之前添加 .Try(),并尝试将 .AtEnd() 添加到关键字 - 但这不会改变任何内容。
我有什么想法可以通过 Superpower 解析器实现我的目标吗?
到目前为止,我发现解决这个问题的唯一方法是为前缀中固定数量的单词生成解析器,从 0(无前缀)开始到某个 MAX_WORDS 计数。代码如下所示:
public class TestParsers
{
public static readonly TokenListParser<StoryToken, string> Text =
Token.EqualTo(StoryToken.Text)
.Select(x => x.Span.ToStringValue());
public static readonly TokenListParser<StoryToken, string> Keyword =
from k1 in Token.EqualToValueIgnoreCase(StoryToken.Text, "some")
from k2 in Token.EqualToValueIgnoreCase(StoryToken.Text, "keyword")
select "some keyword";
public static TokenListParser<StoryToken, (string Prefix, string Keyword)> BuildParser(int wordCount)
{
return
from prefix in Text.Repeat(wordCount)
from keyword in Keyword
select (string.Join(" ", prefix), keyword);
}
public static TokenListParser<StoryToken, (string Prefix, string Keyword)> BuildTextWithKeyword(int maxWordCount)
{
var parser = BuildParser(0);
for (var i = 1; i <= maxWordCount; i++)
{
parser = parser.Try().Or(BuildParser(i));
}
return parser;
}
public static readonly TokenListParser<StoryToken, (string Prefix, string Keyword)> TextWithKeyword =
BuildTextWithKeyword(10);
}
及对应用法:
var storyTokenizer = StoryTokenizer.Get();
TokenList<StoryToken> tokens;
tokens = storyTokenizer.Tokenize("Some prefix some keyword");
var rv = TestParsers.TextWithKeyword.AtEnd().TryParse(tokens);
Console.WriteLine($"Prefix: {rv.Value.Prefix}, keyword: {rv.Value.Keyword}");
因此,在这段代码中,Superpower 引擎会尝试多个生成的解析器(如果前缀长度不同),直到成功匹配关键字后的最短可能前缀。
该方法有效,但如果能找到更优雅的解决方案来完成任务就好了。