Как напечатать объект, введите nqp - PullRequest
7 голосов
/ 19 марта 2020

Как напечатать объект в NQP? (Для отладки)

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, которая, наконец, ожидает строку.

Ответы [ 2 ]

7 голосов
/ 19 марта 2020

Сокращение проблемы до MRE :

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

Упрощение решения:

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

В итоге добавьте метод Str.

Это звучит просто, но есть много закулисных вещей, которые нужно рассмотреть / объяснить.

nqp vs raku

Приведенное выше решение - та же самая техника, которую использует raku; когда подпрограмма / операция предполагает, что значение является строкой, но это не так, поведение языка заключается в попытке привести к строке. В частности, посмотрите, есть ли метод Str, который может быть вызван для значения, и, если это так, вызовите его.

В этом случае NQP NQPMu, что намного больше скелетов чем raku's Mu, не предусмотрен метод Str по умолчанию. Поэтому решение состоит в том, чтобы добавить его вручную.

В целом, NQP является довольно враждебным языком, если вы не достаточно хорошо знаете raku и прошли курс по внутренним компонентам Rakudo и NQP .

И как только вы освоите материал в этом курсе, я рекомендую вам рассмотреть IR C каналы # raku-dev и / или # moarvm как ваш первый порт вызова, а не SO (если только ваша цель не состоит в том, чтобы конкретно увеличить охват SO nqp / moarvm).

Отладка кода компилятора

Как вы уже видели, код NQP вы связали вызовы .say в файловом дескрипторе.

Затем вызывается этот метод .

Тело этого метода - $str ~ "\n". Этот код будет пытаться привести $str к строке (так же, как в raku). Это то, что будет генерировать ошибку «Cannot stringify».

Поиск «Cannot stringify» в репозитории NQP соответствует только некоторому коду Java. Бьюсь об заклад, вы не запускаете Rakudo на JVM. Это означает, что сообщение об ошибке должно приходить от MoarVM.

Тот же поиск в репозитории MoarVM дает эту строку в coerce.c в MoarVM .

Взгляд назад в подпрограмма, содержащая эту строку, мы видим этот бит :

/* 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. (Он опирается на внутренний API (6model), которого придерживаются все три уровня компилятора (raku, nqp и backends).)

Настройка метода Str

Вы будете необходимо настроить метод Str в зависимости от ситуации. Например, для печати имени класса, если это объект типа, и значения его атрибута $!bar в противном случае:

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

Несмотря на имя метода, подпрограмма nqp say имеет значение not ожидал raku Str, а скорее нативную строку nqp (которая в конечном итоге является нативной строкой MoarVM в бэкэнде MoarVM). Следовательно, потребность в nqp::coerce_is (которую я нашел, просмотрев , что делает nqp ops c).

self.HOW.name(self) - это еще один пример того, как nqp просто не имеет тонкости, которые есть у раку. Вы могли бы написать тот же код в raku, но идиоматический c способ написать его в raku - self.^name.

2 голосов
/ 19 марта 2020

В настоящее время у меня есть дискриминатор list и hash. Не работает на объекте.

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, '}');
}
...