dpuu has asked for the wisdom of the Perl Monks concerning the following question:
Hi,
I'm writing a script that processes an "executable spec" document that contains embedded code fragments. The script is designed to be a lint tool. I want to extract these fragments, and check that they are valid perl. The extraction part is easy; but how do I check the syntax?
One way to do this is something like
system( qw(perl -c -e), $my_text);
But I was wondering if its possible to do this without the system-call. Basically, I want to call eval($my_text), but I want it to act like it had a -c option (i.e. don't eval, just compile). I can assume there are no "BEGIN" blocks, nor other such nasties that would cause code to be executed during the compile.
Any thoughts? --Dave.
Re: eval, but syntax-check only -- like perl -c
by bbfu (Curate) on Apr 24, 2003 at 18:16 UTC
|
If you're certain that there will be no code that can execute during the compile (a big assumtion... Have you considered use, modules, etc? How do you ensure it?), then why not just something like this:
$code = 'whatever';
eval qq(return 1; $code);
if($@) {
chomp($@);
print "Syntax error: `$@'\n";
} else {
print "Syntax ok\n";
}
bbfu
Black flowers blossom
Fearless on my breath | [reply] [d/l] [select] |
Re: eval, but syntax-check only -- like perl -c
by bluto (Curate) on Apr 24, 2003 at 18:19 UTC
|
Will wrapping this in a non-executable construct
work for you ...
my $snippet = ...
eval "if (0) { $snippet }";
die if $@;
It *seems* to work.
bluto
| [reply] [d/l] |
Re: eval, but syntax-check only -- like perl -c
by particle (Vicar) on Apr 24, 2003 at 19:42 UTC
|
why run without the external call? without your reasoning, it's hard for me to see why any of the solutions posted so far are better... they're full of caveats. if you use File::Temp and perl -c the file, it will work, and can even be configured to clean up after itself. to a user, it should look the same, except it's probably less of a maintenance headache.
~Particle *accelerates*
| [reply] [d/l] [select] |
|
I guess there's nothing particularly wrong with the system call: indeed, in some ways it works better. For example, "use strict" doesn't work inside an eval.
One of the nice things about the 'eval "sub { $txt }"' approach is that it gives me a code ref that I can call later if I want. This means that I can have a two-pass approach: in the first I gather/check all my fragments; and in the second I run selected ones. This isn't my current plan, but is nice to have the flexibility.
--Dave
| [reply] |
|
there's room for that here, as well. create a hash with nice names as keys, and filehandles or filenames or the code snippets themselves as values. you still have the two-pass approach, but if you write to disk and keep the filehandles or filenames around, you can always slurp them in quickly to run them. heck, you can do with them what you will.
~Particle *accelerates*
| [reply] [d/l] |
Re: eval, but syntax-check only -- like perl -c
by vladb (Vicar) on Apr 24, 2003 at 18:16 UTC
|
What if you try embedding your code snippets inside a subroutine and doing eval on that? This would not cause the code to execute and would give you various warning messages (by inspecting the $@ variable) that you may be looking for?
Update: Missed bbfu's reply, but essentially that's what I was trying to say here ;-)
_____________________
# Under Construction
| [reply] [d/l] [select] |
Re: eval, but syntax-check only -- like perl -c
by dpuu (Chaplain) on Apr 24, 2003 at 18:57 UTC
|
Thanks all: I think I'll go with wrapping it in an anonymous sub -- eval will then return something that I can actually run, later, if I want. I'll probably run some rexen on the code to ensure it doesn't contain things like "BEGIN", "CHECK", "use" or named-sub defns. If I overly constrain, then someone will complain and I can fix it later. --Dave | [reply] |
|
You're probably aware of this already, but there will still be ways for sneaky programmers to get around restrictions like that. For instance:
my @backward_chars = ('e', 's', 'u');
my $watch_out = (reverse join "",@backward_chars) . " Data::Dumper;";
eval $watch_out;
my $x = Dumper([1,2,3]);
print $x;
I've managed to use Data::Dumper without "use" every appearing explicitly in the code.
Update: D'oh, I forgot to consider the all-important "wrap in an anonymous subroutine" part, which would seem to defer actual execution of the code. My bad.
$perlmonks{seattlejohn} = 'John Clyman'; | [reply] [d/l] |
Re: eval, but syntax-check only -- like perl -c
by eduardo (Curate) on Apr 24, 2003 at 18:05 UTC
|
UPDATE: on second thought, I can't figure out how you would use B::Deparse intelligently, so don't pay much attention to my post :)
As far as I can tell, as you probably know, only perl can parse Perl. You might have a good shot at trying to use something crazy like B::Deparse (look in the USING B::Deparse AS A MODULE section), but even that's not going to successfully figure out the syntax of "all of perl."
Unless one of the considerably more knowledgable monks corrects me, and I hope they do, you are pretty much stuck with "perl -c" (though I would usually do "perl -wc" :)
| [reply] |
|
| [reply] [d/l] [select] |
|
|