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

I am trying to write a command parser which is like cisco CLI.(still in basic level). Here is the snippet of the code.
use warnings; use Carp; use Parse::RecDescent; $|=1; my $GlobalConfigMode=q( Interface: ('fa' |'gigabit' |'atm') module '/' slot {print "SUCCESS"} + module: /\d+/<commit>|<error?:invalid module > slot:/\d+/<commit> |<error?: error near slot: > ); my $parser= new Parse::RecDescent($GlobalConfigMode) or croak "Could n +ot create command parser" ; while(<DATA>){ chomp; if($parser->Interface($_)){ print "$_ \t success\n" ; } else { print "$_ Command not found\n"; } print"------------------\n"; } __DATA__ fa 0/1 atm 0/1 gigabit 0/1 fastethernet co gigabit 01111 fa 0/1 enable gigabit 0/1 disable fastethenet asdf
Problem here is that I get different result if I take this value from STDIN .(ie changing the <DATA> in while loop to <STDIN>) Though basic parsing works, if I give additional characters it does not complain.(ie if I give input as <fa 0/1 junkcharcctrasdfadsf> from commandline)

I am wondering is there any difference in these two mode of inputs ?
Expected result is
1. fa 0/1 --> success
2. fa 0/1 enable -> fail
3. gigabit 0/1 --> success.
4. gigabit 0/1 enable/remove --> fail

Replies are listed 'Best First'.
Re: Difference between Inputs taken from __DATA__ and STDIN.
by jdporter (Paladin) on Aug 20, 2007 at 12:29 UTC

    Ok, you say what your expected result is, but what do you actually get? Does either mode of reading input give the correct result? That "expected result" doesn't look at all like what the script actually produces. First of all, there are more input cases than just "fa" and "gigabit". And I assume that "Command not found" means "fail".

    When I tried it, I got the same result both ways, and it showed "success" for all four of the cases you mention in your "expected result". However, I do see some "Command not found" results for other cases, such as the "fastethernet" ones.

    I'm guessing you might have line ending issues. Some spurious "\r" characters in the input?

    A word spoken in Mind will reach its own level, in the objective world, by its own weight
      Hi,
      Yeah command not found means fail. But when I enter say,
      fa 0/1 enable
      from windows command prompt I get the message
      Success..
      E:\Documents and Settings\raghrao>commandparser01.pl fa 0/1 enable SUCCESSfa 0/1 enable success ------------------ gigabit 0/1 asdf SUCCESSgigabit 0/1 asdf success ------------------

      where as it should have been fail,as per the grammar defined. I am not sure if this is windows conmmand prompt issue ?.
      I want the user to enter only (fa|gigabit) 0/1
      If user enters extra word it should have been a fail case(command not found)..
      Thanks and Regards
      Raghu
Re: Difference between Inputs taken from __DATA__ and STDIN.
by ikegami (Patriarch) on Aug 21, 2007 at 14:23 UTC

    DATA is different than STDIN in only one situation as far as I can tell (DATA's starting point isn't at offset zero), but it isn't relevant here.

    Your code gives the same undesired behaviour (gigabit 0/1 disable gives success) no matter the source of the input. Junk at the end of the input is never considered an error since you never check that there is no junk at the end of the input.

    Other problems:

    • Your code allows atm0/1. (Note the lack of space).
    • strict and warnings isn't enabled in your grammar.
    • Using indirect method calls (such as new Class) is error-prone and best avoided.
    • Single-quoted here-docs (<<'WORD') are better than multiline q() constructs since you don't have to escape anything.

    Fixes:

    use strict; use warnings; use Carp qw( Carp ); use Parse::RecDescent qw( ); $|=1; my $GlobalConfigMode = <<'__EOI__'; { use strict; use warnings; my %iftypes = map +($_=>1), qw( fa gigabit atm ); } parse : Interface /\Z/ { $item[1] } Interface : iftype module '/' slot { [ @item[0,1,2,4] ] } iftype : /\w+/ <commit> { $iftypes{$item[1]}||undef } { $item[1] + } | <error?:invalid iftype> module : /\d+/ { $item[1] } | <error:invalid module> slot : /\d+/ { $item[1] } | <error:error near slot> __EOI__ my $ifparser = Parse::RecDescent->new($GlobalConfigMode) or croak "Could not create command parser"; while (<STDIN>) { chomp; if ($ifparser->parse($_)) { print "$_ \t success\n" ; } else { print "$_ Command not found\n"; } print "------------------\n"; }
      Thanks ikegami, Its lot of help for me.. Regards,