tizen 2.3.1 release
[external/ragel.git] / ragel / main.cpp
index 51e0a29..2b54b51 100644 (file)
@@ -54,7 +54,7 @@
 #include "vector.h"
 #include "version.h"
 #include "common.h"
-#include "xmlparse.h"
+#include "inputdata.h"
 
 using std::istream;
 using std::ostream;
@@ -77,28 +77,20 @@ bool machineSpecFound = false;
 bool wantDupsRemoved = true;
 
 bool printStatistics = false;
-bool frontendOnly = false;
+bool generateXML = false;
 bool generateDot = false;
 
 /* Target language and output style. */
-CodeStyleEnum codeStyle = GenTables;
+CodeStyle codeStyle = GenTables;
 
 int numSplitPartitions = 0;
 bool noLineDirectives = false;
 
 bool displayPrintables = false;
-bool graphvizDone = false;
 
 /* Target ruby impl */
 RubyImplEnum rubyImpl = MRI;
 
-ArgsVector includePaths;
-
-istream *inStream = 0;
-ostream *outStream = 0;
-output_filter *outFilter = 0;
-const char *outputFileName = 0;
-
 /* Print a summary of the options. */
 void usage()
 {
@@ -124,25 +116,26 @@ void usage()
 "   -x                   Run the frontend only: emit XML intermediate format\n"
 "   -V                   Generate a dot file for Graphviz\n"
 "   -p                   Display printable characters on labels\n"
-"   -S <spec>            FSM specification to output (for rlgen-dot)\n"
-"   -M <machine>         Machine definition/instantiation to output (for rlgen-dot)\n"
+"   -S <spec>            FSM specification to output (for graphviz output)\n"
+"   -M <machine>         Machine definition/instantiation to output (for graphviz output)\n"
 "host language:\n"
 "   -C                   The host language is C, C++, Obj-C or Obj-C++ (default)\n"
 "   -D                   The host language is D\n"
 "   -J                   The host language is Java\n"
 "   -R                   The host language is Ruby\n"
 "   -A                   The host language is C#\n"
-"line direcives: (C/D/C# only)\n"
+"line direcives: (C/D/C#)\n"
 "   -L                   Inhibit writing of #line directives\n"
-"code style: (C/Ruby/C# only)\n"
+"code style: (C/D/Java/Ruby/C#)\n"
 "   -T0                  Table driven FSM (default)\n"
+"code style: (C/D/Ruby/C#)\n"
 "   -T1                  Faster table driven FSM\n"
 "   -F0                  Flat table driven FSM\n"
 "   -F1                  Faster flat table-driven FSM\n"
-"code style: (C/C# only)\n"
+"code style: (C/D/C#)\n"
 "   -G0                  Goto-driven FSM\n"
 "   -G1                  Faster goto-driven FSM\n"
-"code style: (C only)\n"
+"code style: (C/D)\n"
 "   -G2                  Really fast goto-driven FSM\n"
 "   -P<N>                N-Way Split really fast goto-driven FSM\n"
        ;       
@@ -154,7 +147,7 @@ void usage()
 void version()
 {
        cout << "Ragel State Machine Compiler version " VERSION << " " PUBDATE << endl <<
-                       "Copyright (c) 2001-2008 by Adrian Thurston" << endl;
+                       "Copyright (c) 2001-2009 by Adrian Thurston" << endl;
        exit(0);
 }
 
@@ -222,7 +215,7 @@ void escapeLineDirectivePath( std::ostream &out, char *path )
        }
 }
 
-void processArgs( int argc, const char **argv, const char *&inputFileName )
+void processArgs( int argc, const char **argv, InputData &id )
 {
        ParamCheck pc("xo:dnmleabjkS:M:I:CDJRAvHh?-:sT:F:G:P:LpV", argc, argv);
 
@@ -237,18 +230,18 @@ void processArgs( int argc, const char **argv, const char *&inputFileName )
                                break;
 
                        case 'x':
-                               frontendOnly = true;
+                               generateXML = true;
                                break;
 
                        /* Output. */
                        case 'o':
                                if ( *pc.paramArg == 0 )
                                        error() << "a zero length output file name was given" << endl;
-                               else if ( outputFileName != 0 )
+                               else if ( id.outputFileName != 0 )
                                        error() << "more than one output file name was given" << endl;
                                else {
                                        /* Ok, remember the output file name. */
-                                       outputFileName = pc.paramArg;
+                                       id.outputFileName = pc.paramArg;
                                }
                                break;
 
@@ -311,7 +304,7 @@ void processArgs( int argc, const char **argv, const char *&inputFileName )
                                if ( *pc.paramArg == 0 )
                                        error() << "please specify an argument to -I" << endl;
                                else {
-                                       includePaths.append( pc.paramArg );
+                                       id.includePaths.append( pc.paramArg );
                                }
                                break;
 
@@ -343,16 +336,17 @@ void processArgs( int argc, const char **argv, const char *&inputFileName )
                                printStatistics = true;
                                break;
                        case '-': {
-                               char *eq = strchr( pc.paramArg, '=' );
+                               char *arg = strdup( pc.paramArg );
+                               char *eq = strchr( arg, '=' );
 
                                if ( eq != 0 )
                                        *eq++ = 0;
 
-                               if ( strcmp( pc.paramArg, "help" ) == 0 )
+                               if ( strcmp( arg, "help" ) == 0 )
                                        usage();
-                               else if ( strcmp( pc.paramArg, "version" ) == 0 )
+                               else if ( strcmp( arg, "version" ) == 0 )
                                        version();
-                               else if ( strcmp( pc.paramArg, "error-format" ) == 0 ) {
+                               else if ( strcmp( arg, "error-format" ) == 0 ) {
                                        if ( eq == 0 )
                                                error() << "expecting '=value' for error-format" << endl;
                                        else if ( strcmp( eq, "gnu" ) == 0 )
@@ -362,12 +356,13 @@ void processArgs( int argc, const char **argv, const char *&inputFileName )
                                        else
                                                error() << "invalid value for error-format" << endl;
                                }
-                               else if ( strcmp( pc.paramArg, "rbx" ) == 0 )
+                               else if ( strcmp( arg, "rbx" ) == 0 )
                                        rubyImpl = Rubinius;
                                else {
                                        error() << "--" << pc.paramArg << 
                                                        " is an invalid argument" << endl;
                                }
+                               free( arg );
                                break;
                        }
 
@@ -430,110 +425,96 @@ void processArgs( int argc, const char **argv, const char *&inputFileName )
                        /* It is interpreted as an input file. */
                        if ( *pc.curArg == 0 )
                                error() << "a zero length input file name was given" << endl;
-                       else if ( inputFileName != 0 )
+                       else if ( id.inputFileName != 0 )
                                error() << "more than one input file name was given" << endl;
                        else {
                                /* OK, Remember the filename. */
-                               inputFileName = pc.curArg;
+                               id.inputFileName = pc.curArg;
                        }
                        break;
                }
        }
 }
 
-void process( const char *inputFileName, const char *intermed )
+void process( InputData &id )
 {
-       const char *xmlFileName = intermed;
-       bool wantComplete = true;
-       bool outputActive = true;
-
        /* Open the input file for reading. */
-       assert( inputFileName != 0 );
-       ifstream *inFile = new ifstream( inputFileName );
+       assert( id.inputFileName != 0 );
+       ifstream *inFile = new ifstream( id.inputFileName );
        if ( ! inFile->is_open() )
-               error() << "could not open " << inputFileName << " for reading" << endp;
+               error() << "could not open " << id.inputFileName << " for reading" << endp;
 
        /* Used for just a few things. */
        std::ostringstream hostData;
 
-       if ( machineSpec == 0 && machineName == 0 )
-               hostData << "<host line=\"1\" col=\"1\">";
+       /* Make the first input item. */
+       InputItem *firstInputItem = new InputItem;
+       firstInputItem->type = InputItem::HostData;
+       firstInputItem->loc.fileName = id.inputFileName;
+       firstInputItem->loc.line = 1;
+       firstInputItem->loc.col = 1;
+       id.inputItems.append( firstInputItem );
 
-       Scanner scanner( inputFileName, *inFile, hostData, 0, 0, 0, false );
+       Scanner scanner( id, id.inputFileName, *inFile, 0, 0, 0, false );
        scanner.do_scan();
 
        /* Finished, final check for errors.. */
        if ( gblErrorCount > 0 )
                exit(1);
-       
+
        /* Now send EOF to all parsers. */
-       terminateAllParsers();
+       id.terminateAllParsers();
 
-       /* Finished, final check for errors.. */
+       /* Bail on above error. */
        if ( gblErrorCount > 0 )
                exit(1);
 
-       if ( machineSpec == 0 && machineName == 0 )
-               hostData << "</host>\n";
-
-       /* Open the XML file for writing. */
-       ostream *xmlOutFile = new ofstream( xmlFileName );
-
-       /* Open the XML file for reading. */
-       ifstream *xmlInFile = new ifstream( xmlFileName );
-       if ( ! xmlInFile->is_open() )
-               error() << "could not open " << xmlFileName << " for reading" << endl;
+       /* Locate the backend program */
+       /* Compiles machines. */
+       id.prepareMachineGen();
 
-       /* Bail on above error. */
        if ( gblErrorCount > 0 )
                exit(1);
 
-       /* Locate the backend program */
-       if ( generateDot ) {
-               wantComplete = false;
-               outputActive = false;
-       }
-
-       XmlScanner xmlScanner( xmlFileName, *xmlInFile );
-       XmlParser xmlParser( xmlFileName, outputActive, wantComplete );
-       xmlParser.init();
+       id.makeOutputStream();
 
-       /* Write the machines, then the surrounding code. */
-       writeMachines( *xmlOutFile, hostData.str(), inputFileName, xmlParser );
+       /* Generates the reduced machine, which we use to write output. */
+       if ( !generateXML ) {
+               id.generateReduced();
 
-       /* Close the input and the intermediate file. */
-       delete xmlOutFile;
-       delete inFile;
+               if ( gblErrorCount > 0 )
+                       exit(1);
+       }
 
-       /* Bail on above error. */
+       id.verifyWritesHaveData();
        if ( gblErrorCount > 0 )
                exit(1);
 
-       xml_parse( *xmlInFile, xmlFileName, 
-               outputActive, wantComplete,
-               xmlScanner, xmlParser );
+       /*
+        * From this point on we should not be reporting any errors.
+        */
+
+       id.openOutput();
+       id.writeOutput();
+
+       /* Close the input and the intermediate file. */
+       delete inFile;
 
        /* If writing to a file, delete the ostream, causing it to flush.
         * Standard out is flushed automatically. */
-       if ( outputFileName != 0 ) {
-               delete outStream;
-               delete outFilter;
+       if ( id.outputFileName != 0 ) {
+               delete id.outStream;
+               delete id.outFilter;
        }
 
-       /* Finished, final check for errors.. */
-       if ( gblErrorCount > 0 ) {
-               /* If we opened an output file, remove it. */
-               if ( outputFileName != 0 )
-                       unlink( outputFileName );
-               exit(1);
-       }
+       assert( gblErrorCount == 0 );
 }
 
 char *makeIntermedTemplate( const char *baseFileName )
 {
        char *result = 0;
        const char *templ = "ragel-XXXXXX.xml";
-       char *lastSlash = strrchr( baseFileName, '/' );
+       const char *lastSlash = strrchr( baseFileName, '/' );
        if ( lastSlash == 0 ) {
                result = new char[strlen(templ)+1];
                strcpy( result, templ );
@@ -547,71 +528,16 @@ char *makeIntermedTemplate( const char *baseFileName )
        return result;
 };
 
-const char *openIntermed( const char *inputFileName, const char *outputFileName )
-{
-       srand(time(0));
-       const char *result = 0;
-
-       /* Which filename do we use as the base? */
-       const char *baseFileName = outputFileName != 0 ? outputFileName : inputFileName;
-
-       /* The template for the intermediate file name. */
-       const char *intermedFileName = makeIntermedTemplate( baseFileName );
-
-       /* Randomize the name and try to open. */
-       char fnChars[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-       char *firstX = strrchr( intermedFileName, 'X' ) - 5;
-       for ( int tries = 0; tries < 20; tries++ ) {
-               /* Choose a random name. */
-               for ( int x = 0; x < 6; x++ )
-                       firstX[x] = fnChars[rand() % 52];
-
-               /* Try to open the file. */
-               int fd = ::open( intermedFileName, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR|S_IWUSR );
-
-               if ( fd > 0 ) {
-                       /* Success. Close the file immediately and return the name for use
-                        * by the child processes. */
-                       ::close( fd );
-                       result = intermedFileName;
-                       break;
-               }
-
-               if ( errno == EACCES ) {
-                       error() << "failed to open temp file " << intermedFileName << 
-                                       ", access denied" << endp;
-               }
-       }
-
-       if ( result == 0 )
-               error() << "abnormal error: cannot find unique name for temp file" << endp;
-
-       return result;
-}
-
-
-void cleanExit( const char *intermed, int status )
-{
-       unlink( intermed );
-       exit( status );
-}
-
 /* Main, process args and call yyparse to start scanning input. */
 int main( int argc, const char **argv )
 {
-       const char *inputFileName = 0;
-       processArgs( argc, argv, inputFileName );
-
-       /* If -M or -S are given and we're not generating a dot file then invoke
-        * the frontend. These options are not useful with code generators. */
-       if ( machineName != 0 || machineSpec != 0 ) {
-               if ( !generateDot )
-                       frontendOnly = true;
-       }
+       InputData id;
+
+       processArgs( argc, argv, id );
 
        /* Require an input file. If we use standard in then we won't have a file
         * name on which to base the output. */
-       if ( inputFileName == 0 )
+       if ( id.inputFileName == 0 )
                error() << "no input file given" << endl;
 
        /* Bail on argument processing errors. */
@@ -619,18 +545,14 @@ int main( int argc, const char **argv )
                exit(1);
 
        /* Make sure we are not writing to the same file as the input file. */
-       if ( inputFileName != 0 && outputFileName != 0 && 
-                       strcmp( inputFileName, outputFileName  ) == 0 )
+       if ( id.inputFileName != 0 && id.outputFileName != 0 && 
+                       strcmp( id.inputFileName, id.outputFileName  ) == 0 )
        {
-               error() << "output file \"" << outputFileName  << 
+               error() << "output file \"" << id.outputFileName  << 
                                "\" is the same as the input file" << endp;
        }
 
-       const char *intermed = openIntermed( inputFileName, outputFileName );
-       process( inputFileName, intermed );
-
-       /* Clean up the intermediate. */
-       cleanExit( intermed, 0 );
+       process( id );
 
        return 0;
 }