【问题标题】:Parslet grammar for rules starting identical以相同开头的规则的 Parslet 语法
【发布时间】:2015-12-08 18:59:04
【问题描述】:

我想提供一个解析器来解析所谓的 Subversion 配置身份验证文件(请参阅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 解析器或解析器/编译器生成方面的专家,所以如果这是一个无法解决的基本问题,我也想知道这一点。

【问题讨论】:

    标签: ruby parsing peg parslet


    【解决方案1】:

    简而言之:交换 OR 条件。

    Parlset 规则使用输入流,直到获得匹配项,然后停止。 如果您有两个可能的选项(或),则尝试第一个,只有当它不匹配时才尝试第二个。

    在您的情况下,由于您的所有路径都以“/”开头,它们都匹配路径规则的第一部分,因此永远不会探索后半部分。

    您需要先尝试匹配完整路径,如果失败则仅匹配“顶部”。

    # 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) }
    

    还有……

    “top”真的是特例吗?所有路径都以“/”结尾,因此 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)
    

    【讨论】:

    • 非常感谢您的回答,但这些似乎不是问题所在。请参阅我的答案和相应的要点。
    【解决方案2】:

    似乎是我没有正确解决问题。我试图在创建一个包含一些单元测试的小示例语法时重现该问题,但现在一切正常。

    如果您对此感兴趣,请查看要点https://gist.github.com/mliebelt/a36ace0641e61f49d78f。您应该能够下载该文件,并直接从命令行运行它。你必须先安装parsletminitest 应该已经包含在当前的 Ruby 版本中。

    我只添加了newline 的(缺失)规则,并添加了 3 个单元测试来测试所有案例:

    • 根:/
    • 只有一个元素的路径:/my
    • 包含多个元素的路径:/my/path

    按预期工作,所以我在这里得到两个案例:

    • 仅顶部元素
    • 一个或多个路径元素

    也许这可以帮助其他人如何调试这样的情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-18
      • 2012-05-05
      • 1970-01-01
      相关资源
      最近更新 更多