我可以在匹配树中插入命名捕获而不实际匹配任何内容吗?

时间:2017-01-18 22:31:09

标签: grammar perl6

我很好奇我是否可以在没有任何内容的情况下将内容插入到匹配树中。我正试图解决没有相关的问题。

在这个例子中,我有一个令牌inventory_amount = Subquery(account.inventory_set.filter(item=OuterRef('pk')).values('amount')) Item.objects.annotate(inventory_amount=Coalesce(inventory_amount, Value(0))) 来检查它的匹配是哈希中的一个键。我试图以某种方式将该哈希的值插入到匹配树中。我想我可以拥有一个始终匹配的令牌market,然后以某种方式查看树以查看long_market_string匹配的内容。

market

有没有办法在创建grammar OrderNumber::Grammar { token TOP { <channel> <product> <market> <long_market_string> '/' <revision> } token channel { <[ M F P ]> } token product { <[ 0..9 A..Z ]> ** 4 } token market { (<[ A..Z ]>** 1..2) <?{ %Market_Shortcode{$0}:exists }> } # this should figure out what market matched # I don't particularly care how this happens as long as # I can insert this into the match tree token long_market_string { <?> } token revision { <[ A..C ]> } } 树时弄乱它?

我可以做一些颠倒事情的事情:

Match

但是,处理这种情况。我对插入任意数量的东西更感兴趣。

2 个答案:

答案 0 :(得分:1)

标记是一种方法,所以如果你编写了一个方法来完成令牌为你做的所有设置工作,你几乎可以做任何事情。

这是没有规定的,目前并不容易 (我只有一个模糊的想法,从哪里开始查看源代码来解决它)

您可以轻松完成的工作是添加到结果的.made / .ast.made.ast是同义词)

$/ = grammar {
  token TOP {
    .*
    {
      make 'World'
    }
  }
}.parse('Hello');

say "$/ $/.made()";  # Hello World

它甚至不必在语法内部

'asdf' ~~ /{make 42}/;
say $/;     # 「」
say $/.made # 42

大多数情况下,您会为此类事物使用Actions类

grammar example-grammar {
  token TOP {
    [ <number> | <word> ]+ % \s*
  }
  token word {
    <.alpha>+
  }
  token number {
    \d+
    { make +$/ }
  }
}

class example-actions {
  method TOP    ($/) { make $/.pairs.map:{ .key => .value».made} }
  method number ($/) { #`( already done in grammar, so this could be removed ) }
  method word   ($/) { make ~$/ }
}

.say for example-grammar.parse(
  'Hello 123 World',
  :actions(example-actions)
).made».perl

# :number([123])
# :word(["Hello", "World"])

答案 1 :(得分:0)

听起来你想要将匹配树颠覆为做匹配树实际上不应该做的事情。匹配树跟踪输入字符串中匹配的子字符串,而不是解析器生成的任意数据。如果你想跟踪任意数据,那么AST树有什么问题?

当然,从某种意义上说,AST树必须镜像解析树,因为匹配方法成功完成后,它以自下而上的方式构建。但是AST本身在“附加到任何给定节点的对象”的意义上并没有那么受限制。考虑例如:

grammar G {
    token TOP { <foo> <bar> {make "TOP is: " ~ $<foo> ~ $<bar>} }
    token foo { foo {make "foo"} }
    token bar { bar {make "bar"} }
}
G.parse("foobar");

此处$/.made将只是字符串“TOP is:foobar”,而匹配树具有子节点,其中包含用于构建顶级AST的组件。如果那么回到你的例子,我们可以做到:

grammar G {
    my %Market_Shortcode = :AA('Double A');
    token TOP    {
        <channel> <product> <market>
        {} # Force the computation of the $/ object. Note that this will also terminate LTM here.
        <long_market_string(~$<market>)> '/' <revision>
        }

    token channel    { <[ M F P ]> }
    token product    { <[ 0..9 A..Z ]> ** 4 }

    token market     {
        (<[ A..Z ]>** 1..2) <?{ %Market_Shortcode{$0}:exists }>
        }

    token long_market_string($shortcode) { <?> { say 'c='~$shortcode; make %Market_Shortcode{$shortcode} } }

    token revision   { <[ A..C ]> }
    }

G.parse('M0000AA/A');

$<long_market_string>.ast现在将成为“双A”。当然,我可能不会使用token long_market_name而只是使token market的AST成为%Market_Shortcode中的任何内容(或者是一个包含短名和长名的Market对象,如果你想跟踪的话两个一起)。

这种事情的一个不那么简单的例子就像是Python的语法。由于Python的块级结构是基于行的,因此您的语法(以及匹配树)需要以某种方式反映这一点。但是你也可以通过用分号将它们分开来将几个简单的语句链接在一起。现在,您可能希望块的AST成为语句列表,而单行的AST本身可能是多个语句的列表。因此,您可以通过(例如)flatmap构造块的AST来组合行的列表(或沿着这些行的某些内容,具体取决于您如何表示if和{{}等块语句1}})。

现在,如果你真的,真的,真的想要对匹配树做一些令人讨厌的事情,我当然非常确定它可以做到。你必须自己用while来实现解析代码,其API没有文档和内部,并且可能至少涉及一些下载到nqp :: ops。指向here的东西可能会有用。其他相关文件是Rakudo仓库中的method long_market_name。另请注意,Matches的字符串化是通过从输入字符串中提取匹配的子字符串来计算的,因此如果您希望它有用地进行字符串化,则必须继承Match。