7

如何在 NQP 中打印对象?(用于调试目的)

  • 在 Raku 中很容易:

    1. 这是在其短循环代码中调用gist
    2. dd如本文所示的微型数据转储
class Toto { has $.member = 42; }
class Titi { has $.member = 41; has $.toto = Toto.new }
my $ti = Titi.new;
say $ti;
# Titi.new(member => 41, toto => Toto.new(member => 42))
dd $ti;
# Titi $ti = Titi.new(member => 41, toto => Toto.new(member => 42))
  • 在 NQP 中似乎更复杂
class Toto { has $!member; sub create() {$!member := 42}};
class Titi { has $!member; has $!toto; sub create() {$!member := 41; $!toto := Toto.new; $!toto.create; }}
my $ti := Titi.new;
say($ti);
Cannot stringify this object of type P6opaque (Titi)

当然,没有.gist方法,代码调用nqp::encode最终需要一个字符串。

4

2 回答 2

7

将问题简化为MRE

class foo {}
say(foo.new); # Cannot stringify ...

简化解决方案:

class foo { method Str () { 'foo' } }
say(foo.new); # foo

总之,添加一个Str方法。

这听起来很简单,但有很多幕后的东西需要考虑/解释。

nqp vs 乐

上述解决方案与 raku 使用的技术相同;当例程/操作期望值是字符串,但不是,语言行为是尝试强制转换为字符串。具体来说,看看是否有Str可以在该值上调用的方法,如果有,就调用它。

在这种情况下, NQP'sNQPMuraku'sMu更加准系统,不提供任何默认Str方法。所以一个解决方案是手动添加一个。

更一般地说,NQP 是一种非常具有敌意的语言,除非您非常了解 raku 并且已经通过A course on Rakudo 和 NQP internals

一旦您掌握了该课程中的材料,我建议您将 IRC 频道#raku-dev和/或#moarvm作为您的第一个停靠港而不是 SO(除非您的目标是专门增加 SO 覆盖率nqp/moarvm)。

调试编译器代码

如您所见,您链接的 NQP 代码调用.say文件句柄。

然后调用这个方法

那个方法的主体是$str ~ "\n"。该代码将尝试强制$str转换为字符串(就像在 raku 中一样)。这就是将产生“无法字符串化”错误的原因。

在 NQP 存储库中搜索“无法字符串化”仅匹配一些 Java 代码。而且我敢打赌你没有在 JVM 上运行 Rakudo。这意味着错误消息必须来自 MoarVM。

在 MoarVM 存储库中的相同搜索在MoarVM 中产生了这一行coerce.c

回顾包含该行的例程,我们看到这一点

/* Check if there is a Str method. */
    MVMROOT(tc, obj, {
        strmeth = MVM_6model_find_method_cache_only(tc, obj,
            tc->instance->str_consts.Str);
});

这显示了用 C 语言编写的后端,它正在寻找并调用一个名为Str. (它依赖于编译器的所有三层(raku、nqp 和后端)都遵循的内部 API(6model)。)

自定义Str方法

您需要根据需要自定义Str方法。例如,如果是类型对象,则打印类的名称,否则打印其$!bar属性的值:

class foo {
  has $!bar;
  method Str () { self ?? nqp::coerce_is($!bar) !! self.HOW.name(self) }
}
say(foo.new(bar=>42)); # 42

尽管有方法名称,但 nqpsay例程并不需要 raku Str,而是需要一个 nqp 本机字符串(最终成为 MoarVM 后端上的 MoarVM 本机字符串)。因此需要nqp::coerce_is(我通过浏览nqp ops doc发现)。

self.HOW.name(self)是 nqp 没有 raku 所具有的细节的另一个例子。您可以在 raku 中编写相同的代码,但在 raku 中编写它的惯用方式是self.^name.

于 2020-03-19T20:52:59.880 回答
2

目前,我所拥有的是一个listhash鉴别器。它不适用于对象。

sub print_something ($value, :$indent = 0, :$no-indent=0) {
    if nqp::ishash($value) {
        print_hash($value, :$indent);
    } elsif nqp::islist($value) {
        print_array($value, :$indent);
    } else {
        if $no-indent {
            say($value);
        } else {
            say_indent($indent, $value);
        }
    }
}

在哪里

sub print_indent ($int, $string) {
    my $res := '';
    my $i := 0;
    while $i < $int {
        $res := $res ~ '  ';
        $i := $i + 1;
    }
    $res := $res ~ $string;
    print($res);
}

sub print_array (@array, :$indent = 0) {
    my $iter := nqp::iterator(@array);
    say_indent($indent, '[');
    while $iter {
        print_value(nqp::shift($iter), :indent($indent+1));
    }
    say_indent($indent, ']');
}

sub print_hash (%hash, :$indent = 0) {
    my $iter := nqp::iterator(%hash);
    say_indent($indent, '{');
    while $iter {
        my $pair := nqp::shift($iter);
        my $key := nqp::iterkey_s($pair);
        my $value := nqp::iterval($pair);
        print_indent($indent + 1, $key ~ ' => ');
        print_value($value, :indent($indent+1), :no-indent(1));
    }
    say_indent($indent, '}');
}
于 2020-03-19T17:35:49.303 回答