NAME Devel::Declare::Parser - Devel-Declare parser's for Devel-Declare DESCRIPTION Parser is a higher-level API sitting on top of Devel::Declare. It is used by Devel::Declare to simplify exporting of Devel::Declare magic. Devel-Declare allows you to modify a subroutine call as it is being compiled (or right before it is compiled). A parser should subclass this package and implement the rewrite() method. By the time rewrite is called parse() will have already read the current declaration line into an array of datastructures called 'parts'. Rewrite's job is to move, copy, or create new 'parts' which will then be assembled into the new line. SYNOPSIS package MyParser; use strict; use warnings; use base 'Devel::Declare::Parser'; # Register your parser with a simple name. # This is optional, but helpful to people using your parser. __PACKAGE__->register( 'my_method' ); sub rewrite { my $self = shift; # If there is more than one argument before the opening { throw an # exception. if ( @{ $self->parts } > 1 ) { # Get the parts and remove the first (good) item. my @parts = @{ $self->parts }; shift( @parts ); # use bail() instead of die or croak for context. $self->bail( "Syntax error near: " . join( ' and ', map { $self->format_part($_)} @parts ) ); } # Set the first argument, or make it undef. $self->new_parts([ $self->parts->[0] || 'undef' ]); } sub inject { my $self = shift; # Inject self-shifting into the codeblock return ('my $self = shift'); } 1; To use it: package My::Package; use strict; use warnings; use MyParser qw/my_func/; sub my_func { my ( $name, $code ) = @_; croak( "You did not define a subroutine" ) unless $code; return $code unless $name; no strict 'refs'; *$name = $code; } my_func do_all_stuff { # Automatically have $self $self->do_stuff; $self->do_more_stuff; } my $anon = my_func { $self->... }; 1; INTERNALS WARNING Parser objects are blessed arrays, not hashrefs. If you want to create a new accessor use the add_accessor() class method. It will take care of assigning an unused array element to the attribute, and will create a read/write accessor sub for you. __PACKAGE__->add_accessor( 'my_accessor' ); There are many public and private methods on the parser base class. Only the public methods are fully documented. Be sure to refer often to the list of private methods at the end of this document, accidently overriding a private method could have devestating consequences. CLASS METHODS These are methods not related to parsing. Some of these should be used by a subclass, others by tools that provide your interface. FOR INTERFACES register( $name, $class ) Register a parser class under a short name. get_parser( $name ) Get the parser class registered under $name. enhance( $class, $function ) Enhance $function in $class to use the magic provided by this parser. add_accessor( $name ) Add an accessor to this parser, takes care of obtaining an array index for you. DEBUG($bool) Turn debugging on/off. UTILITY bail( @messages ) Like croak, dies providing you context information. Since the death occurs inside the parser croak provides useless information. diag( @message ) Like carp, warns providing you context information. Since the warn occurs inside the parser carp provides useless information. end_quote($start_char) Find the end-character for the provide starting quote character. As in '{' returns '}' and '(' returns ')'. If there is no counter-part the start character is returned: "'" return "'". filename() Filename the rewrite is occuring against. linenum() Linenum the rewrite is occuring on. format_part() Returns the stringified form of a part datastructure. prefix() Returns everything on the line up to the declaration statement. This might be something like '$x = ' suffix() Returns everything on the line from the ending statement character to the end of the actual line. This might be something like '|| die(...)'. NOTE This will only work after parsing is complete. EVENTUAL OUTPUT Parser is designed such that it will transform any and all calls to the desired method into proper method calls. That is this: function x { ... } Will become this: function( 'x', sub { ... }); Note Parser does not read in the entire codeblock, rather it injects a statement into the start of the block that uses a callback to attach the ');' to the end of the statement. This is per the documentation of Devel::Declare. Reading in the entire sub is not a desirable scenario. WORKFLOW OVERVIEW When an enhanced function is found the proper parser will be instanciated, thanks to Devel-Declare it just knows what line to manipulate. The offset and declarator name are provided to the new object. Finally the parse() method is called. The parse() method will check if the call is contained,as in a call where all the parameters are contained within a set of parens. If the call is not contained the parser will parse the entire line into parts. parts are placed in an arrayref in the parts() method. Once the parts are ready the rewrite() method is called. The rewrite() method will take the parts, do what it will with them, and then place the modified/replaces parts into the new_parts() accessor. Once rewrite() is finished the _apply_rewrite() method will join the prefix data, the function call, the parts, and the postfix data into the new line string. If there is a codeblock at the end of the line it will have some code injected into it to append text to the end of the function call. The new line is given to Devel-Declare, and compiling continues with the new line. PARSED PARTS Each item between the declarator and the end of the statement (; or {) will be turned into a part datastructure. Each type of element has a different form. operator, variable or other non-string/non-quote These will be strings, nothing more "string" "=>" "," "+" "$var" bareword or package name These will be arrayrefs containing the stringified word/package name and undef. [ "string", undef ] [ "My::Package", undef ] [ "sub_name", undef ] quoted item (includes things wrapped in [] or ()) These will be an arrayref containing a string of everything between the opening and closing quote character, and the starting quote character. [ "string", "'" ] [ "qw/a b c/", "[" ] [ "a => 'apple', b => 'bat'", "(" ] The parse() methid will populate the parts() accessor with an arrayref containing all the parsed parts. print Dumper( $parser->parts() ); $VAR1 = [ [ 'bareword', undef ], '=>', [ 'quoted string', '\'' ], ..., ]; ACCESSORS These are the read/write accessors used by Parser. Not all of these act on an array element, some will directly alter the current line. line() This will retrieve the current line from Devel-Declare. If given a value that value will be set as the current line using Devel-Declare. name() Name of the declarator as provided via the parser. declarator() Name of the declarator as provided via the Devel-Declare. original_offset() Offset on the line when the parsing was started. offset() Current line offset. parts() Arrayref of parts (may be undef) new_parts() Arrayref of new parts (may be undef) end_char() Will be set to the character just after the completely parsed line (usually { or ;) original() Set to the original line at construction. NOTE this will likely not be complete, if the declaration spans multiple lines it will not be known when this is set. prototype() Used internally for prototype tracking. contained() True if the parser determined this was a contained call. OVERRIDABLE METHODS These are methods you can, should, or may override in your baseclass. rewrite() You must override this. type() Returns 'const', see the Devel::Declare docs for other options. quote_chars() Specify the starting characters for quoted strings. (returns a list) end_chars() Characters to recognise as end of statement characters (; and {) (returns a list) end_hook() A chance for you to modify the new line just before it is set. inject() Code to inject into functions enhanced by this parser. args() Should return a list of names which will be injected as shifted scalars in codeblocks created by function that have been enhanced by this parser. API METHODS This is a general description of the inner working of the parser, most of these are used internally before rewrite() is called. Many of these will do nothing, or possibly do damage if used within rewrite(). This section is mainly useful if you want to patch Parser, or override parse() with your own implementation Not Recommended. parse() POSITION TRACKING advance( $num_chars ) Advances the offset by $num_chars. skip_declarator() Skips the declarator at the start of the line. Only call this once, and within parse() skipspace() Advances the offset past any whitespace. PART RETRIEVAL (MODIFYING) These get parts from the current position in the line. These WILL modify the line and/or the position in the line. get_item() Returns a part datastructure. get_remaining_items() peek_quote() PART CHECKING These check against parts that have already been parsed out of the line. has_comma() has_fat_comma() has_keyword() has_non_string_or_quote_parts() has_string_or_quote_parts() LOOKING AHEAD These *should* not modify anything, but rather return parts of the line yet to be parsed. peek_is_block() peek_is_end() peek_is_other() peek_is_quote() peek_is_word() peek_item() peek_item_type() peek_num_chars($num) peek_other() peek_remaining() peek_word() PRIVATE METHODS LIST This is a list of private methods. This list is provided to help you avoid overriding something you shouldn't. This list is not guarenteed to be complete. _apply_rewrite() _block_end_injection() _close() _contained() _debug() _edit_block_end() _is_defenition() _item_via_() _linestr_offset_from_dd() _move_via_() _new() _open() _peek_is_package() _peek_is_word() _quoted_from_dd() _sanity() _scope_end() _stash() _unstash() AUTHORS Chad Granum exodist7@gmail.com COPYRIGHT Copyright (C) 2010 Chad Granum Devel-Declare-Parser is free software; Standard perl licence. Devel-Declare-Parser is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.