Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options

Syntax Error Checking

by madhatter (Sexton)
on Jan 01, 2001 at 13:24 UTC ( #49165=perlquestion: print w/replies, xml ) Need Help??

madhatter has asked for the wisdom of the Perl Monks concerning the following question:

Is there a way to make sure syntax is valid with code in a $variable ?

eval($variable) || print "ERROR: $@";

Except I don't want to the contents of $variable to be run. Just want to check the syntax for errors.

the mad hatter

Replies are listed 'Best First'.
Re: Syntax Error Checking
by chromatic (Archbishop) on Jan 01, 2001 at 13:50 UTC
    The only way I'm aware of to compile but not execute code in a variable is to wrap it in an anonymous subroutine block:
    my $goodcode = <<'CODE'; print "Hello, how are you?\n" CODE my $badcode = <<'CODE'; pront "Hello, how are you\n?' CODE { no strict; local $^W = 0; eval "sub {\n$goodcode\n}"; } print "Goodcode error: $@\n" if $@; { no strict; local $^W = 0; eval "sub {\n$badcode\n}"; } print "Badcode error: $@\n" if $@;
    You can get more elaborate if you like, writing your own warning handler to capture them instead of printing them. It's late and I shouldn't be posting.

    Oh, and the newlines are in there in case you have comments somewhere in your code string. I did once, and it commented out the ending brace of the sub. Beware.

    Update: If you want to suppress or log warnings, you can get rid of the 'no strict' and 'local' lines, and use a variant of this instead: local $SIG{__WARN__} = sub {};

    It'll create and throw away a new anonymous sub (unless eval() checks for void context -- I dunno), but it'll do the compile-but-not-execute waltz.

      Be very wary of using this with untrusted data. In the general case, sure, it might be a useful way to see if code compiles cleanly in the event you aren't prepared to execute it yet, but DO NOT use this method as any form of secure "compile only" test. What if the user provided "1 }; system("do something evil");" as their code?

      My first thought was to extend this eval method into using a 'reval' with a Safe compartment (with an obscenely strict opcode mask), which would prevent any "extra" code from being executed, but it seems as though the opcode mask is checked at compile time, which means the compilation would fail for legitimate stuff.

      That won't stop an embedded BEGIN block from executing.
      my $coderef = eval <<'END'; sub { print "hello world\n"; BEGIN { print "you lose!\n"; } print "goodbye world\n"; } END
      That prints "you lose". Too bad.

      -- Randal L. Schwartz, Perl hacker

        Too bad, true. But I still don't understand why it doesn't print the two other lines.

        In my Great Foolishness(tm), I had always thought that there could only be one BEGIN block by package. So as I was reading merlyn's answer to the question, I thought to myself: "Ah! easy! I just have to embed the eval'd block in another BEGIN block, to make sure it'll be evaluated first."

        Alas, the "you lose!" block was the first printed in the block and then the "hello/goodbye" lines. Mmmh, too bad.

        Then, I realized that anything outside the BEGIN block in the sub was not executed.

        What is happening exactly? Does a BEGIN code inside a sub never returns? Can someone point me a node where all these mysteries are unveiled? Thanks!

        PerlMonger::Paris(http => '');</kbd>
Re: Syntax Error Checking
by Fastolfe (Vicar) on Jan 01, 2001 at 22:01 UTC
    Update: Bleh, all of these still fall prey to the BEGIN { ... } issue.. Anyone can still inject code that will be executed as the rest is compiled. I don't see any way around this.. You might just try s/BEGIN\s*{/BEGIN { die "No BEGIN block allowed"; }/g, but I'm not confident that that would catch all occurrences of BEGIN blocks (or END for that matter!), and it could easily hit something legitimate in the code body itself. Your best/only option may in fact be to try and use a Safe compartment.

    The original message: Actually I just thought of a better way to do this:

    sub test_code { my $code = shift; $code = 'die "OK\n"; ' . $code; eval $code; $@ eq "OK\n"; }
Re: Syntax Error Checking
by Fastolfe (Vicar) on Jan 01, 2001 at 21:56 UTC
    Why not just send the code straight to "perl -cw" itself?
    sub test_code { my $code_to_check = shift; my $pid = open(PERL, "|-"); if (!$pid) { die "fork: $!" unless defined $pid; exec("/usr/bin/perl", "-cw") or die "/usr/bin/perl: $!"; } print PERL $code_to_check; close(PERL); return !$?; # $? == 0 means compile succeeded }
    You may want to close STDIN/STDERR to handle any output the forked Perl process gives, and just rely on the exit status, or use open2/open3 if you want to capture the compilation messages Perl gives you.
      perl -cwe 'BEGIN{print "Hello world\n"}'
      Any more questions? :-)
Re: Syntax Error Checking
by cpatti (Initiate) on Sep 14, 2001 at 23:44 UTC
    Looking at all the responses, using perl -c was the first thing I thought of.

    That begs the question though - why doesn't Perl include a built in Syntax::Check module that you could run on either a string or a file that would return some simple success or failure code?

    Just wondering :)

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://49165]
Approved by root
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (2)
As of 2022-01-20 04:10 GMT
Find Nodes?
    Voting Booth?
    In 2022, my preferred method to securely store passwords is:

    Results (56 votes). Check out past polls.