扩展MooseX :: Declare时注入代码中的语法错误

时间:2011-10-29 03:25:44

标签: perl parsing moose

这是一个很大的问题,所以请耐心等待。最后有一罐金子。

由于大多数实验原因,我正在尝试制作一个MooseX :: Declare的自定义扩展,它可以做一些对特定爱好项目有用的额外魔法。例如,我想让class关键字注入一些额外的东西,比如从List :: Util等导入有用的实用程序,打开各种额外的编译指示(除了strict和{{1 }},自动导入我的全局配置对象,依此类推。

所以我编写了以下测试并开始考虑是否可以让它工作。令人惊讶的是,我能够获得99%的方式,但现在我遇到了一个我无法弄清楚的问题。我的自定义warnings关键字因注入代码中的语法错误而死亡。

class

#!/usr/bin/env perl use MyApp::Setup; class Foo { use Test::More tests => 1; has beer => ( is => 'ro', default => 'delicious' ); method something { is $self->beer, 'delicious'; } } Foo->new->something; 如下所示。将来它会做更多的东西,但是现在它只是在我的MX :: D子类上调用MyApp::Setup

import

那个班级看起来像这样:

package MyApp::Setup;

use strict;
use warnings;

use MyApp::MooseX::Declare;

sub import { 
    goto &MyApp::MooseX::Declare::import;
}

1;

我设置了三个关键字类,只是包含一个替换package MyApp::MooseX::Declare; use Moose; use MyApp::MooseX::Declare::Syntax::Keyword::Class; use MyApp::MooseX::Declare::Syntax::Keyword::Role; use MyApp::MooseX::Declare::Syntax::Keyword::Namespace; extends 'MooseX::Declare'; sub import { my ($class, %args) = @_; my $caller = caller; for my $keyword ( __PACKAGE__->keywords ) { warn sprintf 'setting up keyword %s', $keyword->identifier; $keyword->setup_for($caller, %args, provided_by => __PACKAGE__ ); } } sub keywords { # override the 'class' keyword with our own return ( MyApp::MooseX::Declare::Syntax::Keyword::Class->new( identifier => 'class' ), MyApp::MooseX::Declare::Syntax::Keyword::Role->new( identifier => 'role' ), MyApp::MooseX::Declare::Syntax::Keyword::Namespace->new( identifier => 'namespace' ) ); } 1; 的额外角色。

MX::D::Syntax::NamespaceHandling

(另外两个是相同的。)

在真正的MX :: D中,NamespaceHandling内容由一个名为MooseSetup的独立角色组成,该角色本身由关键字class组成。在一个地方做这一切似乎有效;不过,我不知道结构上的轻微偏差是否是我问题的根源。有一次,我有自己的MooseSetup版本,但这导致了我无法弄清楚的构图冲突。

最后,肉和土豆是我的NamespaceHandling版本,它覆盖了package MyApp::MooseX::Declare::Syntax::Keyword::Class; use Moose; extends 'MooseX::Declare::Syntax::Keyword::Class'; with 'MyApp::MooseX::Declare::Syntax::NamespaceHandling'; 1; 方法。其中大部分只是从原始版本中复制粘贴。

parse

当我运行测试时,一切似乎都很好 - 我收到警告消息,指出正在调用正确的方法并且正在设置包“Foo”。然后它死了:

  t / default.t第5行的

语法错误,靠近“{package Foo”

所以似乎某些东西在导致语法错误的package MyApp::MooseX::Declare::Syntax::NamespaceHandling; use Moose::Role; use Carp 'croak'; use Moose::Util 'does_role'; use MooseX::Declare::Util 'outer_stack_peek'; with 'MooseX::Declare::Syntax::NamespaceHandling'; # this is where the meat is! sub parse { my ($self, $ctx) = @_; # keyword comes first $ctx->skip_declarator; # read the name and unwrap the options $self->parse_specification($ctx); my $name = $ctx->namespace; my ($package, $anon); # we have a name in the declaration, which will be used as package name if (defined $name) { $package = $name; # there is an outer namespace stack item, meaning we namespace below # it, if the name starts with :: if (my $outer = outer_stack_peek $ctx->caller_file) { $package = $outer . $package if $name =~ /^::/; } } # no name, no options, no block. Probably { class => 'foo' } elsif (not(keys %{ $ctx->options }) and $ctx->peek_next_char ne '{') { return; } # we have options and/or a block, but not name else { $anon = $self->make_anon_metaclass or croak sprintf 'Unable to create an anonymized %s namespace', $self->identifier; $package = $anon->name; } warn "setting up package [$package]"; # namespace and mx:d initialisations $ctx->add_preamble_code_parts( "package ${package}", sprintf( "use %s %s => '%s', file => __FILE__, stack => [ %s ]", $ctx->provided_by, outer_package => $package, $self->generate_inline_stack($ctx), ), ); # handle imports and setup here (TODO) # allow consumer to provide specialisations $self->add_namespace_customizations($ctx, $package); # make options a separate step $self->add_optional_customizations($ctx, $package); # finish off preamble with a namespace cleanup # we'll use namespace::sweep instead #$ctx->add_preamble_code_parts( # $ctx->options->{is}->{dirty} # ? 'use namespace::clean -except => [qw( meta )]' # : 'use namespace::autoclean' #); # clean up our stack afterwards, if there was a name $ctx->add_cleanup_code_parts( ['BEGIN', 'MooseX::Declare::Util::outer_stack_pop __FILE__', ], ); # actual code injection $ctx->inject_code_parts( missing_block_handler => sub { $self->handle_missing_block(@_) }, ); # a last chance to change things $self->handle_post_parsing($ctx, $package, defined($name) ? $name : $anon); } 1; 声明之前或之后注入了一些代码,但我无法弄清楚是什么。我已经尝试随机玩package子中的各种项目(我实际上并不知道他们在这一点上做了什么)但我似乎无法消除甚至改变错误。当然,没有办法(我知道)实际检查生成的代码,这可能会产生线索。

感谢您的帮助。

一些更新:在浏览MooseX :: Declare :: Context之后,我添加了一些parse语句,以确切了解通过调用print注入的内容。这是生成(整理)的实际代码:

inject_code_parts

我不能说我知道所做的一切(特别是 package Foo; use MyApp::MooseX::Declare outer_package => 'Foo', file => __FILE__, stack => [ MooseX::Declare::StackItem->new(q(identifier), q(class), q(handler), q(MyApp::MooseX::Declare::Syntax::Keyword::Class), q(is_dirty), q(0), q(is_parameterized), q(0), q(namespace), q(Foo)) ];; BEGIN { Devel::Declare::Context::Simple->inject_scope('BEGIN { MooseX::Declare::Util::outer_stack_pop __FILE__ }') }; ; 的事情),但这对我来说在语法上都很好。我仍然认为在所有这些之前注入代码会导致语法错误。

1 个答案:

答案 0 :(得分:1)

嗯,这是一个调试会议的地狱,但我终于找到了问题并弄明白了。在打开MooseX::Declare::ContextDevel::Declare::Context::Simple(前任代理人)之后,我能够跟踪流程并通过大量转储到STDOUT,我意识到MooseSetup.pm中的一些额外处理程序,我以为我已经正确地编写了我的关键字类,实际上并没有。因此注入的结果代码没有附加正确的阴影/清理内容。

无论如何,我现在有一个看似完全正常工作的定制MooseX :: Declare!我真的很兴奋 - 这意味着我可以输入

use MyApp::Setup; 

class MyApp::Foo { ... }

并且一个class语句设置了一大堆特定于应用程序的样板。弧度。