Skip to content

djerius/Sub-QuoteX-Utils

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NAME

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

VERSION

version 0.09

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:

DESCRIPTION

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

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 Sub::Quote::quote_sub(), 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.

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

Usage

Typically, "quote_subs" is used rather than the lower level inlinify_* routines. 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 "Storing Chunk Values") and manipulated by the code snippets.

Storing Chunk Values

A code chunk may have it's value stored in a lexical variable by adding the 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 store has no sigil, e.g. x, then the calling context is taken into account. In list context, the value is stored in @x, in scalar context it is stored in $x and in void context it is not stored at all.

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

Captures

Sub::Quote keeps track of captured variables in hashes, 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.

"quote_subs" manages this for the caller, but when using the low level routines ( "inlinify_coderef", "inlinify_method", "inlinify_code" ) the caller must manage the captures. These routines store per-chunk captures in their \%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 %global_capture hash should be passed to "quote_sub" in Sub::Quote, 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 );

FUNCTIONS

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 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 @_ to avoid changing @_ for the other chunks. This can be changed on a per-chunk basis by specifying the local option in each specification.

Specifications may take one of the following forms:

  • $coderef

    If $coderef is inlineable (i.e, generated by "quote_sub" in Sub::Quote) it will be directly inlined, else code to invoke it will be generated.

  • [ $coderef, %option ]

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

  • [ $object, $method, %option ]

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

  • [ $string, %option ]

    Inline a chunk of code in a string. See "inlinify_code" for available options.

  • $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 capture option to quote_subs.

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

Options which may be passed as the last parameter include all of the options accepted by Sub::Quote::quote_sub, as well as:

  • name => string

    An optional name for the compiled subroutine.

  • capture => hashref

    A hash containing captured variable names and values. See the documentation of the \%captures argument to "quote_sub" in Sub::Quote for more information.

  • lexicals => _scalar | arrayref _

    One or more lexical variables to declare. If specified, 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;
      }
    

inlinify_coderef

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

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

See "Captures" for more information on %global_capture.

Available options are:

  • name => string

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

  • local => boolean

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

      local @_ = ...;
    

    rather than

      @_ = ...;
    
  • store => 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 "Storing Chunk Values".

  • args => arrayref | hashref | string | undef

    This specified the values of @_.

    • if not specified, the value of @_ is unchanged.

    • if the value is undef, @_ will be empty.

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

      • changes to its contents will be reflected in calls to the code.
      • there is the danger of memory leaks, as any non-weakened references in the structure will be destroyed only when both %global_capture and any subroutines based on this are destroyed.
    • if a string, this is inlined directly, e.g.

        args => q[( 'FRANK' )]
      

      results in

        @_ = ( 'FRANK' )
      

inlinify_method

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

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

See "Captures" for more information on %global_capture.

Available options are:

  • name => string

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

  • local => boolean

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

      local @_ = ...;
    

    rather than

      @_ = ...;
    
  • store => 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 "Storing Chunk Values".

  • args => arrayref | hashref | string | undef

    This specified the values of @_.

    • if not specified, the value of @_ is unchanged.

    • if the value is undef, @_ will be empty.

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

      • changes to its contents will be reflected in calls to the code.
      • there is the danger of memory leaks, as any non-weakened references in the structure will be destroyed only when both %global_capture and any subroutines based on this are destroyed.
    • if a string, this is inlined directly, e.g.

        args => q[( 'FRANK' )]
      

      results in

        @_ = ( 'FRANK' )
      

inlinify_code

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

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

Available options are:

  • capture => hashref

    A hash containing captured variable names and values. See the documentation of the \%captures argument to "quote_sub" in Sub::Quote for more information.

  • name => string

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

  • local => boolean

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

      local @_ = ...;
    

    rather than

      @_ = ...;
    
  • store => 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 "Storing Chunk Values".

  • args => arrayref | hashref | string | undef

    This specified the values of @_.

    • if not specified, the value of @_ is unchanged.

    • if the value is undef, @_ will be empty.

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

      • changes to its contents will be reflected in calls to the code.
      • there is the danger of memory leaks, as any non-weakened references in the structure will be destroyed only when both %global_capture and any subroutines based on this are destroyed.
    • if a string, this is inlined directly, e.g.

        args => q[( 'FRANK' )]
      

      results in

        @_ = ( 'FRANK' )
      

SEE ALSO

Sub::Quote

AUTHOR

Diab Jerius djerius@cpan.org

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

About

Sugar for Sub::Quote

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages