/* ************************************************************************ * An illustration how such a theoretical (abstract) thing as * FSM can be actually used for mundane programming tasks * * The problem: * write a filter converting an input stream into an output * stream * * Input stream: * made up of words separated by whitespaces: not-a-white-space+ * whitespace: {' ','\n','\t','\r','\f'} * comments: \/* ... *\/, cannot be nested * (the backspace here and below is used as an esc-character) * * Output stream * throw away all the comments * one word per line (no leading/trailing whitespaces) * * For example, the following input stream * * aaa bbb c, * d \n * * : e*\/**\// /* aaa * /* ****a/ //// * * / *\/ *\/ * t1 /***\/ t2 * k :+*+/+ * * should be converted as * aaa * bbb * c, * d * : * e*\/ * *\/ * t1 * t2 * k * :+*+/+ * * * The following elementary auutomaton implements the whole thing * * State Expecting char Action New State * 0 white-space putc('\n') 1 * 0 '/' - 2 * 0 any putc(c) 0 * * 1 white-space - 1 * 1 any putpack(c) 0 * * 2 '*' - 3 * 2 any putc('/'),putback(c) 0 * * 3 '*' - 4 * 3 any - 3 * * 4 '/' - 1 * 4 any putback(c) 3 * * Note, a transition to a 'New State' entails reading a new character * from the input stream and putting it into a variable 'c' * * $Id: sample_fsm.c,v 1.2 2002/10/25 23:12:19 oleg Exp oleg $ * ************************************************************************ */ #include #include /* The automaton: takes the current char 'c' */ /* and processes it depending on its current */ /* state of mind */ /* Returns the result of processing a char */ static enum FSM_RESULT { FSM_ate, FSM_regurgitated } automaton(const int c) { static enum {Q_regular, Q_whitespace, Q_beg_comment, Q_comment, Q_end_comment} state = Q_whitespace; /* Starting state */ switch(state) { case Q_regular: if( isspace(c) ) putchar('\n'), state = Q_whitespace; else if( c == '/' ) state = Q_beg_comment; else putchar(c); return FSM_ate; case Q_whitespace: return isspace(c) ? FSM_ate : (state = Q_regular, FSM_regurgitated); case Q_beg_comment: return c == '*' ? (state = Q_comment, FSM_ate) : (putchar('/'), state = Q_regular, FSM_regurgitated); case Q_comment: if( c == '*' ) state = Q_end_comment; return FSM_ate; case Q_end_comment: if( c=='/' ) return state = Q_whitespace, FSM_ate; return state = Q_comment, FSM_regurgitated; } } static volatile void help(void) { fprintf(stderr,"A filter that lists all the words from the input\n"); fprintf(stderr,"stream one per line, ignoring C-style comments\n\n"); exit(4); } int main(const int argc, const char * argv[]) { register int c; if( argc > 1 ) help(); /* No arguments are expected */ while( (c=getchar()) != EOF ) if( automaton(c) == FSM_regurgitated ) ungetc(c,stdin); automaton(' '); /* Treat the EOF as a whitespace */ return 0; }