Line directives need to use the fileName stored in the InputLoc stuctures from
[external/ragel.git] / ragel / rlscan.rl
index d450fe1..3c325c3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2006-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ *  Copyright 2006-2007 Adrian Thurston <thurston@complang.org>
  */
 
 /*  This file is part of Ragel.
@@ -25,6 +25,7 @@
 
 #include "ragel.h"
 #include "rlscan.h"
+#include "inputdata.h"
 
 //#define LOG_TOKENS
 
@@ -41,6 +42,12 @@ enum InlineBlockType
        SemiTerminated
 };
 
+#ifdef _WIN32
+#define PATH_SEP '\\'
+#else
+#define PATH_SEP '/'
+#endif
+
 
 /*
  * The Scanner for Importing
@@ -141,7 +148,7 @@ void Scanner::flushImport()
        }
 }
 
-void Scanner::directToParser( Parser *toParser, char *tokFileName, int tokLine, 
+void Scanner::directToParser( Parser *toParser, const char *tokFileName, int tokLine, 
                int tokColumn, int type, char *tokdata, int toklen )
 {
        InputLoc loc;
@@ -196,7 +203,7 @@ void Scanner::pass()
        /* If no errors and we are at the bottom of the include stack (the
         * source file listed on the command line) then write out the data. */
        if ( includeDepth == 0 && machineSpec == 0 && machineName == 0 )
-               xmlEscapeHost( output, ts, te-ts );
+               id.inputItems.tail->data.write( ts, te-ts );
 }
 
 /*
@@ -236,15 +243,17 @@ ostream &Scanner::scan_error()
 {
        /* Maintain the error count. */
        gblErrorCount += 1;
-       cerr << fileName << ":" << line << ":" << column << ": ";
+       cerr << makeInputLoc( fileName, line, column ) << ": ";
        return cerr;
 }
 
-bool Scanner::recursiveInclude( char *inclFileName, char *inclSectionName )
+/* An approximate check for duplicate includes. Due to aliasing of files it's
+ * possible for duplicates to creep in. */
+bool Scanner::duplicateInclude( char *inclFileName, char *inclSectionName )
 {
-       for ( IncludeStack::Iter si = includeStack; si.lte(); si++ ) {
-               if ( strcmp( si->fileName, inclFileName ) == 0 &&
-                               strcmp( si->sectionName, inclSectionName ) == 0 )
+       for ( IncludeHistory::Iter hi = parser->includeHistory; hi.lte(); hi++ ) {
+               if ( strcmp( hi->fileName, inclFileName ) == 0 &&
+                               strcmp( hi->sectionName, inclSectionName ) == 0 )
                {
                        return true;
                }
@@ -262,6 +271,107 @@ void Scanner::updateCol()
        lastnl = 0;
 }
 
+void Scanner::handleMachine()
+{
+       /* Assign a name to the machine. */
+       char *machine = word;
+
+       if ( !importMachines && inclSectionTarg == 0 ) {
+               ignoreSection = false;
+
+               ParserDictEl *pdEl = id.parserDict.find( machine );
+               if ( pdEl == 0 ) {
+                       pdEl = new ParserDictEl( machine );
+                       pdEl->value = new Parser( fileName, machine, sectionLoc );
+                       pdEl->value->init();
+                       id.parserDict.insert( pdEl );
+                       id.parserList.append( pdEl->value );
+               }
+
+               parser = pdEl->value;
+       }
+       else if ( !importMachines && strcmp( inclSectionTarg, machine ) == 0 ) {
+               /* found include target */
+               ignoreSection = false;
+               parser = inclToParser;
+       }
+       else {
+               /* ignoring section */
+               ignoreSection = true;
+               parser = 0;
+       }
+}
+
+void Scanner::handleInclude()
+{
+       if ( active() ) {
+               char *inclSectionName = word;
+               char **includeChecks = 0;
+
+               /* Implement defaults for the input file and section name. */
+               if ( inclSectionName == 0 )
+                       inclSectionName = parser->sectionName;
+
+               if ( lit != 0 )
+                       includeChecks = makeIncludePathChecks( fileName, lit, lit_len );
+               else {
+                       char *test = new char[strlen(fileName)+1];
+                       strcpy( test, fileName );
+
+                       includeChecks = new char*[2];
+
+                       includeChecks[0] = test;
+                       includeChecks[1] = 0;
+               }
+
+               long found = 0;
+               ifstream *inFile = tryOpenInclude( includeChecks, found );
+               if ( inFile == 0 ) {
+                       scan_error() << "include: failed to locate file" << endl;
+                       char **tried = includeChecks;
+                       while ( *tried != 0 )
+                               scan_error() << "include: attempted: \"" << *tried++ << '\"' << endl;
+               }
+               else {
+                       /* Don't include anything that's already been included. */
+                       if ( !duplicateInclude( includeChecks[found], inclSectionName ) ) {
+                               parser->includeHistory.append( IncludeHistoryItem( 
+                                               includeChecks[found], inclSectionName ) );
+
+                               Scanner scanner( id, includeChecks[found], *inFile, parser,
+                                               inclSectionName, includeDepth+1, false );
+                               scanner.do_scan( );
+                               delete inFile;
+                       }
+               }
+       }
+}
+
+void Scanner::handleImport()
+{
+       if ( active() ) {
+               char **importChecks = makeIncludePathChecks( fileName, lit, lit_len );
+
+               /* Open the input file for reading. */
+               long found = 0;
+               ifstream *inFile = tryOpenInclude( importChecks, found );
+               if ( inFile == 0 ) {
+                       scan_error() << "import: could not open import file " <<
+                                       "for reading" << endl;
+                       char **tried = importChecks;
+                       while ( *tried != 0 )
+                               scan_error() << "import: attempted: \"" << *tried++ << '\"' << endl;
+               }
+
+               Scanner scanner( id, importChecks[found], *inFile, parser,
+                               0, includeDepth+1, true );
+               scanner.do_scan( );
+               scanner.importToken( 0, 0, 0 );
+               scanner.flushImport();
+               delete inFile;
+       }
+}
+
 %%{
        machine section_parse;
 
@@ -277,80 +387,14 @@ void Scanner::updateCol()
        action import_err { scan_error() << "bad import statement" << endl; }
        action write_err { scan_error() << "bad write statement" << endl; }
 
-       action handle_machine
-       {
-               /* Assign a name to the machine. */
-               char *machine = word;
-
-               if ( !importMachines && inclSectionTarg == 0 ) {
-                       ignoreSection = false;
-
-                       ParserDictEl *pdEl = parserDict.find( machine );
-                       if ( pdEl == 0 ) {
-                               pdEl = new ParserDictEl( machine );
-                               pdEl->value = new Parser( fileName, machine, sectionLoc );
-                               pdEl->value->init();
-                               parserDict.insert( pdEl );
-                       }
-
-                       parser = pdEl->value;
-               }
-               else if ( !importMachines && strcmp( inclSectionTarg, machine ) == 0 ) {
-                       /* found include target */
-                       ignoreSection = false;
-                       parser = inclToParser;
-               }
-               else {
-                       /* ignoring section */
-                       ignoreSection = true;
-                       parser = 0;
-               }
-       }
+       action handle_machine { handleMachine(); }
+       action handle_include { handleInclude(); }
+       action handle_import { handleImport(); }
 
        machine_stmt =
                ( KW_Machine TK_Word @store_word ';' ) @handle_machine
                <>err mach_err <>eof mach_err;
 
-       action handle_include
-       {
-               if ( active() ) {
-                       char *inclSectionName = word;
-                       char *inclFileName = 0;
-
-                       /* Implement defaults for the input file and section name. */
-                       if ( inclSectionName == 0 )
-                               inclSectionName = parser->sectionName;
-
-                       if ( lit != 0 ) 
-                               inclFileName = prepareFileName( lit, lit_len );
-                       else
-                               inclFileName = fileName;
-
-                       /* Check for a recursive include structure. Add the current file/section
-                        * name then check if what we are including is already in the stack. */
-                       includeStack.append( IncludeStackItem( fileName, parser->sectionName ) );
-
-                       if ( recursiveInclude( inclFileName, inclSectionName ) )
-                               scan_error() << "include: this is a recursive include operation" << endl;
-                       else {
-                               /* Open the input file for reading. */
-                               ifstream *inFile = new ifstream( inclFileName );
-                               if ( ! inFile->is_open() ) {
-                                       scan_error() << "include: could not open " << 
-                                                       inclFileName << " for reading" << endl;
-                               }
-
-                               Scanner scanner( inclFileName, *inFile, output, parser,
-                                               inclSectionName, includeDepth+1, false );
-                               scanner.do_scan( );
-                               delete inFile;
-                       }
-
-                       /* Remove the last element (len-1) */
-                       includeStack.remove( -1 );
-               }
-       }
-
        include_names = (
                TK_Word @store_word ( TK_Literal @store_lit )? |
                TK_Literal @store_lit
@@ -360,27 +404,6 @@ void Scanner::updateCol()
                ( KW_Include include_names ';' ) @handle_include
                <>err incl_err <>eof incl_err;
 
-       action handle_import
-       {
-               if ( active() ) {
-                       char *importFileName = prepareFileName( lit, lit_len );
-
-                       /* Open the input file for reading. */
-                       ifstream *inFile = new ifstream( importFileName );
-                       if ( ! inFile->is_open() ) {
-                               scan_error() << "import: could not open " << 
-                                               importFileName << " for reading" << endl;
-                       }
-
-                       Scanner scanner( importFileName, *inFile, output, parser,
-                                       0, includeDepth+1, true );
-                       scanner.do_scan( );
-                       scanner.importToken( 0, 0, 0 );
-                       scanner.flushImport();
-                       delete inFile;
-               }
-       }
-
        import_stmt =
                ( KW_Import TK_Literal @store_lit ';' ) @handle_import
                <>err import_err <>eof import_err;
@@ -388,24 +411,26 @@ void Scanner::updateCol()
        action write_command
        {
                if ( active() && machineSpec == 0 && machineName == 0 ) {
-                       output << "<write"
-                                       " def_name=\"" << parser->sectionName << "\""
-                                       " line=\"" << line << "\""
-                                       " col=\"" << column << "\""
-                                       ">";
+                       InputItem *inputItem = new InputItem;
+                       inputItem->type = InputItem::Write;
+                       inputItem->loc.line = line;
+                       inputItem->loc.col = column;
+                       inputItem->name = parser->sectionName;
+                       inputItem->pd = parser->pd;
+                       id.inputItems.append( inputItem );
                }
        }
 
        action write_arg
        {
                if ( active() && machineSpec == 0 && machineName == 0 )
-                       output << "<arg>" << tokdata << "</arg>";
+                       id.inputItems.tail->writeArgs.append( strdup(tokdata) );
        }
 
        action write_close
        {
                if ( active() && machineSpec == 0 && machineName == 0 )
-                       output << "</write>\n";
+                       id.inputItems.tail->writeArgs.append( 0 );
        }
 
        write_stmt =
@@ -460,7 +485,6 @@ void Scanner::token( int type, char *start, char *end )
 void Scanner::processToken( int type, char *tokdata, int toklen )
 {
        int *p, *pe, *eof;
-       
 
        if ( type < 0 )
                p = pe = eof = 0;
@@ -486,14 +510,9 @@ void Scanner::startSection( )
 {
        parserExistsError = false;
 
-       if ( includeDepth == 0 ) {
-               if ( machineSpec == 0 && machineName == 0 )
-                       output << "</host>\n";
-       }
-
        sectionLoc.fileName = fileName;
        sectionLoc.line = line;
-       sectionLoc.col = 0;
+       sectionLoc.col = column;
 }
 
 void Scanner::endSection( )
@@ -506,7 +525,7 @@ void Scanner::endSection( )
                InputLoc loc;
                loc.fileName = fileName;
                loc.line = line;
-               loc.col = 0;
+               loc.col = column;
 
                parser->token( loc, TK_EndSection, 0, 0 );
        }
@@ -515,11 +534,89 @@ void Scanner::endSection( )
                if ( machineSpec == 0 && machineName == 0 ) {
                        /* The end section may include a newline on the end, so
                         * we use the last line, which will count the newline. */
-                       output << "<host line=\"" << line << "\">";
+                       InputItem *inputItem = new InputItem;
+                       inputItem->type = InputItem::HostData;
+                       inputItem->loc.line = line;
+                       inputItem->loc.col = column;
+                       id.inputItems.append( inputItem );
                }
        }
 }
 
+bool isAbsolutePath( const char *path )
+{
+#ifdef _WIN32
+       return isalpha( path[0] ) && path[1] == ':' && path[2] == '\\';
+#else
+       return path[0] == '/';
+#endif
+}
+
+char **Scanner::makeIncludePathChecks( const char *thisFileName, 
+               const char *fileName, int fnlen )
+{
+       char **checks = new char*[2];
+       long nextCheck = 0;
+
+       bool caseInsensitive = false;
+       long length = 0;
+       char *data = prepareLitString( InputLoc(), fileName, fnlen, 
+                       length, caseInsensitive );
+
+       /* Absolute path? */
+       if ( isAbsolutePath( data ) )
+               checks[nextCheck++] = data;
+       else {
+               /* Search from the the location of the current file. */
+               const char *lastSlash = strrchr( thisFileName, PATH_SEP );
+               if ( lastSlash == 0 )
+                       checks[nextCheck++] = data;
+               else {
+                       long givenPathLen = (lastSlash - thisFileName) + 1;
+                       long checklen = givenPathLen + length;
+                       char *check = new char[checklen+1];
+                       memcpy( check, thisFileName, givenPathLen );
+                       memcpy( check+givenPathLen, data, length );
+                       check[checklen] = 0;
+                       checks[nextCheck++] = check;
+               }
+
+               /* Search from the include paths given on the command line. */
+               for ( ArgsVector::Iter incp = id.includePaths; incp.lte(); incp++ ) {
+                       long pathLen = strlen( *incp );
+                       long checkLen = pathLen + 1 + length;
+                       char *check = new char[checkLen+1];
+                       memcpy( check, *incp, pathLen );
+                       check[pathLen] = PATH_SEP;
+                       memcpy( check+pathLen+1, data, length );
+                       check[checkLen] = 0;
+                       checks[nextCheck++] = check;
+               }
+       }
+
+       checks[nextCheck] = 0;
+       return checks;
+}
+
+ifstream *Scanner::tryOpenInclude( char **pathChecks, long &found )
+{
+       char **check = pathChecks;
+       ifstream *inFile = new ifstream;
+       
+       while ( *check != 0 ) {
+               inFile->open( *check );
+               if ( inFile->is_open() ) {
+                       found = check - pathChecks;
+                       return inFile;
+               }
+               check += 1;
+       }
+
+       found = -1;
+       delete inFile;
+       return 0;
+}
+
 %%{
        machine rlscan;
 
@@ -836,6 +933,7 @@ void Scanner::endSection( )
 
        # Parser definitions. 
        parser_def := |*
+               'length_cond' => { token( KW_Length ); };
                'machine' => { token( KW_Machine ); };
                'include' => { token( KW_Include ); };
                'import' => { token( KW_Import ); };