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

Hi all,
A (hopefully) quick question regarding Parse::RecDescent. I'm experimenting with it, trying to create my own grammars, parsers etc. Which works quite well. However I have a problem reading/using variables from the main programm (package) from within the grammar.
Example:
package TelnetClient::Functions; .. { my $checksyntax; my $parser; my $grammar; sub init_parser { my $grammar = q§ ... §; my $parser = new Parse::RecDescent($grammar); } }
etc. How do I refer to $checksyntax in the grammar? $::checksyntax does not work, and $TelnetClient::Functions::checksyntax is also non-existant when running the parser..
(I'm trying to make it so I can set the variable outside of the parser to indicate whether it should just check the syntax or do other things as well)
Thanks
C.

Replies are listed 'Best First'.
Re: Package variables and Parse::RecDescent
by Fletch (Bishop) on Jan 07, 2003 at 20:06 UTC

    You don't, as you've declared $checksyntax as a lexical in the block enclosing init_parser. It's only visible to the code literally between the outer two {}'s in your example. You probably want to declare it with our $checksyntax, then it would be accessable as $TelnetClient::Functions::checksyntax elsewhere.

    Addendum: An alternative to using package variables would be to implement some variant of the Builder pattern. You have a interface which your grammar calls when it recognizes chunks, and you pass different implementors of that interface (one which builds a syntax tree, the other which does nothing as just a check that the input is valid, etc.). Consult your copy of the GangOfFour book for more details.

      I've no idea what you mean by Builder pattern etc. Do you have an example of that?
      C.
Re: Package variables and Parse::RecDescent
by Hofmator (Curate) on Jan 07, 2003 at 20:15 UTC
    Your problem here is that my $checksyntax declares a lexical variable, while both $::checksyntax and $TelnetClient::Functions::checksyntax refer to (different!) package variables. For a good introduction read up Dominus' Coping with Scoping.

    A different issue is that you can only access package variables out of a grammar of Parse::RecDescent. The grammar itself lives in its own lexical scope (if I understood that correctly). To do so you have to fully qualify them, like $TelnetClient::Functions::checksyntax

    -- Hofmator

      Hmm, so even though my grammar is in the same block as $checksyntax, by the time it is used the scope of $checksyntax is gone? So I'll have to make it global, hohum.
      C.
        The problem is that your grammar might look to you like a piece of code but is in fact just a string. You could move the definition of that string to any prior place in your program, it doesn't make any difference to Parse::RecDescent (P::RD).

        Your grammar (the string) is taken apart into pieces (parsed) in P::RD and then the code to do the actual work is produced - outside your lexical scope. So apparently it can't access any lexical variables in this scope. The documenation of P::RD says to that:

        The action executes within a special namespace belonging to the active parser, so care must be taken in correctly qualifying variable names

        -- Hofmator

Re: Package variables and Parse::RecDescent
by demerphq (Chancellor) on Jan 07, 2003 at 21:15 UTC
    Two niggly comments to add to the existing correct explanations of your problem:

    my $parser = new Parse::RecDescent($grammar);
    Is a bad meme in Perl. I know its in the docs (it shouldnt be, even TheDamian has commented on why its a bad idea) but use the unambiguous and easier to read (IMO)
    my $parser = Parse::RecDescent->new($grammar) or die "Failed to create + parser.\n";
    and you'll one day save yourself some bizarre debugging. Note that checking the return of a constructor for an object you didnt write is usually a good idea.

    The other is that while q§ ... §; sure is funky looking, it is not as suitable as using HereDoc quoting when using PRD and embedded code:

    my $grammar=<<'END_OF_GRAMMAR'; ... END_OF_GRAMMAR my $parser = Parse::RecDescent->new($grammar) or die "Failed to create + parser.\n";
    Personally I tend to roll them into one:
    my $parser = Parse::RecDescent->new(<<'END_OF_GRAMMAR') or die "Failed + to create parser.\n"; ... END_OF_GRAMMAR
    The main reason that <<'END_OF_GRAMMAR' is better is that it is the only normal Perl quoting construct that does not require backslashes or quotes to be escaped. Which in turn can greatly simplify the embedded code that you have in your grammar.

    HTH

    --- demerphq
    my friends call me, usually because I'm late....

      You're right about the 'new Parse::RecDescent' stuff, I've read somewhere it's better the other way around. Though personally I don't like ' .. or die .. ' since usually I want to continue afterwards with some sort of warning. (My program works without the parser, if its not available). So I usually do 'my $parser = ...' and then check the result afterwards.

      The HereDioc stuff sounds like a better idea than searching for funky characters that won't appear in the text, I'll give it a whirl.
      Thanks,
      C.

      Just another small niggle. According to my camel (page 63), quoting as I'm doing using q§ .. §; (or any other character), doesn't interpolate. In my embedded code I'm using quotes, double quotes, variables etc. without problems.
      C.
        Er. Not sure this ones valid. :-)

        My point was mostly about escaping back slahes or a potential § somewhere in your code. For instance

        E:\blead> perl -e "print q§/\\\./§" /\\./
        So the regex that starts off being "match a backslash followed by a period" becomes "match a backslash followed by any non newline character". Not exactly the same thing. Whereas
        #!perl print <<'ENDOFHERE'; /\\\./ ENDOFHERE
        Outputs the expected /\\\./

        For this reason, I tend to do my best to avoid using the q// operator when Im trying to quote code. After all you never know when you'll stick a double backslash into the code, and it might save you hours of debugging trying to figure what the problem was, wheras the HEREDOC notation is only slightly more characters to write, with virtually no chance of this happening.

        --- demerphq
        my friends call me, usually because I'm late....

Re: Package variables and Parse::RecDescent
by tall_man (Parson) on Jan 07, 2003 at 22:55 UTC
    Since the "my" variables are within a local scope, and you are defining subroutines within that scope, you can take advantage of the fact that the subroutines act as closures.

    Why not add another subroutine before init_parser, like this:

    sub getChecksyntax { return $checksyntax; }

    If you call it from the code blocks of your grammar you should have what you need.

      That works wonderfully, thanks!
      C.