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


Hi

For a script I am writing I have a need to have a command interpreter at a server so that it knows what I want to do. There are a whole swag of different things I'm wanting to tell it and all the commands have different formats and argument requirements etc.

i.e.
Log this ontime that
create this with thing1 thing2 thing3
etc. etc.

I am getting a whole range commands now and the way I've delt with it is firstly using a switch that checks the first key word i.e. /^log/ and then farm them off to now overwieldly subroutines the are large combinations of if's and reg ex's to work out which particular command format was used.

I'm thinking there has to be a prettier way, I mean it all works...its just not....'nice'. Is there a module or similar that is designed for this purpose so that after defining acceptable log formats it auto throws the bads ones and can dump acceptable results to the correct subroutines?

How have others solved this problem? Would say having a hash of subroutines as keys and reg ex's as the values and searching them on input be better? I'm guessing that would make insertion of new commands easier than trawling through a huge subroutine for the right place to insert.

Any incite would be appreciated =)..

Regards Paul.

Replies are listed 'Best First'.
Re: command input processing
by ikegami (Patriarch) on Oct 21, 2004 at 23:23 UTC

    How about:

    sub log { ... } sub create { ... } my %commands = ( log => \&log, create => \&create, ... ); local $_ = $command; if (/^(\w+)/ && $commands{$1}) { &{$commands{$1}}(); } else { # Error } # Alternative: # # local $_ = $command; # if (/^(\w+)\s+(.*)/ && $commands{$1}) { # &{$commands{$1}}($1, $2); # } else { # # Error # }

    If the command will always equal the function name, you can get rid of %commands. Check if the function exists with UNIVERSAL::can() and call it. Refer to an AUTOLOAD for an example on how to call a function when you have its name as a string.

      The would be a slight improvement on what I have currently =), but most of my variability is having 10 different formats for say the log command so I still end up with a horrid list, maybe I can't escape that.

      I'm expanding the interpreter to be a lot more featured so that it can process conditional things like...


      if ( event >= other thing ) { do this; and maybe more things }

      This makes the processing a bit more complicated so I guess I probably more curious as to how languages say perl, intepret language structure/grammar as I'm guessing they don't have some horrid long if loop

      I suppose if I'm going to have say <25 permutations of commands only hardwiring the formats works, but making it reevaluate chunks in brackets as separate pieces starts to make life interesting and it would be nice to make it say the following...


      if ( event1 > thing ) { data = var; goto func1 }
      and if ( event1 > thing ) { goto func1; data = var; }

      by just teaching it the formats...

      if () {} data = var goto func

      rather than limiting the system to the permutations I'd hard coded. I guess this running a bit deeper that the original question i posted, appologies for not being more specific the first time.


      Regards Paul
        I probably more curious as to how languages say perl, intepret language structure/grammar

        Ignore perlfunc functions for a moment. What you describe and perl don't compare. In your scenario, the script calls functions defined in the language itself. In perl, the script calls functions the script defines. Perl doesn't care what the function is called, or how many arguments it has, or anything, because all functions look the same to perl.

        Except, of course, builtin functions, which perlfunc functions may be. I don't know how that's done. C, C++ and Java don't have any builtin functions as far as I know. There's "new", but it's an operator. And yeah, there's surely a big code or data structure to parse the operators.

        What compilers do is make a tree. In compilers that create binaries or bytecode, the tree is serialized into instructions. perl keeps the program in tree form. For perl, executing the program is simply navigating the tree and taking actions based on the type of node it finds. Here's an example:

        >perl -MO=Terse -e "$a = $a + 1" LISTOP (0x18a01ec) leave [1] OP (0x18a01d0) enter COP (0x18a0210) nextstate BINOP (0x18a024c) sassign BINOP (0x18a0270) add [3] UNOP (0x18a02b4) null [15] PADOP (0x18a02d4) gvsv 2 SVOP (0x18a0294) const SPECIAL #0 Nullsv UNOP (0x18a02f4) null [15] PADOP (0x18a0314) gvsv 1 -e syntax OK

        It takes lots of code and/or lots of tables to build this tree. 387772 is a parser I wrote for a relatively simple grammar. Look how long it is, and the language it parses only has conditionals, loops, line continations, literals and variables, nothing else. There are tools to assist you, such as lex+yacc and Parser::RecDescent, but it's still a lot of work. (Coding is only a small portion of it, too. The amount of thought that should be put in the design of a language is enourmous, but that's off topic.)

        And then you keep adding things and adding things and you've created your own language (c.f. PHP :). If you want a more full featured language you've got two options:

        • write your own interpreter (using something like Parse::RecDescent or byacc)
        • let your users just write perl and have a wrapper hide details like use MyLanguageModule qw( blah blah )

        If you've got users that can handle it I'd go for the later as it's less work for you and they don't have to learn yet another language.