Devel::Declare 模块包含以相对安全的方式扩展 Perl 语法的工具。
使用Devel::Declare,您将在with 标记上创建一个钩子,当解析器到达该单词时,它将停止解析器。从那里,您可以控制解析器,并且可以提前阅读,直到到达{ 符号。到那时,您就拥有了需要处理的内容,因此您将其重写为有效的 Perl,并将其传递回解析器。
在文件With.pm:
package With;
use warnings;
use strict;
use Devel::Declare;
sub import {
my $caller = caller;
Devel::Declare->setup_for (
$caller => {with => {const => \&parser}}
);
no strict 'refs';
*{$caller.'::with'} = sub ($&) {
$_[1]() for $_[0];
$_[0]
}
}
our $prefix = '';
sub get {substr Devel::Declare::get_linestr, length $prefix}
sub set { Devel::Declare::set_linestr $prefix . $_[0]}
sub parser {
local $prefix = substr get, 0, length($_[0]) + $_[1];
my $with = strip_with();
strip_space();
set "scalar($with), sub " . get;
}
sub strip_space {
my $skip = Devel::Declare::toke_skipspace length $prefix;
set substr get, $skip;
}
sub strip_with {
strip_space;
my $with;
until (get =~ /^\{/) {
(my $line = get) =~ s/^([^{]+)//;
$with .= $1;
set $line;
strip_space;
}
$with =~ s/\s+/ /g;
$with
}
并使用它:
use With;
sub Window::add {say "window add: ", $_[1]->str}
sub Window::new {bless [] => 'Window'}
sub Box::new {bless [] => 'Box'}
sub Box::add {push @{$_[0]}, @_[1..$#_]}
sub Box::str {"Box(@{$_[0]})"}
sub Button::new {"Button($_[1])"}
with Window->new {
$_->add(with Box->new {
for my $num (1 .. 4) {
$_->add(Button->new($num))
}
})
};
哪些打印:
窗口添加:框(按钮(1)按钮(2)按钮(3)按钮(4))
完全不同的方法是完全跳过with 关键字并编写一个例程来生成构造函数子例程:
BEGIN {
for my $name (qw(VBox)) { # and any others you want
no strict 'refs';
*$name = sub (&@) {
use strict;
my $code = shift;
my $with = "Gtk2::$name"->new(@_);
$code->() for $with;
$with
}
}
}
然后你的代码可能看起来像
for (Gtk2::Window->new('toplevel')) {
$_->set_title('Test Application');
$_->add(VBox {
my $box = $_;
$box->add(Gtk2::Button->new("Button $_")) for (1..4);
});
$_->show_all;
}