1 /* This file is part of Shapes.
3 * Shapes is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
8 * Shapes is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with Shapes. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright 2008, 2009 Henrik Tidefelt
19 #include "sourcelocation.h"
20 #include "charconverters.h"
21 #include "shapesexceptions.h"
24 #include "utf8tools.h"
34 using namespace Shapes
;
40 std::map
< const Ast::FileID
*, Ast::FileInfo
*, Ast::FileIDPtrLess
> theSourceFiles
;
45 /* Make sure this is initialized before any SPECIAL file is initialized!
47 std::vector
< const char * > theInMemorySources
;
52 class FileID_OpenError
: public Exception
55 const Ast::FileID
* fileID_
;
58 FileID_OpenError( const Ast::FileID
* fileID
, const char * msg
)
59 : fileID_( fileID
), msg_( msg
)
61 virtual ~FileID_OpenError( )
63 virtual void display( std::ostream
& os
) const
65 os
<< Ast::THE_UNKNOWN_LOCATION
<< locsep
<< "Low-level source location FileID problem: " << msg_
<< std::endl
;
67 virtual Interaction::ExitCode
exitCode( ) const { return Interaction::EXIT_INTERNAL_ERROR
; }
72 const char * Ast::FileID::INPUTLIST_PREFIX
= "#";
73 const size_t Ast::FileID::INPUTLIST_PREFIX_LENGTH
= Ast::FileID::inputlistPrefixLength( ); /* Don't do before Ast::SourceLocation::INPUTLIST_PREFIX is initialized! */
76 Ast::FileInfo::FileInfo( const std::string
& name
)
81 Ast::FileID::FileID( Kind kind
, dev_t st_dev
, ino_t st_ino
, const char * strData
)
82 : kind_( kind
), st_dev_( st_dev
), st_ino_( st_ino
), strData_( strData
)
86 Ast::FileID::build_stat( const struct stat
& stat
, const std::string
& filename
)
88 const FileID
* tmp
= new FileID( NORMAL_FILE
, stat
.st_dev
, stat
.st_ino
, 0 );
89 typedef typeof Ast::theSourceFiles MapType
;
90 MapType::iterator i
= Ast::theSourceFiles
.find( tmp
);
91 if( i
!= Ast::theSourceFiles
.end( ) )
96 const_cast< FileID
* >( tmp
)->strData_
= strdup( filename
.c_str( ) );
97 Ast::theSourceFiles
.insert( MapType::value_type( tmp
, new Ast::FileInfo( filename
) ) );
102 Ast::FileID::build_inMemory( ino_t index
, const std::string
& sourceName
)
104 const FileID
* tmp
= new FileID( IN_MEMORY
, 0, index
, 0 );
105 typedef typeof Ast::theSourceFiles MapType
;
106 MapType::iterator i
= Ast::theSourceFiles
.find( tmp
);
107 if( i
!= Ast::theSourceFiles
.end( ) )
112 Ast::theSourceFiles
.insert( MapType::value_type( tmp
, new Ast::FileInfo( sourceName
) ) );
117 Ast::FileID::build_special( const char * specialName
)
119 const FileID
* tmp
= new FileID( SPECIAL
, 0, 0, specialName
);
120 typedef typeof Ast::theSourceFiles MapType
;
121 MapType::iterator i
= Ast::theSourceFiles
.find( tmp
);
122 if( i
!= Ast::theSourceFiles
.end( ) )
127 Ast::theSourceFiles
.insert( MapType::value_type( tmp
, 0 ) );
128 tmp
->initInfo( specialName
);
133 Ast::FileID::build_internal( const char * locationName
)
135 /* A SPECIAL FileID has no associated FileInfo object in Ast::theSourceFiles. Instead,
136 * the name of the source location (which does not have any line or column information by definition)
137 * is stored directly in the strData_ field.
138 * For efficiency, we must not make a deep copy of locationName, so the caller must take care of memory
139 * management. In most cases locatioName is a string litteral, so then there's no problem.
141 return new FileID( INTERNAL
, 0, 0, locationName
);
145 Ast::FileID::is_inMemory( const std::string
& filename
, const FileID
** fileDst
)
147 if( strncmp( filename
.c_str( ), INPUTLIST_PREFIX
, INPUTLIST_PREFIX_LENGTH
) != 0 )
152 ino_t ind
= strtol( filename
.c_str( ) + INPUTLIST_PREFIX_LENGTH
, & end
, 10 );
155 std::ostringstream msg
;
156 msg
<< "Error in error message: Failed to extract suffix from inputlist filename: " << filename
;
157 Kernel::thePostCheckErrorsList
.push_back( new Exceptions::InternalError( msg
) );
161 if( ind
< 0 || Interaction::theInMemorySources
.size( ) <= static_cast< size_t >( ind
) ||
162 Interaction::theInMemorySources
[ ind
] == 0 )
164 std::ostringstream msg
;
165 msg
<< "Error in error message: Inputlist index " << ind
<< " is out of range in: " << filename
;
166 Kernel::thePostCheckErrorsList
.push_back( new Exceptions::InternalError( msg
) );
169 *fileDst
= Ast::FileID::build_inMemory( ind
, filename
);
174 Ast::FileID::initInfo( const std::string
& name
) const
176 Ast::FileInfo
* res
= Ast::theSourceFiles
[ this ];
179 Ast::theSourceFiles
[ this ] = new Ast::FileInfo( name
);
183 std::cerr
<< "Internal error. Ast::FileID::initInfo called multiple times for the file: " << name
<< std::endl
;
184 exit( Interaction::EXIT_INTERNAL_ERROR
);
189 Ast::FileID::name( ) const
191 if( kind_
== INTERNAL
)
195 Ast::FileInfo
* res
= Ast::theSourceFiles
[ this ];
198 std::cerr
<< "Internal error. Ast::FileID::name called before initializaiton." << std::endl
;
199 exit( Interaction::EXIT_INTERNAL_ERROR
);
201 return res
->name_
.c_str( );
204 std::vector
< Ast::Node
* > &
205 Ast::FileID::nodes( ) const
207 Ast::FileInfo
* res
= Ast::theSourceFiles
[ this ];
210 std::cerr
<< "Internal error. Ast::FileID::expressions called before initializaiton." << std::endl
;
211 exit( Interaction::EXIT_INTERNAL_ERROR
);
213 return res
->topLevelNodes_
;
217 Ast::FileID::hasPosition( ) const
219 return kind_
!= INTERNAL
;
223 Ast::FileID::operator < ( const Ast::FileID
& other
) const
225 if( kind_
== NORMAL_FILE
&& other
.kind_
!= NORMAL_FILE
)
229 if( kind_
!= NORMAL_FILE
&& other
.kind_
== NORMAL_FILE
)
233 if( kind_
== NORMAL_FILE
)
235 if( st_dev_
< other
.st_dev_
)
239 if( st_dev_
> other
.st_dev_
)
243 return st_ino_
< other
.st_ino_
;
245 if( kind_
== IN_MEMORY
&& other
.kind_
!= IN_MEMORY
)
249 if( kind_
!= IN_MEMORY
&& other
.kind_
== IN_MEMORY
)
253 if( kind_
== IN_MEMORY
)
255 return st_ino_
< other
.st_ino_
;
257 /* We reach here if both values are SPECIAL or INTERNAL. */
258 return strData_
< other
.strData_
;
262 Ast::FileID::open( std::ifstream
* ifsMem
, std::istringstream
* issMem
) const
268 ifsMem
->open( strData_
);
269 if( ! ifsMem
->is_open( ) )
271 throw Exceptions::FileID_OpenError( this, "Failed to open NORMAL file." );
277 issMem
->str( Interaction::theInMemorySources
[ st_ino_
] );
283 /* This is an error, see below. */
287 throw Exceptions::FileID_OpenError( this, "FileID::open is not applicable to SPECIAL or INTERNAL files" );
291 Ast::FileID::setMem( const char * data
)
293 if( kind_
!= IN_MEMORY
)
295 std::cerr
<< "Internal error: FileID::setMem is only applicable to IN_MEMORY objects." << std::endl
;
296 exit( Interaction::EXIT_INTERNAL_ERROR
);
298 if( Interaction::theInMemorySources
.size( ) <= st_ino_
)
300 Interaction::theInMemorySources
.resize( st_ino_
);
302 Interaction::theInMemorySources
[ st_ino_
] = data
;
306 Ast::FileID::inMemoryIndex( ) const
308 if( kind_
!= IN_MEMORY
)
310 std::cerr
<< "Internal error: FileID::inMemoryIndex is only applicable to IN_MEMORY objects." << std::endl
;
311 exit( Interaction::EXIT_INTERNAL_ERROR
);
313 return static_cast< int >( st_ino_
);
317 Ast::FileID::build_fresh_inMemory( )
319 std::ostringstream fullName
;
320 ino_t index
= Interaction::theInMemorySources
.size( );
321 fullName
<< INPUTLIST_PREFIX
<< index
;
322 Interaction::theInMemorySources
.push_back( 0 );
323 return Ast::FileID::build_inMemory( index
, fullName
.str( ) );
327 Ast::FileID::inputlistPrefixLength( )
329 return strlen( INPUTLIST_PREFIX
);
332 Ast::SourceLocation::SourceLocation( )
333 : file_( Ast::FileID::build_internal( "*bison-uninitialized*" ) ), firstLine( 1 ), firstColumn( 0 ), lastLine( 1 ), lastColumn( 0 )
336 Ast::SourceLocation::SourceLocation( const Ast::FileID
* file
)
337 : file_( file
), firstLine( 1 ), firstColumn( 0 ), lastLine( 1 ), lastColumn( 0 )
340 Ast::SourceLocation::SourceLocation( const Ast::SourceLocation
& orig
)
341 : file_( orig
.file_
), firstLine( orig
.firstLine
), firstColumn( orig
.firstColumn
), lastLine( orig
.lastLine
), lastColumn( orig
.lastColumn
)
344 Ast::SourceLocation::SourceLocation( const Ast::SourceLocation
& firstLoc
, const Ast::SourceLocation
& lastLoc
)
345 : file_( firstLoc
.file_
), firstLine( firstLoc
.firstLine
), firstColumn( firstLoc
.firstColumn
), lastLine( lastLoc
.lastLine
), lastColumn( lastLoc
.lastColumn
)
349 Ast::SourceLocation::isUnknown( ) const
351 return file_
== SourceLocation::UNKNOWN_FILE
;
355 Ast::SourceLocation::contains( const Ast::SourceLocation
& loc2
) const
360 ( firstLine
< loc2
.firstLine
||
361 ( firstLine
== loc2
.firstLine
&& firstColumn
<= loc2
.firstColumn
) )
363 ( lastLine
> loc2
.lastLine
||
364 ( lastLine
== loc2
.lastLine
&& lastColumn
>= loc2
.lastColumn
) )
369 Ast::operator << ( std::ostream
& os
, const Ast::SourceLocation
& self
)
371 if( self
.file_
== 0 )
373 os
<< "< unknown location >" ;
377 os
<< self
.file_
->name( ) ;
378 if( ! self
.file_
->hasPosition( ) )
383 if( Interaction::characterColumnInBytes
)
385 if( self
.firstLine
== self
.lastLine
)
387 os
<< self
.firstLine
<< "(" << self
.firstColumn
<< "-" << self
.lastColumn
<< ")" ;
391 os
<< self
.firstLine
<< "(" << self
.firstColumn
<< ")-" << self
.lastLine
<< "(" << self
.lastColumn
<< ")" ;
396 if( self
.firstLine
== self
.lastLine
)
398 os
<< self
.firstLine
<< "(" ;
400 size_t col
= self
.file_
->byteColumnToUTF8Column( self
.firstLine
, self
.firstColumn
);
401 if( col
!= std::numeric_limits
< size_t >::max( ) )
412 size_t col
= self
.file_
->byteColumnToUTF8Column( self
.lastLine
, self
.lastColumn
);
413 if( col
!= std::numeric_limits
< size_t >::max( ) )
426 os
<< self
.firstLine
<< "(" ;
428 size_t col
= self
.file_
->byteColumnToUTF8Column( self
.firstLine
, self
.firstColumn
);
429 if( col
!= std::numeric_limits
< size_t >::max( ) )
439 << self
.lastLine
<< "(" ;
441 size_t col
= self
.file_
->byteColumnToUTF8Column( self
.lastLine
, self
.lastColumn
);
442 if( col
!= std::numeric_limits
< size_t >::max( ) )
459 Ast::SourceLocation::copy( std::ostream
* os
) const
462 std::istringstream iss
;
463 std::istream
* is
= 0;
466 is
= file_
->open( & iFile
, & iss
);
468 catch( const Exceptions::FileID_OpenError
& ball
)
470 *os
<< "[UNABLE-TO-COPY-SOURCE]" ;
475 for( ; line
< firstLine
; ++line
)
477 is
->ignore( std::numeric_limits
< std::streamsize
>::max( ), '\n' );
479 is
->ignore( firstColumn
, '\0' );
481 size_t col1
= firstColumn
;
482 for( ; line
< lastLine
; ++line
)
484 getline( *is
, tmpLine
);
485 *os
<< tmpLine
<< std::endl
;
488 getline( *is
, tmpLine
);
489 *os
<< tmpLine
.substr( 0, lastColumn
- col1
) ;
493 Ast::SourceLocation::findExpression( ) const
495 typedef typeof file_
->nodes( ) ListType
;
496 ListType
& exprs
= file_
->nodes( );
497 for( ListType::iterator i
= exprs
.begin( ); i
!= exprs
.end( ); ++i
)
499 Ast::Expression
* tmp
= (*i
)->findExpressionSameFile( *this );
509 Ast::FileID::byteColumnToUTF8Column( size_t line
, size_t byteCol
) const
511 static const Ast::FileID
* fileInCache
= 0;
512 static size_t lineInCache
= 0;
513 static std::string cachedLine
;
520 if( this != fileInCache
||
521 line
!= lineInCache
)
525 std::istringstream iss
;
526 std::istream
* is
= open( & iFile
, & iss
);
527 for( lineInCache
= 1; lineInCache
< line
; ++lineInCache
)
529 is
->ignore( std::numeric_limits
< std::streamsize
>::max( ), '\n' );
533 std::ostringstream msg
;
534 msg
<< "Error in error message: Source location's line (" << line
<< ") is way beyond end of file: " << name( ) ;
535 Kernel::thePostCheckErrorsList
.push_back( new Exceptions::InternalError( msg
) );
538 getline( *is
, cachedLine
);
539 /* is->eof( ) is acceptable here, since it "just" means that there was no newline
540 * terminating the last line in the file.
544 // std::ostringstream msg;
545 // msg << "Error in error message: Source location's line (" << line << ") is one beyond end of file: " << filename ;
546 // Kernel::thePostCheckErrorsList.push_back( new Exceptions::InternalError( msg );
551 return byteColumnToUTF8Column( cachedLine
, byteCol
);
555 Ast::FileID::byteColumnToUTF8Column( const std::string
& line
, size_t byteCol
)
558 const char * end
= line
.c_str( ) + byteCol
;
559 for( const char * src
= line
.c_str( ); src
!= end
&& *src
!= '\0'; ++src
)
561 if( Helpers::utf8leadByte( *src
) )
570 Ast::FileID::UTF8ColumnTobyteColumn( size_t line
, size_t utf8Col
) const
573 std::istringstream iss
;
574 std::istream
* is
= open( & iFile
, & iss
);
575 for( size_t i
= 1; i
< line
; ++i
)
577 is
->ignore( std::numeric_limits
< std::streamsize
>::max( ), '\n' );
581 throw Exceptions::OutOfRange( "Line number is past end of file." );
583 std::string lineData
;
584 getline( *is
, lineData
);
585 return UTF8ColumnTobyteColumn( lineData
, utf8Col
);
589 Ast::FileID::UTF8ColumnTobyteColumn( const std::string
& line
, size_t utf8Col
)
591 const char * src
= line
.c_str( );
592 for( ; utf8Col
> 0 && *src
!= '\0'; ++src
)
594 if( Helpers::utf8leadByte( *src
) )
599 return src
- line
.c_str( );