package Getopt::EX::Func; use v5.14; use warnings; use Carp; use Exporter 'import'; our @EXPORT = qw(); our @EXPORT_OK = qw(parse_func callable); our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] ); use Data::Dumper; use Scalar::Util qw(blessed); sub callable { my $target = shift; blessed $target and $target->can('call'); } sub new { my $class = shift; my $obj = bless [ @_ ], $class; } sub append { my $obj = shift; push @$obj, @_; } sub call { my $obj = shift; unshift @_, @$obj; my $name = shift; no strict 'refs'; goto &$name; } sub closure { my $name = shift; my @argv = @_; sub { package main; # XXX no strict 'refs'; unshift @_, @argv; goto &$name; } } ## ## sub { ... } ## funcname(arg1,arg2,arg3=val3) ## funcname=arg1,arg2,arg3=val3 ## my $paren_re = qr/( \( (?: [^()]++ | (?-1) )*+ \) )/x; sub parse_func { my $opt = ref $_[0] eq 'HASH' ? shift : {}; local $_ = shift; my $noinline = $opt->{noinline}; my $pointer = $opt->{pointer}; my $caller = caller; my @func; if (not $noinline and /^sub\s*{/) { my $sub = eval $_; if ($@) { warn "Error in function -- $_ --.\n"; die $@; } croak "Unexpected result from eval.\n" if ref $sub ne 'CODE'; @func = ($sub); } elsif (m{^ &? (?<name> [\w:]+ ) (?<arg> $paren_re | =.* )? $}x) { my $name = $+{name}; my $arg = $+{arg} // ''; $arg =~ s/^ (?| \( (.*) \) | = (.*) ) $/$1/x; my $pkg = $opt->{PACKAGE} || $caller; $name =~ s/^/$pkg\::/ unless $name =~ /::/; @func = ($name, arg2kvlist($arg)); } else { return undef; } __PACKAGE__->new( $pointer ? closure(@func) : @func ); } ## ## convert "key1,key2,key3=val3" to (key1=>1, key2=>1, key3=>"val3") ## sub arg2kvlist { my @kv; for (@_) { while (/\G (?<k> [\w:]+ ) (?: = (?<v> (?: [^,()]++ | ${paren_re} )*+ ) )? ,*/xgc ) { push @kv, ( $+{k}, $+{v} // 1 ); } my $pos = pos() // 0; if ($pos != length) { die "parse error in \"$_\".\n"; } } @kv; } 1; =head1 NAME Getopt::EX::Func - Function call interface =head1 SYNOPSIS use Getopt::EX::Func qw(parse_func); my $func = parse_func(...); $func->call; =head1 DESCRIPTION This module provides the way to create function call object used in L<Getopt::EX> module set. If your script has B<--begin> option which tells the script to call specific function at the beginning of execution. You can do it like this: use Getopt::EX::Func qw(parse_func); GetOptions("begin:s" => $opt_begin); my $func = parse_func($opt_begin); $func->call; Then script can be invoked like this: % example -Mfoo --begin 'repeat(debug,msg=hello,count=2)' In this example, function C<repeat> should be declared in module C<foo> or in start up rc file such as F<~/.examplerc>. Actual function call is done in this way: repeat ( debug => 1, msg => 'hello', count => '2' ); As you can notice, arguments in the function call string is passed in I<name> =E<gt> I<value> style. Parameter without value (C<debug> in this example) is assigned value 1. Function itself can be implemented like this: our @EXPORT = qw(repeat); sub repeat { my %opt = @_; print Dumper \%opt if $opt{debug}; for (1 .. $opt{count}) { say $opt{msg}; } } It is also possible to declare the function in-line: % example -Mfoo --begin 'sub{ say "wahoo!!" }' Function C<say> can be used because the function is executed under C<use v5.14> context.