class ActionBody { friend class ActionGen; ActionBody * next; // The next action in the chain public: ActionBody(void) : next(0) {} virtual void execute(const int curr_char) = 0;// Execute this action void execute_all(const int curr_char); // Execute this action and // all that follows };
class NoAction: public ActionBody { public: void execute(const int curr_char) { cerr << "NoAction" << endl; } };
curr_char
into a formatting buffer
class FormatCurrChar : public ActionBody { public: void execute(const int curr_char) { My_stdout.output_char(curr_char); } };
class CallOutputSpace : public ActionBody { public: void execute(const int curr_char) { My_stdout.output_space(); } };
class ActionGen { ActionBody * body; ActionBody * last_in_chain; protected: ActionGen(ActionBody * born_body) : body(born_body), last_in_chain(born_body) {} public: operator ActionBody * (void) const { return body; } // Grow a chain further by a next_body // (and whatever hangs from it) ActionGen& operator + (const ActionGen& next_body) { last_in_chain->next = next_body; last_in_chain = next_body.last_in_chain; assert( last_in_chain->next == 0 ); return *this; } };
template <class T> class MA : public ActionGen { public: MA(void) : ActionGen(new T) {} };
struct Arc; typedef Arc Node; // Node is just a bunch of arcs struct Arc // Arc of a FSM { int activator; // character-activator or ANY ActionBody * const actions; // byte-compiled actions Node * next_node; // Node to follow };
#define ANY 0xf00 // Matches any symbol extern Arc N_space[]; // Read characters and fill the current paragraph Arc N_filling [] = { {' ',MA<CallOutputSpace>(),N_space}, {'\t',MA<CallOutputSpace>(),N_space}, {'\n',MA<CallOutputSpace>(),N_space}, {ANY,MA<FormatCurrChar>() + MA<NoAction>() + MA<NoAction>(),N_filling} }; // Got white spaces: ignore more than one Arc N_space [] = { {' ', MA<NoAction>(),N_space}, // ignore the second wspace etc {'\t', MA<NoAction>(),N_space}, {'\n', MA<NoAction>(),N_space}, {ANY, MA<FormatCurrChar>(),N_filling} }; static const Node * const Start_node = N_filling;
Note how several actions can be put together with a +
sign
void main(void) { const char * sample_string = "abc a\t \nb"; const Node * Current_node = Start_node; /* Drive the automaton */ for(register const char * cp = sample_string; *cp != '\0'; cp++) { register const Arc * curr_arc; for( curr_arc=Current_node; ; curr_arc++) if( curr_arc->activator == ANY || curr_arc->activator == *cp ) { curr_arc->actions->execute_all(*cp); Current_node = curr_arc->next_node; break; } } cerr << "Done" << endl; }