| iMatix home page | Libero home page | Libero documentation | << | < | > | >> |
Libero Version 2.32 |
One of the classic uses for state machines is in scanning and parsing text - e.g. source programs. People usually do this with tools like lex and yacc, or using languages like Awk and Perl that are good at pattern matching. When I explain that Libero uses a state-machine model, some people think that I am asking them to use Libero in place of the honoured tools I just mentioned. The next example shows why this is not the case.
Here I present a C/C++ comment stripper based on an example by Robert Martin (who makes a finite-state machine code generator for C++). In C and C++, comments are either enclosed between '/*' and '*/', or begin with '//' until the end of the line. This program removes all comments from a C or C++ source program, with some checking for errors like an unmatched '/*':
! ! C/C++ comment stripper dialog ! ! Written: 95/03/30 Pieter Hintjens <ph@imatix.com> ! Revised: 96/05/15 Frans Janssens <frans@sebb.bel.alcatel.be> ! -source=stripper.c -schema=lrschema.c After-Init: (--) Ok -> Outside-Comment + Get-Char-From-Input (--) Error -> + Terminate-The-Program Outside-Comment: (--) Quote -> Skip-Quote + Put-Char-To-Output + Get-Char-From-Input (--) Slash -> After-Slash + Get-Char-From-Input (--) Star -> Outside-Comment + Put-Char-To-Output + Get-Char-From-Input (--) NewLine -> Outside-Comment + Put-Char-To-Output + Get-Char-From-Input (--) Other -> Outside-Comment + Put-Char-To-Output + Get-Char-From-Input Skip-Quote: (--) Quote -> Outside-Comment + Put-Char-To-Output + Get-Char-From-Input (--) Slash -> Skip-Quote + Put-Char-To-Output + Get-Char-From-Input (--) Star -> Skip-Quote + Put-Char-To-Output + Get-Char-From-Input (--) NewLine -> Skip-Quote + Put-Char-To-Output + Get-Char-From-Input (--) Other -> Skip-Quote + Put-Char-To-Output + Get-Char-From-Input After-Slash: (--) Quote -> Outside-Comment + Put-Slash-To-Output + Put-Char-To-Output + Get-Char-From-Input (--) Slash -> Skip-Eoln + Get-Char-From-Input (--) Star -> Skip-Comment + Start-New-Comment + Get-Char-From-Input (--) NewLine -> Outside-Comment + Put-Slash-To-Output + Put-Char-To-Output + Get-Char-From-Input (--) Other -> Outside-Comment + Put-Slash-To-Output + Put-Char-To-Output + Get-Char-From-Input Skip-Eoln: (--) Quote -> Skip-Eoln + Get-Char-From-Input (--) Slash -> Skip-Eoln + Get-Char-From-Input (--) Star -> Skip-Eoln + Get-Char-From-Input (--) NewLine -> Outside-Comment + Put-Char-To-Output + Get-Char-From-Input (--) Other -> Skip-Eoln + Get-Char-From-Input Skip-Comment: (--) Quote -> Skip-Comment + Get-Char-From-Input (--) Slash -> After-Comment-Slash + Get-Char-From-Input (--) Star -> After-Comment-Star + Get-Char-From-Input (--) NewLine -> Skip-Comment + Get-Char-From-Input (--) Other -> Skip-Comment + Get-Char-From-Input (--) End-Of-File -> + Signal-Unterminated-Comment + Terminate-The-Program After-Comment-Star: (--) Quote -> Skip-Comment + Get-Char-From-Input (--) Slash -> Outside-Comment + Other-If-Nested-Comment + Get-Char-From-Input (--) Star -> After-Comment-Star + Get-Char-From-Input (--) NewLine -> Skip-Comment + Get-Char-From-Input (--) Other -> Skip-Comment + Get-Char-From-Input (--) End-Of-File -> + Signal-Unterminated-Comment + Terminate-The-Program After-Comment-Slash: (--) Quote -> Skip-Comment + Get-Char-From-Input (--) Slash -> Skip-Comment + Get-Char-From-Input (--) Star -> Skip-Comment + Start-New-Comment + Get-Char-From-Input (--) NewLine -> Skip-Comment + Get-Char-From-Input (--) Other -> Skip-Comment + Get-Char-From-Input (--) End-Of-File -> + Signal-Unterminated-Comment + Terminate-The-Program Defaults: (--) Exception -> + Terminate-The-Program (--) End-Of-File -> + Terminate-The-Program
This is what Libero generates when I compile the dialog:
/*===========================================================================* * * * stripper.c description... * * * * Written: 96/12/31 Pieter Hintjens* * Revised: 96/12/31 * * * * Skeleton generated by LIBERO 2.20 on 31 Dec, 1996, 14:00. * *===========================================================================*/ #include "prelude.h" /* Universal include file */ #include "stripper.d" /* Include dialog data */ /*- Function prototypes -----------------------------------------------------*/ /*- Global variables used in this source file only --------------------------*/ static int feedback; /* Feedback for calling program */ static char *some_param; /* Example of parameter */ /******************************** M A I N ********************************/ int my_funct (char *p_some_param) { feedback = 0; /* No errors so far */ some_param = p_some_param; /* Local copy of parameters */ # include "stripper.i" /* Include dialog interpreter */ } /************************* INITIALISE THE PROGRAM ************************/ MODULE initialise_the_program (void) { the_next_event = ok_event; } /*************************** GET EXTERNAL EVENT **************************/ MODULE get_external_event (void) { } /************************* TERMINATE THE PROGRAM ************************/ MODULE terminate_the_program (void) { the_next_event = terminate_event; } /************************** GET CHAR FROM INPUT **************************/ MODULE get_char_from_input (void) { } /*************************** PUT CHAR TO OUTPUT **************************/ MODULE put_char_to_output (void) { } /************************** PUT SLASH TO OUTPUT **************************/ MODULE put_slash_to_output (void) { } /*************************** START NEW COMMENT ***************************/ MODULE start_new_comment (void) { } /********************** SIGNAL UNTERMINATED COMMENT **********************/ MODULE signal_unterminated_comment (void) { } /************************ OTHER IF NESTED COMMENT ************************/ MODULE other_if_nested_comment (void) { }
This is the completed comment-stripper program:
/*=========================================================================* * * * stripper.c C/C++ comment stripper * * * * Written: 96/12/31 Pieter Hintjens* * Revised: 96/12/31 * * * *=========================================================================*/ #include "prelude.h" /* Standard include file */ #include "stripper.d" /* Include dialog data */ /*- Global variables used in this source file only ------------------------*/ static int thisch, /* Current character */ comment_level, /* Comment nesting level */ feedback; /* Feedback for calling program */ int main (void) { feedback = 0; /* No errors so far */ # include "stripper.i" /* Include dialog interpreter */ } /************************ INITIALISE THE PROGRAM ***********************/ MODULE initialise_the_program (void) { the_next_event = ok_event; } /************************* GET CHAR FROM INPUT *************************/ MODULE get_char_from_input (void) { thisch = getchar (); switch (thisch) { case '"': the_next_event = quote_event; break; case '/': the_next_event = slash_event; break; case '*': the_next_event = star_event; break; case '\n': the_next_event = newline_event; break; case EOF: the_next_event = end_of_file_event; break; default: the_next_event = other_event; } } /************************** PUT CHAR TO OUTPUT *************************/ MODULE put_char_to_output (void) { putchar (thisch); } /************************* PUT SLASH TO OUTPUT *************************/ MODULE put_slash_to_output (void) { putchar ('/'); } /************************** START NEW COMMENT **************************/ MODULE start_new_comment (void) { comment_level++; /* We have a new comment */ } /*********************** OTHER IF NESTED COMMENT ***********************/ MODULE other_if_nested_comment (void) { comment_level--; /* We've finished a comment */ if (comment_level > 0) /* but maybe it was nested... */ raise_exception (other_event); } /********************* SIGNAL UNTERMINATED COMMENT *********************/ MODULE signal_unterminated_comment (void) { fprintf (stderr, "stripper: unterminated comment\n"); feedback = 1; /* Return code for main program */ } /*************************** GET EXTERNAL EVENT ************************/ MODULE get_external_event (void) { } /************************ TERMINATE THE PROGRAM ************************/ MODULE terminate_the_program (void) { the_next_event = terminate_event; }
The program works and is easily understood. However, because the dialog is really too low-level, it makes the problem seem more complex than it actually is. In fact, this is a good example of the kind of problem that I would never use Libero for. I would write this as a straight C program.
Moral: Libero is not worth the overhead on trivial problems. By contrast, when you are wrestling with a really complex problem, Libero lets you break it down into smaller, simpler pieces, and keep the central logic in a single place.
Second moral: simplicity can be deceptive. It often happens that the first stab at a problem produces a simple solution, as here. However, when the solution meets reality, reality often gets its own way and imposes all kind of nasty complexities that mess-up the clean solution. A dialog is very good at handling such things. So, it can be better to start-out on the right footing and use a dialog from the start.
| << | <
| > | >>
| Introduction to Libero | The Coke Machine Example | Example of Using a Telephone | Example of Controlling a Telephone | Source Code For Phone.c | Example of a C/C++ Comment Stripper | Example of Parsing An Arithmetic Expression | Dialogs For Dummies | Frequently Asked Questions |
Copyright © 1996-97 iMatix |