如何使用Devel :: Declare注入多行?

时间:2014-08-06 22:55:57

标签: perl

我想使用Devel::Declare注入多行Perl代码。但是,Devel::Declare::set_linestr()无法处理多行。

通常我会将多个语句一起作为一行加入。这些语句必须在单独的行上以保留其行号以用于错误报告目的。这是为了解决this bug in Method::Signaturesthis related bug。我愿意接受其他解决方案。

例如,Method :: Signatures目前会转换此代码......

use Method::Signatures;

func hello(
    $who = "World",
    $greeting = get_greeting($who)
) {
    die "$greeting, $who";
}

......进入这个......

func  \&hello; sub hello  { BEGIN { Method::Signatures->inject_scope('') }; my $who = (@_ > 0) ? ($_[0]) : ( get_greeting($who)); my $greeting = (@_ > 1) ? ($_[1]) : ( "Hello"); Method::Signatures->too_many_args_error(2) if @_ > 2;
    die "$greeting, $who";
}

die $who然后报告第4行而不是第7行。

我希望它是这个(或者可能涉及#line)。

func  \&hello; sub hello  { BEGIN { Method::Signatures->inject_scope('') };
    my $who = (@_ > 0) ? ($_[0]) : ( "World");
    my $greeting = (@_ > 1) ? ($_[1]) : ( get_greeting($who));
    Method::Signatures->too_many_args_error(2) if @_ > 2;
    die "$greeting, $who";
}

这不仅忠实地重现行号,如果get_greeting cro会报告从正确的行调用。

2 个答案:

答案 0 :(得分:3)

我找到了一种方法来做到这一点,但这很麻烦而且很慢。

我注意到你可以在其中注入带有文字换行符的字符串并且它们可以工作。我认为解析器知道足以继续通过字符串中的换行符。我认为可以利用它来欺骗解析器继续前进。

package Foo;

use strict;
use warnings;
use v5.12;

use parent "Devel::Declare::MethodInstaller::Simple";

sub import {
    my $class = shift;
    my $caller = caller;

    $class->install_methodhandler(
        into            => $caller,
        name            => 'method'
    );
}

sub parse_proto {
    my $self = shift;
    return q[print __LINE__."\n"; my $__empty = q{
};print __LINE__."\n"; $__empty = q{
};print __LINE__."\n";];
}

1;

它有效......除了__LINE__没有增加。 Perl解析器出现故障?我尝试了一个带有换行符的正则表达式,它也没有增加该行。

但子程序确实有效!

sub __empty() { '' }

sub parse_proto {
    my $self = shift;
    return q[print __LINE__."\n"; Foo::__empty(
);print __LINE__."\n"; Foo::__empty(
);print __LINE__."\n";];
}

这是一个不变的子程序调用,Perl应该优化它,对吗?唉,没有。似乎调用中的换行符欺骗了优化器。从好的方面来说,这避免了“在无效环境中无用的常量”警告。在不利方面,它为每个参数引入了一个子程序调用,这是为每个子程序调用添加的不可接受的开销量。

也许其他人可以想出一种聪明的方法来将新行发送到Perl语法中?

答案 1 :(得分:3)

根据您自己的回答,以下工作:

sub __empty() { '' }

sub parse_proto {
    my $self = shift;
    return q[print __LINE__."\n"; Foo::__empty(
);print __LINE__."\n"; Foo::__empty(
);print __LINE__."\n";];
}

但是引入了不可接受的开销,因为必须为每个参数调用__empty()函数。通过有条件地使用永远不会评估为真的条件调用__empty()可以消除开销。

sub __empty() { '' }

sub parse_proto {
    my $self = shift;
    return q[print __LINE__."\n"; 0 and Foo::__empty(
);print __LINE__."\n"; 0 and Foo::__empty(
);print __LINE__."\n";];
}