我想提供一个解析器来解析所谓的Subversion配置auth文件(参见patch based authorization in the Subversion red book)。在这里,我想为目录定义规则,如
[/]
* = r
[/trunk]
@PROJECT = rw
因此我遇到的语法部分是路径定义。我目前在Parslet中有以下规则:
rule(:auth_rule_head) { (str('[') >> path >> str(']') >> newline).as(:arh) }
rule(:top) { (str('/')).as(:top) }
rule(:path) { (top | ((str('/') >> path_ele).repeat)).as(:path) }
rule(:path_ele) { ((str('/').absent? >> any).repeat).as(:path_ele) }
所以我想分成两种情况:
[/]
(根目录)[/<dir>]
可能会重复,但必须以/
有问题的规则似乎是定义替代方案的path
,此处为/
XOR,例如/trunk
我为这些测试用例定义了测试用例,并在运行测试用例时遇到以下错误:
Failed to match sequence (SPACES '[' PATH ']' NEWLINE) at line 1 char 3.
`- Expected "]", but got "t" at line 1 char 3.
所以问题似乎是,top
一直在选择替代(规则:路径)。
这个问题的解决方案(作为语法)是什么?我认为应该有一个解决方案,这看起来像是从这里到那里发生的惯用语。我不是PEG解析器或解析器/编译器生成的专家,所以如果这是一个不可解决的基本问题,我也想知道。
答案 0 :(得分:1)
简而言之:交换OR条件。
Parlset规则消耗输入流直到它们匹配,然后它们停止。 如果您有两个可能的选项(OR),则尝试第一个选项,并且只有当它没有匹配时才尝试第二个选项。
在您的情况下,因为您的所有路径都以&#39; /&#39;开头。它们都与路径规则的第一部分相匹配,因此从未探索过后半部分。
您需要首先尝试匹配完整路径,并且只匹配&#39; top&#39;如果失败了
# changing this
rule(:path) { (top | ((str('/') >> path_ele).repeat)).as(:path) }
# to this
rule(:path) { ((str('/') >> path_ele).repeat) | top).as(:path) }
# fixes your first problem :)
另外......小心那些在循环中不会消耗任何东西的规则。 默认情况下重复是重复(0)。通常需要重复(1)。
rule(:path) { ((str('/') >> path_ele).repeat(1)) | top).as(:path) }
也...
&#34; top&#34;真的是一个特例吗?所有路径都以&#34; /&#34;结尾,因此top只是零长度路径。
rule(:path) { (path_ele.repeat(0) >> str('/')).as(:path) }
或者
rule(:path) { (str('/') >> path_ele.repeat(0)).as(:path) }
rule(:path_ele) { ((str('/').absent? >> any).repeat(0)).as(:path_ele) >> str('/') }
# assuming "//" is valid otherwise repeat(1)
答案 1 :(得分:0)
似乎是我没有解决问题。我试图在创建一个包含一些单元测试的小例子语法时重现这个问题,但是现在,事情正在发挥作用。
如果您对此感兴趣,请查看要点https://gist.github.com/mliebelt/a36ace0641e61f49d78f。您应该能够下载该文件,并直接从命令行运行它。您必须先安装parslet
,minitest
应该已包含在当前的Ruby版本中。
我只为newline
添加了(缺失)规则,并添加了3个单元测试来测试所有情况:
/
/my
/my/path
按预期工作,所以我在这里得到两个案例:
也许这可以帮助其他人如何调试这样的情况。