Whitespace with perl Switch module - perl

The following trivially short perl program works fine (and does nothing, as expected):
use Switch;
switch (1) {}
However if I put the program on one line, as follows,
use Switch; switch (1) {}
I get an error
richard#affric:~$ perl foo.pl
syntax error at foo.pl line 1, near ") {"
Execution of foo.pl aborted due to compilation errors.
Can anyone explain this to me?
(Edit: Yes, I know Switch.pm is deprecated, and, yes, I know that the code is more readable with the line break.)

The Switch module is written as a Perl filter, which essentially means that the Switch module gets to see and make changes to the source code before the perl parser gets it.
I believe what you're seeing here is just a bug in the filter. If you use the backend B::Deparse module then you get a chance to see what nasty things Switch has done to your program. For instance, for your initial version
perl -MO=Deparse myswitch.pl
produces
use Switch;
S_W_I_T_C_H: while (1) {
local $_S_W_I_T_C_H;
&Switch::switch(1);
}
continue {
(last);
}
so you can see that there is plenty of scope for error.
If I had to guess I would say that Switch is simply expecting all of the line starting use Switch to contain module options and nothing else. If you use the -M switch to include the module, like this
perl -MO=Deparse -MSwitch -E'switch (1) { }'
then the result is this
use Switch;
S_W_I_T_C_H: while (1) {
local $_S_W_I_T_C_H;
&Switch::switch(1);
}
continue {
last;
}
-e syntax OK
which is different in that neither a compile time nor a run time error is raised.
Update
The problem seems to be an issue with the Filter::Util::Call module, on which Switch is based. The problem is that the text that this module passes to source filters begins only on the line following the use Switch statement. Anything after it on the same line is processed directly by the perl parser as normal.
That means that, in your second case, perl is seeing the (unmodified) program switch (1) {} while Switch has nothing to do as there is no code at all after this line to be filtered.
I think this is highly likely to be an inherent part of perl, and there is no way to intercept the source stream in the mid-line. But it's a non-critical bug and, now that you know the cause, you should be able to work around it easily.

Related

Why do `perl Foo.pm` and `perl -I. -mFoo -e1` behave differently?

With this code in Foo.pm:
use strict;
use warnings;
package Foo;
BEGIN {
$Foo::AUTHORITY = 'cpan:ETHER';
}
1;
Loading the file as a module gives no errors:
$ perl -I. -mFoo -e1
$
And yet, loading the file directly does:
$ perl Foo.pm
Name "Foo::AUTHORITY" used only once: possible typo at Foo.pm line 6.
Moreover, perl -e'require "Foo.pm"' also does not warn.
Why is there this difference? Clearly the file is being parsed differently,
but how and why?
"Why" from a technical point of view, or from a language design point of view?
From a language point of view, it makes sense because a variable referred to within a module may well be part of the module's public API. For example, Data::Dumper exposes a bunch of package variables that alter its behaviour. (Arguably bad design, but ho hum.) These variables might only be referred to once in the module, but can potentially be referred to from other parts of the program.
If it's only referred to in the main script once, and no modules refer to it, then it's more likely to be a mistake, so we get this warning within the script, but not in the module.
From a technical point of view, this warning is generated by gv.c. Personally I can't make head nor tail of the exact conditions under which it's triggered.
Surely the exception was made because some modules do
if ($Me::Setting) {
...
} else {
...
}
We didn't always have our and use vars (the latter depending on yet an other exception for imported symbols).
Warnings are issued with warn (Perl side) or Perl_warner (C side). The line in question is this one.

Why is this program valid? I was trying to create a syntax error

I'm running ActiveState's 32 bit ActivePerl 5.14.2 on Windows 7. I wanted to mess around with a Git pre-commit hook to detect programs being checked in with syntax errors. (Somehow I just managed to do such a bad commit.) So as a test program I randomly jotted this:
use strict;
use warnings;
Syntax error!
exit 0;
However, it compiles and executes with no warnings, and errorlevel is zero on exit. How is this valid syntax?
Perl has a syntax called "indirect method notation". It allows
Foo->new($bar)
to be written as
new Foo $bar
So that means
Syntax error ! exit 0;
is the same as
error->Syntax(! exit 0);
or
error->Syntax(!exit(0));
Not only is it valid syntax, it doesn't result in a run-time error because the first thing executed is exit(0).
I don't know why, but this is what Perl makes of it:
perl -MO=Deparse -w yuck
BEGIN { $^W = 1; }
use warnings;
use strict 'refs';
'error'->Syntax(!exit(0));
yuck syntax OK
It seems that the parser thinks you're calling the method Syntax on the error-object... Strange indeed!
The reason you do not get an error is that the first executed code is
exit(0);
Because you did not have a semicolon on the first line:
Syntax error!
The compiler will guess (incorrectly) that this is a subroutine call with a not operator ! thrown in. It will then execute the arguments to this subroutine, which happens to be exit(0), at which point the program exits and sets errorlevel to 0. Nothing else is executed, so no more runtime errors are reported.
You will notice that if you change exit(0) to something like print "Hello world!" you do get an error:
Can't locate object method "Syntax" via package "error" ...
and your error level will be set:
> echo %errorlevel%
255
As noted above this is caused by the indirect method calling notation. You can warn on this:
use strict;
use warnings;
no indirect;
Syntax error!
exit 0;
Produces:
Indirect call of method "Syntax" on object "error" at - line 5.
This requires the indirect CPAN module.
You can also use no indirect "fatal"; to cause the program to die (this is what I do)
Try Perl 6, it seems to fulfill your expectations more readily:
===SORRY!=== Error while compiling synerror.p6
Negation metaoperator not followed by valid infix
at synerror.p6:1
------> Syntax error!⏏<EOL>
expecting any of:
infix
infix stopper
In this paper, we aim to answer a long-standing open problem in the
programming languages community: is it possible to smear paint on the
wall without creating valid Perl?
TLDR; Hardly

In Perl, is there any way to tie a stash?

Similar to the way AUTOLOAD can be used to define subroutines on demand, I am wondering if there is a way to tie a package's stash so that I can intercept access to variables in that package.
I've tried various permutations of the following idea, but none seem to work:
{package Tie::Stash;
use Tie::Hash;
BEGIN {our #ISA = 'Tie::StdHash'}
sub FETCH {
print "calling fetch\n";
}
}
{package Target}
BEGIN {tie %Target::, 'Tie::Stash'}
say $Target::x;
This dies with Bad symbol for scalar ... on the last line, without ever printing "calling fetch". If the say $Target::x; line is removed, the program runs and exits properly.
My guess is that the failure has to do with stashes being like, but not the same as hashes, so the standard tie mechanism is not working right (or it might just be that stash lookup never invokes tie magic).
Does anyone know if this is possible? Pure Perl would be best, but XS solutions are ok.
You're hitting a compile time internal error ("Bad symbol for scalar"), this happens while Perl is trying to work out what '$Target::x' should be, which you can verify by running a debugging Perl with:
perl -DT foo.pl
...
### 14:LEX_NORMAL/XOPERATOR ";\n"
### Pending identifier '$Target::x'
Bad symbol for scalar at foo.pl line 14.
I think the GV for '::Target' is replaced by something else when you tie() it, so that whatever eventually tries to get to its internal hash cannot. Given that tie() is a little bit of a mess, I suspect what you're trying to do won't work, which is also suggested by this (old) set of exchanges on p5p:
https://groups.google.com/group/perl.perl5.porters/browse_thread/thread/f93da6bde02a91c0/ba43854e3c59a744?hl=en&ie=UTF-8&q=perl+tie+stash#ba43854e3c59a744
A little late to the question, but although it's not possible to use tie to do this, Variable::Magic allows you to attach magic to a stash and thereby achieve something similar.

Can I insert break point into source perl program?

I want the perl program launch debugger when some condition hit. Some other language has debug() statement supported by library, is there any similar statement in perl?
If I understand you correctly, you need to use a specific debugger variable in your code - $DB::single. Setting this to a true value in your code will cause the debugger to stop on that line.
$x = 1234;
$DB::single = 1;
enter_problematic_sub_now();
Your code will then stop at the line with $DB::single set to 1.
Of course, if you can't actually ensure that your code is running in the debugger then you have another problem entirely. You will need to run your code via perl -d as well.
Have you tried adding the -d switch to the shebang line at the top of your script? Something like
#!/usr/bin/perl -d
use strict;
use warnings;
$|=1;$\="\n";
print "Test";
It really depends exactly how it gets launched, but at least in simple cases this should start the debugger.
Edit:
You can then set a breakpoint on a specific line with a certain condition using
> b [line] [condition]
and hit
> c
to continue with running the script - the debugger will stop at the specified line when the condition is met
Well, there is something which will enable you to do something like breakpoints, but the functionality is wider: Perl Debugger.
Essentially the -d switch allows you to communicate with the perl executable, and allows the perl executable to communicate with you.
More

Why does this Perl BEGIN block act differently in the debugger?

I have some Perl code that runs fine outside the debugger:
% perl somefile.pl
but when I run it inside the debugger:
% perl -d somefile.pl
it behaves differently.
The files in question (there are several) are part of the test suite for a large Perl module (~20K lines of code). The tests do a lot of setup work at compile time and use BEGIN blocks. Here's some minimal reproduction code:
BEGIN
{
package MyEx;
sub new { bless {}, shift }
package main;
eval { die MyEx->new };
if($#)
{
die "Really die" unless($#->isa('MyEx'));
}
}
print "OK\n";
If you put that in somefile.pl and run it, it prints "OK" as expected. If you run it in the debugger with perl -d somefile.pl, it dies with this error:
Can't call method "isa" without a package or object reference ...
The upshot is that $# is not an object when the code runs under the debugger. Instead, it's an unblessed scalar containing this string:
" at somefile.pl line 9
eval {...} called at somefile.pl line 9
main::BEGIN() called at somefile.pl line 16
eval {...} called at somefile.pl line 16
"
(Internal newlines and spacing preserved. That's the literal text, even the "..."s.)
I need code like this to run in the debugger. Using the debugger in the test suite is an important part of my workflow. The module uses exception objects and does a lot of stuff at compile time and expects an object thrown to be an object when caught.
My question (finally) is this: How can I get this to work? Is there a workaround? Is this a bug in the perl debugger module? What's the best way to go about getting this resolved? (I know that's several questions, but they're all related.)
I'm using perl 5.10.0 on Mac OS X 10.5.5.
The dieLevel thing suggested by Adam Bellaire looked promising, and indeed something (can't find out what) is setting it to 1 for me. But I set it to 0 using a ~/.perldb file and the problem persists. In fact, I set all three of the related settings to 0. My ~/.perldb file:
parse_options('dieLevel=0 warnLevel=0 signalLevel=0');
I confirmed that the settings are in effect by running the o command in the debugger. I see them all set to 0 when I run perl -de 0 and also when running the actual somefile.pl file.
Thanks, brian. I used perlbug to file a bug (RT 60890) and I've begun to sprinkle local $SIG{'__DIE__'} in all the appropriate places in my code. (I also noted in the bug that perldoc perldebug still seems to imply that the default dieLevel is 0.)
This is a problem with perl5db.pl creating __DIE__ handlers. If I localize $SIG{__DIE__} in your eval, things work as you expect.
eval {
local $SIG{__DIE__};
die MyEx->new
};
If you don't do that, you're getting the handler from DB::dbdie, which uses Carp::longmess. That shouldn't happen if dieLevel is 0, but by default it is 1, and it gets set to 1 if it is not defined. This was a patch to perl5db.pl back in 2001, and previously the default had been 0.
You're supposed to turn this off with:
PERLDB_OPT="dieLevel=0" perl5.10.0 -d program
But there is still a code reference in $SIG{__DIE__} after that, and it's a reference to dbdie. I think this is a bug in handling the global variable $prevdie in perl5db.pl's dieLevel. At the end of that subroutine, there is:
# perl5db.pl dieLevel, around line 7777
elsif ($prevdie) {
$SIG{__DIE__} = $prevdie;
print $OUT "Default die handler restored.\n";
}
But notice that after restoring $SIG{__DIE__}, it keeps the previous value in $prevdie, meaning whatever is in there leaks to another call. When I run that command line, there are two calls to dieLevel before it handles PERLDB_OPT, so $prevdie is probably dirty.
So, that's as far as I got before I didn't want to think about perl5db.pl anymore.
I consider it a bug any time code behaves differently in the debugger.
Your problem might be related to this: Debugger corrupts symbol table munging. Essentially, the debugger appears to play some tricks with local -- presumably as part of sandboxing things to provide interactivity. Obviously, messing with the symbol table can have unexpected side-effects. I'd guess that the debugger is localizing $# and thus obscuring your object. I can't think of a work-around.
Is it possible you have an RC file or environment variable (PERLDB_OPTS) that is modifying the dieLevel option of the debugger? I personally haven't used dieLevel but apparently when it's set to a value greater than zero it can force stack unwinding and "tends to hopelessly destroy any program that takes its exception handling seriously." (Quote from here).