package Sub::QuoteX::Utils;

# ABSTRACT: Sugar for Sub::Quote

use 5.006;

use strict;
use warnings;

our $VERSION = '0.08';

use Sub::Quote
  qw( quoted_from_sub inlinify capture_unroll sanitize_identifier quote_sub );

use Scalar::Util qw( weaken refaddr blessed );
use Carp;

use Exporter 'import';

our @EXPORT_OK = qw(
  quote_subs
  inlinify_coderef
  inlinify_method
  inlinify_code
);

our %EXPORT_TAGS = ( all => \@EXPORT_OK );


#pod =func quote_subs
#pod
#pod   my $coderef = quote_subs( $spec, ?$spec, ... , ?\%options );
#pod
#pod Creates a compiled subroutine from syntactically complete chunks of
#pod code or from snippets of code.
#pod
#pod Chunks may be extracted from code previously inlined by L<Sub::Quote>,
#pod specified as strings containing code, or generated to accomodate
#pod invoking object methods or calling non-inlineable code.
#pod
#pod By default each chunk will localize C<@_> to avoid changing C<@_> for
#pod the other chunks. This can be changed on a per-chunk basis by
#pod specifying the C<local> option in each specification.
#pod
#pod Specifications may take one of the following forms:
#pod
#pod =over
#pod
#pod =item C<$coderef>
#pod
#pod If C<$coderef> is inlineable (i.e, generated by
#pod L<Sub::Quote/quote_sub>) it will be directly inlined, else code to
#pod invoke it will be generated.
#pod
#pod =item C<[ $coderef, %option ]>
#pod
#pod This is another way of specifying a code reference, allowing
#pod more manipulation; see L</inlinify_coderef> for available options.
#pod
#pod =item C<[ $object, $method, %option ]>
#pod
#pod Inline a method call. A weakened reference to C<$object> is kept to
#pod avoid leaks. Method lookup is performed at runtime.  See
#pod L</inlinify_method> for available options.
#pod
#pod =item C<[ $string, %option ]>
#pod
#pod Inline a chunk of code in a string. See L</inlinify_code> for
#pod available options.
#pod
#pod =item C<$scalarref>
#pod
#pod Inline a snippet of code stored in the referenced scalar.  Snippets
#pod need not be syntactically complete, and thus may be used to enclose
#pod chunks in blocks. For example, to catch exceptions thrown by a chunk:
#pod
#pod    $coderef = quote_subs( \'eval {', \&chunk_as_func, \'};' );
#pod
#pod Specify any required captured values in the C<capture> option to
#pod C<quote_subs>.
#pod
#pod =back
#pod
#pod If the C<store> option is passed in a specification, a lexical
#pod variable with the specified name will automatically be created.
#pod See L</Storing Chunk Values>.
#pod
#pod Options which may be passed as the last parameter include all of the
#pod options accepted by L<< C<Sub::Quote::quote_sub>|Sub::Quote/quote_sub
#pod >>, as well as:
#pod
#pod =over
#pod
#pod =item C<name> => I<string>
#pod
#pod An optional name for the compiled subroutine.
#pod
#pod =item C<capture> => I<hashref>
#pod
#pod A hash containing captured variable names and values.  See the
#pod documentation of the C<\%captures> argument to L<Sub::Quote/quote_sub>
#pod for more information.
#pod
#pod
#pod =item C<lexicals> => I<scalar | arrayref >
#pod
#pod One or more lexical variables to declare. If specified, B<quote_subs>
#pod will enclose the generated code in a block and will declare these
#pod variables at the start of the block.  For example,
#pod
#pod   quote_subs( \'@x = 33;',
#pod               \'@y = 22;',
#pod               lexicals => [ '@x', '@y' ]
#pod   );
#pod
#pod will result in code equivalent to:
#pod
#pod   {
#pod     my ( @x, @y );
#pod     @x = 33;
#pod     @y = 22;
#pod   }
#pod
#pod
#pod =back
#pod
#pod
#pod =cut

# quote_subs( [], [], {} );
sub quote_subs {

    my @caller = caller( 0 );

# need to duplicate these bits from Sub::Quote::quote_sub, as they rely upon caller
    my %option = (
        lexicals     => [],
        package      => $caller[0],
        hints        => $caller[8],
        warning_bits => $caller[9],
        hintshash    => $caller[10],
        'HASH' eq ref $_[-1] ? %{ pop @_ } : (),
    );
    my %qsub_opts
      = map { $_ => $option{$_} } qw[ package hints warning_bits hintshash ];

    if ( $option{name} ) {
        my $subname = $option{name};
        my $package = $subname =~ s/(.*)::// ? $1 : $option{package};
        $option{name} = join '::', $package, $subname;
    }

    my %global_capture = %{ delete $option{capture} || {} };

    my @code;
    for my $thing ( @_ ) {

        my @arr = 'ARRAY' eq ref $thing ? @$thing : ($thing);

        # invoke appropriate inlinify subroutine, then remove
        # non-optional arguemnts from the argument list
        if ( 'CODE' eq ref $arr[0] ) {

            push @code, inlinify_coderef( \%global_capture, @arr ), q[;] ;
        }

        elsif ( blessed $arr[0] ) {

            push @code, inlinify_method( \%global_capture, @arr ), q[;] ;

            # this one gets two non-optional arguments, remove the
            # first; the second is done below
            shift @arr;
        }

        elsif ( !ref $arr[0] ) {

            push @code, inlinify_code( \%global_capture, @arr ), q[;] ;
        }

	elsif ( 'SCALAR' eq ref $arr[0] ) {

	    push @code, ${ $arr[0] };

	}
	else {

	    croak( "don't understand argument in $_[@{[ scalar @code ]}]\n" );
	}
        # remove the remaining non-optional argument
        shift @arr;

        my %opt = @arr;

        # if we're storing the results in a lexical variable, declare it
        if ( defined $opt{store} ) {
            $option{lexicals} = [ $option{lexicals} ]
              unless 'ARRAY' eq ref $option{lexicals};

            if ( $opt{store} =~ /^[\$@%]/ ) {
                push @{ $option{lexicals} }, $opt{store};
            }
            else {
                push @{ $option{lexicals} },
		  '$' . $opt{store},
                  '@' . $opt{store};
            }
        }
    }

    $option{lexicals} = [ $option{lexicals} ]
      unless 'ARRAY' eq ref $option{lexicals};
    if ( @{ $option{lexicals} } ) {

        # uniqify
        my %lex;
        @lex{ @{ $option{lexicals} } } = 1;

        unshift @code, qq/{ my ( @{[ join ', ', keys %lex ]} );/;

        push @code, '}';
    }


    quote_sub(
        ( delete $option{name} || () ),
        join( "\n", @code ),
        \%global_capture, \%qsub_opts
    );

}

sub _process_options {

    my ( $option, $capture ) = @_;

    $option->{provide_args} = 1;

    if ( exists $option->{args} ) {

	if ( defined $option->{args} ) {

	    if ( my $ref = ref $option->{args} ) {

		my $arg = $option->{args};
		$capture->{'$arg'} = \$arg;

		$option->{args}
		  = 'ARRAY' eq $ref ? '@{$arg}'
		    : 'HASH' eq $ref  ? '%{$arg}'
		      :   croak( q[args option must be an arrayref, hashref, or string] );
	    }
	}

	# args explicitly undef, set @_ to ();
	else {

            $option->{args} = '()';
	    $option->{provide_args} = 0;
	}
    }

    else {

	$option->{args} = '@_';
    }
}


#pod =func inlinify_coderef
#pod
#pod   my $code = inlinify_coderef( \%global_capture, $coderef, %options );
#pod
#pod Generate code which will execute C<$coderef>. If C<$coderef> is
#pod inlineable, it is inlined, else code which will invoke it is generated.
#pod
#pod See L</Captures> for more information on C<%global_capture>.
#pod
#pod Available options are:
#pod
#pod =over
#pod
#pod =item C<name> => I<string>
#pod
#pod An optional string used as part of the hash key for this chunk's captures.
#pod
#pod =item C<local> => I<boolean>
#pod
#pod If true (the default) changes to C<@_> will be local, e.g.
#pod
#pod   local @_ = ...;
#pod
#pod rather than
#pod
#pod   @_ = ...;
#pod
#pod =item C<store> => I<variable>
#pod
#pod If specified, the result of the generated code will be stored in the variable
#pod of the given name.  For example
#pod
#pod   store => '@x'
#pod
#pod would result in code equivalent to:
#pod
#pod   @x = &$coderef;
#pod
#pod The variable is not declared. See L</Storing Chunk Values>.
#pod
#pod =item C<args> => I<arrayref> | I<hashref> | I<string> | C<undef>
#pod
#pod This specified the values of C<@_>.
#pod
#pod =over
#pod
#pod =item *
#pod
#pod if not specified, the value of C<@_> is unchanged.
#pod
#pod =item *
#pod
#pod if the value is C<undef>, C<@_> will be empty.
#pod
#pod =item *
#pod
#pod if the value is a reference to an array or hash, C<@_> will be set
#pod equal to its contents. Note that the reference is I<cached>, so
#pod
#pod =over
#pod
#pod =item *
#pod
#pod changes to its contents will be reflected in calls to the code.
#pod
#pod =item *
#pod
#pod there is the danger of memory leaks, as any non-weakened references in
#pod the structure will be destroyed only when both C<%global_capture> and
#pod any subroutines based on this are destroyed.
#pod
#pod =back
#pod
#pod =item *
#pod
#pod if a string, this is inlined directly, e.g.
#pod
#pod   args => q[( 'FRANK' )]
#pod
#pod results in
#pod
#pod   @_ = ( 'FRANK' )
#pod
#pod =back
#pod
#pod =back
#pod
#pod =cut

sub inlinify_coderef {

    my ( $global_capture, $coderef, %option ) = @_;

    croak( "\$coderef must be a CODEREF\n" )
      unless 'CODE' eq ref $coderef;

    my $qtd = quoted_from_sub( $coderef );

    my %capture;
    _process_options( \%option, \%capture );

    my $code;

    if ( $qtd ) {

	$code = $qtd->[1];
	$capture{$_} = $qtd->[2]{$_} for keys %{ $qtd->[2] };
    }
    else {

	$code = q[&$sub;];
	$capture{ '$sub' } = \$coderef;
    }


    inlinify_code( $global_capture, $code, capture => \%capture, %option );
}

#pod =func inlinify_method
#pod
#pod   my $code = inlinify_method( \%global_capture, $object, $method,  %options );
#pod
#pod Generate code which will invoke the method named by C<$method> on
#pod C<$object>.  While method resolution is performed at runtime,
#pod C<inlinify_method> checks that C<$method> is available for C<$object>
#pod and will C<croak> if not.
#pod
#pod See L</Captures> for more information on C<%global_capture>.
#pod
#pod Available options are:
#pod
#pod =over
#pod
#pod =item C<name> => I<string>
#pod
#pod An optional string used as part of the hash key for this chunk's captures.
#pod
#pod =item C<local> => I<boolean>
#pod
#pod If true (the default) changes to C<@_> will be local, e.g.
#pod
#pod   local @_ = ...;
#pod
#pod rather than
#pod
#pod   @_ = ...;
#pod
#pod =item C<store> => I<variable>
#pod
#pod If specified, the result of the generated code will be stored in the variable
#pod of the given name.  For example
#pod
#pod   store => '@x'
#pod
#pod would result in code equivalent to:
#pod
#pod   @x = $object->$method( @_ );
#pod
#pod The variable is not declared. See L</Storing Chunk Values>.
#pod
#pod =item C<args> => I<arrayref> | I<hashref> | I<string> | C<undef>
#pod
#pod This specified the values of C<@_>.
#pod
#pod =over
#pod
#pod =item *
#pod
#pod if not specified, the value of C<@_> is unchanged.
#pod
#pod =item *
#pod
#pod if the value is C<undef>, C<@_> will be empty.
#pod
#pod =item *
#pod
#pod if the value is a reference to an array or hash, C<@_> will be set
#pod equal to its contents. Note that the reference is I<cached>, so
#pod
#pod =over
#pod
#pod =item *
#pod
#pod changes to its contents will be reflected in calls to the code.
#pod
#pod =item *
#pod
#pod there is the danger of memory leaks, as any non-weakened references in
#pod the structure will be destroyed only when both C<%global_capture> and
#pod any subroutines based on this are destroyed.
#pod
#pod =back
#pod
#pod =item *
#pod
#pod if a string, this is inlined directly, e.g.
#pod
#pod   args => q[( 'FRANK' )]
#pod
#pod results in
#pod
#pod   @_ = ( 'FRANK' )
#pod
#pod =back
#pod
#pod =back
#pod
#pod =cut

sub inlinify_method {

    my ( $global_capture, $object, $method, %option ) = @_;

    weaken $object;

    croak( "\$method must be a method name\n" )
      unless ref $method eq '';

    croak( "object does not provide a method named $method" )
      unless $object->can( $method );


    my %capture = ( '$r_object' => \\$object );

    $option{name} ||= refaddr $capture{'$r_object'};

    _process_options( \%option, \%capture );

    inlinify_code( $global_capture,
		   join( '',
			 '${$r_object}->',
			 $method,
			 $option{provide_args} ? '( @_ )' : '()',
			 'if defined ${$r_object};',
		       ),

		   capture => \%capture, %option );

}

#pod =func inlinify_code
#pod
#pod   my $code = inlinify_code( \%global_capture, $code,  %options );
#pod
#pod Generate code which inlines C<$code> handling captures specified in C<%options>.
#pod
#pod Available options are:
#pod
#pod =over
#pod
#pod =item C<capture> => I<hashref>
#pod
#pod A hash containing captured variable names and values.  See the
#pod documentation of the C<\%captures> argument to L<Sub::Quote/quote_sub>
#pod for more information.
#pod
#pod =item C<name> => I<string>
#pod
#pod An optional string used as part of the hash key for this chunk's captures.
#pod
#pod =item C<local> => I<boolean>
#pod
#pod If true (the default) changes to C<@_> will be local, e.g.
#pod
#pod   local @_ = ...;
#pod
#pod rather than
#pod
#pod   @_ = ...;
#pod
#pod =item C<store> => I<variable>
#pod
#pod If specified, the result of the generated code will be stored in the variable
#pod of the given name.  For example
#pod
#pod   store => '@x'
#pod
#pod would result in code equivalent to:
#pod
#pod   @x = ... code ...;
#pod
#pod The variable is not declared. See L</Storing Chunk Values>.
#pod
#pod =item C<args> => I<arrayref> | I<hashref> | I<string> | C<undef>
#pod
#pod This specified the values of C<@_>.
#pod
#pod =over
#pod
#pod =item *
#pod
#pod if not specified, the value of C<@_> is unchanged.
#pod
#pod =item *
#pod
#pod if the value is C<undef>, C<@_> will be empty.
#pod
#pod =item *
#pod
#pod if the value is a reference to an array or hash, C<@_> will be set
#pod equal to its contents. Note that the reference is I<cached>, so
#pod
#pod =over
#pod
#pod =item *
#pod
#pod changes to its contents will be reflected in calls to the code.
#pod
#pod =item *
#pod
#pod there is the danger of memory leaks, as any non-weakened references in
#pod the structure will be destroyed only when both C<%global_capture> and
#pod any subroutines based on this are destroyed.
#pod
#pod =back
#pod
#pod =item *
#pod
#pod if a string, this is inlined directly, e.g.
#pod
#pod   args => q[( 'FRANK' )]
#pod
#pod results in
#pod
#pod   @_ = ( 'FRANK' )
#pod
#pod =back
#pod
#pod =back
#pod
#pod =cut

sub inlinify_code {

    my ( $global_capture, $code, %option ) = @_;

    my %capture = %{ delete $option{capture} || {} };

    _process_options( \%option, \%capture );

    my $r_capture = \%capture;

    $option{name} ||= refaddr $r_capture;
    my $cap_name = q<$capture_for_> . sanitize_identifier( $option{name} );
    $global_capture->{$cap_name} = \$r_capture;

    $option{args} ||= '@_';
    $option{local} = 1 unless defined $option{local};


    my $inlined_code
      = inlinify( $code, $option{args},
        capture_unroll( $cap_name, $r_capture, 0 ),
        $option{local} );

    if ( my $variable = $option{store} ) {

	my @code;

        if ( $variable =~ /^[\$@%]/ ) {

	    @code = ( qq/$variable = do {/,
		      $inlined_code,
		      q/};/ );

        }
        else {
	    @code = (
                 q/if ( defined wantarray() ) { /,
                 q/    if ( wantarray() ) {/,
		qq/        \@$variable = do {/, $inlined_code, q/};/,
		 q/    }/,
                 q/    else {/,
                qq/        \$$variable = do {/, $inlined_code, q/};/,
		 q/    }/,
		 q/} else { /, $inlined_code, q/ }/,
		 q/;/
		 );
        }

	return join( "\n", '',@code );
    }

    return $inlined_code;
}

1;

#
# This file is part of Sub-QuoteX-Utils
#
# This software is Copyright (c) 2016 by Smithsonian Astrophysical Observatory.
#
# This is free software, licensed under:
#
#   The GNU General Public License, Version 3, June 2007
#

=pod

=encoding UTF-8

=head1 NAME

Sub::QuoteX::Utils - Sugar for Sub::Quote

=head1 VERSION

version 0.08

=head1 SYNOPSIS

 use Sub::Quote;
 use Sub::QuoteX::Utils qw[ quote_subs ];
 
 my $sub;
 
 # class with method
 {
     package Yipee;
     use Moo;
     sub halloo { shift; print "Yipee, @_\n" }
 }
 
 # and the object
 my $object = Yipee->new;
 
 # quoted sub
 my $foo = quote_sub(
   q[ print "$foo: @_\n"],
   { '$foo' => \"Foo" }
 );
 
 
 # bare sub
 sub bar { print "Bar: @_\n" }
 
 
 # create single subroutine. each invoked piece of code will have a
 # localized view of @_
 $sub = quote_subs(
     \&bar,                             # bare sub
     $foo,                              # quoted sub
     [ q[ print "$goo: @_\n"],          # code in string with capture
       capture => { '$goo' => \"Goo" },
     ],
     [ $object, 'halloo' ],             # method call
 );
 
 
 # and run it
 $sub->( "Common" );
 
 # Bar: Common
 # Goo: Common
 # Foo: Common
 # Yipee: Common
 
 
 # now, give each a personalized @_
 $sub            = quote_subs(
     [ \&bar,                           # bare sub
       args      => [qw( Bar )]
     ],
     [ $foo,                            # quoted sub
       args      => [qw( Foo )]
     ],
     [ q[ print "$goo, @_\n"],          # code in string with capture
       capture => { '$goo' => \"Goo" },
       args    => [qw( Goo )],
     ],
     [ $object, 'halloo',               # method call
         args    => [qw( Yipee )]
     ],
 );
 
 $sub->( "Common" );
 
 # Bar: Bar
 # Foo: Foo
 # Goo: Goo
 # Yipee: Yipee
 
 # now, explicitly empty @_
 $sub = quote_subs(
     [ \&bar,                           # bare sub
       args => undef
     ],
     [ $foo,                            # quoted sub
       args => undef
     ],
     [ q[ print "$goo, @_\n"],          # code in string with capture
       capture => { '$goo' => \"Goo" },
       args    => undef,
     ],
     [ $object, 'halloo',               #method call
       args => undef
     ],
 );
 
 $sub->( "Common" );
 
 # Bar:
 # Foo:
 # Goo:
 # Yipee:

=head1 DESCRIPTION

B<Sub::QuoteX::Utils> provides a simplified interface to the process of
combining L<Sub::Quote> compatible code references with new code.

L<Sub::Quote> provides a number of routines to make code more
performant by inlining syntactically complete chunks of code into a
single compiled subroutine.

When a chunk of code is compiled into a subroutine by L<<
C<Sub::Quote::quote_sub()>|Sub::Quote/quote_sub >>, B<Sub::Quote>
keeps track of the code and any captured variables used to construct
that subroutine, so that new code can be added to the original code
and the results compiled into a new subroutine.

B<Sub::QuoteX::Utils> makes that latter process a little easier.

=head2 Usage

Typically, L</quote_subs> is used rather than the lower level
C<inlinify_*> routines.  C<quote_subs> is passed a list of chunk
specifications or snippets of code, and generates code which is
isolated in a Perl block.  Each code chunk is additionally isolated in
its own block, while code snippets are in the main block.  This
permits manipulation of the code chunk values.  This is schematically
equivalent to

  {
    <snippet>
    do { <chunk> };
    <snippet>
    do { <chunk> };
    do { <chunk> };
  }

The values of each chunk may be stored (see L</Storing Chunk Values>)
and manipulated by the code snippets.

=head2 Storing Chunk Values

A code chunk may have it's value stored in a lexical variable by
adding the C<store> option to the chunk's options.  For example,

  quote_subs( [ q{ sqrt(2); },    { store => '$x' } ],
              [ q{ log(2);  },    { store => '$y' } ],
              [ q{  ( 0..10 ); }, { store => '@z' } ], 
              \q{print $x + $y, "\n";},
  );

would result in code equivalent to:

  {
    my ( $x, $y, @z );

    $x = do { sqrt(2) };
    $y = do { log(2) };
    @z = do { ( 0.. 10 ) };
    print $x + $y, "\n";
  }

If the variable passed to C<store> has no sigil, e.g. C<x>, then the
calling context is taken into account.  In list context, the value is
stored in C<@x>, in scalar context it is stored in C<$x> and in void
context it is not stored at all.

Automatic declaration of the variables occurs only when
C<quote_subs> is used to generate the code.

=head2 Captures

B<Sub::Quote> keeps track of captured variables in hashes, I<copying>
the values.  For example,

 use Sub::Quote;
 
 my $sound = 'woof';
 
 my $emit = quote_sub( q{ print "$sound\n" }, { '$sound' => \$sound } );
 
 &$emit; # woof
 
 $sound = 'meow';
 
 &$emit; # woof

When combining chunks of inlined code, each chunk has it's own set of
captured values which must be kept distinct.

L</quote_subs> manages this for the caller, but when using the low
level routines ( L</inlinify_coderef>, L</inlinify_method>,
L</inlinify_code> ) the caller must manage the captures.  These
routines store per-chunk captures in their C<\%global_capture> argument.
The calling routine optionally may provide a mnemonic (but unique!)
string which will be part of the key for the chunk.

The C<%global_capture> hash should be passed to
L<Sub::Quote/quote_sub>, when the final subroutine is compiled.  For
example,

  my %global_capture;
  my $code = inlinify_coderef( \%global_capture, $coderef, %options );

  # add more code to $code [...]

  $new_coderef = Sub::Quote::quote_sub( $code, \%global_capture );

=head1 FUNCTIONS

=head2 quote_subs

  my $coderef = quote_subs( $spec, ?$spec, ... , ?\%options );

Creates a compiled subroutine from syntactically complete chunks of
code or from snippets of code.

Chunks may be extracted from code previously inlined by L<Sub::Quote>,
specified as strings containing code, or generated to accomodate
invoking object methods or calling non-inlineable code.

By default each chunk will localize C<@_> to avoid changing C<@_> for
the other chunks. This can be changed on a per-chunk basis by
specifying the C<local> option in each specification.

Specifications may take one of the following forms:

=over

=item C<$coderef>

If C<$coderef> is inlineable (i.e, generated by
L<Sub::Quote/quote_sub>) it will be directly inlined, else code to
invoke it will be generated.

=item C<[ $coderef, %option ]>

This is another way of specifying a code reference, allowing
more manipulation; see L</inlinify_coderef> for available options.

=item C<[ $object, $method, %option ]>

Inline a method call. A weakened reference to C<$object> is kept to
avoid leaks. Method lookup is performed at runtime.  See
L</inlinify_method> for available options.

=item C<[ $string, %option ]>

Inline a chunk of code in a string. See L</inlinify_code> for
available options.

=item C<$scalarref>

Inline a snippet of code stored in the referenced scalar.  Snippets
need not be syntactically complete, and thus may be used to enclose
chunks in blocks. For example, to catch exceptions thrown by a chunk:

   $coderef = quote_subs( \'eval {', \&chunk_as_func, \'};' );

Specify any required captured values in the C<capture> option to
C<quote_subs>.

=back

If the C<store> option is passed in a specification, a lexical
variable with the specified name will automatically be created.
See L</Storing Chunk Values>.

Options which may be passed as the last parameter include all of the
options accepted by L<< C<Sub::Quote::quote_sub>|Sub::Quote/quote_sub
>>, as well as:

=over

=item C<name> => I<string>

An optional name for the compiled subroutine.

=item C<capture> => I<hashref>

A hash containing captured variable names and values.  See the
documentation of the C<\%captures> argument to L<Sub::Quote/quote_sub>
for more information.

=item C<lexicals> => I<scalar | arrayref >

One or more lexical variables to declare. If specified, B<quote_subs>
will enclose the generated code in a block and will declare these
variables at the start of the block.  For example,

  quote_subs( \'@x = 33;',
              \'@y = 22;',
              lexicals => [ '@x', '@y' ]
  );

will result in code equivalent to:

  {
    my ( @x, @y );
    @x = 33;
    @y = 22;
  }

=back

=head2 inlinify_coderef

  my $code = inlinify_coderef( \%global_capture, $coderef, %options );

Generate code which will execute C<$coderef>. If C<$coderef> is
inlineable, it is inlined, else code which will invoke it is generated.

See L</Captures> for more information on C<%global_capture>.

Available options are:

=over

=item C<name> => I<string>

An optional string used as part of the hash key for this chunk's captures.

=item C<local> => I<boolean>

If true (the default) changes to C<@_> will be local, e.g.

  local @_ = ...;

rather than

  @_ = ...;

=item C<store> => I<variable>

If specified, the result of the generated code will be stored in the variable
of the given name.  For example

  store => '@x'

would result in code equivalent to:

  @x = &$coderef;

The variable is not declared. See L</Storing Chunk Values>.

=item C<args> => I<arrayref> | I<hashref> | I<string> | C<undef>

This specified the values of C<@_>.

=over

=item *

if not specified, the value of C<@_> is unchanged.

=item *

if the value is C<undef>, C<@_> will be empty.

=item *

if the value is a reference to an array or hash, C<@_> will be set
equal to its contents. Note that the reference is I<cached>, so

=over

=item *

changes to its contents will be reflected in calls to the code.

=item *

there is the danger of memory leaks, as any non-weakened references in
the structure will be destroyed only when both C<%global_capture> and
any subroutines based on this are destroyed.

=back

=item *

if a string, this is inlined directly, e.g.

  args => q[( 'FRANK' )]

results in

  @_ = ( 'FRANK' )

=back

=back

=head2 inlinify_method

  my $code = inlinify_method( \%global_capture, $object, $method,  %options );

Generate code which will invoke the method named by C<$method> on
C<$object>.  While method resolution is performed at runtime,
C<inlinify_method> checks that C<$method> is available for C<$object>
and will C<croak> if not.

See L</Captures> for more information on C<%global_capture>.

Available options are:

=over

=item C<name> => I<string>

An optional string used as part of the hash key for this chunk's captures.

=item C<local> => I<boolean>

If true (the default) changes to C<@_> will be local, e.g.

  local @_ = ...;

rather than

  @_ = ...;

=item C<store> => I<variable>

If specified, the result of the generated code will be stored in the variable
of the given name.  For example

  store => '@x'

would result in code equivalent to:

  @x = $object->$method( @_ );

The variable is not declared. See L</Storing Chunk Values>.

=item C<args> => I<arrayref> | I<hashref> | I<string> | C<undef>

This specified the values of C<@_>.

=over

=item *

if not specified, the value of C<@_> is unchanged.

=item *

if the value is C<undef>, C<@_> will be empty.

=item *

if the value is a reference to an array or hash, C<@_> will be set
equal to its contents. Note that the reference is I<cached>, so

=over

=item *

changes to its contents will be reflected in calls to the code.

=item *

there is the danger of memory leaks, as any non-weakened references in
the structure will be destroyed only when both C<%global_capture> and
any subroutines based on this are destroyed.

=back

=item *

if a string, this is inlined directly, e.g.

  args => q[( 'FRANK' )]

results in

  @_ = ( 'FRANK' )

=back

=back

=head2 inlinify_code

  my $code = inlinify_code( \%global_capture, $code,  %options );

Generate code which inlines C<$code> handling captures specified in C<%options>.

Available options are:

=over

=item C<capture> => I<hashref>

A hash containing captured variable names and values.  See the
documentation of the C<\%captures> argument to L<Sub::Quote/quote_sub>
for more information.

=item C<name> => I<string>

An optional string used as part of the hash key for this chunk's captures.

=item C<local> => I<boolean>

If true (the default) changes to C<@_> will be local, e.g.

  local @_ = ...;

rather than

  @_ = ...;

=item C<store> => I<variable>

If specified, the result of the generated code will be stored in the variable
of the given name.  For example

  store => '@x'

would result in code equivalent to:

  @x = ... code ...;

The variable is not declared. See L</Storing Chunk Values>.

=item C<args> => I<arrayref> | I<hashref> | I<string> | C<undef>

This specified the values of C<@_>.

=over

=item *

if not specified, the value of C<@_> is unchanged.

=item *

if the value is C<undef>, C<@_> will be empty.

=item *

if the value is a reference to an array or hash, C<@_> will be set
equal to its contents. Note that the reference is I<cached>, so

=over

=item *

changes to its contents will be reflected in calls to the code.

=item *

there is the danger of memory leaks, as any non-weakened references in
the structure will be destroyed only when both C<%global_capture> and
any subroutines based on this are destroyed.

=back

=item *

if a string, this is inlined directly, e.g.

  args => q[( 'FRANK' )]

results in

  @_ = ( 'FRANK' )

=back

=back

=head1 SEE ALSO

L<Sub::Quote>

=head1 AUTHOR

Diab Jerius <djerius@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2016 by Smithsonian Astrophysical Observatory.

This is free software, licensed under:

  The GNU General Public License, Version 3, June 2007

=cut

__END__


#pod =head1 SYNOPSIS
#pod
#pod # EXAMPLE: examples/synopsis.pl
#pod
#pod =head1 DESCRIPTION
#pod
#pod B<Sub::QuoteX::Utils> provides a simplified interface to the process of
#pod combining L<Sub::Quote> compatible code references with new code.
#pod
#pod L<Sub::Quote> provides a number of routines to make code more
#pod performant by inlining syntactically complete chunks of code into a
#pod single compiled subroutine.
#pod
#pod When a chunk of code is compiled into a subroutine by L<<
#pod C<Sub::Quote::quote_sub()>|Sub::Quote/quote_sub >>, B<Sub::Quote>
#pod keeps track of the code and any captured variables used to construct
#pod that subroutine, so that new code can be added to the original code
#pod and the results compiled into a new subroutine.
#pod
#pod B<Sub::QuoteX::Utils> makes that latter process a little easier.
#pod
#pod =head2 Usage
#pod
#pod Typically, L</quote_subs> is used rather than the lower level
#pod C<inlinify_*> routines.  C<quote_subs> is passed a list of chunk
#pod specifications or snippets of code, and generates code which is
#pod isolated in a Perl block.  Each code chunk is additionally isolated in
#pod its own block, while code snippets are in the main block.  This
#pod permits manipulation of the code chunk values.  This is schematically
#pod equivalent to
#pod
#pod   {
#pod     <snippet>
#pod     do { <chunk> };
#pod     <snippet>
#pod     do { <chunk> };
#pod     do { <chunk> };
#pod   }
#pod
#pod The values of each chunk may be stored (see L</Storing Chunk Values>)
#pod and manipulated by the code snippets.
#pod
#pod =head2 Storing Chunk Values
#pod
#pod A code chunk may have it's value stored in a lexical variable by
#pod adding the C<store> option to the chunk's options.  For example,
#pod
#pod   quote_subs( [ q{ sqrt(2); },    { store => '$x' } ],
#pod               [ q{ log(2);  },    { store => '$y' } ],
#pod               [ q{  ( 0..10 ); }, { store => '@z' } ], 
#pod               \q{print $x + $y, "\n";},
#pod   );
#pod
#pod would result in code equivalent to:
#pod
#pod   {
#pod     my ( $x, $y, @z );
#pod
#pod     $x = do { sqrt(2) };
#pod     $y = do { log(2) };
#pod     @z = do { ( 0.. 10 ) };
#pod     print $x + $y, "\n";
#pod   }
#pod
#pod If the variable passed to C<store> has no sigil, e.g. C<x>, then the
#pod calling context is taken into account.  In list context, the value is
#pod stored in C<@x>, in scalar context it is stored in C<$x> and in void
#pod context it is not stored at all.
#pod
#pod Automatic declaration of the variables occurs only when
#pod C<quote_subs> is used to generate the code.
#pod
#pod
#pod =head2 Captures
#pod
#pod B<Sub::Quote> keeps track of captured variables in hashes, I<copying>
#pod the values.  For example,
#pod
#pod # EXAMPLE: examples/captures.pl
#pod
#pod When combining chunks of inlined code, each chunk has it's own set of
#pod captured values which must be kept distinct.
#pod
#pod L</quote_subs> manages this for the caller, but when using the low
#pod level routines ( L</inlinify_coderef>, L</inlinify_method>,
#pod L</inlinify_code> ) the caller must manage the captures.  These
#pod routines store per-chunk captures in their C<\%global_capture> argument.
#pod The calling routine optionally may provide a mnemonic (but unique!)
#pod string which will be part of the key for the chunk.
#pod
#pod The C<%global_capture> hash should be passed to
#pod L<Sub::Quote/quote_sub>, when the final subroutine is compiled.  For
#pod example,
#pod
#pod   my %global_capture;
#pod   my $code = inlinify_coderef( \%global_capture, $coderef, %options );
#pod
#pod   # add more code to $code [...]
#pod
#pod   $new_coderef = Sub::Quote::quote_sub( $code, \%global_capture );
#pod
#pod
#pod =head1 SEE ALSO
#pod
#pod L<Sub::Quote>
