Синтаксическая ошибка во вставленном коде при расширении MooseX :: Declare - PullRequest
3 голосов
/ 29 октября 2011

Это большой, поэтому, пожалуйста, потерпите меня.В конце есть горшок с золотом.

По экспериментальным причинам я пытаюсь создать собственное расширение MooseX :: Declare, которое создает дополнительную магию, полезную для конкретного хобби-проекта.Например, я хочу, чтобы ключевое слово class добавляло немного дополнительных вещей, таких как импорт полезных утилит из List :: Util и т. П., Включение различных дополнительных прагм (кроме strict и warnings), автоматический импортмой глобальный объект Config и т. д.

Поэтому я написал следующий тест и отправился посмотреть, смогу ли я заставить его работать.Удивительно, но мне удалось пройти 99% пути, но теперь я столкнулся с проблемой, которую не могу понять.Мое пользовательское ключевое слово 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;

MyApp::Setup выглядит следующим образом.В будущем он сделает еще кое-что, но сейчас он просто вызывает import в моем подклассе MX :: D:

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.

package MyApp::MooseX::Declare::Syntax::Keyword::Class;

use Moose;

extends 'MooseX::Declare::Syntax::Keyword::Class';
with 'MyApp::MooseX::Declare::Syntax::NamespaceHandling';

1;

(два других идентичных.)

В реальном MX ::D, материал NamespaceHandling состоит из отдельной роли, называемой MooseSetup, которая сама состоит из класса ключевых слов.Делать все это в одном месте, кажется, работает;Я не знаю, является ли небольшое отклонение в структуре источником моей проблемы.В какой-то момент у меня была своя собственная версия MooseSetup, но это привело к конфликтам композиций, которые я не мог понять.

Наконец, мясо и картошка - это моя версия NamespaceHandling, которая переопределяет метод parse,Большая его часть просто копируется и вставляется из оригинала.

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;

Когда я запускаю тест, все выглядит хорошо - я получаю предупреждающие сообщения, указывающие, что вызываются правильные методыи что пакет "Foo" настраивается.Затем он умирает с:

синтаксической ошибкой в ​​строке t / default.t 5, рядом с "{package Foo"

Так что похоже на что-то вводит некоторый код прямо перед или после объявления package, которое вызывает синтаксическую ошибку, но я не могу понять, что.Я пытался случайным образом поиграть с различными предметами в сабвуфере parse (на самом деле я не знаю, что они все делают в данный момент), но я не могу устранить или даже изменить ошибку.И, конечно же, я не могу (насколько мне известно) проверить фактически сгенерированный код, который может дать подсказку.

Спасибо за вашу помощь.

Хостинг imgur.com

Некоторые обновления: Осмотрев внутри MooseX :: Declare :: Context, я добавил несколько операторов 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__ }') }; ;

Я не могу сказать, что знаю, что все это делает (особенно вещь outer_stack_pop), но все это выглядит синтаксически нормально для меня.Я все еще думаю, что что-то вводит код перед всем этим, что вызывает синтаксическую ошибку.

1 Ответ

1 голос
/ 30 октября 2011

Ну, это был адский сеанс отладки, но я наконец-то отследил проблему и выяснил ее. После взлома как MooseX::Declare::Context, так и Devel::Declare::Context::Simple (к которому пришли бывшие делегаты) я смог отследить поток и через обильный дамп в STDOUT я понял, что некоторые из дополнительных обработчиков из MooseSetup.pm, которые, как мне показалось, были правильными составлены из моих классов ключевых слов, на самом деле там не было. Таким образом, полученный код, который был внедрен, не имеет соответствующих вложенных теней и средств очистки.

В любом случае, теперь у меня есть то, что кажется полностью работающим настроенным MooseX :: Declare! Я действительно взволнован об этом - это означает, что я могу напечатать

use MyApp::Setup; 

class MyApp::Foo { ... }

и этот оператор class создает целую путаницу шаблонного приложения. Rad.

...