[comp.lang.c++] inline function extractor source

cdk@neptune.dsc.com (Colin Kelley) (06/19/91)

Here's the source code to a tool for extracting inline function definitions.
I'm placing it under the Gnu GPL.

Can someone please make it available for FTP?  I don't have direct internet
access, but I'd like to make it available for those who do.

                        -Colin Kelley, Digital Sound Corporation
                        cdk%neptune%dschub@hub.ucsb.edu
                        ...!pyramid!ucsbcsl!dschub!neptune!cdk
                        (805) 566-3000 x3175
---------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Makefile
#	extract-inline.cc
#	event.h
#	comment.h
#	ident.h
#	keyword.h
#	inline.h
#	event.cc
#	comment.cc
#	ident.cc
#	keyword.cc
#	inline.cc
#	move-if-change
# This archive created: Tue Jun 18 10:05:41 1991
export PATH; PATH=/bin:$PATH
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
This code is covered under the Gnu General Public License, version 1.

This is a set of C++ classes for scanning C++.  The application provided
here is a C++ inline extractor.  It takes a single code file, with inline and
non-inline functions intermingled, and splits it into two files, one with
the inline functions, the other with the remainder.

The recommended usage for a hypothetical module called `module', is:

    - keep the module class definitions in `module.h'
    - add one line to the end of `module.h': #include "module-inline.h"
    - place all the code, both inline and not, in `module.code'
    - put this in your Makefile:

module-inline.h module.cc: module.code
	extract-inline module.code module-inline.tmp module.cc
	move-if-change module-inline.tmp module-inline.h

These classes are useful for other C++ tools as well, such as formatting
documentation.

                        -Colin Kelley, June 1991
			Digital Sound Corporation
                        cdk%neptune%dschub@hub.ucsb.edu
                        ...!pyramid!ucsbcsl!dschub!neptune!cdk
                        (805) 566-3000 x3175
SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
CC = g++
CFLAGS = -Wall -I/usr/local/lib/g++-include -g
OBJS = event.o comment.o ident.o keyword.o inline.o
SRC = README Makefile extract-inline.cc \
      event.h comment.h ident.h keyword.h inline.h \
      event.cc comment.cc ident.cc keyword.cc inline.cc \
      move-if-change
RESULTS = extract-inline
RESULTOBJS = extract-inline.o

all:		$(RESULTS)

clean:
		rm -f $(OBJS) $(RESULTOBJS) $(RESULTS)

cleanly:	clean all


########################################################################

extract-inline:	extract-inline.o $(OBJS)
		$(CC) $(CFLAGS) -o extract-inline extract-inline.o $(OBJS)

extract-inline.o:	extract-inline.cc
		$(CC) $(CFLAGS) -c extract-inline.cc

extract-inline.cc: event.h comment.h ident.h keyword.h inline.h
		touch extract-inline.cc

########################################################################

event.o:	event.cc
		$(CC) $(CFLAGS) -c event.cc

event.cc:	event.h
		touch event.cc

event.h:	
		touch event.h

#----------------------------------------------------------------------

comment.o:	comment.cc
		$(CC) $(CFLAGS) -c comment.cc

comment.cc:	comment.h
		touch comment.cc

comment.h:	event.h
		touch comment.h

#----------------------------------------------------------------------

ident.o:	ident.cc
		$(CC) $(CFLAGS) -c ident.cc

ident.cc:	ident.h
		touch ident.cc

ident.h:	event.h
		touch ident.h

#----------------------------------------------------------------------

keyword.o:	keyword.cc
		$(CC) $(CFLAGS) -c keyword.cc

keyword.cc:	keyword.h
		touch keyword.cc

keyword.h:	event.h ident.h
		touch keyword.h

#----------------------------------------------------------------------

inline.o:	inline.cc
		$(CC) $(CFLAGS) -c inline.cc

inline.cc:	inline.h
		touch inline.cc

inline.h:	event.h keyword.h
		touch inline.h

########################################################################

shar:		extract-inline.shar

extract-inline.shar:	$(SRC)
		shar $(SRC) > extract-inline.tmp
		move-if-change extract-inline.tmp extract-inline.shar
SHAR_EOF
fi # end of overwriting check
if test -f 'extract-inline.cc'
then
	echo shar: will not over-write existing file "'extract-inline.cc'"
else
cat << \SHAR_EOF > 'extract-inline.cc'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

/*
 * This is the main program for the inline extractor.  It pipelines state
 * machines to simulate C++ scanning:
 *
 *   comment | ident | keyword | inline
 */

#include "event.h"
#include "comment.h"
#include "ident.h"
#include "keyword.h"
#include "inline.h"

extern "C" void perror( const char* );


static void usage()
{
  fprintf( stderr,
"usage:  extract-inline source-file inline-file remain-file\n"
"        reads from <source-file>, writing all inline functions to\n"
"        <inline-file>, with the remainder passed through to <remain-file>.\n");
}


int __main() { return 0; }

int main( int argc, char* argv[] )
{
  if (argc != 4)
  {
    usage();
    exit( 1 );
  }
  const char* srcFileName = argv[1];
  const char* inlineFileName = argv[2];
  const char* remainFileName = argv[3];

  FILE* srcFile = fopen( srcFileName, "r" );
  if (!srcFile)
  {
    perror( srcFileName );
    exit( 1 );
  }
  FILE* inlineFile = fopen( inlineFileName, "w" );
  if (!inlineFile)
  {
    perror( inlineFileName );
    exit( 1 );
  }
  FILE* remainFile = fopen( remainFileName, "w" );
  if (!remainFile)
  {
    perror( remainFileName );
    exit( 1 );
  }

  fprintf( inlineFile,
"/*\n"
" * WARNING\n"
" * Don't change this file!  It was machine-generated from `%s'.\n"
" */\n", srcFileName );
  fprintf( inlineFile, "#line 1 \"%s\"\n", srcFileName );

  fprintf( remainFile,
"/*\n"
" * WARNING\n"
" * Don't change this file!  It was machine-generated from `%s'.\n"
" */\n", srcFileName );
  fprintf( remainFile, "#line 1 \"%s\"\n", srcFileName );

  OutputEvent outputRemainEvent( remainFile );
  OutputEvent outputInlineEvent( inlineFile );
  ExtractInlineFSM extractInlineFSM( &outputRemainEvent, &outputInlineEvent );
  KeywordFSM keywordFSM( &extractInlineFSM );
  IdentNumberFSM identNumberFSM( &keywordFSM );
  CommentQuotedFSM commentQuotedFSM( &identNumberFSM );

  Event *event;
  while ((event = NextEvent( srcFile )) != NULL)
  {
    commentQuotedFSM.Inject( event );
  }
}
SHAR_EOF
fi # end of overwriting check
if test -f 'event.h'
then
	echo shar: will not over-write existing file "'event.h'"
else
cat << \SHAR_EOF > 'event.h'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#pragma once

/*
 * This module has the basic declarations which form the foundation for
 * pipelined state machines which scan C and C++ code.  It defines the
 * abstract Event, EventSink, and FSM (Finite State Machine) classes which
 * are used throughout.  It also defines classes for all the common character
 * types.  Note that in order to be able to ask an object its type, the
 * Event class must define an enum with all event types in it.  This obviously
 * impedes the reusability of the class, since this essentially amounts to
 * the class knowing all types which will subtype it.  Welcome to C++.
 */

extern "C"
{
#include <stdio.h>
}

typedef int Boolean;
#define TRUE 1
#define FALSE 0


extern "C" char* malloc( unsigned int size );
extern "C" char* realloc( char* buffer, unsigned int newSize );
extern "C" void free( char* buffer );


class CharAccumulator
{
  public:
    inline CharAccumulator()
    {
      cursor = 0;
      size = AllocSize;
      buffer = malloc( size );
    }
    inline ~CharAccumulator()
    {
      free( buffer );
    }
    inline void AddChar( char toAdd );
    inline const char* AccumulatorBuffer() const
    {
      // make sure it's NUL-terminated
        buffer[cursor] = '\0';
      // return buffer pointer
        return buffer;
    }
  private:
    const int AllocSize = 500;
    inline void ExpandSize()
    {
      size += AllocSize;
      buffer = realloc( buffer, size );
    }
    char* buffer;
    int cursor;
    int size;
};

inline void CharAccumulator::AddChar( char toAdd )
{
  // make sure there's always room for terminating NUL
    if (cursor + 1 >= size)
    {
      ExpandSize();
    }
  // add character
    buffer[cursor] = toAdd;
  // move cursor
    cursor++;
}

class Event
{
  public:
    enum EventType
    {
      // primitive characters of interest
        Space,
        Tab,
        Pound,
        Star,
        Slash,
        BackSlash,
        NewLine,
        SingleQuote,
        DoubleQuote,
        LeftParen,
        RightParen,
        LeftBrace,
        RightBrace,
        LeftBracket,
        RightBracket,
        Character,
      // higher-level events
        EscapedNewLine,
        WhiteSpace,
        Indent,
        Comment,
        CplusComment,
        InterfaceCommand,
        ImplementationCommand,
        FunctionCommand,
        QuotedCharacter,
        QuotedString,
        Ident,
        Number,
        Keyword,
        LineDirective,
        BlockBegin,
        BlockEnd,
        Header,
    };
    Event()
    {
      startingRow = 0;
      startingColumn = 0;
      next = 0;
    }
    Event( int r, int c )
    {
      startingRow = r;
      startingColumn = c;
      next = 0;
    }
    virtual ~Event();
    virtual void Output( FILE* outFile, int deltaIndent ) const = 0;
    virtual EventType ThisEventCode() const = 0;
    virtual Boolean IsCharEvent() const = 0;
    inline int StartingRow() const { return startingRow; }
    inline int StartingColumn() const { return startingColumn; }
    inline Link( Event* nextEvent ) { next = nextEvent; }
    inline Event* Next() const { return next; }
  private:
    int startingRow;
    int startingColumn;
    Event* next;
};

class EventSink
{
  public:
    EventSink() { }
    ~EventSink() { }
    void virtual Inject( Event* event ) = 0;
  private:
    char dummy;     // some stupid compilers complain if there are no members
};

class OutputEvent : public EventSink
{
  public:
    OutputEvent( FILE* f )
    {
      outFile = f;
    }
    ~OutputEvent()
    {
      fflush( outFile );
    }
    void Inject( Event* event );
  private:
    FILE* outFile;
};

class EventList
{
  public:
    EventList() { head = tail = NULL; }
    ~EventList()
    {
      for (Event* event = head; event != NULL; )
      {
        Event* next = event->Next();
        delete event;
        event = next;
      }
      head = tail = NULL;
    }
    void EmitList( EventSink* sink )
    {
      for (Event* event = head; event != NULL; )
      {
        Event* next = event->Next();
        sink->Inject( event );  // this will delete event
        event = next;
      }
      head = tail = NULL;
    }
    void AddToEnd( Event* newEvent )
    {
      if (tail)
      {
        tail->Link( newEvent );
        tail = newEvent;
      }
      else
      {
        head = tail = newEvent;
      }
    }
    int StartingRow() const
    {
      if (!head)
      {
        return 0;
      }
      else
      {
        return head->StartingRow();
      }
    }
    int StartingColumn() const
    {
      if (!head)
      {
        return 0;
      }
      else
      {
        return head->StartingColumn();
      }
    }
  private:
    Event* head;
    Event* tail;
};

class FSM : public EventSink
{
  public:
    FSM( EventSink* eventSink )
    {
      nextEventSink = eventSink;
    }
    ~FSM() { }
  protected:
    void Emit( Event* event )
    {
      nextEventSink->Inject( event );
    }
  private:
    EventSink* nextEventSink;
};

class CharEvent : public Event
{
  public:
    CharEvent() { }
    CharEvent( int r, int c ) : Event( r, c ) { }
    ~CharEvent();
    void Output( FILE* outFile, int deltaIndent ) const = 0;
    virtual EventType ThisEventCode() const = 0;
    Boolean IsCharEvent() const;
    virtual char ThisChar() const = 0;
  private:
};

class NonCharEvent : public Event
{
  public:
    NonCharEvent() { }
    NonCharEvent( int r, int c ) : Event( r, c ) { }
    ~NonCharEvent();
    void Output( FILE* outFile, int deltaIndent ) const = 0;
    virtual EventType ThisEventCode() const = 0;
    Boolean IsCharEvent() const;
  private:
};

class AccumulatorEvent : public NonCharEvent
{
  public:
    AccumulatorEvent( int r, int c ) : NonCharEvent( r, c )
    {
      accumulator.CharAccumulator();
    }
    ~AccumulatorEvent();
    inline void AddChar( char toAdd )
    {
      accumulator.AddChar( toAdd );
    }
    inline const char* AccumulatorBuffer() const
    {
      return accumulator.AccumulatorBuffer();
    }
    void Output( FILE* outFile, int deltaIndent ) const;
  protected:
    CharAccumulator accumulator;
  private:
    int virtual OutputFirstLine( FILE* outFile, const char* buffer ) const;
    void virtual OutputRemainingLines( FILE* outFile, const char* remaining,
                               int deltaIndent ) const;
    void virtual OutputHeader( FILE* outFile ) const = 0;
    void virtual OutputTrailer( FILE* outFile ) const = 0;
};

class SpaceEvent : public CharEvent
{
  public:
    SpaceEvent( int r, int c ) : CharEvent( r, c ) { }
    ~SpaceEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class TabEvent : public CharEvent
{
  public:
    TabEvent( int r, int c ) : CharEvent( r, c ) { }
    ~TabEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
    int NumberOfSpaces()
    {
      return 8 - (unsigned(StartingColumn()) % 8);
    }
  private:
};

class PoundEvent : public CharEvent
{
  public:
    PoundEvent( int r, int c ) : CharEvent( r, c ) { }
    ~PoundEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class StarEvent : public CharEvent
{
  public:
    StarEvent( int r, int c ) : CharEvent( r, c ) { }
    ~StarEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class SlashEvent : public CharEvent
{
  public:
    SlashEvent( int r, int c ) : CharEvent( r, c ) { }
    ~SlashEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class BackSlashEvent : public CharEvent
{
  public:
    BackSlashEvent( int r, int c ) : CharEvent( r, c ) { }
    ~BackSlashEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class NewLineEvent : public CharEvent
{
  public:
    NewLineEvent( int r, int c ) : CharEvent( r, c ) { }
    ~NewLineEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class SingleQuoteEvent : public CharEvent
{
  public:
    SingleQuoteEvent( int r, int c ) : CharEvent( r, c ) { }
    ~SingleQuoteEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class DoubleQuoteEvent : public CharEvent
{
  public:
    DoubleQuoteEvent( int r, int c ) : CharEvent( r, c ) { }
    ~DoubleQuoteEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class MatchingEvent : public CharEvent
{
  public:
    MatchingEvent( int r, int c ) : CharEvent( r, c )
    {
      level = 0;
    }
    ~MatchingEvent();
    int level;
  private:
};

class LeftParenEvent : public MatchingEvent
{
  public:
    LeftParenEvent( int r, int c ) : MatchingEvent( r, c )
    {
    }
    ~LeftParenEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class RightParenEvent : public MatchingEvent
{
  public:
    RightParenEvent( int r, int c ) : MatchingEvent( r, c )
    {
    }
    ~RightParenEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class LeftBraceEvent : public MatchingEvent
{
  public:
    LeftBraceEvent( int r, int c ) : MatchingEvent( r, c )
    {
    }
    ~LeftBraceEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class RightBraceEvent : public MatchingEvent
{
  public:
    RightBraceEvent( int r, int c ) : MatchingEvent( r, c )
    {
    }
    ~RightBraceEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class LeftBracketEvent : public MatchingEvent
{
  public:
    LeftBracketEvent( int r, int c ) : MatchingEvent( r, c )
    {
    }
    ~LeftBracketEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class RightBracketEvent : public MatchingEvent
{
  public:
    RightBracketEvent( int r, int c ) : MatchingEvent( r, c )
    {
      level = 0;
    }
    ~RightBracketEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class LineDirectiveEvent : public NonCharEvent
{
  public:
    LineDirectiveEvent( int r, int c ) : NonCharEvent( r, c )
    {
    }
    ~LineDirectiveEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
};

class CharacterEvent : public CharEvent
{
  public:
    CharacterEvent( int r, int c, char ch ) : CharEvent( r, c )
    {
      thisChar = ch;
    }
    ~CharacterEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    char ThisChar() const;
  private:
    char thisChar;
};


Event* NextEvent( FILE* inFile );
SHAR_EOF
fi # end of overwriting check
if test -f 'comment.h'
then
	echo shar: will not over-write existing file "'comment.h'"
else
cat << \SHAR_EOF > 'comment.h'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#pragma once

/*
 * This state machine extracts comments, both C and C++ style, and quoted
 * characters and strings, from a character event stream.  It introduces
 * the following event types:
 *   CommentEvent
 *   CplusCommentEvent
 *   QuotedCharacterEvent
 *   QuotedStringEvent
 */

#include "event.h"


class CommentEvent : public AccumulatorEvent
{
  public:
    inline CommentEvent( int r, int s ) : AccumulatorEvent( r, s ) { }
    ~CommentEvent();
    EventType ThisEventCode() const;
  private:
    void OutputRemainingLines( FILE* outFile, const char* remaining,
                               int deltaIndent ) const;
    void OutputHeader( FILE* outFile ) const;
    void OutputTrailer( FILE* outFile ) const;
};


class CplusCommentEvent : public CommentEvent
{
  public:
    inline CplusCommentEvent( int r, int s ) : CommentEvent( r, s ) { }
    ~CplusCommentEvent();
    EventType ThisEventCode() const;
  private:
    void OutputHeader( FILE* outFile ) const;
    void OutputTrailer( FILE* outFile ) const;
};


class QuotedEvent : public AccumulatorEvent
{
  public:
    inline QuotedEvent( int r, int s ) : AccumulatorEvent( r, s ) { }
  private:
};

class QuotedCharacterEvent : public QuotedEvent
{
  public:
    inline QuotedCharacterEvent( int r, int s ) : QuotedEvent( r, s ) { }
    inline ~QuotedCharacterEvent() { }
    EventType ThisEventCode() const;
  private:
    void OutputHeader( FILE* outFile ) const;
    void OutputTrailer( FILE* outFile ) const;
};

class QuotedStringEvent : public QuotedEvent
{
  public:
    inline QuotedStringEvent( int r, int s ) : QuotedEvent( r, s ) { }
    inline ~QuotedStringEvent() { }
    EventType ThisEventCode() const;
  private:
    void OutputHeader( FILE* outFile ) const;
    void OutputTrailer( FILE* outFile ) const;
};

class CommentQuotedFSM : public FSM
{
  public:
    CommentQuotedFSM( EventSink* eventSink ) : FSM( eventSink )
    {
       state = StartState;
       comment = NULL;
       quoted = NULL;
    }
    ~CommentQuotedFSM()
    {
      if (comment)
      {
        delete comment;
      }
      if (quoted)
      {
        delete quoted;
      }
    }
    void Inject( Event* event );
  private:
    void AddCommentChar( char c );
    void AddQuotedChar( char c )
    {
      quoted->AddChar( c );
    }
    EventType quoteCode;
    CommentEvent* comment;
    QuotedEvent* quoted;
    enum CommentQuotedState
    {
      // comment states
        WaitForCommentOrQuote,
        WaitForStartStarOrSlash,
        SavingCplusComment,
        SavingComment,
        WaitForEndSlash,

      // quoted states
        SaveString,
        EscapeCharacter,
    };
    CommentQuotedState state;
    const CommentQuotedState StartState = WaitForCommentOrQuote;
};
SHAR_EOF
fi # end of overwriting check
if test -f 'ident.h'
then
	echo shar: will not over-write existing file "'ident.h'"
else
cat << \SHAR_EOF > 'ident.h'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#pragma once

/*
 * This state machine extracts identifiers and numbers from a character
 * event stream.  It introduces the following event types:
 *   IdentEvent
 *   NumberEvent
 */

#include "event.h"


class IdentEvent : public AccumulatorEvent
{
  public:
    inline IdentEvent( int r, int s ) : AccumulatorEvent( r, s ) { }
    ~IdentEvent();
    EventType ThisEventCode() const;
  private:
    void OutputHeader( FILE* outFile ) const;
    void OutputTrailer( FILE* outFile ) const;
};

class NumberEvent : public AccumulatorEvent
{
  public:
    inline NumberEvent( int r, int s ) : AccumulatorEvent( r, s ) { }
    ~NumberEvent();
    EventType ThisEventCode() const;
  private:
    void OutputHeader( FILE* outFile ) const;
    void OutputTrailer( FILE* outFile ) const;
};

class IdentNumberFSM : public FSM
{
  public:
    IdentNumberFSM( EventSink* eventSink ) : FSM( eventSink )
    {
       state = StartState;
       ident = NULL;
       number = NULL;
    }
    ~IdentNumberFSM()
    {
      if (ident)
      {
        delete ident;
      }
      if (number)
      {
        delete number;
      }
    }
    void Inject( Event* event );
  private:
    void AddIdentChar( char c )
    {
      ident->AddChar( c );
    }
    void AddNumberChar( char c )
    {
      number->AddChar( c );
    }
    IdentEvent* ident;
    NumberEvent* number;
    enum IdentNumberState
    {
      WaitForIdentOrNumber,
      SavingIdent,
      SavingNumber,
    };
    IdentNumberState state;
    const IdentNumberState StartState = WaitForIdentOrNumber;
};
SHAR_EOF
fi # end of overwriting check
if test -f 'keyword.h'
then
	echo shar: will not over-write existing file "'keyword.h'"
else
cat << \SHAR_EOF > 'keyword.h'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#pragma once

/*
 * This state machine converts identifiers which are keywords into keyword
 * events.  It introduces event type (surprise):
 *   KeywordEvent
 */

#include "event.h"
#include "ident.h"

extern "C" int strlen( const char* );
extern "C" int strcmp( const char*, const char* );
extern "C" const char* strcpy( char*, const char* );

class KeywordEvent : public NonCharEvent
{
  public:
    inline KeywordEvent( int r, int s, const char* kw ) : NonCharEvent( r, s )
    {
      keyword = malloc( strlen( kw ) + 1 );
      strcpy( keyword, kw );
    }
    ~KeywordEvent();
    void Output( FILE* outFile, int indent ) const;
    EventType ThisEventCode() const;
    inline int IsKeyword( const char* kw ) const
    {
      return strcmp( kw, keyword ) == 0;
    }
  private:
    char* keyword;
    void OutputHeader( FILE* outFile ) const;
    void OutputTrailer( FILE* outFile ) const;
};

class KeywordFSM : public FSM
{
  public:
    KeywordFSM( EventSink* eventSink ) : FSM( eventSink ) { }
    ~KeywordFSM() { }
    void Inject( Event* event );
  private:
    // degenerate state machine!
};
SHAR_EOF
fi # end of overwriting check
if test -f 'inline.h'
then
	echo shar: will not over-write existing file "'inline.h'"
else
cat << \SHAR_EOF > 'inline.h'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#pragma once

/*
 * This state machine extracts everything from the `inline' keyword up until
 * the trailing right brace and places it in a separate file.  It complains
 * (but continues) if `inline' is found nested in braces.
 */

#include "event.h"
#include "keyword.h"

class ExtractInlineFSM : public FSM
{
  public:
    ExtractInlineFSM( EventSink* nSink, EventSink* iSink ) : FSM( nSink )
    {
      state = StartState;
      braceLevel = 0;
      inlineSink = iSink;
    }
    ~ExtractInlineFSM() { }
    void Inject( Event* event );
    void EmitInline( Event* event )
    {
      inlineSink->Inject( event );
    }
  private:
    enum ExtractInlineState
    {
      CopyingCode,
      ExtractingInline,
    };
    ExtractInlineState state;
    const ExtractInlineState StartState = CopyingCode;
    EventSink* inlineSink;
    int braceLevel;
};
SHAR_EOF
fi # end of overwriting check
if test -f 'event.cc'
then
	echo shar: will not over-write existing file "'event.cc'"
else
cat << \SHAR_EOF > 'event.cc'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#include "event.h"


Event::~Event()
{
}


CharEvent::~CharEvent()
{
}

Boolean CharEvent::IsCharEvent() const
{
  return TRUE;
}


NonCharEvent::~NonCharEvent()
{
}

Boolean NonCharEvent::IsCharEvent() const
{
  return FALSE;
}


AccumulatorEvent::~AccumulatorEvent()
{
  accumulator.CharAccumulator::~CharAccumulator();
}

void AccumulatorEvent::Output( FILE* outFile, int indent ) const
{
  const char* buffer = accumulator.AccumulatorBuffer();

  // output header
    OutputHeader( outFile );

  // output first line
    int i = OutputFirstLine( outFile, buffer );

  // output remaining lines
    if (buffer[i] != '\0')
    {
      OutputRemainingLines( outFile, &buffer[i], indent );
    }

  // output trailer
    OutputTrailer( outFile );
}

int AccumulatorEvent::OutputFirstLine( FILE* outFile, const char* buffer ) const
{
  for (int i = 0; buffer[i] != '\0'; i++)
  {
    putc( buffer[i], outFile );
    if (buffer[i] == '\n')
    {
      return i + 1;
    }
  }
  return i;
}

void AccumulatorEvent::OutputRemainingLines( FILE* outFile,
                                             const char* remaining,
                                             int ) const
{
  // don't use any special indenting
    fputs( remaining, outFile );
}


void OutputEvent::Inject( Event* event )
{
  event->Output( outFile, 0 );
  delete event;
}


SpaceEvent::~SpaceEvent()
{
}

void SpaceEvent::Output( FILE* outFile, int ) const
{
  putc( ' ', outFile );
}

EventType SpaceEvent::ThisEventCode() const
{
  return Event::Space;
}

char SpaceEvent::ThisChar() const
{
  return ' ';
}


TabEvent::~TabEvent()
{
}

void TabEvent::Output( FILE* outFile, int ) const
{
  putc( '\t', outFile );
}

EventType TabEvent::ThisEventCode() const
{
  return Event::Tab;
}

char TabEvent::ThisChar() const
{
  return '\t';
}


StarEvent::~StarEvent()
{
}

void StarEvent::Output( FILE* outFile, int ) const
{
  putc( '*', outFile );
}

EventType StarEvent::ThisEventCode() const
{
  return Event::Star;
}

char StarEvent::ThisChar() const
{
  return '*';
}


SlashEvent::~SlashEvent()
{
}

void SlashEvent::Output( FILE* outFile, int ) const
{
  putc( '/', outFile );
}

EventType SlashEvent::ThisEventCode() const
{
  return Event::Slash;
}

char SlashEvent::ThisChar() const
{
  return '/';
}


PoundEvent::~PoundEvent()
{
}

void PoundEvent::Output( FILE* outFile, int ) const
{
  putc( '#', outFile );
}

EventType PoundEvent::ThisEventCode() const
{
  return Event::Pound;
}

char PoundEvent::ThisChar() const
{
  return '#';
}


BackSlashEvent::~BackSlashEvent()
{
}

void BackSlashEvent::Output( FILE* outFile, int ) const
{
  putc( '\\', outFile );
}

EventType BackSlashEvent::ThisEventCode() const
{
  return Event::BackSlash;
}

char BackSlashEvent::ThisChar() const
{
  return '\\';
}


NewLineEvent::~NewLineEvent()
{
}

void NewLineEvent::Output( FILE* outFile, int ) const
{
  putc( '\n', outFile );
}

EventType NewLineEvent::ThisEventCode() const
{
  return Event::NewLine;
}

char NewLineEvent::ThisChar() const
{
  return '\n';
}


SingleQuoteEvent::~SingleQuoteEvent()
{
}

void SingleQuoteEvent::Output( FILE* outFile, int ) const
{
  putc( '\'', outFile );
}

EventType SingleQuoteEvent::ThisEventCode() const
{
  return Event::SingleQuote;
}

char SingleQuoteEvent::ThisChar() const
{
  return '\'';
}


DoubleQuoteEvent::~DoubleQuoteEvent()
{
}

void DoubleQuoteEvent::Output( FILE* outFile, int ) const
{
  putc( '"', outFile );
}

EventType DoubleQuoteEvent::ThisEventCode() const
{
  return Event::DoubleQuote;
}

char DoubleQuoteEvent::ThisChar() const
{
  return '"';
}


MatchingEvent::~MatchingEvent()
{
}


LeftParenEvent::~LeftParenEvent()
{
}

void LeftParenEvent::Output( FILE* outFile, int ) const
{
  putc( '(', outFile );
}

EventType LeftParenEvent::ThisEventCode() const
{
  return Event::LeftParen;
}

char LeftParenEvent::ThisChar() const
{
  return '(';
}


RightParenEvent::~RightParenEvent()
{
}

void RightParenEvent::Output( FILE* outFile, int ) const
{
  putc( ')', outFile );
}

EventType RightParenEvent::ThisEventCode() const
{
  return Event::RightParen;
}

char RightParenEvent::ThisChar() const
{
  return ')';
}


LeftBraceEvent::~LeftBraceEvent()
{
}

void LeftBraceEvent::Output( FILE* outFile, int ) const
{
  putc( '{', outFile );
}

EventType LeftBraceEvent::ThisEventCode() const
{
  return Event::LeftBrace;
}

char LeftBraceEvent::ThisChar() const
{
  return '{';
}


RightBraceEvent::~RightBraceEvent()
{
}

void RightBraceEvent::Output( FILE* outFile, int ) const
{
  putc( '}', outFile );
}

EventType RightBraceEvent::ThisEventCode() const
{
  return Event::RightBrace;
}

char RightBraceEvent::ThisChar() const
{
  return '}';
}


LeftBracketEvent::~LeftBracketEvent()
{
}

void LeftBracketEvent::Output( FILE* outFile, int ) const
{
  putc( '[', outFile );
}

EventType LeftBracketEvent::ThisEventCode() const
{
  return Event::LeftBracket;
}

char LeftBracketEvent::ThisChar() const
{
  return '[';
}


RightBracketEvent::~RightBracketEvent()
{
}

void RightBracketEvent::Output( FILE* outFile, int ) const
{
  putc( ']', outFile );
}

EventType RightBracketEvent::ThisEventCode() const
{
  return Event::RightBracket;
}

char RightBracketEvent::ThisChar() const
{
  return ']';
}


LineDirectiveEvent::~LineDirectiveEvent()
{
}

void LineDirectiveEvent::Output( FILE* outFile, int ) const
{
  fprintf( outFile, "\n#line %d\n", StartingRow() );
}

EventType LineDirectiveEvent::ThisEventCode() const
{
  return Event::LineDirective;
}


CharacterEvent::~CharacterEvent()
{
}

void CharacterEvent::Output( FILE* outFile, int ) const
{
  putc( thisChar, outFile );
}

EventType CharacterEvent::ThisEventCode() const
{
  return Event::Character;
}

char CharacterEvent::ThisChar() const
{
  return thisChar;
}


Event* NextEvent( FILE* inFile )
{
  int c = getc( inFile );

  if (c == EOF)
  {
    return (Event*)NULL;
  }

  static int row = 1;
  static int column = 0;
  Event *event;

  switch (c)
  {
    case ' ':
      event = new SpaceEvent( row, column );
      break;

    case '\t':
      event = new TabEvent( row, column );
      break;

    case '#':
      event = new PoundEvent( row, column );
      break;

    case '*':
      event = new StarEvent( row, column );
      break;

    case '/':
      event = new SlashEvent( row, column );
      break;

    case '\\':
      event = new BackSlashEvent( row, column );
      break;

    case '\n':
      event = new NewLineEvent( row, column );
      row++;
      column = -1;
      break;

    case '\'':
      event = new SingleQuoteEvent( row, column );
      break;

    case '"':
      event = new DoubleQuoteEvent( row, column );
      break;

    case '(':
      event = new LeftParenEvent( row, column );
      break;

    case ')':
      event = new RightParenEvent( row, column );
      break;

    case '{':
      event = new LeftBraceEvent( row, column );
      break;

    case '}':
      event = new RightBraceEvent( row, column );
      break;

    case '[':
      event = new LeftBracketEvent( row, column );
      break;

    case ']':
      event = new RightBracketEvent( row, column );
      break;

    default:
      event = new CharacterEvent( row, column, (char)c );
      break;
  }
  column++;

  return event;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'comment.cc'
then
	echo shar: will not over-write existing file "'comment.cc'"
else
cat << \SHAR_EOF > 'comment.cc'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#include "comment.h"


CommentEvent::~CommentEvent()
{
}

void CommentEvent::OutputRemainingLines( FILE* outFile,
                                         const char* remaining,
                                         int deltaIndent ) const
{
  for (int i = 0; remaining[i] != '\0';)
  {
    // find current indentation
      for (int lineIndent = 0; remaining[i] == ' '; lineIndent++, i++)
        ;
    // add in delta
      lineIndent += deltaIndent;

    // indent this much on output
      for (int j = 0; j < lineIndent; j++)
      {
        putc( ' ', outFile );
      }

    // output remaining characters on line
      for (; remaining[i] != '\0'; i++)
      {
        putc( remaining[i], outFile );
        if (remaining[i] == '\n')
        {
          i++;
          break;
        }
      }
  }
}

void CommentEvent::OutputHeader( FILE* outFile ) const
{
  fputs( "/*", outFile );
}

void CommentEvent::OutputTrailer( FILE* outFile ) const
{
  fputs( "*/", outFile );
}

EventType CommentEvent::ThisEventCode() const
{
  return Event::Comment;
}


CplusCommentEvent::~CplusCommentEvent()
{
}

void CplusCommentEvent::OutputHeader( FILE* outFile ) const
{
  fputs( "//", outFile );
}

void CplusCommentEvent::OutputTrailer( FILE* outFile ) const
{
}

EventType CplusCommentEvent::ThisEventCode() const
{
  return Event::CplusComment;
}


void QuotedCharacterEvent::OutputHeader( FILE* outFile ) const
{
  fputs( "'", outFile );
}

void QuotedCharacterEvent::OutputTrailer( FILE* outFile ) const
{
  fputs( "'", outFile );
}

EventType QuotedCharacterEvent::ThisEventCode() const
{
  return Event::SingleQuote;
}


void QuotedStringEvent::OutputHeader( FILE* outFile ) const
{
  fputs( "\"", outFile );
}

void QuotedStringEvent::OutputTrailer( FILE* outFile ) const
{
  fputs( "\"", outFile );
}

EventType QuotedStringEvent::ThisEventCode() const
{
  return Event::DoubleQuote;
}


void CommentQuotedFSM::AddCommentChar( char c )
{
  comment->AddChar( c );
}

void CommentQuotedFSM::Inject( Event* event )
{
  switch (state)
  {
    case WaitForCommentOrQuote:
      switch (event->ThisEventCode())
      {
        case Event::Slash:
          // swallow slash
            delete event;
          // transition
            state = WaitForStartStarOrSlash;
          break;

        case Event::SingleQuote:
          // remember it was single quote
            quoteCode = Event::SingleQuote;
          // swallow single quote
            delete event;
          // allocate quoted string storage
            quoted = new QuotedCharacterEvent( event->StartingRow(),
                                               event->StartingColumn() );
          // transition
            state = SaveString;
          break;

        case Event::DoubleQuote:
          // remember it was double quote
            quoteCode = Event::DoubleQuote;
          // swallow double quote
            delete event;
          // allocate quoted string storage
            quoted = new QuotedStringEvent( event->StartingRow(),
                                            event->StartingColumn() );
          // transition
            state = SaveString;
          break;

        default:
          // emit this event
            Emit( event );
          break;
      }
      break;

    case WaitForStartStarOrSlash:
      switch (event->ThisEventCode())
      {
        case Event::Star:
          // swallow star
            delete event;
          // transition
            comment = new CommentEvent( event->StartingRow(),
                                        event->StartingColumn() - 2 );
            state = SavingComment;
          break;

        case Event::Slash:
          // swallow second slash
            delete event;
          // transition
            comment = new CplusCommentEvent( event->StartingRow(),
                                             event->StartingColumn() - 2 );
            state = SavingCplusComment;
          break;

        default:
          // emit buffered slash
            Event* bufferedSlash = new SlashEvent( event->StartingRow(),
                                                   event->StartingColumn() - 1);
            Emit( bufferedSlash );
          // then emit this event
            Emit( event );
          // transition back where we came from
            state = WaitForCommentOrQuote;
      }
      break;

    case SavingComment:
      switch (event->ThisEventCode())
      {
        case Event::Star:
          // swallow star
            delete event;
          // transition
            state = WaitForEndSlash;
          break;

        default:
          // verify that we have a char
            if (!event->IsCharEvent())
            {
              fprintf( stderr, "non-char event in comment!\n" );
              exit( 1 );
            }
          // add this character to comment
            AddCommentChar( ((CharEvent*)event)->ThisChar() );
          // swallow event
            delete event;
          break;
      }
      break;

    case SavingCplusComment:
      switch (event->ThisEventCode())
      {
        case Event::NewLine:
          // emit buffered comment event
            Emit( comment );
            comment = NULL;
          // emit new line
            Emit( event );
          // transition
            state = WaitForCommentOrQuote;
          break;

        default:
          // verify that we have a char
            if (!event->IsCharEvent())
            {
              fprintf( stderr, "non-char event in comment!\n" );
              exit( 1 );
            }
          // add this character to comment
            AddCommentChar( ((CharEvent*)event)->ThisChar() );
          // swallow event
            delete event;
          break;
      }
      break;

    case WaitForEndSlash:
      switch (event->ThisEventCode())
      {
        case Event::Slash:
          // swallow slash
            delete event;
          // emit the comment event we've been building
            Emit( comment );
            comment = NULL;
          // transition back to beginning
            state = WaitForCommentOrQuote;
          break;

        case Event::Star:
          // save this star
            AddCommentChar( '*' );
          // swallow event
            delete event;
          // stay in same state
            ;
          break;
        
        default:
          // save buffered star
            AddCommentChar( '*' );
          // verify that we have a char
            if (!event->IsCharEvent())
            {
              fprintf( stderr, "non-char event in comment!\n" );
              exit( 1 );
            }
          // then add this character to comment
            AddCommentChar( ((CharEvent*)event)->ThisChar() );
          // swallow event
            delete event;
          // transition back to saving comment
            state = SavingComment;
          break;
      }
      break;

    /*
     * Quoted String FSM portion begins here.
     */

    case SaveString:
      if( event->ThisEventCode() == quoteCode)
      {
        // emit saved string event
          Emit( quoted );
          quoted = NULL;
        // transition
          state = WaitForCommentOrQuote;
      }
      else if( event->ThisEventCode() == Event::BackSlash)
      {
        // save backslash in quoted string
          AddQuotedChar( '\\' );
        // swallow event
          delete event;
        // transition
          state = EscapeCharacter;
        break;
      }
      else
      {
        // verify that we have a char
          if (!event->IsCharEvent())
          {
            fprintf( stderr, "non-char event in comment!\n" );
            exit( 1 );
          }
        // add this character to quoted string
          AddQuotedChar( ((CharEvent*)event)->ThisChar() );
        // swallow event
          delete event;
        // same state
          ;
        break;
      }
      break;

    case EscapeCharacter:
      // verify that we have a char
        if (!event->IsCharEvent())
        {
          fprintf( stderr, "non-char event in comment!\n" );
          exit( 1 );
        }
      // add this character regardless
        AddQuotedChar( ((CharEvent*)event)->ThisChar() );
      // swallow event
        delete event;
      // transition back
        state = SaveString;
      break;
  }
}
SHAR_EOF
fi # end of overwriting check
if test -f 'ident.cc'
then
	echo shar: will not over-write existing file "'ident.cc'"
else
cat << \SHAR_EOF > 'ident.cc'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#include "ident.h"


IdentEvent::~IdentEvent()
{
}

void IdentEvent::OutputHeader( FILE* outFile ) const
{
}

void IdentEvent::OutputTrailer( FILE* outFile ) const
{
}

EventType IdentEvent::ThisEventCode() const
{
  return Event::Ident;
}


NumberEvent::~NumberEvent()
{
}

void NumberEvent::OutputHeader( FILE* outFile ) const
{
}

void NumberEvent::OutputTrailer( FILE* outFile ) const
{
}

EventType NumberEvent::ThisEventCode() const
{
  return Event::Number;
}


void IdentNumberFSM::Inject( Event* event )
{
  switch (state)
  {
    case WaitForIdentOrNumber:
      switch (event->ThisEventCode())
      {
        case Event::Character:
          char c = ((CharacterEvent*)event)->ThisChar();
          if ((c >= 'A' && c <= 'Z') ||
              (c >= 'a' && c <= 'z') ||
              c == '_' || c == '$')
          {
            // swallow char event
              delete event;
            // start ident event
              ident = new IdentEvent( event->StartingRow(),
                                      event->StartingColumn() );
              ident->AddChar( c );
            // transition
              state = SavingIdent;
          }
          else if (c >= '0' && c <= '9')
          {
            // swallow char event
              delete event;
            // start number event
              number = new NumberEvent( event->StartingRow(),
                                        event->StartingColumn() );
              number->AddChar( c );
            // transition
              state = SavingNumber;
          }
          else
          {
            // emit this event
              Emit( event );
          }
          break;

        default:
          // emit this event
            Emit( event );
          break;
      }
      break;

    case SavingIdent:
      switch (event->ThisEventCode())
      {
        case Event::Character:
          char c = ((CharacterEvent*)event)->ThisChar();
          if ((c >= 'A' && c <= 'Z') ||
              (c >= 'a' && c <= 'z') ||
              (c >= '0' && c <= '9') ||
              c == '_' || c == '$')
          {
            // add character to ident
              AddIdentChar( c );
            // swallow char event
              delete event;
            // same state
              ;
          }
          else
          {
            // emit ident event
              Emit( ident );
              ident = NULL;
            // emit this event
              Emit( event );
            // transition
              state = WaitForIdentOrNumber;
          }
          break;

        default:
          // emit ident event
            Emit( ident );
            ident = NULL;
          // emit this event
            Emit( event );
          // transition
            state = WaitForIdentOrNumber;
          break;
      }
      break;

    case SavingNumber:
      switch (event->ThisEventCode())
      {
        case Event::Character:
          char c = ((CharacterEvent*)event)->ThisChar();
          if ((c >= '0' && c <= '9') ||
              (c >= 'A' && c <= 'F') ||
              (c >= 'a' && c <= 'f') ||
              c == 'x' || c == 'X')
          {
            // add character to number
              AddNumberChar( c );
            // swallow char event
              delete event;
            // same state
              ;
          }
          else
          {
            // emit number event
              Emit( number );
              number = NULL;
            // emit this event
              Emit( event );
            // transition
              state = WaitForIdentOrNumber;
          }
          break;

        default:
          // emit number event
            Emit( number );
            number = NULL;
          // emit this event
            Emit( event );
          // transition
            state = WaitForIdentOrNumber;
          break;
      }
      break;
  }
}
SHAR_EOF
fi # end of overwriting check
if test -f 'keyword.cc'
then
	echo shar: will not over-write existing file "'keyword.cc'"
else
cat << \SHAR_EOF > 'keyword.cc'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#include "keyword.h"


KeywordEvent::~KeywordEvent()
{
  free( keyword );
}

void KeywordEvent::Output( FILE* outFile, int indent ) const
{
  fputs( keyword, outFile );
}

EventType KeywordEvent::ThisEventCode() const
{
  return Event::Keyword;
}


void KeywordFSM::Inject( Event* event )
{
  if (event->ThisEventCode() == Event::Ident)
  {
    static const char* keywords[] = {
      "asm", "auto", "break", "case", "catch", "char", "class", "const",
      "continue", "default", "delete", "do", "double", "else", "enum",
      "extern", "float", "for", "friend", "goto", "if", "inline", "int",
      "long", "new", "operator", "private", "protected", "public", "register",
      "return", "short", "signed", "sizeof", "static", "struct", "switch",
      "template", "this", "throw", "try", "typedef", "union", "unsigned",
      "virtual", "void", "volatile", "while", NULL };

    const char* str = ((IdentEvent*)event)->AccumulatorBuffer();

    for (int i = 0; keywords[i] != NULL; i++)
    {
      if (strcmp( str, keywords[i] ) == 0)
      {
        // emit keyword event
          KeywordEvent* keywordEvent =
                  new KeywordEvent( event->StartingRow(),
                                    event->StartingColumn(), str );
          Emit( keywordEvent );
        // destroy old event
          delete event;
        return;
      }
    }
  }

  // emit event
    Emit( event );
}
SHAR_EOF
fi # end of overwriting check
if test -f 'inline.cc'
then
	echo shar: will not over-write existing file "'inline.cc'"
else
cat << \SHAR_EOF > 'inline.cc'
/*
 * This code is covered under the Gnu General Public License, version 1.
 * Colin Kelley, June 1991
 */

#include "event.h"
#include "inline.h"


void ExtractInlineFSM::Inject( Event* event )
{
  switch (state)
  {
    case CopyingCode:
      switch (event->ThisEventCode())
      {
        case Event::LeftBrace:
          braceLevel++;
          // emit event
            Emit( event );
          break;

        case Event::RightBrace:
          braceLevel--;
          // emit event
            Emit( event );
          break;

        case Event::Keyword:
          // check if it's `inline'
            if (((KeywordEvent*)event)->IsKeyword( "inline" ))
            {
              // don't handle 'inline' if it's enclosed in braces
                if (braceLevel != 0)
                {
                  fprintf( stderr, "inline at line %d at brace level %d!\n",
                                   event->StartingRow(), braceLevel );
                  Emit( event );
                  break;
                }
              // start inline with #line directive
                EmitInline( new LineDirectiveEvent(event->StartingRow(),
                            event->StartingColumn() ) );
              // then inline keyword
                EmitInline( event );
              // transition
                state = ExtractingInline;
              break;
            }
            else
            {
              ; // fall through
            }

        default:
          // emit event
            Emit( event );
          break;
      }
      break;

    case ExtractingInline:
      switch (event->ThisEventCode())
      {
        case Event::LeftBrace:
          braceLevel++;
          // add this to inline function
            EmitInline( event );
          break;

        case Event::RightBrace:
          braceLevel--;
          // add this to inline function
            EmitInline( event );
          // stop if outer-most right brace
            if (braceLevel == 0)
            {
              // mark current line in remain file
                Emit( new LineDirectiveEvent( event->StartingRow() + 1,
                                              event->StartingColumn() ) );
              // add on an extra new line for appearance
                EmitInline( new NewLineEvent( 0, 0 ) );
              // transition
                state = CopyingCode;
            }
          break;

        default:
          // add this to inline function
            EmitInline( event );
          break;
      }
  }
}
SHAR_EOF
fi # end of overwriting check
if test -f 'move-if-change'
then
	echo shar: will not over-write existing file "'move-if-change'"
else
cat << \SHAR_EOF > 'move-if-change'
#!/bin/sh
if test -r $2; then
  if cmp $1 $2 > /dev/null; then
    echo $2 is unchanged
    rm $1
  else
    mv $1 $2
  fi
else
  mv $1 $2
fi
SHAR_EOF
chmod +x 'move-if-change'
fi # end of overwriting check
#	End of shell archive
exit 0