Это большой, поэтому, пожалуйста, потерпите меня.В конце есть горшок с золотом.
По экспериментальным причинам я пытаюсь создать собственное расширение 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
), но все это выглядит синтаксически нормально для меня.Я все еще думаю, что что-то вводит код перед всем этим, что вызывает синтаксическую ошибку.