| iMatix home page
| Libero home page | Libero documentation
| << | < | > | >>
Libero Libero
Version 2.32

Example of a C/C++ Comment Stripper

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
iMatix
Copyright © 1996-97 iMatix