当前位置: 首页 > 知识库问答 >
问题:

如何编写可定制的语法?

全弘深
2023-03-14

对于我正在编写的聊天机器人,我想让它的解析器可定制,这样人们就不需要修改机器人本身来为他们想要的任何类型的聊天消息添加钩子。解析器使用语法。目前,我用一个类似这样的类来处理这个问题:

class Rule {
    has Regex:D $.matcher is required;
    has         &.parser  is required;

    method new(::?CLASS:_: Regex:D $matcher, &parser) {
        self.bless: :$matcher, :&parser
    }

    method match(::?CLASS:D: Str:D $target --> Replier:_) {
        $target ~~ $!matcher;
        $/.defined ?? &!parser(self, $/) !! Nil
    }
}

然后,将从解析器的actions类中循环一个数组。这允许人们为解析器添加他们自己的“规则”,这解决了我的问题,但这是笨拙的,这是重新发明语法!我真正想要的是人们能够为我的解析器写一些类似俚语的东西。虽然augment可以用于此,但在这种情况下它没有用处,因为用户可能希望在运行时更改如何扩充解析器,但augment在编译时处理。如何做到这一点?

共有1个答案

国景铄
2023-03-14

这需要5行或10行样板,这取决于是否使用actions类。

在撰写本文时,如果您查看一下Metamodel::GrammarHOW,您会发现:

class Perl6::Metamodel::GrammarHOW
    is Perl6::Metamodel::ClassHOW
    does Perl6::Metamodel::DefaultParent
{
}

语法是类的延伸!这意味着可以在其中声明元方法。在Perl 6中如何使类参数化的基础上构建?,如果用户为grammar和actions类提供角色,则可以在通过参数化进行解析之前将它们混合在一起。如果你以前写过俚语,这听起来可能很熟悉;混合在这样的角色中是$*LANG.refine\u俚语的工作原理!

如果您希望语法中的令牌是可扩充的,那么可以将其设置为原型令牌。之后所需要的只是一个参数化元方法,它将混合在参数中,这将是某种角色:

grammar Foo::Grammar {
    token TOP { <foo> }

    proto token foo          {*}
          token foo:sym<foo> { <sym> }

    method ^parameterize(Foo::Grammar:U $this is raw, Mu $grammar-role is raw --> Foo::Grammar:U) {
        my Foo::Grammar:U $mixin := $this.^mixin: $grammar-role;
        $mixin.^set_name: $this.^name ~ '[' ~ $grammar-role.^name ~ ']';
        $mixin
    }
}

class Foo::Actions {
    method TOP($/) { make $<foo>.made; }

    method foo:sym<foo>($/) { make ~$<sym>; }

    method ^parameterize(Foo::Actions:U $this is raw, Mu $actions-role is raw --> Foo::Actions:U) {
        my Foo::Actions:U $mixin := $this.^mixin: $actions-role;
        $mixin.^set_name: $this.^name ~ '[' ~ $actions-role.^name ~ ']';
        $mixin
    }
}

然后可以这样声明要混合的角色:

role Bar::Grammar {
    token foo:sym<bar> { <sym> }
}

role Bar::Actions {
    method foo:sym<bar>($/) { make ~$<sym>; }
}

现在,如果需要,可以在解析之前使用BarFoo进行扩充:

Foo::Grammar.subparse: 'foo', actions => Foo::Actions.new;
say $/ && $/.made; # OUTPUT: foo
Foo::Grammar.subparse: 'bar', actions => Foo::Actions.new;
say $/ && $/.made; # OUTPUT: #<failed match>

Foo::Grammar[Bar::Grammar].subparse: 'foo', actions => Foo::Actions[Bar::Actions].new;
say $/ && $/.made; # OUTPUT: foo
Foo::Grammar[Bar::Grammar].subparse: 'bar', actions => Foo::Actions[Bar::Actions].new;
say $/ && $/.made; # OUTPUT: bar

编辑:元方法可以接受任意数量的角色作为参数,参数化可以使用任何签名。这意味着,如果您稍微调整参数化元方法,您可以使语法或操作类的参数化接受任意数量的角色:

method ^parameterize(Mu $this is raw, *@roles --> Mu) {
    my Mu $mixin := $this.^mixin: |@roles;
    $mixin.^set_name: $this.^name ~ '[' ~ @roles.map(*.^name).join(', ') ~ ']';
    $mixin
}
 类似资料:
  • 虽然 Glide 内置了大部分常用模型(URL, Uri, 文件路径等)的支持,你还是可能偶尔会遇到一种 Glide 不支持的类型。你也可能会遇到需要定制或调整 Glide 默认行为的情况。你甚至可能会想要集成一种新的拉取图片的方法,或更换 Glide 目前支持的 集成库 之外的网络库。 好在 Glide 是可扩展的。要添加对一种新的模型(Model)类型的支持,你需要按照以下步骤来执行: 实现一

  • 本高级教程上接教程 6。我们将把我们的网页投票转换成一个独立的Python包,这样你可以在其它项目中重用或者分享给其它人。 如果你最近没有完成教程1–6,我们建议你阅读它们使得你的示例项目与下面描述的相匹配。 可重用很重要 设计、构建、测试和维护一个网页应用有许多工作要做。许多Python 和 Django 项目都有常见的共同问题。如果我们可以节省一些这些重复的工作会不会很棒? 可重用性是Pyth

  • 在我们的项目中,我们有一个solr模式,它的值带有多个几乎重复的字段。我的意思是我们有一个示例field field,我们在solr中存储为field、field_w和field_l,它们在搜索中都有不同的boost因子(动态类型不是_w或_l,而是类似的)。 其他人是如何使用Solr处理持久性的?有一个想法是将类的JSON序列化为Solr字段,这样,每当模式或模型发生变化时,写就会改变,序列化/

  • 问题内容: 我在Elasticsearch索引中的文档中存储了值。 我需要对值进行一些日期操作,并返回要在过滤器中使用的布尔值。 该脚本涵盖了几行,但我无法运行它。 我编写了其他可以正常工作的脚本,但是我对Groovy的了解甚少,而对Elastic search的了解却很少。 我可以用脚本找到的每个样本只有一行,只有一行。 所以基本上我将如何采用这个完全有效的脚本 并把它变成像 我对创建一个只写一

  • 问题内容: 我正在尝试为我的简单类生成hashCode()方法,但是我什么也没用。我将不胜感激任何帮助。我已经实现了equals()方法,该方法如下所示,并且还想知道是否需要实现compareTo()方法。我已经导入了java.lang.Character以使用character.hashCode(),但它似乎不起作用。 提前致谢… 正在给我java.lang.Comparable转换错误的com

  • null 但这也不起作用。我如何添加'X'作为这个switch语句的默认值,什么可以帮助我防止自己再次犯这个错误?