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

Hi,

I am a still a perl beginner and have NO knowledge in parsing structured languages (are they lex, yacc???)....

But, I am tired of having big huge if statements, which are hard to extend and test for valid characters, etc.

I currently support some remote network comands like:
"shutdown"
"show connections vpn1"
"lock_user john"
"unlock_user john"


What is the easiest perl module to look at that would support this? I don't expect my "language" to be much more complicated that the examples above...but would like to learn something new ;-)

I am shooting for simple!

thanks!
  • Comment on simpliest way to add "language" to my app

Replies are listed 'Best First'.
Re: simpliest way to add "language" to my app
by jonadab (Parson) on Oct 05, 2003 at 03:36 UTC

    Something that's relatively simple is to have a hash, with the keys being the command, and the values being anonymous subroutines.

    my %command= ( shutdown => sub { print "Shutting down...\n"; exit 0; }, lock_user => sub { my ($user) = @_; # Insert code to lock $user here. }, unlock_user => sub { my ($user) = @_; # Insert code here to unlock $user }, show => sub { # Insert show code here }, lock => sub { $_ = shift; if ($command{lock_$_}) { $command{lock_$_}->(@_); } else { die "Don't know how to lock $_\n"; } }, unlock => sub { $_ = shift; if ($command{unlock_$_}) { $command{unlock_$_}->(@_); } else { die "Don't know how to unlock $_\n"; } }, help => sub {# update per L~R's suggestion: print "Current commands: "; print join "\n", sort { $a cmp $b } keys %command; }, ); my ($cmd, @args) = @ARGV; # Or however you get them. $cmd = lc $cmd; # Force lowercase per L~R's suggestion. my $sub = $command{$cmd}; $sub = $command{help} if not defined $sub; $sub->(@args);

    $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
      jonadab,
      Depending on how forgiving and user friendly smackdab wants to be, your solution can be improved:
    • Pass raw data to parsing sub
    • If match found, execute accordingly
    • If match not found, give user a list of currently supported commands

      The parsing sub may do things like:

    • Force lower case
    • Change (un)?lock user (un)?lock_user
    • Allow synonyms such as halt for shutdown
    • Regular expressions to handle common typos
    • Append non matches to a log for further analysis to help improve parsing routine

      The only other thing that I would suggest is changing the key assignment from anonymous subs to named subs.

      shutdown => sub { print "Shutting down...\n"; exit 0; } becomes shutdown => \&shutdown
      Cheers - L~R
        Pass raw data to parsing sub

        Well, he said the simplest way...

        If match not found, give user a list of currently supported commands

        Now, that would certainly be a worthwhile enhancement. And easy enough to do, too.

        Force lower case

        I almost included that... but for some reason I didn't. Perhaps I should have.

        Allow synonyms such as halt for shutdown

        That may be the best argument for the named subs you mention, actually.


        $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
Re: simpliest way to add "language" to my app
by idsfa (Vicar) on Oct 05, 2003 at 03:38 UTC

    You may be looking for a hash of closures ...

    %lang = ( "shutdown" => sub { ... }, "show" => sub { ... }, "lock_user" => sub { ... } # etc etc ); . . . &{$lang{$input}}(@args);

    This is nice and simple and easy to extend to more commands or more complicated functions.


    Remember, when you stare long into the abyss, you could have been home eating ice cream.
Re: simpliest way to add "language" to my app
by rkg (Hermit) on Oct 05, 2003 at 13:47 UTC
Re: simpliest way to add "language" to my app
by princepawn (Parson) on Oct 05, 2003 at 03:41 UTC
    A simple but powerful language is the Unix shell itself. See The Unix Shell as a 4GL for details.

    Each of the "commands" you list above can be a shell script. To iterate over hosts:

    for HOST in waterloo.ca king.com lola.edu do show_status $HOST done

    Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality.