Nested "if", Missing right curly or square bracket - perl

I currently have the following:
elsif ($line =~ /^(\s*)(if|elif|else)\s*(.+)*\s*:\s*$/) {
# Multiline If
# Print the If/Elif condition
if ($2 eq "if"){
print "$1$2 ($3){\n";
}
elsif ($2 eq "elif"){
print "$1elsif ($3){\n";
}
elsif ($2 eq "else"){
print "$1$2 $3{\n";
}
# Add the space before the word "if"/"elif"/"else" to the stack
push(#indentation_stack, $1);
}
I am getting the stated error, but I'm not sure why. In the final elsif, if I add a \ before the { in the print statement, the code doesn't generate an error.
I.E:
elsif ($2 eq "else"){
print "$1$2 $3\{\n";
}
Could someone please explain to me why this is occurring?
Thanks for your help!

Tricky! The problem is that the following is the start of hash lookup:
$3{
You want the equivalent of
$3 . "{"
which can be written as
"${3}{"
The following works in this case because the \ can't possibly be part of the variable there:
"$3\{"
But that trick can't always be used. For example, consider
$foo . "bar"
If you try
"$foo\bar"
You'll find you get
$foo . chr(0x08) . "ar"
because "\b" returns the "bell" character. That leaves you with
"${foo}bar"

Related

How can I format boolean operators with perltidy?

To my knowledge, perltidy is extremely handy and helpful when it comes to code formatting. However, I haven't found a way to fine-tune perltidy. For example, I need the && and || formatted so that there are two spaces before and after them. Like this:
$some && $x > 7;
Can I do it? If so, how?
It's easy enough to rig up your own tidy script with PPI that you can run after perltidy. Proof-of-concept:
use PPI;
my $doc = PPI::Document->new($ARGV[0]);
for my $op (#{$doc->find('PPI::Token::Operator')}) {
if ($op eq '&&' || $op eq '||') {
$op->{content} = " $op ";
}
}
print $doc;
And if we run this script on itself, we get:
$ perl je8tidy.pl je8tidy.pl
use PPI;
my $doc = PPI::Document->new($ARGV[0]);
for my $op (#{$doc->find('PPI::Token::Operator')}) {
if ($op eq '&&' || $op eq '||') {
$op->{content} = " $op ";
}
}
print $doc;
It did insert the extra spaces around the only || operator on line 4.
perltidy does not have an option to add two spaces before and after operators.
Furthermore, if you manually add 2 spaces, then run perltidy, it will convert the 2 spaces to a single space by default. However, there are options for skipping selected lines of code.

lowercase everything except content between single quotes - perl

Is there a way in perl to replace all text in input line except ones within single quotes(There could be more than one) using regex, I have achieved this using the code below but would like to see if it can be done with regex and map.
while (<>) {
my $m=0;
for (split(//)) {
if (/'/ and ! $m) {
$m=1;
print;
}
elsif (/'/ and $m) {
$m=0;
print;
}
elsif ($m) {
print;
}
else {
print lc;
}
}
}
**Sample input:**
and (t.TARGET_TYPE='RAC_DATABASE' or (t.TARGET_TYPE='ORACLE_DATABASE' and t.TYPE_QUALIFIER3 != 'racinst'))
**Sample output:**
and (t.target_type='RAC_DATABASE' or (t.target_type='ORACLE_DATABASE' and t.type_qualifier3 != 'racinst'))
You can give this a shot. All one regexp.
$str =~ s/(?:^|'[^']*')\K[^']*/lc($&)/ge;
Or, cleaner and more documented (this is semantically equivalent to the above)
$str =~ s/
(?:
^ | # Match either the start of the string, or
'[^']*' # some text in quotes.
)\K # Then ignore that part,
# because we want to leave it be.
[^']* # Take the text after it, and
# lowercase it.
/lc($&)/gex;
The g flag tells the regexp to run as many times as necessary. e tells it that the substitution portion (lc($&), in our case) is Perl code, not just text. x lets us put those comments in there so that the regexp isn't total gibberish.
Don't you play too hard with regexp for such a simple job?
Why not get the kid 'split' for it today?
#!/usr/bin/perl
while (<>)
{
#F = split "'";
#F = map { $_ % 2 ? $F[$_] : lc $F[$_] } (0..#F);
print join "'", #F;
}
The above is for understanding. We often join the latter two lines reasonably into:
print join "'", map { $_ % 2 ? $F[$_] : lc $F[$_] } (0..#F);
Or enjoy more, making it a one-liner? (in bash shell) In concept, it looks like:
perl -pF/'/ -e 'join "'", map { $_ % 2 ? $F[$_] : lc $F[$_] } (0..#F);' YOUR_FILE
In reality, however, we need to respect the shell and do some escape (hard) job:
perl -pF/\'/ -e 'join "'"'"'", map { $_ % 2 ? $F[$_] : lc $F[$_] } (0..#F);' YOUR_FILE
(The single-quoted single quote needs to become 5 letters: '"'"')
If it doesn't help your job, it helps sleep.
One more variant with Perl one-liner. I'm using hex \x27 for single quotes
$ cat sql_str.txt
and (t.TARGET_TYPE='RAC_DATABASE' or (t.TARGET_TYPE='ORACLE_DATABASE' and t.TYPE_QUALIFIER3 != 'racinst'))
$ perl -ne ' { #F=split(/\x27/); for my $val (0..$#F) { $F[$val]=lc($F[$val]) if $val%2==0 } ; print join("\x27",#F) } ' sql_str.txt
and (t.target_type='RAC_DATABASE' or (t.target_type='ORACLE_DATABASE' and t.type_qualifier3 != 'racinst'))
$

how to check an empty input and regulate the type of input in perl

Guys how do i check an empty input in perl? Including spaces,tabs,newlines etc..
here's my sample code but its not working: what am i doing wrong?
my $number=int (1+rand 100);
while (<>) {
chomp $_;
last if ($_=~/exit|quit/i or $_ eq $number);
print "too high! \n" if (defined $_ && $_ > $number);
print "too low!\n" if (defined $_ && $_ < $number);
print $_;
}
So basically, the user input something, if it's a number it compares to the default random number. It prints low or high depending on the number. But when i just press enter without entering something it still goes to that if statement and gives an error that what i entered isnt numeric (due to this code $_ < $number).
So another question is how to handle input to allow only the word "exit" or "quit" and numbers. Other than that it exits.
The while(<>){...} will loop as long as the return value of the <> is defined, i.e. you aren't at EOF. So $_ is always defined inside the loop.
To assert that some input is numeric, you can use looks_like_number from Scalar::Util or use a simple regex:
unless (/\A[0-9]+\z/) {
print "not numeric!\n";
next;
}
After that, we can treat the value of $_ as a number integer, and can be sure that use warnings won't complain. E.g.
# remove newline from input
chomp;
# Test for abort condition
last if /\b(?:quit|exit)\b/;
# Assert numeric input
unless (/\A[0-9]+\z/) {
print "not numeric!\n";
next;
}
# Check input against secret $number
if ($_ == $number) {
print "Correct!\n";
last;
} elsif ($_ < $number) {
print "too low\n";
} elsif ($_ > $number) {
print "too high\n";
}
Sorry for the silly question, Got the answer to my first question.
you just have to add this code to the first condition
$_=~/^\s*$/
which compares if the input is any whitespace
and for the second one to limit only the "exit" and "quit" as a valid non digit input add this one to the first regex comparison in the first condition
$_=~/(exit|quit|\D)/i
notice the \D which only matches non digit characters. But since its an "OR" it will short circuit once a specific non digit character (exit or quit) is entered, terminating the loop instantly.
Thanks guys

perl exact string match

I have following Perl code to prompt user for yes/no answer. If the user enters anything else than yes/no, keep prompting. No other word is acceptable. I don't know why this code doesn't work. I tested with answer "noooooo" and I was expecting it to prompt again but it does not enter the while loop.
Can anyone help find my mistake here?
#files = A, B, C;
foreach $c (#files) {
if (-e $c ) {
print " $c already exists. Do you want to overwrite? (yes or no): ";
chomp ($file_yes_no = <STDIN>);
while ($file_yes_no !~ m/yes{1}|no{1}/i ) {
print "$c already exists. Do you want to overwrite? (yes or no): ";
chomp ($file_yes_no = <STDIN>);
}
if ($file_yes_no =~ m/yes/i ) {
if (system ("cp -f /home/old_path/ /home/new_path/ == 0) {
print "$c successfully copied;
} else {
die "Error: Copy failed, Check the message above";
}
}
else { print "No files copied\n; }
I would just use the string equality operator eq instead of a regex.
if( $file_yes_no eq 'yes' ) ...
If I wanted it case insensitive I'd first convert to lowercase with lc.
The problem with your regex is it will happily match any string containing the letters yes sequentially. If you wish, you can match the start and end of the string like this:
if ($file_yes_no =~ m/^yes$/i ) ...
But I personally prefer the first option.
Oh, I missed the first part... Hmmmm. Same deal, if you must use regex.
m/^(yes|no)$/i
Once again I'd be more inclined to avoid regex
You should use following Perl regular expression for matching only yes or no (case insensitive):
m/^(yes|no)$/i
For yes only, use:
m/^yes$/i
Because you're using a regular expression. You could write the regular expression to match the beginning or end of the string ... like this:
while( $file_yes_no !~ /^(yes|no)$/ ) {
The ^ and $ are the beginning and end of the string. Also you can omit the m.
Or you could just check the values explicitly:
while( $file_yes_no ne "yes" and $file_yes_no ne "no" ) {
Also you have a typo in your system command but I'm assuming that was just copying it here. You really shouldn't branch out to a shell for that. Look into File::Copy which gives you a copy function

How to write a simple sub in perl that takes a string and returns a string?

sub getHeading
{
my $var = $_[0];
my $match;
if ($match = ($var =~ m/$fivetonine/))
{
return "=";
}
if ($match = ($var =~ m/$tentofourteen/))
{
return "==";
}
if ($match = ($var =~ m/$fifteentonineteen/)){
return "===";
}
return "===";
}
my $ref_to_getHeading = \getHeading;
and I am calling it via:
$html =~ s/(.*)<font size="([^"]+)">(.+)<\/font>(.*)/$ref_to_getHeading($2)$1$3$4$ref_to_getHeading($2)/m;
I am wanting to pass a string in to this function, I want to check if it is one of 3 different matches and return the appropriate number of = signs, I am doing this wrong but I can't figure out how to make it take parameters? I get a run time error saying $var is initialised? I tried using #_ but I don't really understand what the difference is.
Any help much appreciated, I have never written perl before and this is my first real program.
Double mistake there.
First, you aren't taking a reference to a function - You need to add the ampersand.
But even if you do that, it won't work. You are missing the /e flag in your substitution: You can't dereference a coderef within a string like you'd normally do with (scalar|hash|array)ref:
my $example = sub { return "hello" };
say "$example->()"; #Will stringify the coderef.
You either need the /e flag,
$html =~ s/etc/$ref_to_getHeading->($2) . "$1$3$4" . $ref_to_getHeading->($2)/em;
Or a little trick:
$html =~ s/etc/#{[$ref_to_getHeading->($2)]}$1$3$4#{[$ref_to_getHeading->($2)]}/m;
EDIT: Gosh, am I a slow typist..
Anyhow, with either way, you should be able to call the sub directly, so no need for the coderef.
The line my $ref_to_getHeading = \getHeading; doesn't do what you think it does. To take a reference to a subroutine:
my $ref_to_getHeading = \&getHeading; # note the &
So you were actually calling getHeading and storing the result. Since you passed no arguments, you got the undefined value warning.
The substitution however will never call the coderef, for that to happen, you need to add the e modifier to run the replacement text through eval:
$html =~ s/.../join '' => getHeading($2), $1, $3, $4, getHeading($2)/me;
you may run into issues here with getHeading resetting the match vars too early. In which case, try writing it this way:
$html =~ s{...}{
my $body = $1 . $3 . $4;
my $heading = getHeading($2);
$heading . $body . $heading
}me;
The bracket change for s/// was not necessary, I just find it easier to read a multi-line curly block.