New to Perl, problems with curly brackets / syntax - perl

I have recently started programming in Perl (I'll skip the long story) but I've stumbled upon a few errors that I can't seem to understand:
syntax error at /usr/sbin/test.pl line 238, near ") {"
syntax error at /usr/sbin/test.pl line 247, near "} else"
syntax error at /usr/sbin/test.pl line 258, near ") {"
syntax error at /usr/sbin/test.pl line 276, near ") {"
syntax error at /usr/sbin/test.pl line 304, near "}"
syntax error at /usr/sbin/test.pl line 308, near "}"
syntax error at /usr/sbin/test.pl line 323, near "}"
it seems to be something to do with the brackets surrounding if and else
I'm experienced in C, C#, Java, PHP, Lua, and others so I'm a bit embaressed to get stuck on syntax errors..
I've pasted a sample of code that generates a syntax error:
if (substr(ToString($buffer),0,4) == 'HELO') {
$contype = 'smtp';
send($client,'250 Welcome',0);
} elsif (substr(ToString($buffer),0,4) == 'EHLO') {
$contype = 'esmtp';
send($client,'250-$hostname Welcome',0);
send($client,'250 SIZE $msgmaxsize',0);
}
do {
recv($client,$buffer,1024,0);
} while (ToString($buffer) != 'QUIT') {
if (substr(ToString($buffer),0,10) == 'MAIL FROM:')
{
$sender = ToString($buffer);
$sender =~ m/<(.*?)>/;
send($client,'250 OK',0);
} else {
send($client,'503 I was expecting MAIL FROM',0);
send($client,'221 Bye',0);
break;
}
}
unfortunately I can not show the entire program.
Perl version 5.10.1

This makes no sense:
...
do {
recv($client,$buffer,1024,0);
} while (ToString($buffer) != 'QUIT') {
if (substr(ToString($buffer),0,10) == 'MAIL FROM:')
...
You're combining a statement modifier (do {..} while...; with a while () {} loop. It's either or.
So write something like:
...
while ( recv($client,$buffer,1024,0) ) {
last if ToString($buffer) eq 'QUIT';
if (substr(ToString($buffer),0,10) eq 'MAIL FROM:') {
...
}
}
etc.

Besides the mistake of using == and != (which are number-comparison operators) instead of eq and ne for string comparisons, you are missing a semi-colon after the while's test. That is, you have
do { ...; } while (...) { if (...) { ... } else {...}}
Note that Perl, like C, supports both forms
while (expr) { stuff }
and
do { stuff } while (expr)
and I'm supposing you meant to use the latter form.
If the above accounts for the error at line 238, then it's possible that the error at line 247 might go away when you correct it, if it somehow causes a dangling else; but without compiling the code, I don't quite see how.

Related

Using Number::Phone to validate and format

I'm trying to use Number::Phone from CPAN to accomplish 2 tasks:
Validate a Phone Number; and
Format the number in E.164 Notation.
However, I'm unable to figure out how it works. My sample code is:
#!/usr/bin/perl -w
use strict;
use warnings;
use Number::Phone;
foreach my $fnum ( '17888888', '97338888888', '00923455555333', '+97366767777' , '38383838') {
my $phone = Number::Phone->new($fnum);
my $norm = "";
eval {
$norm = $phone->format_using('E123'); # or 'Raw'
print "E164 => '$norm'\n";
} or do {
print STDERR "Unable to parse '$fnum'\n";
}
}
Expected output:
E164 => '+97317888888'
E164 => '+97338888888'
E164 => '+923455555333'
E164 => '+97366767777'
E164 => '+97338383838'
But the results were incorrect. I tried using Number::Phone::Normalize, but still not successful:
#!/usr/bin/perl -w
use strict;
use warnings;
use Number::Phone::Normalize;
my %params = (
'CountryCode'=>'973',
'IntlPrefix' =>'00',
'CountryCodeOut'=>'973',
'IntlPrefixOut' => '+',
);
my $nlz = Number::Phone::Normalize->new( %params );
foreach my $number ('17888888', '97338888888', '00923455555333', '+97366767777' , '38383838') {
my $e164 = $nlz->intl( $number );
print "E164 => '$e164'\n";
}
with the same expected output of:
E164 => '+97317888888'
E164 => '+97338888888'
E164 => '+923455555333'
E164 => '+97366767777'
E164 => '+97338383838'
However, this produced the wrong results too. The snippet Java code below works perfectly, and it's what I'm trying to achieve in Perl.
// Uses libphonenumber: http://code.google.com/p/libphonenumber/
// setenv CLASSPATH .:libphonenumber-8.5.2.jar
// libphonenumber
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
public class ValidateList {
public static void main(String[] args) {
try {
if (args.length != 1) {
throw new IllegalArgumentException("Invalid number of arguments.");
}
String file = args[0];
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
try (java.io.BufferedReader br = new java.io.BufferedReader(new java.io.FileReader(file))) {
String line = null;
while ((line = br.readLine()) != null) {
try {
PhoneNumber phoneNumber = phoneUtil.parse(line, "BH");
boolean isValid = phoneUtil.isValidNumber(phoneNumber);
if (isValid) {
System.out.println( "E164 => " + phoneUtil.format(phoneNumber, PhoneNumberFormat.E164) );
}
else {
System.err.println( "Invalid => " + line);
}
}
catch (NumberParseException e) {
System.err.println("NumberParseException for ("+line+"): " + e.toString());
}
}
}
}
catch (Exception e) {
System.err.println(e);
System.err.println("Usage: java ValidateList <fileNameWithPhoneNumbers>");
}
}
}
% cat input.txt
17888888
97338888888
00923455555333
+97366767777
38383838
% javac -cp libphonenumber-8.5.2.jar ValidateList.java
% java -cp .:libphonenumber-8.5.2.jar ValidateList input.txt
E164 => +97317888888
E164 => +97338888888
E164 => +923455555333
E164 => +97366767777
E164 => +97338383838
Your input is greatly appreciated.
When I run the first example code for the numbers, two of those fail to be parsed:
17888888 - this is obvious, when calling Number::Phone without a country code, this will not be parsed as it's unclear what country this is from
00923455555333 - 923 is, according to a quick google search, the country code for Pakistan. The Wikipedia page for dialing codes in Pakistan shows no 455, leading me to think that this is not a known area code to either Number::Phone or Wikipedia. I suspect it is an invalid number.
So for the first Number: specify which country this is supposed to be from.
If you are certain the other number is correct, you know more about that than the developer of Number::Phone currently and I'm sure he'd be happy to receive your input in the form of a more complete Number::Phone localized package.
The fact that your Java code accepts the (probably) invalid number does not necessarily mean it is more correct, just that it is less picky in what it declares to be a correct number.
Edit:
Asking Phone::Number to parse the input '+923455555333' instead of '00923455555333' leads to the desired output.
Looking at the source of Phone::Number:
# ... processing input arguments
$number = "+$number" unless($number =~ /^\+/);
It becomes clear that the 00 is interpreted as '+00' and then rejected as being an invalid number.
View some discussion on that here
It seems to me you will have to handle this yourself.
One way may be to simply replace leading 00 with '+' - preferably only if parsing failed.
The other number can be parsed if you make it clear what country it should belong to.
Perhaps like so:
my $phone = Number::Phone->new($fnum);
unless ($phone){
$phone = Number::Phone->new('BH',$fnum);
if ( !$phone && $fnum =~ s/^00/+/ ){
# You should probably check the discussion I linked.
# There may well be problems with this approach!
$phone = Number::Phone->new($fnum);
}
}

Converting Parts of an Array in powershell

I'm supposed to tag this as homework, but I'm not sure how. Not asking for an answer, just to be pointed in the right direction of what I'm doing wrong. Basically, this is supposed to make a complete sentence out of ANY user input, with the exception of -AllCaps, which is functioning properly. My problem is the other part.
param ($title, [switch]$AllCaps)
$ex="to|a|the|at|in|of|with|and|but|or"
function Proper($ts)
{
$nt=foreach($word in $ts)
{
$word=$word.ToLower()
if($word -match $ex)
{
if($word -eq $ts[0])
{
$letters=$word -csplit("")
$letters[1]=$letters[1].ToUpper()
$word=$letters -join("")
}
else
{
$word=$word
}
}
else
{
$letters=$word -csplit("")
$letters[1]=$letters[1].ToUpper()
$word=$letters -join("")
}
"$word"
}
$nt=$nt -join(" ")
Write-host $nt
}
if($AllCaps)
{
$title=$title.ToUpper()
"$title"
}
else
{
$ts=$title -split(" ")
$newtitle=Proper $ts
}
So, when I execute the script passing in "the waffle of kings", expected output is "The Waffle of Kings" - instead, the code seems to be completely ignoring the "else" in the first "if else" statement, and the output I'm getting is "The waffle of kings". Since "waffle" doesn't match anything in $ex, it should be moving to the else part, capitalizing the first letter.
Less of note is that it doesn't write to the console without including "$word" within the foreach loop, though the way I have it written, the Write-Host should be doing that for me. If I take out the write-host nothing changes, but if I take out the "$word" in the loop, I get no output at all.
Using the above mentioned
$ex='^(to|a|the|at|in|of|with|and|but|or)$'
I ended up with a good one-liner, but have expanded it out here:
Function Proper($words)
{
$ex='^(to|a|the|at|in|of|with|and|but|or)$'
$nt = $(
foreach ($word in $words)
{
$word = $word.ToLower();
if ($word -match $ex)
{
$word
} else
{
$word = ([String]$word[0]).ToUpper()+$word.Substring(1);
$word
}
}
) -join " "
return $nt
}
I hope that helps.
Thanks, Chris.

Sed : Add a line at the starting of each TCL proc

I have a TCL proc like this, & want to add a line after the start of the proc, the puts "puts " entered myproc" " line
proc myproc { {filename "input.txt"}
{var1 "x"}
{var2 "y"}
{var3 "z"}
{var4 ""}
{var5 "0"}
{var6 "0"}
{var7 0}
} {
puts " entered myproc"
Can you help?
& it should also work for
proc myproc2 { N val } {
puts " entered myproc"
# comment line
set ret {} for { set i 0 } { $i < $N } { incr i } { lappend ret $val }
return $ret
}
If all you want to do is get an execution trace of your code, such as a call stack dump etc, then you don't need to modify your source code at all. You can use tcl itself to do it for you.
Tcl has no reserved keywords, none at all. Not even proc is reserved. You can therefore redefine it:
rename proc _proc
# Now proc no longer exists but we have _proc instead.
# Use it to redefine "proc":
_proc proc {name arguments body} {
set body "puts \"entered $name\";$body"
_proc $name $arguments $body
}
Just do that before running any of your own code and you'll find that every proc prints out when it's being entered on each call.
This is how a lot of tcl debuggers and profilers work - using tcl to redifine itself.
From your comments it looks like you're trying to also print how deep the stack is with each call. To do that you need to add more code to each proc definition. The most straightforward way is of course something like this:
_proc proc {name arguments body} {
set preamble"set dist2top \[info level\];puts \"\$dist2top entered $name\""
set body "$preamble;$body"
_proc $name $arguments $body
}
But as you can see, writing code inside strings can quickly become unmanagable. There are several tricks you can use to make it more manageable. One of the more common is to split $body by line and use list commands to manipulate code. It should reduce at least one level of quoting hell. My favorite is to use a templating technique similar to how you'd write html templates in MVC frameworks. I usually use string map for this:
_proc proc {name arguments body} {
_proc $name $arguments [string map [list %NAME% $name %BODY% $body] {
set dist2top [info level]
puts "$dist2top entered: %NAME%"
%BODY%
}]
}
The last argument in the _proc definition is just a string but it looks like a code block which makes it easier to read. No nasty quoting hell with this technique.
Using awk you can do:
awk '/^ *proc/ {$0 = $0 "\nputs \" entered myproc\""} 1' RS= proc-file.tcl
Gives this file:
proc myproc { {filename "input.txt"}
{var1 "x"}
{var2 "y"}
{var3 "z"}
{var4 ""}
{var5 "0"}
{var6 "0"}
{var7 0}
} {
puts " entered myproc"

Using Parse::Lex, is there a way to return tokens only in certain states/conditions

Assuming that i need to tokenize and parse only multiline comments, how will i do that using Parse::Lex. When using flex-bison, the default action for any pattern in the rules section of the lex file used to be 'skip'.
%%
.* ;
%%
How to do this here ?
[EDIT] Well, i tried that, i'm still missing something - here is my code - and result. Where have i gone wrong ??
my simplified lex file:
use Parse::Lex;
use Regexp::Common;
use YParser;
my $lexer;
my #token = (
qw|esp:TA abcdefgh|,
qw(esp:REST .|\n),
);
Parse::Lex->trace;
Parse::Lex->exclusive('esp');
$lexer = Parse::Lex->new(#token);
$lexer->from(\*STDIN);
$lexer->skip(qr! [ \t]+ | $RE{balanced}{-begin=>'/*'}{-end=>'*/'} !xms);
$lexer->start('esp');
my $j = YParser->new();
$j->YYParse(yylex => \&lex);
sub lex {
my $token = $lexer->next;
return ('', undef) if $lexer->eoi;
if ($token->name eq 'TA' || $token->name eq 'REST') {
return ($token->name, {LINENO => $lexer->line, TEXT => $token->text});
}
}
my simplified grammar file
% token TA REST
%%
Program: Element
| Program Element
;
Element: TA
| REST
;
%%
Input file:
abcdefgh
/*sdf*/
Result:
perl lexfile.pl < inputfile
Trace is ON in class Parse::Lex
Can't call method "name" on an undefined value at qnlex.pl line 26, <STDIN> line 1.
Use the skip setting, shown here using Regexp::Common to help construct a regexp matching balanced pairs of comment delimiters. I've assumed /* */ as the comment delimiters, but they could be anything.
$lexer->skip(qr! [ \t]+ | $RE{balanced}{-begin=>'/*'}{-end=>'*/'} !xms);
The [ \t]+ alternative is left in place since that's the default.
Well, i figured this out :) Very simple - all i have to do is make the lex get the next token when encountering tokens i want to skip. Below is code to skip passing the token 'REST' to the parser.
sub lex {
my $token;
NEXTTOKEN:
$token = $lexer->next;
return ('', undef) if $lexer->eoi;
if ($token->name eq 'TA') {
return ($token->name, {LINENO => $lexer->line, TEXT => $token->text});
}
elsif ($token->name eq 'REST') {
goto NEXTTOKEN;
}
}

Selenium PHP "verify" fails do not include line numbers

Methods like verifyText do not report the line number of the failure, making it hard to find the point of failure.
The code created by Selenium IDE PHPUnit export looks like this:
try {
$this->assertEquals($text, $this->getTitle());
} catch (PHPUnit_Framework_AssertionFailedError $e) {
array_push($this->verificationErrors, $e->toString());
}
The output for this line looks like line 2 below, which is completely untraceable
Failed asserting that '' matches PCRE pattern "/Harlem/".
Failed asserting that two strings are equal.
Failed asserting that '(Pattern A)' matches PCRE pattern "/\(Pattern B\)/".
I've amended the call to include the referenced text which lets me search for the text failure, but in a large test this is not sufficient. How do I get the line number/stacktrace for each verify failure within my code?
public function verifyTitle($text) {
$title = $this->getTitle();
try {
$this->assertEquals($text, $title);
} catch (PHPUnit_Framework_AssertionFailedError $e) {
array_push($this->verificationErrors,
"Title is '$title' but should be '$text'");
}
}
Note: in order to get stacktraces returning references to my code for asserts, I am using the stacktrace hack
Created this verification Method to include (too much) stacktrace:
public function appendVerification($message,$e) {
array_push($this->verificationErrors,$message."\n".$this->dumpStack($e));
}
I also updated the referenced dumpStack method for PHPUnit tests to dumbly strip out framework calls by class name
protected function dumpStack(Exception $e) {
$stack = '';
foreach ($e->getTrace() as $trace) {
if (isset($trace['file']) &&
isset($trace['line'])) {
if (!isFramework($trace['file']))
$stack .= PHP_EOL .
$trace['file'] . ':' .
$trace['line'] . ' ';
}
}
return $stack;
}
function isFramework($fileName) {
$test = ((preg_match("/PHPUnit/i",$fileName) +
preg_match("/php.phpunit/i",$fileName)) > 0);
return $test;
}
End result, clickable in netbeans, free of any PHPUnit framework line numbers
Failed asserting that 'waitForElementPresent failed for selector: css=input#address.valid' is false.
C:\dev\Automation_Dev\phpTests\library\SeleniumUtilsTestCase.php:228
C:\dev\Automation_Dev\phpTests\library\SeleniumUtilsTestCase.php:115
C:\dev\Automation_Dev\phpTests\library\SeleniumTestCase.php:176
C:\dev\Automation_Dev\phpTests\usecases\CreateListingTest.php:72
C:\dev\Automation_Dev\phpTests\usecases\CreateListingTest.php:39
C:\dev\Automation_Dev\phpTests\usecases\CreateListingTest.php:23
C:\xampp\php\phpunit:46