=item expand_string Expands a string to possibly a set of strings. Inputs: =over 4 =item * String to expand. =item * Function used to look up the variable. The function will be passed the variable to be interpolated in using $_. The function should return a list of replacement values. The function may set any return value to undef to signify that the variable should be left intact and unexpanded. An empty string signifies removal of the variable in its entirety. And returning an array will cause the whole expand_string function to return each possible item as an array. This allows you to pass in a single string to expand into a plethora of output strings all at once. The default function is below. It is recommended that the given function call the default function in cases where it cannot resolve the variable. The default function will die with an error if it cannot resolve the variable which may be caught with eval. =back The variable may have modifiers: (variable:+foo) This means that if variable is non-zero length, use "foo" instead. If the variable expands to nothing (or undef), use that instead (blank, or leave the variable unchanged as appropriate). (variable:-foo) This means that if variable is zero length (or undef), use "foo" instead. (variable:/blah/baz) (variable:|blah|baz) This means to expand variable, and, if successful, modify the value before replacement using "s/blah/baz/g". Regular expressions are allowed. Two forms are permitted in case one is using the special character you need. Note that the ) or } characters are not permitted in here. =cut sub expand_string { my $self = shift; my $string = shift; my $func = shift || sub { $self->expand_variable(@_) }; local $_; my $varre = qr/ ([^:]*?) # variable name (?::(.*?))? # special instructions /x; my @s = split / (?: # either (\()$varre(\)) # parenthesis variable ) | # or... (?: (\{)$varre(\}) # brace variable )/x, $string; my $output; my $replace_sub; $replace_sub = sub { return '' if @_ == 0; return @_ if @_ == 1; my ($pre, $open, $var, $alternate, $close, @post) = @_[0, defined $_[1] ? (1..4) : (5..8), 9..$#_]; map { if ($alternate) { my ($cmd, $alt) = $alternate =~ /^(.)(.*)$/; if ($cmd eq '+') { if (defined and length) { $_ = $alt; } } elsif ($cmd eq '-') { unless (defined and length) { $_ = $alt; } } elsif ($cmd eq '/' or $cmd eq '|') { my ($re,$replace) = split /$cmd/, $alt; eval "s$cmd$re$cmd$replace$cmd"; die $@ if $@; } } if (not defined) { $_ = join '', $open, $var, $alternate ? (':',$alternate) : (), $close; } my $this = $pre . $_; map { $this . $_ } $replace_sub->(@post); } do { local $_ = $var; ($func->()) }; }; my %rc; { my $i = 0; $rc{$_} ||= ++$i foreach $replace_sub->(@s); } my @r = sort { $rc{$a} <=> $rc{$b} } keys %rc; if (wantarray) { @r; } elsif (scalar @r == 1) { $r[0]; } else { \@r; } } =item expand_variable Expands $_ to the values that it cares about. Returns an array of values for this variable. =cut sub expand_variable { my $self = shift; # just a dummy for now. qw(A B); # could be: # map { /^(.*)\.\.(.*)/ ? $1..$2 : $_ } split /,/ # or something like that, for example. }