"Nuller" <nuller@goej.net> wrote:
[8<8<8]
> Hvad gør en professionel der har forskellige kommandoer der skal
fortolkes?
Det første jeg ville erkende er det er en parsningsopgave og derfor kigge
efter værktøjer/design til parsning.
Desuden ville jeg erkende at hvis man kan sende kommandoer til et program,
kan man principielt modtage vilkårligt sludder. Derfor skal parsningen være
robust, og der bør ikke være ikke runtime uverificerede antagelser om at
kommandoen nok ikke er længere end 16 (vælg selv et passende magic number)
tegn.
Idet jeg antager at du programmerer i C++, er det oplagt at kigge på
* funktionen strtok (også C)
* Boost.Tokenizer
* Boost.Regex
* Boost.Spirit
Boost bibliotekerne finder du på
www.boost.org.
De er under alle omstændigheder værd at kigge på.
Personligt vil jeg foretrække Spirit, fordi det er simpelt at bruge (når man
har lært det) og kraftfuldt når udvalget af kommandoer ændrer sig.
> Og Hvordan?
<C++ kode>
#include <iostream>
#include <string>
#include <boost/spirit/core.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
using namespace std;
using namespace boost;
using namespace boost::spirit;
// The functions which does the "real" work
enum tag_sub_abcd
{ OPQ,
RST
};
void foo()
{
cout << "FOO command executed" << endl;
}
void bar()
{
cout << "BAR command executed" << endl;
}
void abcd(tag_sub_abcd sub_abcd, int value)
{
cout << "ABCD command executed" << endl;
switch(sub_abcd) {
case OPQ: {
cout << " OPQ sub command executed" << endl;
}
break;
case RST: {
cout << " RST sub command executed" << endl;
}
break;
}
cout << " value: " << value << endl;
}
// Helper functions and function objects for the parser
template <typename T>
class do_assign_actor
{
public:
do_assign_actor(T& t_arg, const T& value_arg) :
t(t_arg), value(value_arg) {}
void operator()(const char* /*begin*/, const char* /*end*/) const
{ t = value; }
private:
mutable T& t;
const T value;
};
template <typename T>
inline do_assign_actor<T> assign(T& t_arg, const T& value_arg)
{
return do_assign_actor<T>(t_arg, value_arg);
}
class skip_arg
{
public:
typedef void (*func_type)();
skip_arg(func_type func_arg) :
func(func_arg) {}
void operator()(const char* /*begin*/, const char* /*end*/) const
{ func(); }
private:
func_type func;
};
// The command parser
struct cmd_parser : public grammar<cmd_parser>
{
cmd_parser(bool& cont_arg) :
cont(cont_arg) {}
template <typename ScannerT>
struct definition
{
definition(cmd_parser const& self)
{
cmd_abcd_p
= strlit<>("ABCD")
>> +space_p // followed by one or more spaces
>> ( strlit<>("OPQ") [assign(cmd_adcb_param_1, OPQ)]
| strlit<>("RST") [assign(cmd_adcb_param_1, RST)]
)
>> +space_p
>> int_p [assign(cmd_adcb_param_2)]
>> end_p // allow nothing after the integer
;
cmd_foo_p
= strlit<>("FOO")
>> end_p // allow nothing after the command
;
cmd_bar_p
= strlit<>("BAR")
>> end_p // allow nothing after the command
;
cmd_quit_p
= ( chlit<>('q')
| chlit<>('Q')
)
>> *anychar_p
;
cmd_p
= cmd_abcd_p [bind(abcd, ref(cmd_adcb_param_1),
ref(cmd_adcb_param_2))]
| cmd_foo_p [skip_arg(foo)]
| cmd_bar_p [skip_arg(bar)]
| cmd_quit_p [assign(self.cont, false)]
;
}
rule<ScannerT> cmd_p, cmd_abcd_p, cmd_foo_p, cmd_bar_p, cmd_quit_p;
tag_sub_abcd cmd_adcb_param_1;
int cmd_adcb_param_2;
rule<ScannerT> const& start() const
{ return cmd_p; }
};
bool& cont;
};
int main()
{
string str;
bool cont = true;
cmd_parser cmd_p(cont);
while (cont && getline(cin, str)) {
parse_info<> info = parse(str.c_str(), cmd_p, nothing_p);
if (!info.full) {
cout << str << endl;
fill_n(ostream_iterator<char>(cout), info.stop-str.c_str(), ' ');
cout << "^\n"
<< "Parsing failed\n" << endl;
}
}
}
<C++ kode/>
Pas på linier, der er wrappet rund af news-programmet!
Det burde virke på de mange platforme med mange compilere.
Bemærk at programmet er komplet med hensyn til:
* vilkårlig størrelse kommando
* fejlhåndtering
* ved out-of-memory situation
* ved fejl i kommandoen
Venlig hilsen
Mogens Hansen