Superpower中两个TextParser用Or组合解析失败的解决方案咨询
解决Superpower解析器Or组合的回溯问题
这是个典型的解析器回溯问题,我来帮你分析和解决:
问题原因
Superpower的Or运算符默认不会自动回溯输入位置。当你用identifier001.Or(identifier002)时,解析器会先尝试第一个解析器identifier001(匹配insert(...)):
- 解析
interrupt(12)时,identifier001会先匹配前两个字符"in"(和insert的前缀一致),但第三个字符是t而不是insert需要的s,此时第一个解析器失败,但输入指针已经停在了第3位,不会回到初始位置去尝试第二个解析器identifier002,所以抛出了语法错误。
解决方案
要让解析器在第一个选项失败时回溯到初始位置,尝试第二个选项,有两种简单的方式:
方式1:用Try()包装每个解析器
Try()方法会捕获解析失败的情况,并将输入指针重置到解析前的位置,这样后续的Or就能从正确的起点尝试下一个解析器:
TextParser<List<string>> combined = identifier001.Try().Or(identifier002.Try());
方式2:使用Parser.Choice()方法
Superpower提供了更简洁的Choice方法,它内部已经封装了回溯逻辑,专门用于处理多个解析器的"任一匹配"场景:
TextParser<List<string>> combined = Parser.Choice(identifier001, identifier002);
修正后的完整代码
using Superpower; using Superpower.Parsers; using System.Collections.Generic; using System; class Program { static void Main() { string testInterrupt = "interrupt(12)"; string testInsert = "insert(20,12)"; string testDelete = "delete(18)"; TextParser<List<string>> identifier001 = from prefix in Span.EqualTo("insert") from open in Character.EqualTo('(') from num0 in Character.Numeric.Many() from comma1 in Character.EqualTo(',') from num1 in Character.Numeric.Many() from close in Character.EqualTo(')') select new List<string> { prefix.ToStringValue(), string.Join("", num0), string.Join("", num1) }; TextParser<List<string>> identifier002 = from prefix in Span.EqualTo("interrupt") from open in Character.EqualTo('(') from num0 in Character.Numeric.Many() from close in Character.EqualTo(')') select new List<string> { prefix.ToStringValue(), string.Join("", num0) }; // 使用Choice方法组合解析器 TextParser<List<string>> combined = Parser.Choice(identifier001, identifier002); // 测试interrupt(12) try { var res = combined.Parse(testInterrupt); Console.WriteLine("解析interrupt(12)结果:"); foreach (var item in res) { Console.WriteLine(item); } } catch (Exception ex) { Console.WriteLine($"解析interrupt(12)失败: {ex.Message}"); } // 测试insert(20,12) try { var res = combined.Parse(testInsert); Console.WriteLine("\n解析insert(20,12)结果:"); foreach (var item in res) { Console.WriteLine(item); } } catch (Exception ex) { Console.WriteLine($"解析insert(20,12)失败: {ex.Message}"); } // 测试delete(18)(应该失败) try { var res = combined.Parse(testDelete); Console.WriteLine("\n解析delete(18)结果:"); foreach (var item in res) { Console.WriteLine(item); } } catch (Exception ex) { Console.WriteLine($"\n解析delete(18)失败(符合预期): {ex.Message}"); } } }
运行结果
interrupt(12)会输出:interrupt 12insert(20,12)会输出:insert 20 12delete(18)会抛出错误,符合你的需求。
内容的提问来源于stack exchange,提问作者cppxaxa
相关产品推荐
相关产品推荐

