How do I add new lines to a Perl script using PPI?












3















What I'd ideally like to be able to do is scan a bunch of files that are implicitly importing a bunch of functions via Test::Most. I'd like to explicitly import the functions within the file. So basically I'll check the use statements to see if they already exist and, if they don't, I'd like to add an additional use statement for the function(s) in question. For example, I might add use Test::Differences qw( eq_or_diff ); if there's an eq_or_diff in the file, but no use Test::Differences. It'll get a little more complicated, but that's the basic idea.



As a proof of concept, I've tried to add just a single word into an existing script, but I can't figure it out. insert_after() returns true on success. I only ever get a false value, but I don't see any debugging info as to why the line could not be added.



use strict;
use warnings;

use PPI::Document ();
use PPI::Token::Word ();
use Test::More;

my $script = <<'EOF';
use strict;
use warnings;

use DateTime ();
use Git::Helpers qw( checkout_root );
use LWP::UserAgent ();

my $foo = 'bar';
EOF

my $doc = PPI::Document->new( $script );

my $includes = $doc->find('PPI::Statement::Include');
my @use = grep { $_->type eq 'use' } @{$includes};
my $second_last = $use[-2];

diag 'Trying to insert after ' . $second_last->module;

my $word = PPI::Token::Word->new('use');
isa_ok( $word, 'PPI::Element', 'word is an Element' );
isa_ok( $second_last, 'PPI::Element', 'use is an Element' );

ok( $second_last->insert_after($word), 'word inserted' );

diag $doc->serialize;

done_testing();


The output of my script is as follows. You'll note that the document does not appear to have been altered:



# trying to insert after Git::Helpers
ok 1 - 'word is an Element' isa 'PPI::Element'
ok 2 - 'use is an Element' isa 'PPI::Element'
not ok 3 - word inserted
# failed test 'word inserted'
# at so.pl line 31.
# use strict;
# use warnings;
#
# use DateTime ();
# use Git::Helpers qw( checkout_root );
# use LWP::UserAgent ();
#
# my $foo = 'bar';
1..3
# looks like you failed 1 test of 3.









share|improve this question

























  • The documentation says that insert_after does some syntax checking and prevents inserting wrong code. You probably need to insert a whole PPI::Statement::Include

    – UjinT34
    Nov 15 '18 at 22:29
















3















What I'd ideally like to be able to do is scan a bunch of files that are implicitly importing a bunch of functions via Test::Most. I'd like to explicitly import the functions within the file. So basically I'll check the use statements to see if they already exist and, if they don't, I'd like to add an additional use statement for the function(s) in question. For example, I might add use Test::Differences qw( eq_or_diff ); if there's an eq_or_diff in the file, but no use Test::Differences. It'll get a little more complicated, but that's the basic idea.



As a proof of concept, I've tried to add just a single word into an existing script, but I can't figure it out. insert_after() returns true on success. I only ever get a false value, but I don't see any debugging info as to why the line could not be added.



use strict;
use warnings;

use PPI::Document ();
use PPI::Token::Word ();
use Test::More;

my $script = <<'EOF';
use strict;
use warnings;

use DateTime ();
use Git::Helpers qw( checkout_root );
use LWP::UserAgent ();

my $foo = 'bar';
EOF

my $doc = PPI::Document->new( $script );

my $includes = $doc->find('PPI::Statement::Include');
my @use = grep { $_->type eq 'use' } @{$includes};
my $second_last = $use[-2];

diag 'Trying to insert after ' . $second_last->module;

my $word = PPI::Token::Word->new('use');
isa_ok( $word, 'PPI::Element', 'word is an Element' );
isa_ok( $second_last, 'PPI::Element', 'use is an Element' );

ok( $second_last->insert_after($word), 'word inserted' );

diag $doc->serialize;

done_testing();


The output of my script is as follows. You'll note that the document does not appear to have been altered:



# trying to insert after Git::Helpers
ok 1 - 'word is an Element' isa 'PPI::Element'
ok 2 - 'use is an Element' isa 'PPI::Element'
not ok 3 - word inserted
# failed test 'word inserted'
# at so.pl line 31.
# use strict;
# use warnings;
#
# use DateTime ();
# use Git::Helpers qw( checkout_root );
# use LWP::UserAgent ();
#
# my $foo = 'bar';
1..3
# looks like you failed 1 test of 3.









share|improve this question

























  • The documentation says that insert_after does some syntax checking and prevents inserting wrong code. You probably need to insert a whole PPI::Statement::Include

    – UjinT34
    Nov 15 '18 at 22:29














3












3








3








What I'd ideally like to be able to do is scan a bunch of files that are implicitly importing a bunch of functions via Test::Most. I'd like to explicitly import the functions within the file. So basically I'll check the use statements to see if they already exist and, if they don't, I'd like to add an additional use statement for the function(s) in question. For example, I might add use Test::Differences qw( eq_or_diff ); if there's an eq_or_diff in the file, but no use Test::Differences. It'll get a little more complicated, but that's the basic idea.



As a proof of concept, I've tried to add just a single word into an existing script, but I can't figure it out. insert_after() returns true on success. I only ever get a false value, but I don't see any debugging info as to why the line could not be added.



use strict;
use warnings;

use PPI::Document ();
use PPI::Token::Word ();
use Test::More;

my $script = <<'EOF';
use strict;
use warnings;

use DateTime ();
use Git::Helpers qw( checkout_root );
use LWP::UserAgent ();

my $foo = 'bar';
EOF

my $doc = PPI::Document->new( $script );

my $includes = $doc->find('PPI::Statement::Include');
my @use = grep { $_->type eq 'use' } @{$includes};
my $second_last = $use[-2];

diag 'Trying to insert after ' . $second_last->module;

my $word = PPI::Token::Word->new('use');
isa_ok( $word, 'PPI::Element', 'word is an Element' );
isa_ok( $second_last, 'PPI::Element', 'use is an Element' );

ok( $second_last->insert_after($word), 'word inserted' );

diag $doc->serialize;

done_testing();


The output of my script is as follows. You'll note that the document does not appear to have been altered:



# trying to insert after Git::Helpers
ok 1 - 'word is an Element' isa 'PPI::Element'
ok 2 - 'use is an Element' isa 'PPI::Element'
not ok 3 - word inserted
# failed test 'word inserted'
# at so.pl line 31.
# use strict;
# use warnings;
#
# use DateTime ();
# use Git::Helpers qw( checkout_root );
# use LWP::UserAgent ();
#
# my $foo = 'bar';
1..3
# looks like you failed 1 test of 3.









share|improve this question
















What I'd ideally like to be able to do is scan a bunch of files that are implicitly importing a bunch of functions via Test::Most. I'd like to explicitly import the functions within the file. So basically I'll check the use statements to see if they already exist and, if they don't, I'd like to add an additional use statement for the function(s) in question. For example, I might add use Test::Differences qw( eq_or_diff ); if there's an eq_or_diff in the file, but no use Test::Differences. It'll get a little more complicated, but that's the basic idea.



As a proof of concept, I've tried to add just a single word into an existing script, but I can't figure it out. insert_after() returns true on success. I only ever get a false value, but I don't see any debugging info as to why the line could not be added.



use strict;
use warnings;

use PPI::Document ();
use PPI::Token::Word ();
use Test::More;

my $script = <<'EOF';
use strict;
use warnings;

use DateTime ();
use Git::Helpers qw( checkout_root );
use LWP::UserAgent ();

my $foo = 'bar';
EOF

my $doc = PPI::Document->new( $script );

my $includes = $doc->find('PPI::Statement::Include');
my @use = grep { $_->type eq 'use' } @{$includes};
my $second_last = $use[-2];

diag 'Trying to insert after ' . $second_last->module;

my $word = PPI::Token::Word->new('use');
isa_ok( $word, 'PPI::Element', 'word is an Element' );
isa_ok( $second_last, 'PPI::Element', 'use is an Element' );

ok( $second_last->insert_after($word), 'word inserted' );

diag $doc->serialize;

done_testing();


The output of my script is as follows. You'll note that the document does not appear to have been altered:



# trying to insert after Git::Helpers
ok 1 - 'word is an Element' isa 'PPI::Element'
ok 2 - 'use is an Element' isa 'PPI::Element'
not ok 3 - word inserted
# failed test 'word inserted'
# at so.pl line 31.
# use strict;
# use warnings;
#
# use DateTime ();
# use Git::Helpers qw( checkout_root );
# use LWP::UserAgent ();
#
# my $foo = 'bar';
1..3
# looks like you failed 1 test of 3.






perl






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 15 '18 at 22:14







oalders

















asked Nov 15 '18 at 21:55









oaldersoalders

3,65311631




3,65311631













  • The documentation says that insert_after does some syntax checking and prevents inserting wrong code. You probably need to insert a whole PPI::Statement::Include

    – UjinT34
    Nov 15 '18 at 22:29



















  • The documentation says that insert_after does some syntax checking and prevents inserting wrong code. You probably need to insert a whole PPI::Statement::Include

    – UjinT34
    Nov 15 '18 at 22:29

















The documentation says that insert_after does some syntax checking and prevents inserting wrong code. You probably need to insert a whole PPI::Statement::Include

– UjinT34
Nov 15 '18 at 22:29





The documentation says that insert_after does some syntax checking and prevents inserting wrong code. You probably need to insert a whole PPI::Statement::Include

– UjinT34
Nov 15 '18 at 22:29












1 Answer
1






active

oldest

votes


















6














Looking at the source of PPI::Statement:



# As above, you can insert a statement, or a non-significant token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Statement') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') and ! $Element->significant ) {
return $self->__insert_after($Element);
}
'';
}


A "non-significant token" is something like whitespace or a comment.



You are trying to insert a single, significant token at the top level (after a statement). That's not allowed.



You'll have to build a full PPI::Statement::Include element.





Here's some (rather ugly) proof-of-concept code:



# ...
diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
do {
my $synthetic_use = PPI::Statement::Include->new;
for my $child (
PPI::Token::Word->new('use'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Word->new('Test::Differences'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Quote::Single->new("'eq_or_diff'"),
PPI::Token::Structure->new(';'),
) {
ok $synthetic_use->add_element($child);
}
$synthetic_use
},
PPI::Token::Whitespace->new("n"),
) {
ok $insertion_point->insert_after($new_element);
}
}

diag $doc->serialize;


But it's much easier to let PPI parse a given fragment and just use those objects:



diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
reverse PPI::Document->new( "nuse Test::Differences qw( eq_or_diff );")->elements
) {
ok $insertion_point->insert_after($new_element->remove);
}
}

diag $doc->serialize;


Beware: Using $new_element->remove instead of just $new_element is crucial. You need to detach $new_element from its old containing document because otherwise the destruction of the temporary PPI::Document instance will wipe out all child elements, including those already added to $doc.






share|improve this answer





















  • 2





    You can use the PPI parser to generate the statement. my $use_doc = PPI::Document->new("use Test::Differences;"); my $include = ($doc2->children)[0];

    – Schwern
    Nov 15 '18 at 23:00











  • @Schwern That's exactly what I'm working on now. :-)

    – melpomene
    Nov 15 '18 at 23:00











  • @Schwern It looks like the destruction of my temporary PPI::Document is wiping out my parsed elements, but I'm still debugging.

    – melpomene
    Nov 15 '18 at 23:06











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53328446%2fhow-do-i-add-new-lines-to-a-perl-script-using-ppi%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









6














Looking at the source of PPI::Statement:



# As above, you can insert a statement, or a non-significant token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Statement') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') and ! $Element->significant ) {
return $self->__insert_after($Element);
}
'';
}


A "non-significant token" is something like whitespace or a comment.



You are trying to insert a single, significant token at the top level (after a statement). That's not allowed.



You'll have to build a full PPI::Statement::Include element.





Here's some (rather ugly) proof-of-concept code:



# ...
diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
do {
my $synthetic_use = PPI::Statement::Include->new;
for my $child (
PPI::Token::Word->new('use'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Word->new('Test::Differences'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Quote::Single->new("'eq_or_diff'"),
PPI::Token::Structure->new(';'),
) {
ok $synthetic_use->add_element($child);
}
$synthetic_use
},
PPI::Token::Whitespace->new("n"),
) {
ok $insertion_point->insert_after($new_element);
}
}

diag $doc->serialize;


But it's much easier to let PPI parse a given fragment and just use those objects:



diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
reverse PPI::Document->new( "nuse Test::Differences qw( eq_or_diff );")->elements
) {
ok $insertion_point->insert_after($new_element->remove);
}
}

diag $doc->serialize;


Beware: Using $new_element->remove instead of just $new_element is crucial. You need to detach $new_element from its old containing document because otherwise the destruction of the temporary PPI::Document instance will wipe out all child elements, including those already added to $doc.






share|improve this answer





















  • 2





    You can use the PPI parser to generate the statement. my $use_doc = PPI::Document->new("use Test::Differences;"); my $include = ($doc2->children)[0];

    – Schwern
    Nov 15 '18 at 23:00











  • @Schwern That's exactly what I'm working on now. :-)

    – melpomene
    Nov 15 '18 at 23:00











  • @Schwern It looks like the destruction of my temporary PPI::Document is wiping out my parsed elements, but I'm still debugging.

    – melpomene
    Nov 15 '18 at 23:06
















6














Looking at the source of PPI::Statement:



# As above, you can insert a statement, or a non-significant token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Statement') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') and ! $Element->significant ) {
return $self->__insert_after($Element);
}
'';
}


A "non-significant token" is something like whitespace or a comment.



You are trying to insert a single, significant token at the top level (after a statement). That's not allowed.



You'll have to build a full PPI::Statement::Include element.





Here's some (rather ugly) proof-of-concept code:



# ...
diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
do {
my $synthetic_use = PPI::Statement::Include->new;
for my $child (
PPI::Token::Word->new('use'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Word->new('Test::Differences'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Quote::Single->new("'eq_or_diff'"),
PPI::Token::Structure->new(';'),
) {
ok $synthetic_use->add_element($child);
}
$synthetic_use
},
PPI::Token::Whitespace->new("n"),
) {
ok $insertion_point->insert_after($new_element);
}
}

diag $doc->serialize;


But it's much easier to let PPI parse a given fragment and just use those objects:



diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
reverse PPI::Document->new( "nuse Test::Differences qw( eq_or_diff );")->elements
) {
ok $insertion_point->insert_after($new_element->remove);
}
}

diag $doc->serialize;


Beware: Using $new_element->remove instead of just $new_element is crucial. You need to detach $new_element from its old containing document because otherwise the destruction of the temporary PPI::Document instance will wipe out all child elements, including those already added to $doc.






share|improve this answer





















  • 2





    You can use the PPI parser to generate the statement. my $use_doc = PPI::Document->new("use Test::Differences;"); my $include = ($doc2->children)[0];

    – Schwern
    Nov 15 '18 at 23:00











  • @Schwern That's exactly what I'm working on now. :-)

    – melpomene
    Nov 15 '18 at 23:00











  • @Schwern It looks like the destruction of my temporary PPI::Document is wiping out my parsed elements, but I'm still debugging.

    – melpomene
    Nov 15 '18 at 23:06














6












6








6







Looking at the source of PPI::Statement:



# As above, you can insert a statement, or a non-significant token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Statement') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') and ! $Element->significant ) {
return $self->__insert_after($Element);
}
'';
}


A "non-significant token" is something like whitespace or a comment.



You are trying to insert a single, significant token at the top level (after a statement). That's not allowed.



You'll have to build a full PPI::Statement::Include element.





Here's some (rather ugly) proof-of-concept code:



# ...
diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
do {
my $synthetic_use = PPI::Statement::Include->new;
for my $child (
PPI::Token::Word->new('use'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Word->new('Test::Differences'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Quote::Single->new("'eq_or_diff'"),
PPI::Token::Structure->new(';'),
) {
ok $synthetic_use->add_element($child);
}
$synthetic_use
},
PPI::Token::Whitespace->new("n"),
) {
ok $insertion_point->insert_after($new_element);
}
}

diag $doc->serialize;


But it's much easier to let PPI parse a given fragment and just use those objects:



diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
reverse PPI::Document->new( "nuse Test::Differences qw( eq_or_diff );")->elements
) {
ok $insertion_point->insert_after($new_element->remove);
}
}

diag $doc->serialize;


Beware: Using $new_element->remove instead of just $new_element is crucial. You need to detach $new_element from its old containing document because otherwise the destruction of the temporary PPI::Document instance will wipe out all child elements, including those already added to $doc.






share|improve this answer















Looking at the source of PPI::Statement:



# As above, you can insert a statement, or a non-significant token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Statement') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') and ! $Element->significant ) {
return $self->__insert_after($Element);
}
'';
}


A "non-significant token" is something like whitespace or a comment.



You are trying to insert a single, significant token at the top level (after a statement). That's not allowed.



You'll have to build a full PPI::Statement::Include element.





Here's some (rather ugly) proof-of-concept code:



# ...
diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
do {
my $synthetic_use = PPI::Statement::Include->new;
for my $child (
PPI::Token::Word->new('use'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Word->new('Test::Differences'),
PPI::Token::Whitespace->new(' '),
PPI::Token::Quote::Single->new("'eq_or_diff'"),
PPI::Token::Structure->new(';'),
) {
ok $synthetic_use->add_element($child);
}
$synthetic_use
},
PPI::Token::Whitespace->new("n"),
) {
ok $insertion_point->insert_after($new_element);
}
}

diag $doc->serialize;


But it's much easier to let PPI parse a given fragment and just use those objects:



diag 'Trying to insert after ' . $second_last->module;

{
my $insertion_point = $second_last;
for my $new_element (
reverse PPI::Document->new( "nuse Test::Differences qw( eq_or_diff );")->elements
) {
ok $insertion_point->insert_after($new_element->remove);
}
}

diag $doc->serialize;


Beware: Using $new_element->remove instead of just $new_element is crucial. You need to detach $new_element from its old containing document because otherwise the destruction of the temporary PPI::Document instance will wipe out all child elements, including those already added to $doc.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 15 '18 at 23:11

























answered Nov 15 '18 at 22:36









melpomenemelpomene

61.9k54994




61.9k54994








  • 2





    You can use the PPI parser to generate the statement. my $use_doc = PPI::Document->new("use Test::Differences;"); my $include = ($doc2->children)[0];

    – Schwern
    Nov 15 '18 at 23:00











  • @Schwern That's exactly what I'm working on now. :-)

    – melpomene
    Nov 15 '18 at 23:00











  • @Schwern It looks like the destruction of my temporary PPI::Document is wiping out my parsed elements, but I'm still debugging.

    – melpomene
    Nov 15 '18 at 23:06














  • 2





    You can use the PPI parser to generate the statement. my $use_doc = PPI::Document->new("use Test::Differences;"); my $include = ($doc2->children)[0];

    – Schwern
    Nov 15 '18 at 23:00











  • @Schwern That's exactly what I'm working on now. :-)

    – melpomene
    Nov 15 '18 at 23:00











  • @Schwern It looks like the destruction of my temporary PPI::Document is wiping out my parsed elements, but I'm still debugging.

    – melpomene
    Nov 15 '18 at 23:06








2




2





You can use the PPI parser to generate the statement. my $use_doc = PPI::Document->new("use Test::Differences;"); my $include = ($doc2->children)[0];

– Schwern
Nov 15 '18 at 23:00





You can use the PPI parser to generate the statement. my $use_doc = PPI::Document->new("use Test::Differences;"); my $include = ($doc2->children)[0];

– Schwern
Nov 15 '18 at 23:00













@Schwern That's exactly what I'm working on now. :-)

– melpomene
Nov 15 '18 at 23:00





@Schwern That's exactly what I'm working on now. :-)

– melpomene
Nov 15 '18 at 23:00













@Schwern It looks like the destruction of my temporary PPI::Document is wiping out my parsed elements, but I'm still debugging.

– melpomene
Nov 15 '18 at 23:06





@Schwern It looks like the destruction of my temporary PPI::Document is wiping out my parsed elements, but I'm still debugging.

– melpomene
Nov 15 '18 at 23:06




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53328446%2fhow-do-i-add-new-lines-to-a-perl-script-using-ppi%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Xamarin.iOS Cant Deploy on Iphone

Glorious Revolution

Dulmage-Mendelsohn matrix decomposition in Python