+sub _study {
+ my ($self) = @_;
+ my @hunks = grep { defined && length } split /(\{.+?\})/, $self->template;
+ for (@hunks) {
+ next unless /^\{(.+?)\}$/;
+ $_ = $self->_compile_expansion($1);
+ }
+ $self->{studied} = \@hunks;
+}
+
+sub _op_gen_join {
+ my ($self, $exp) = @_;
+
+ return sub {
+ my ($var) = @_;
+
+ my @pairs;
+ for my $keypair (@{ $exp->{vars} }) {
+ my $key = $keypair->[ 0 ];
+ my $val = $keypair->[ 1 ]->( $var );
+ next if !exists $var->{$key} && $val eq '';
+ Carp::croak "invalid variable ($key) supplied to join operator"
+ if ref $var->{$key};
+
+ push @pairs, $key . '=' . $val;
+ }
+ return join $exp->{arg}, @pairs;
+ };
+}
+
+sub _op_gen_opt {
+ my ($self, $exp) = @_;
+
+ Carp::croak "-opt accepts exactly one argument" if @{ $exp->{vars} } != 1;
+
+ my $value = $exp->{arg};
+ my $varname = $exp->{vars}->[0]->[0];
+
+ return sub {
+ my ($var) = @_;
+ return '' unless exists $var->{$varname} and defined $var->{$varname};
+ return '' if ref $var->{$varname} and not @{ $var->{$varname} };
+
+ return $value;
+ };
+}
+
+sub _op_gen_neg {
+ my ($self, $exp) = @_;
+
+ Carp::croak "-neg accepts exactly one argument" if @{ $exp->{vars} } != 1;
+
+ my $value = $exp->{arg};
+ my $varname = $exp->{vars}->[0]->[0];
+
+ return sub {
+ my ($var) = @_;
+ return $value unless exists $var->{$varname} && defined $var->{$varname};
+ return $value if ref $var->{$varname} && ! @{ $var->{$varname} };
+
+ return '';
+ };
+}
+
+sub _op_gen_prefix {
+ my ($self, $exp) = @_;
+
+ Carp::croak "-prefix accepts exactly one argument" if @{$exp->{vars}} != 1;
+
+ my $prefix = $exp->{arg};
+ my $name = $exp->{vars}->[0]->[0];
+
+ return sub {
+ my ($var) = @_;
+ return '' unless exists $var->{$name} && defined $var->{$name};
+ my $array = ref $var->{$name} ? $var->{$name} : [ $var->{$name} ];
+ return '' unless @$array;
+
+ return join '', map { "$prefix$_" } @$array;
+ };
+}
+
+sub _op_gen_suffix {
+ my ($self, $exp) = @_;
+
+ Carp::croak "-suffix accepts exactly one argument" if @{$exp->{vars}} != 1;
+
+ my $suffix = $exp->{arg};
+ my $name = $exp->{vars}->[0]->[0];
+
+ return sub {
+ my ($var) = @_;
+ return '' unless exists $var->{$name} && defined $var->{$name};
+ my $array = ref $var->{$name} ? $var->{$name} : [ $var->{$name} ];
+ return '' unless @$array;
+
+ return join '', map { "$_$suffix" } @$array;
+ };
+}
+
+sub _op_gen_list {
+ my ($self, $exp) = @_;
+
+ Carp::croak "-list accepts exactly one argument" if @{$exp->{vars}} != 1;