Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Problem with PPI HereDoc's

by clueless newbie (Chaplain)
on Nov 20, 2011 at 17:04 UTC ( #939088=perlquestion: print w/replies, xml ) Need Help??
clueless newbie has asked for the wisdom of the Perl Monks concerning the following question:

Greetings! I have a simple Perl script that uses PPI to locate and update the heredocs (which is mainly SQL) in Perl scripts/modules. For each heredoc in the script/module it clipboards the content of the heredoc and allows you to change it. For most respect it is well-behaved except when it comes to the heredoc terminators - which double (in the sense that what should be EOF becomes EOFEOF). When the fed the script and replacing "Something" with "else"
my($query); $query =<<"ENDOFSQL"; Something ENDOFSQL $query = <<"ENDOFSQL"; Something ENDOFSQL
I'll get
my($query); $query =<<"ENDOFSQL"; /* # Just A Test Something */ else ENDOFSQLENDOFSQL $query = <<"ENDOFSQL"; /* # Just A Test Something */ else ENDOFSQLENDOFSQL
Note the doubling up of the terminators. Here is FindAndUpdateHeredoc script
#!/usr/bin/perl use 5.12.0; use Params::Validate qw(:all); use PPI; use PPI::Dumper; use PPI::Find; use Win32::Clipboard; use strict; use warnings; # Clear the screen system 'cls'; FindAndReplaceHereDocs($ARGV[0],"$ARGV[0].upd") if (-f $ARGV[0]); exit; sub _Comment { return "# Just A Test"; }; sub FindAndReplaceHereDocs { @_=Params::Validate::validate_pos(@_ ,{ type=>SCALAR, callbacks=>{ "File not found"=> sub { + -f shift() } }} ,{ type=>SCALAR } ,{ type=>SCALAR, optional=>1, default=>_Comment() }); ### @_ my ($ExistingCodeFilename_S,$UpdatedCodeFilename_S,$Comment_S) +=@_; my $Document_o=PPI::Document->new($ExistingCodeFilename_S); #PPI::Dumper->new($Document_o,whitespace=>0,locations=>1)->pri +nt; # Stripping out comments #$Document_o->prune('PPI::Token::Comment'); # Sub to find HereDocs local *HereDocs = sub { $_[1]->isa('PPI::Token::HereDoc') }; # Buffer for HereDocs required by the updated document my @NewHereDoc_a; # We'll need a clipboard ... my $Clipboard_o=Win32::Clipboard->new(); # Find the HereDocs in the existing code my $ExistingCodeHereDoc_ar=$Document_o->find(\&HereDocs) || [] +; for my $HereDoc_o (@$ExistingCodeHereDoc_ar) { #PPI::Dumper->new($HereDoc_o,whitespace=>0,locations=>1)-> +print; $Clipboard_o->Empty(); $Clipboard_o->Set(join('',$HereDoc_o->heredoc())); print join('',$HereDoc_o->heredoc())."\n"; { local $|=1; print STDOUT "Update the clipboard then 'Enter':"; <STDIN>; }; # See if the clipboard has been changed if ((my $NewText_s=$Clipboard_o->GetText()) ne join('',$He +reDoc_o->heredoc())) { print "Replaced with:\n$NewText_s\n\n"; chomp($NewText_s); push @NewHereDoc_a,PPI::Document->new(\( $HereDoc_o->content() ."\n" ."/* $Comment_S\n" # Comment .join('',$HereDoc_o->heredoc()) # ... origina +l commented out ."*/\n" # .$NewText_s # ... replace +ment ."\n" #.$HereDoc_o->terminator() # Prevents sp +urious terminator )); my $HereDoc_ar=$NewHereDoc_a[-1]->find(\&HereDocs) || +[]; #PPI::Dumper->new($HereDoc_ar->[0],whitespace=>1,locat +ions=>1)->print; #print join('',$HereDoc_ar->[0]->heredoc())."\n"; if ($HereDoc_o->insert_before($HereDoc_ar->[0])) { $HereDoc_o->delete(); } else { warn "insert_before failed!"; }; }; }; #PPI::Dumper->new($Document_o,whitespace=>0,locations=>1)->pri +nt; $Document_o->save($UpdatedCodeFilename_S) };

No doubt I'm doing something wrong --- but what?

Update The code as it currently stands behaves correctly and will find HereDocs and copy them to the clipboard so they can be pasted to an editor, edited then reclip to amend the HereDoc.

Replies are listed 'Best First'.
Re: Problem with PPI HereDoc's
by Khen1950fx (Canon) on Nov 21, 2011 at 03:29 UTC
    You need to create a new PPI::Document object for each here-doc. I say that because I tried calling two here-docs, but PPI would only give me one. Also, I couldn't replicate your output. How did you replace 'Something' with 'else'? I would modify the docs like this:
    #!/usr/bin/perl use strict; use warnings; use PPI; use PPI::Dumper; my $data; ($data = <<'ENDOFSQL') =~ s/Something/else/; Something ENDOFSQL my($Document) = PPI::Document->new(\$data); my $Dumper = PPI::Dumper->new($Document); $Dumper->print;

      Hi, Khen1950fx,

      Thank you for replying.

      I think

      push @NewHereDoc_a,PPI::Document->new(\( $HereDoc_o->content() ."\n" ."/* $Comment_S\n" # Comment .join('',$HereDoc_o->heredoc()) # ... origina +l commented out ."*/\n" # .$NewText_s # ... replace +ment ."\n" .$HereDoc_o->terminator() ));

      should create a new PPI::Document object for each of the HereDocs that I want to update.

Re: Problem with PPI HereDoc's
by Eliya (Vicar) on Nov 20, 2011 at 22:02 UTC
    ... .$HereDoc_o->terminator() ...

    What happens if you remove that line (i.e. don't explicitly add the terminator)?

    I don't have PPI installed, so this is just a guess... but based on your problem description this is first thing I would try :)

      Right you are!

      It seemed to me that without that terminator the document being passed to the PPI::Document->new() would fail to parse. But having tried your recommendation --- I got what was expected! So with your change the script can be used to Find-And-Replace-HereDocs. Modify to suit yourself and enjoy!

      Thanks again!


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://939088]
Approved by Eliya
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (12)
As of 2018-06-18 18:15 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (110 votes). Check out past polls.