Logo Search packages:      
Sourcecode: ragel version File versions

main.cpp

/*
 *  Copyright 2001-2004 Adrian Thurston <adriant@ragel.ca>
 */

/*  This file is part of Ragel.
 *
 *  Ragel is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 * 
 *  Ragel is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with Ragel; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <unistd.h>

/* Parsing. */
#include "ragel.h"
#include "ptreetypes.h"
#include "rlparse.h"
#include "parsetree.h"

/* Parameters and output. */
#include "pcheck.h"
#include "vector.h"
#include "version.h"

using std::ostream;
using std::ios;
using std::cout;
using std::cerr;
using std::endl;

/* Target language and output style. */
OutputFormat outputFormat = OutCCode;
CodeStyleEnum codeStyle = GenTables;
char *defExtension = ".c";

/* Io globals. */
ostream *outStream = 0;
output_filter *outFilter = 0;
char *inputFile = 0;
char *outputFile = 0;

/* Controls minimization. */
MinimizeLevel minimizeLevel = MinimizePartition2;
bool minimizeEveryOp = false;

/* Graphviz dot file generation. */
char *machineSpec = 0, *machineName = 0;
bool machineSpecFound = false;
bool graphvizDone = false;

/* Print a summary of the options. */
void usage()
{
      cout <<
"usage: ragel [options] file\n"
"general:\n"
"   -h, -H, -?, --help    Print this usage and exit\n"
"   -v, --version         Print version information and exit\n"
"   -o <file>             Write output to <file>\n"
"fsm minimization:\n"
"   -m                    Minimize the number of states (default)\n"
"   -n                    Do not perform minimization\n"
"output:\n"
"   -c                    Generate C code (default)\n"
"   -C                    Generate C++ code\n"
"   -J                    Generate Objective-C code\n"
"   -V                    Generate a Graphviz dotfile\n"
"   -d                    Dump raw state tables\n"
"   -S <spec>             FSM specification to output for -V and -d\n"
"   -M <machine>          Machine definition/instantiation to output for -V and -d\n"
"generated code style:\n"
"   -T0                   Table driven FSM (default)\n"
"   -T1                   Faster table driven FSM\n"
"   -F0                   Flat table driven FSM\n"
"   -F1                   Faster flat table driven FSM\n"
"   -G0                   Goto driven FSM\n"
"   -G1                   Faster goto driven FSM\n"
"   -G2                   Really fast goto driven FSM\n"
      ;     
}

/* Print version information. */
void version()
{
      cout << "Ragel version " VERSION << " " PUBDATE << endl <<
                  "Copyright (c) 2001-2004 by Adrian Thurston" << endl;
}

/* Scans a string looking for the file extension. If there is a file
 * extension then pointer returned points to inside the string
 * passed in. Otherwise returns null. */
char *findFileExtension( char *stemFile )
{
      char *ppos = stemFile + strlen(stemFile) - 1;
      /* Scan backwards from the end looking for the first dot.
       * If we encounter a '/' before the first dot, then stop the scan. */
      while ( 1 ) {
            /* If we found a dot or got to the beginning of the string then
             * we are done. */
            if ( ppos == stemFile || *ppos == '.' )
                  break;

            /* If we hit a / then there is no extension. Done. */
            if ( *ppos == '/' ) {
                  ppos = stemFile;
                  break;
            }
            ppos--;
      } 

      /* If we got to the front of the string then bail we 
       * did not find an extension  */
      if ( ppos == stemFile )
            ppos = 0;

      return ppos;
}

/* Make a file name from a stem. Removes the old filename suffix and
 * replaces it with a new one. Returns a newed up string. */
char *fileNameFromStem( char *stemFile, char *suffix )
{
      int len = strlen( stemFile );
      assert( len > 0 );

      /* Get the extension. */
      char *ppos = findFileExtension( stemFile );

      /* If an extension was found, then shorten what we think the len is. */
      if ( ppos != 0 )
            len = ppos - stemFile;

      /* Make the return string from the stem and the suffix. */
      char *retVal = new char[ len + strlen( suffix ) + 1 ];
      strncpy( retVal, stemFile, len );
      strcpy( retVal + len, suffix );

      return retVal;
}

/* Global parse data pointer. */
extern ParseData *pd;

/* Total error count. */
int gblErrorCount = 0;

/* Print the opening to a program error, then return the error stream. */
ostream &error()
{
      /* Keep the error count. */
      if ( pd != 0 )
            pd->errorCount += 1;
      gblErrorCount += 1;
      
      cerr << PROGNAME ": ";
      return cerr;
}

/* Print the opening to an error in the input, then return the error ostream. */
ostream &error( const BISON_YYLTYPE &loc )
{
      /* Keep the error count. */
      if ( pd != 0 )
            pd->errorCount += 1;
      gblErrorCount += 1;

      cerr << inputFile << ":" << loc.first_line << ":" << 
                  loc.first_column << ": ";
      return cerr;
}

/* Print the opening to an error in the input, then return the error ostream. */
ostream &error( const InputLoc &loc )
{
      /* Keep the error count. */
      if ( pd != 0 )
            pd->errorCount += 1;
      gblErrorCount += 1;

      cerr << inputFile << ":" << loc.line << ":" << 
                  loc.col << ": ";
      return cerr;
}

ostream &error( int first_line, int first_column )
{
      /* Keep the error count. */
      if ( pd != 0 )
            pd->errorCount += 1;
      gblErrorCount += 1;

      cerr << inputFile << ":" << first_line << ":" << first_column << ": ";
      return cerr;
}

/* Print the opening to a warning, then return the error ostream. */
ostream &warning( )
{
      cerr << inputFile << ": warning: ";
      return cerr;
}

/* Print the opening to a warning in the input, then return the error ostream. */
ostream &warning( const InputLoc &loc )
{
      cerr << inputFile << ":" << loc.line << ":" << 
                  loc.col << ": warning: ";
      return cerr;
}

/* Print the opening to a warning in the input, then return the error ostream. */
std::ostream &warning( int first_line, int first_column )
{
      cerr << inputFile << ":" << first_line << ":" << 
                  first_column << ": warning: ";
      return cerr;
}

/* Counts newlines before sending sync. */
int output_filter::sync( )
{
      line += 1;
      return std::filebuf::sync();
}

/* Counts newlines before sending data out to file. */
std::streamsize output_filter::xsputn( const char *s, std::streamsize n )
{
      for ( int i = 0; i < n; i++ ) {
            if ( s[i] == '\n' )
                  line += 1;
      }
      return std::filebuf::xsputn( s, n );
}

void escapeLineDirectivePath( std::ostream &out, char *path )
{
      for ( char *pc = path; *pc != 0; pc++ ) {
            if ( *pc == '\\' )
                  out << "\\\\";
            else
                  out << *pc;
      }
}


/* Main, process args and call yyparse to start scanning input. */
int main(int argc, char **argv)
{
      /* Paramater specification. */
      ParamCheck pc("o:enmabjkcCJVdS:M:T:F:G:vHh?-:", argc, argv);

      while ( pc.check() ) {
            switch ( pc.state ) {
            case ParamCheck::match:
                  switch ( pc.parameter ) {
                  /* Output. */
                  case 'o':
                        if ( *pc.parameterArg == 0 ) {
                              /* Complain, someone used -o "" */
                              error() << "zero length output file name" << endl;
                              exit(1);
                        }
                        else if ( outputFile != 0 ) {
                              /* Complain, two output files given. */
                              error() << "more than one output file" << endl;
                              exit(1);
                        }
                        else {
                              /* Ok, remember the output file name. */
                              outputFile = pc.parameterArg;
                        }
                        break;

                  /* Minimization, mostly hidden options. */
                  case 'e':
                        minimizeEveryOp = true;
                        break;
                  case 'n':
                        minimizeLevel = MinimizeNone;
                        break;
                  case 'm':
                        minimizeLevel = MinimizePartition2;
                        break;
                  case 'a':
                        minimizeLevel = MinimizeApprox;
                        break;
                  case 'b':
                        minimizeLevel = MinimizeStable;
                        break;
                  case 'j':
                        minimizeLevel = MinimizePartition1;
                        break;
                  case 'k':
                        minimizeLevel = MinimizePartition2;
                        break;

                  /* Output formats. */
                  case 'c':
                        outputFormat = OutCCode;
                        defExtension = ".c";
                        break;
                  case 'C':
                        outputFormat = OutCppCode;
                        defExtension = ".cpp";
                        break;
                  case 'J':
                        outputFormat = OutObjCCode;
                        defExtension = ".m";
                        break;
                  case 'V':
                        outputFormat = OutGraphvizDot;
                        defExtension = ".dot";
                        break;
//                case 'p':
//                      outputFormat = OutPrint;
//                      break;
                  case 'd':
                        outputFormat = OutDump;
                        break;

                  /* Machine spec. */
                  case 'S':
                        if ( *pc.parameterArg == 0 ) {
                              /* Complain, need a graph spec. */
                              error() << "please specify an argument to -S" << endl;
                              exit(1);
                        }
                        else if ( machineSpec != 0 ) {
                              error() << "FSM specification to generate already given" << endl;
                              exit(1);
                        }
                        else {
                              /* Ok, remember the path to the machine to generate. */
                              machineSpec = pc.parameterArg;
                        }
                        break;

                  /* Machine path. */
                  case 'M':
                        if ( *pc.parameterArg == 0 ) {
                              /* Complain, need a graph spec. */
                              error() << "please specify an argument to -M" << endl;
                              exit(1);
                        }
                        else if ( machineName != 0 ) {
                              error() << "machine to generate already given" << endl;
                              exit(1);
                        }
                        else {
                              /* Ok, remember the machine name to generate. */
                              machineName = pc.parameterArg;
                        }
                        break;
                  
                  /* Code style. */
                  case 'T':
                        if ( pc.parameterArg[0] == '0' )
                              codeStyle = GenTables;
                        else if ( pc.parameterArg[0] == '1' )
                              codeStyle = GenFTables;
                        else {
                              error() << "invalid parameter" << endl;
                              exit(1);
                        }
                        break;
                  case 'F':
                        if ( pc.parameterArg[0] == '0' )
                              codeStyle = GenFlat;
                        else if ( pc.parameterArg[0] == '1' )
                              codeStyle = GenFFlat;
                        else {
                              error() << "invalid parameter" << endl;
                              exit(1);
                        }
                        break;
                  case 'G':
                        if ( pc.parameterArg[0] == '0' )
                              codeStyle = GenGoto;
                        else if ( pc.parameterArg[0] == '1' )
                              codeStyle = GenFGoto;
                        else if ( pc.parameterArg[0] == '2' )
                              codeStyle = GenIpGoto;
                        else {
                              error() << "invalid parameter" << endl;
                              exit(1);
                        }
                        break;

                  /* Version and help. */
                  case 'v':
                        version();
                        exit(0);
                  case 'H': case 'h': case '?':
                        usage();
                        exit(0);
                  case '-':
                        if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
                              usage();
                              exit(0);
                        }
                        else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
                              version();
                              exit(0);
                        }
                        else {
                              error() << "invalid parameter" << endl;
                              exit(1);
                        }
                  }
                  break;

            case ParamCheck::invalid:
                  error() << "invalid parameter" << endl;
                  exit(1);

            case ParamCheck::noparam:
                  /* It is interpreted as an input file. */
                  if ( *pc.curArg == 0 ) {
                        error() << "zero length input file name" << endl;
                        exit(1);
                  }
                  if ( inputFile != 0 ) {
                        error() << "more than one input file" << endl;
                        exit(1);
                  }
                  /* Remember the filename. */
                  inputFile = pc.curArg;
                  break;
            }
      }

      /* If the user wants minimization at every op, but no minimization level 
       * is given, then print a warning. */
      if ( minimizeEveryOp && minimizeLevel == MinimizeNone )
            warning() << "-e given but minimization is not enabled" << endl;

      /* Look for no input file specified. */
      if ( inputFile == 0 )
            error() << "no input file" << endl;

      /* If -S is given, must be outputing -V or -d. */
      if ( machineSpec != 0 && 
                  outputFormat != OutGraphvizDot && 
                  outputFormat != OutDump &&
                  outputFormat != OutPrint )
      {
            error() << "-S may only be used with -V or -d" << endl;
      }

      /* If -M is given, must be outputing -V or -p. */
      if ( machineName != 0 && 
                  outputFormat != OutGraphvizDot && 
                  outputFormat != OutDump &&
                  outputFormat != OutPrint )
      {
            error() << "-M may only be used with -V or -d" << endl;
      }

      /* Bail on above errors. */
      if ( gblErrorCount > 0 )
            exit(1);

      /* If the output format is code and no output file name is given, then
       * make a default. */
      if ( ( outputFormat == OutCCode ||
                  outputFormat == OutCppCode ||
                  outputFormat == OutObjCCode ) &&
                  outputFile == 0 )
      {
            char *ext = findFileExtension( inputFile );
            if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
                  outputFile = fileNameFromStem( inputFile, ".h" );
            else 
                  outputFile = fileNameFromStem( inputFile, defExtension );
      }

      /* Make sure we are not writing to the same file as the input file. */
      if ( outputFile != 0 && strcmp( inputFile, outputFile  ) == 0 ) {
            error() << "output file \"" << outputFile  << 
                        "\" is the same as the input file" << endl;
      }

      /* Open the input file for reading. */
      yyin = fopen( inputFile, "rt" );
      if ( yyin == 0 ) {
            error() << "could not open " << inputFile << " for reading" << endl;
      }

      /* Bail on above errors. */
      if ( gblErrorCount > 0 )
            exit(1);

      if ( outputFile != 0 ) {
            /* Create the filter on the output and open it. */
            outFilter = new output_filter;
            outFilter->open( outputFile, ios::out|ios::trunc );
            if ( !outFilter->is_open() ) {
                  error() << "error opening " << outputFile << " for writing" << endl;
                  exit(1);
            }

            /* Open the output stream, attaching it to the filter. */
            outStream = new ostream( outFilter );
      }
      else {
            /* Writing out ot std out. */
            outStream = &cout;
      }

      if ( outputFormat == OutCCode || outputFormat == OutCppCode || outputFormat == OutObjCCode ) {
            /* Put a header on the output to indicate that the file was machine generated. */
            *outStream <<
                  "/* Automatically generated by Ragel from \"" << inputFile << "\".\n"
                  " *\n"
                  " * Parts of this file are copied from Ragel source covered by the GNU\n"
                  " * GPL. As a special exception, you may use the parts of this file copied\n"
                  " * from Ragel source without restriction. The remainder is derived from\n"
                  " * \"" << inputFile << "\" and inherits the copyright status of that file.\n"
                  " */\n\n";
      
            /* Initial line directive. */
            *outStream << "#line 1 \"";
            escapeLineDirectivePath( *outStream, inputFile );
            *outStream << "\"\n";
      }

      /* Make a parse data for the first fsm machine. */
      pd = new ParseData( inputFile );

      /* Parse the input! */
      yyparse();

      /* If we are outputting a machine path and the path was not found, then
       * report an error. */
      if ( gblErrorCount == 0 && machineSpec != 0 && !machineSpecFound )
            error() << "machine spec " << machineSpec << " not found" << endl;

      /* If writing to a file, delete the ostream, causing it to flush.
       * Standard out is flushed automatically. */
      if ( outputFile != 0 ) {
            delete outStream;
            delete outFilter;
      }

      /* Finished, final check for errors.. */
      if ( gblErrorCount > 0 ) {
            /* If we opened and output file, remove it. */
            if ( outputFile != 0 )
                  unlink( outputFile );
            exit(1);
      }
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index