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

Dear Monks,

I am generating perl code from a source perl file, the output is a mix of my generated code and the original source. So that possible errors can keep track of where I am in the original file, I'm outputting #line directives referring to the source file.

The problem is that once I've output a few lines of the original source, I'd like to switch back to having error messages refer to the current file; something like #line __LINE__ __FILE__. I haven't been able to find out how to do this or if it's at all possible.

If you have any ideas that'd be great!

Note: The functions which generate the code don't know what file they are writing to, and keeping track of the current output line seems like a big hassle, so generating #line directives for my own code isn't really possible. The only hack I've been able to come up with so far is generating the file with "#line __LINE__ __FILE__", and then in a second step doing

perl -i -pe 'if(/^#\s*line/) { s/__LINE__/$.+1/e; s/__FILE__/$ARGV/; }' -- generated.pl

But this isn't really completely satisfactory either since now the filename is fixed to that value...

Replies are listed 'Best First'.
Re: Reset the effect of #line (patch)
by tye (Sage) on Aug 26, 2013 at 00:41 UTC
    The functions which generate the code don't know what file they are writing to, and keeping track of the current output line seems like a big hassle, so generating #line directives for my own code isn't really possible.

    The Perl compiler only keeps track of one file name and one "current line number" for each stream of code that it is compiling. So supporting a "pop" (or "reset") feature for #line isn't really possible.

    Well, for your definition of the word "possible", which seems a rather non-standard one.

    That said, I think having a "reset" feature for "#line" is a fine idea. In a quick test, '#line 0' had the strange impact of making the next line of input "not from any line" but the line after it being "line 1". '#line 5 ""' is identical to '#line 5' (the empty string is ignored).

    So we could patch Perl so that '#line 0' resets the current line number to be real line number and so that '#line 9 ""' resets the current file name to be the original file name. Of course, '#line 0 ""' would reset both.

    However, keeping track of two different file names and two different "current line number"s for each stream of source code seems like a big hassle. So if you want it done, you'll probably have to write the patch for Perl yourself. See S_incline() in p5git://toke.c..

    - tye        

Re: Reset the effect of #line
by Eily (Monsignor) on Aug 25, 2013 at 18:29 UTC

    Maybe you should provide an example because I'm not sure I understood quite well. But if you read the documentation on #line directives you'll see that you do not have to provide a filename. It's not expressed as such, but it is said that the #line syntax matches the regex:

    /^\# \s* line \s+ (\d+) \s* (?:\s("?)([^"]+)\g2)? \s* $/x
    where the last ? shows that the filename can indeed be omitted.

    So just don't put it there, and it won't be fixed to any value.

    I'd still advise you to post a broader description of your problem (why, and how are you generating perl code?) because it somehow seems like you're doing something wrong (reading from and writing to files without being able to track line numbers shouldn't happen with Perl). Are you sure you can't just have your additions to the code in separate files and do them, to let Perl keep track of files and lines for you?

      Hello Eily, thanks for your response.

      I am writing a small Pod::Parser implementation that does something similar to Test::Pod::Snippets (that module doesn't work in my case and it doesn't look like I'd be able to extend it). I don't feel like this is an XY Problem, my question really does boil down to: how do I reset the effects of the #line directive so that error messages resume their default behavior and refer to the current file and line number again?

      Keeping track of input file names and line numbers isn't the problem, it's output file names and line numbers, since I'm just writing to STDOUT and I have no way of knowing where that'll end up.

      Anyway, here's an example of the general question. This is input.pl:

      print "foo\n"; die "oops" if rand>0.7; print "bar\n";

      Then I run this through my hypothetical script which generates and inserts some code, which produces output.pl:

      #line 1 input.pl print "foo\n"; die "oops" if rand>0.7; #line __LINE__ __FILE__ print "I am some generated code\n"; die "some error" if rand>0.3; #line 3 input.pl print "bar\n";

      The first die in either script always has the error message "oops at input.pl line 2", that's what I want.

      Now the problem is, I'd like to have the error message from the second die be "some error at output.pl line 6". However, it is (unsurprisingly) "some error at input.pl line 5".

      As I said above, even though I could theoretically try to track the output line number, I definitely can't track the output file name. If I replace #line __LINE__ __FILE__ with #line 5, I get the error message "some error at input.pl line 6", which isn't right either.

      I hope this answers your questions and makes the problem clearer?

        Oh well, not putting __FILE__ in the directive won't work there, my bad :).

        Could you eval your generated code instead? You do get the original filename back when you exit the eval scope:

        #line 10 Foo warn; eval <<"BAZ"; #line 100 Bar warn; BAZ warn;
        Warning: something's wrong at Foo line 10. Warning: something's wrong at Bar line 100. Warning: something's wrong at Foo line 15.