File Coverage

blib/lib/Getopt/EX/Func.pm
Criterion Covered Total %
statement 53 65 81.5
branch 11 18 61.1
condition 9 12 75.0
subroutine 12 16 75.0
pod 0 7 0.0
total 85 118 72.0


line stmt bran cond sub pod time code
1             package Getopt::EX::Func;
2              
3             our $VERSION = "3.03";
4              
5 10     10   119673 use v5.14;
  10         32  
6 10     10   44 use warnings;
  10         17  
  10         435  
7 10     10   43 use Carp;
  10         16  
  10         646  
8              
9 10     10   41 use Exporter 'import';
  10         25  
  10         880  
10             our @EXPORT = qw();
11             our @EXPORT_OK = qw(parse_func callable arg2kvlist);
12             our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
13              
14 10     10   49 use Data::Dumper;
  10         15  
  10         480  
15              
16 10     10   48 use Scalar::Util qw(blessed);
  10         35  
  10         1885  
17             sub callable {
18 0     0 0 0 my $target = shift;
19 0 0       0 blessed $target and $target->can('call');
20             }
21              
22             sub new {
23 8     8 0 15 my $class = shift;
24 8         95 my $obj = bless [ @_ ], $class;
25             }
26              
27             sub append {
28 0     0 0 0 my $obj = shift;
29 0         0 push @$obj, @_;
30             }
31              
32             sub call {
33 8     8 0 161 my $obj = shift;
34 8         21 unshift @_, @$obj;
35 8         15 my $name = shift;
36              
37 10     10   71 no strict 'refs';
  10         17  
  10         1008  
38 8         106 goto &$name;
39             }
40              
41             ##
42             ## Create a closure that calls the named function with preset arguments.
43             ## Used internally when parse_func is called with the 'pointer' option.
44             ## The 'package main' may be unnecessary since $name is fully qualified,
45             ## but is kept for safety in case of future changes.
46             ##
47             sub closure {
48 0     0 0 0 my $name = shift;
49 0         0 my @argv = @_;
50             sub {
51             package main;
52 10     10   66 no strict 'refs';
  10         16  
  10         7084  
53 0     0   0 unshift @_, @argv;
54 0         0 goto &$name;
55             }
56 0         0 }
57              
58             ##
59             ## sub { ... }
60             ## funcname(arg1,arg2,arg3=val3)
61             ## funcname=arg1,arg2,arg3=val3
62             ##
63              
64             ##
65             ## Regex to match balanced parentheses, including nested ones.
66             ## Uses recursive subpattern (?-1) to match inner parentheses.
67             ## Possessive quantifiers (++ and *+) prevent backtracking for efficiency.
68             ##
69             my $paren_re = qr/( \( (?: [^()]++ | (?-1) )*+ \) )/x;
70              
71             ##
72             ## Key name pattern for function arguments.
73             ## Should be \w+ but currently allows more characters for historical reasons.
74             ##
75             my $key_re = qr/[^,=*\/]+/;
76              
77             sub parse_func {
78 8 100   8 0 31 my $opt = ref $_[0] eq 'HASH' ? shift : {};
79 8         46 local $_ = shift;
80 8         20 my $noinline = $opt->{noinline};
81 8         14 my $pointer = $opt->{pointer};
82 8         23 my $caller = caller;
83 8   66     24 my $pkg = $opt->{PACKAGE} || $caller;
84              
85 8         16 my @func;
86              
87 8 100 66     528 if (not $noinline and /^sub\s*{/) {
    50          
88 2         256 my $sub = eval "package $pkg; $_";
89 2 50       10 if ($@) {
90 0         0 warn "Error in function -- $_ --.\n";
91 0         0 die $@;
92             }
93 2 50       8 croak "Unexpected result from eval.\n" if ref $sub ne 'CODE';
94 2         8 @func = ($sub);
95             }
96             elsif (m{^ &? (? [\w:]+ ) (? $paren_re | =.* )? $}x) {
97 6         78 my $name = $+{name};
98 6   50     44 my $arg = $+{arg} // '';
99 6         15 $arg =~ s/^ (?| \( (.*) \) | = (.*) ) $/$1/x;
100 6 100       39 $name =~ s/^/$pkg\::/ unless $name =~ /::/;
101 6         21 @func = ($name, arg2kvlist($arg));
102             }
103             else {
104 0         0 return undef;
105             }
106              
107 8 50       49 __PACKAGE__->new( $pointer ? closure(@func) : @func );
108             }
109              
110             ##
111             ## convert "key1,key2,key3=val3" to (key1=>1, key2=>1, key3=>"val3")
112             ##
113             ## *= takes the rest of the string as a value
114             ## e.g., "key1,key2*=a,b,c" => (key1=>1, key2=>"a,b,c")
115             ##
116             ## /= uses the next character as a delimiter
117             ## e.g., "key1,key2/=/a,b,c/,next" => (key1=>1, key2=>"a,b,c", next=>1)
118             ##
119             sub arg2kvlist {
120 24     24 0 175821 my @kv;
121 24         44 for (@_) {
122 24         594 while (/\G \s* (?(?>${key_re}))
123             (?: \*= (?.*)
124             | \/= (?.) (?.*?) \g{delim} (?=,|\z) ,*
125             | (?: = (? (?:[^,()]++ | ${paren_re})*+ ) )? ,* )
126             /xgcs) {
127 34   100     320 push @kv, ( $+{key}, $+{value} // 1 );
128             }
129 24 50 100     87 (pos() // 0) == length or die "parse error in \"$_\".\n";
130             }
131 24         123 @kv;
132             }
133              
134             1;
135              
136             =head1 NAME
137              
138             Getopt::EX::Func - Function call interface
139              
140              
141             =head1 SYNOPSIS
142              
143             use Getopt::EX::Func qw(parse_func);
144              
145             my $func = parse_func("func_name(key=value,flag)");
146              
147             $func->call;
148              
149             =head1 DESCRIPTION
150              
151             This module provides a way to create function call objects used in the
152             L module set.
153              
154             For example, suppose your script has a B<--begin> option that
155             specifies a function to call at the beginning of execution. You can
156             implement it like this:
157              
158             use Getopt::EX::Func qw(parse_func);
159              
160             GetOptions("begin:s" => \$opt_begin);
161              
162             my $func = parse_func($opt_begin);
163              
164             $func->call;
165              
166             The user can then invoke the script as:
167              
168             % example -Mfoo --begin 'repeat(debug,msg=hello,count=2)'
169              
170             The function C should be declared in module C or in a
171             startup rc file such as F<~/.examplerc>. It can be implemented like
172             this:
173              
174             our @EXPORT = qw(repeat);
175             sub repeat {
176             my %opt = @_;
177             print Dumper \%opt if $opt{debug};
178             say $opt{msg} for 1 .. $opt{count};
179             }
180              
181             =head1 FUNCTION SPEC
182              
183             The C function accepts the following string formats.
184              
185             A function name can optionally be prefixed with C<&>, and parameters
186             can be specified in two equivalent forms using parentheses or C<=>:
187              
188             func(key=value,key2=value2)
189             func=key=value,key2=value2
190             &func(key=value)
191              
192             So the following two commands are equivalent:
193              
194             % example --begin 'repeat(debug,msg=hello,count=2)'
195             % example --begin 'repeat=debug,msg=hello,count=2'
196              
197             Both will call the function as:
198              
199             repeat( debug => 1, msg => 'hello', count => '2' );
200              
201             Arguments are passed as I =E I pairs. Parameters
202             without a value (C in this example) are assigned the value 1.
203             Key names should only contain word characters (C<\w>: alphanumeric and
204             underscore). Currently, any characters except C<,>, C<=>, C<*>, and
205             C are accepted for historical reasons, but this may change in the
206             future.
207              
208             Commas normally separate parameters. If a value needs to contain
209             commas, there are two ways to handle this:
210              
211             =over 4
212              
213             =item Parentheses
214              
215             Commas inside parentheses are preserved:
216              
217             func(pattern=(a,b,c),debug)
218              
219             This calls:
220              
221             func( pattern => '(a,b,c)', debug => 1 );
222              
223             Note that the parentheses are included in the value.
224              
225             =item Asterisk-Equals
226              
227             Use C<*=> instead of C<=> to capture the entire remaining string as
228             the value:
229              
230             func(debug,pattern*=a,b,c)
231              
232             This calls:
233              
234             func( debug => 1, pattern => 'a,b,c' );
235              
236             Since C<*=> consumes the rest of the string, no parameters can follow
237             it.
238              
239             =item Slash-Equals with Delimiter
240              
241             Use C followed by a delimiter character to quote a value. The
242             first character after C becomes the delimiter, and the value
243             continues until the same delimiter appears again:
244              
245             func(debug,pattern/=/a,b,c/,verbose)
246              
247             This calls:
248              
249             func( debug => 1, pattern => 'a,b,c', verbose => 1 );
250              
251             The delimiter can be any character. Choose one that does not appear
252             in the value:
253              
254             func(pattern/=/a,b,c/) # / as delimiter
255             func(path/=|/usr/local/bin|) # | as delimiter for paths
256             func(text/=:hello:world:) # : as delimiter
257              
258             For scripting, control characters like BEL (C<\x07>) or US (C<\x1f>,
259             Unit Separator) can be used as delimiters to avoid conflicts with any
260             printable characters:
261              
262             $delim = "\x07"; # BEL
263             $delim = "\x1f"; # US (Unit Separator)
264             $arg = "data/=${delim}any/chars=here,${delim}";
265              
266             =back
267              
268             An anonymous subroutine can also be specified inline:
269              
270             % example --begin 'sub{ say "wahoo!!" }'
271              
272             The function is evaluated under C, so features like C
273             are available.