代码之家  ›  专栏  ›  技术社区  ›  Hunter McMillen zellus

从语法中提取标记

  •  8
  • Hunter McMillen zellus  · 技术社区  · 6 年前

    在此表格中给出输入: #1 @ 1,3: 4x4 我创建的语法是:

    grammar Claim {
      token TOP {
        '#' <id> \s* '@' \s* <coordinates> ':' \s* <dimensions>
      }
    
      token digits {
        <digit>+
      }
    
      token id {
        <digits>
      }
    
      token coordinates {
        <digits> ',' <digits>
      }
    
      token dimensions {
        <digits> 'x' <digits>
      }
    }
    
    say Claim.parse('#1 @ 1,3: 4x4');
    

    我感兴趣的是从坐标中提取匹配的实际标记,即id、x+y,以及从结果解析的维度中提取高度+宽度。我知道我可以把他们从结果中拉出来 Match 对象 Claim.parse(<input>) ,但我必须深入研究每一个语法产品,以获得我需要的值。

    say $match<id>.hash<digits>.<digit>;
    

    2 回复  |  直到 5 年前
        1
  •  8
  •   raiph    6 年前

    对于 the particular challenge you're solving ,使用语法就像用大锤敲螺母。

    正如@Scimon所说,一个正则表达式就可以了。您可以通过适当的布局来保持它的可读性。您可以命名捕获并将它们全部保留在顶级:

    / ^
      '#' $<id>=(\d+) ' '
      '@ ' $<x>=(\d+) ',' $<y>=(\d+)
      ': ' $<w>=(\d+)  x  $<d>=(\d+)
      $
    /;
    
    say ~$<id x y w d>; # 1 1 3 4 4
    

    (前缀 ~ 电话 .Str 在其右侧的值上。拜访 Match 将其字符串化为匹配字符串的对象。)

    挖得少一点乱

    say $match<id>.hash<digits>.<digit>; # [ï½¢1ï½£]
    

    这看起来有点乱,有没有更好的办法?

    你的 say 包括不必要的代码和输出嵌套。您可以简化为以下内容:

    say ~$match<id> # 1
    

    我感兴趣的是从坐标中提取匹配的实际标记,即id、x+y,以及从结果解析的维度中提取高度+宽度。

    比赛 您不再需要依靠Perl 6猜测您的意思(当只有一个的时候,猜猜它猜你指的是哪一个。:)

    得到 y

    say ~$match<coordinates><digits>[1] # 3
    

    如果你想放弃 <digits> 您可以标记模式的哪些部分应存储在编号捕获列表中。一种方法是在这些零件周围加括号:

    token coordinates { (<digits>) ',' (<digits>) }
    

    现在你不必再提了 <数字> :

    say ~$match<coordinates>[1] # 3
    

    token coordinates { $<x>=(<digits>) ',' $<y>=(<digits>) }
    
    say ~$match<coordinates><y> # 3
    

    预挖

    上述技术仍然深入研究自动生成的解析树,默认情况下,解析树与语法规则调用层次结构中隐含的树完全对应。上面的技术只是让你挖掘它的方式看起来有点肤浅。

    另一步是作为解析过程的一部分进行挖掘工作,以便 这很简单。

    将一些代码直接内联到 TOP 用于存储所做的有趣数据的令牌。只需插入一个 {...}

    my $made;
    grammar Claim {
      token TOP {
        '#' <id> \s* '@' \s* <coordinates> ':' \s* <dimensions>
         { $made = ~($<id>, $<coordinatess><x y>, $<dimensions><digits>[0,1]) }
      }
    ...
    

    现在你可以写:

    say $made # 1 1 3 4 4
    

    这说明了您可以在任何规则的任意点编写任意代码——这在大多数解析形式及其相关工具中是不可能的——并且代码可以访问此时的解析状态。

    内联代码既快又脏。使用变量也是如此。

    存储数据的通常做法是使用 make .made 方法。所以不是 $make = 你应该:

    { make ~($<id>, $<coordinatess><x y>, $<dimensions><digits>[0,1]) }
    

    现在你可以写:

    say $match.made # 1 1 3 4 4
    

    那要整洁得多。但还有更多。

    .oO(在计划的第一天) Perl 6 Christmas Advent calendar 一个头衔对我说……)

    在上面的示例中,我构建了一个 …制造的 顶部 节点。对于较大的语法,通常形成一个 sparse subtree (我为此创造了一个术语,因为我找不到一个标准的现有术语)。

    此稀疏子树由 …制造的 飞机的有效载荷 顶部 …制造的

    这方面的规范用例是形成一个 Abstract Syntax Tree 在解析了一些编程代码之后。

    …制造的 ,即 .ast

    say $match.ast # 1 1 3 4 4
    

    让一切变得优雅

    对于可维护性和可重用性,您可以而且通常应该 在规则末尾插入内联代码,但应改为使用 Action objects

    如上所述,添加括号,命名那些括号归零的捕获,如果这是挖掘解析树的一个很好的简化方法的话。

    内联您希望在规则解析期间执行的任何操作。此时您可以完全访问解析状态。这非常便于从解析中提取所需的数据,因为您可以使用 方便功能。您还可以从语法中提取成功匹配规则后要执行的所有操作,以确保这是一个干净的解决方案,并确保单个语法可用于多个操作。

    <.foo> ,在规则名称前加一个点,以切换默认的自动捕获

        2
  •  2
  •   Scimon Proctor    6 年前

    您可以直接引用每个命名部分。因此,要获得Cordinate,您可以访问:

    say $match.<coordinates>.<digits>

    这将返回 digits 比赛。Ig您只需要获取值最简单的方法可能是:

    say $match.<coordinates>.<digits>.map( *.Int) say $match.<coordinates>.<digits>>>.Int 甚至 say $match.<coordinates>.<digits>».Int

    Int s

    对于 id 这更容易你可以直接投下 <id> 与整型匹配:

    say $match.<id>.Int