Keyword-Declare
view release on metacpan or search on metacpan
lib/Keyword/Declare.pm view on Meta::CPAN
my %keywords = %{$^H{+HINTK_KEYWORDS} // {}};
$keywords{$kw} = $sub;
$^H{+HINTK_KEYWORDS} = \%keywords;
}
}
# Install a __DATA__ keyword to overcome bug in Keyword::Simple...
# [REMOVE WHEN UPSTREAM MODULE (Keyword::Simple) IS FIXED]
sub _install_data_handler {
my $DATA_HANDLER = sub {
# Unpack trailing code...
my ($src_ref) = @_;
# Convert to data handle...
my $data = $$src_ref;
$data =~ s{ \A [^\n]* \n }{}xms;
$data .= "\n" unless substr($data,-1) eq "\n";
# Create end-of-__DATA__ marker unlikely to be in the data...
my $END_DATA = "\3" x 253; # \3 is ASCII END-OF-TEXT, 253 is max ident length
# Replace trailing code with code that opens a local *DATA handle...
$$src_ref = qq{BEGIN {open *DATA, '<', \\<<'$END_DATA'\n$data$END_DATA\n} 1; };
};
_replacement_define('__DATA__', $DATA_HANDLER);
}
# Remove special prefix on names of internal named captures...
sub _deprefix {
my ($hash_ref) = @_;
return undef if !defined $hash_ref;
return { map { my $key = $_; $key =~ s{^____K_D___}{}; $key => $hash_ref->{$_} }
keys %{$hash_ref} };
}
# Generate the source code that actually installs a keyword...
sub _build_keyword_code {
my ($keyword_name, $keyword_sig, $keyword_ID, $keyword_block, $block_location, $block_hashline, $prefix_var)
= @{shift()}{qw< keyword sig_desc ID block location hashline prefix >};
# Generate the keyword definition and set up its unique lexical ID...
return qq{
\$^H{"Keyword::Declare active:$keyword_name:\Q$keyword_sig\E"} = $keyword_ID;
Keyword::Declare::_replacement_define('$keyword_name', Keyword::Declare::_get_dispatcher_for('$keyword_name',
$keyword_ID, sub
#line $block_hashline
{ $keyword_impls[$keyword_ID]{sig_vars_unpack} do $keyword_block }));
};
}
# Locate prefix code for keyword...
sub _get_prefix {
state $source_cache = {};
my ($trail_ref, $keyword) = @_;
my $filename = (caller 2)[1];
my $source = $source_cache->{$filename} //= do { local (*ARGV, $/); @ARGV=$filename; <> };
my $trailing = $$trail_ref;
$trailing =~ s/\s+\z//;
$source =~ s{\b$keyword\s*\Q$trailing\E\s*\z}{};
return 'qq{' . quotemeta($source) . '}';
}
# Install keyword's source-code generator, and return a dispatcher sub for that keyword
# (building a closure for it, if necessary)...
sub _get_dispatcher_for {
my ($keyword_name, $keyword_ID, $keyword_generator) = @_;
# Install the keyword generator sub...
$keyword_impls[$keyword_ID]{generator} = $keyword_generator;
# This will dispatch any keyword of the specified name...
state %dispatcher_for;
return $dispatcher_for{$keyword_name} //= sub {
my ($src_ref) = @_;
my ($package, $file, $line) = caller;
local $PPR::ERROR;
# Which variants of this keyword are currently in scope???
my @candidate_IDs = @^H{ grep { m{^ Keyword::Declare \s+ active:$keyword_name:}xms } keys %^H };
# Which keywords are allowed in nested code at this point...
my @active_IDs = @^H{ grep { m{^ Keyword::Declare \s+ active:}xms } keys %^H };
my $lexical_keywords
= @active_IDs ? join '|', reverse sort map { $keyword_impls[$_]{skip_matcher} } @active_IDs
: '(?!)';
$lexical_keywords = "(?(DEFINE) (?<PerlKeyword> $lexical_keywords ) )";
# Which of them match the keyword's actual arguments???
my @viable_IDs
= grep { $$src_ref =~ m{ \A $keyword_impls[$_]{sig_matcher} $lexical_keywords $PPR::GRAMMAR }xms }
@candidate_IDs;
# If none of them match...game over!!!
if (!@viable_IDs) {
my $error = eval "no strict;sub{\n" . ($PPR::ERROR//q{}) . '}'
? " $keyword_name "
. do{ my $src = $$src_ref;
$src =~ s{ \A \s*+ (\S++ [^\n]*+) \n .* }{$1}xs;
$src;
}
: do{ my $err = $@;
$err =~ s{^}{ }gm;
$err =~ s{\(eval \d++\) line \d++}
{ "$file line " . $PPR::ERROR->line($line) }eg;
$err
};
croak "Invalid "
. join(" or ", uniqstr map { $keyword_impls[$_]{desc} } @candidate_IDs)
. " at $file line $line.\nExpected:"
. join("\n ", q{}, uniqstr map { $keyword_impls[$_]{syntax} } @candidate_IDs)
. "\nbut found:\n$error"
. "\nCompilation failed";
( run in 0.936 second using v1.01-cache-2.11-cpan-5a3173703d6 )