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, 2013, 2014 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 /* Used to first test ( ind < 0 ) below, but I'm currently on a platform where ino_t is unsigned,
162 * so the test gives a compiler warning. Hence, rather than checking ( ind < 0 ) we now check that
163 * the ino_t is unsigned.
165 if( std::numeric_limits
< ino_t
>::is_signed
){
166 Kernel::thePostCheckErrorsList
.push_back( new Exceptions::InternalError( "Ast::FileID::is_inMemory: Incorrectly assumed that ino_t is unsigned." ) );
169 if( Interaction::theInMemorySources
.size( ) <= static_cast< size_t >( ind
) ||
170 Interaction::theInMemorySources
[ ind
] == 0 )
172 std::ostringstream msg
;
173 msg
<< "Error in error message: Inputlist index " << ind
<< " is out of range in: " << filename
;
174 Kernel::thePostCheckErrorsList
.push_back( new Exceptions::InternalError( msg
) );
177 *fileDst
= Ast::FileID::build_inMemory( ind
, filename
);
182 Ast::FileID::initInfo( const std::string
& name
) const
184 Ast::FileInfo
* res
= Ast::theSourceFiles
[ this ];
187 Ast::theSourceFiles
[ this ] = new Ast::FileInfo( name
);
191 std::cerr
<< "Internal error. Ast::FileID::initInfo called multiple times for the file: " << name
<< std::endl
;
192 exit( Interaction::EXIT_INTERNAL_ERROR
);
197 Ast::FileID::name( ) const
199 if( kind_
== INTERNAL
)
203 Ast::FileInfo
* res
= Ast::theSourceFiles
[ this ];
206 std::cerr
<< "Internal error. Ast::FileID::name called before initializaiton." << std::endl
;
207 exit( Interaction::EXIT_INTERNAL_ERROR
);
209 return res
->name_
.c_str( );
212 std::vector
< Ast::Node
* > &
213 Ast::FileID::nodes( ) const
215 Ast::FileInfo
* res
= Ast::theSourceFiles
[ this ];
218 std::cerr
<< "Internal error. Ast::FileID::nodes called before initializaiton." << std::endl
;
219 exit( Interaction::EXIT_INTERNAL_ERROR
);
221 return res
->topLevelNodes_
;
225 Ast::FileID::hasPosition( ) const
227 return kind_
!= INTERNAL
;
231 Ast::FileID::operator < ( const Ast::FileID
& other
) const
233 if( kind_
== NORMAL_FILE
&& other
.kind_
!= NORMAL_FILE
)
237 if( kind_
!= NORMAL_FILE
&& other
.kind_
== NORMAL_FILE
)
241 if( kind_
== NORMAL_FILE
)
243 if( st_dev_
< other
.st_dev_
)
247 if( st_dev_
> other
.st_dev_
)
251 return st_ino_
< other
.st_ino_
;
253 if( kind_
== IN_MEMORY
&& other
.kind_
!= IN_MEMORY
)
257 if( kind_
!= IN_MEMORY
&& other
.kind_
== IN_MEMORY
)
261 if( kind_
== IN_MEMORY
)
263 return st_ino_
< other
.st_ino_
;
265 /* We reach here if both values are SPECIAL or INTERNAL. */
266 return strData_
< other
.strData_
;
270 Ast::FileID::open( std::ifstream
* ifsMem
, std::istringstream
* issMem
) const
276 ifsMem
->open( strData_
);
277 if( ! ifsMem
->is_open( ) )
279 throw Exceptions::FileID_OpenError( this, "Failed to open NORMAL file." );
285 issMem
->str( Interaction::theInMemorySources
[ st_ino_
] );
291 /* This is an error, see below. */
295 throw Exceptions::FileID_OpenError( this, "FileID::open is not applicable to SPECIAL or INTERNAL files" );
299 Ast::FileID::setMem( const char * data
)
301 if( kind_
!= IN_MEMORY
)
303 std::cerr
<< "Internal error: FileID::setMem is only applicable to IN_MEMORY objects." << std::endl
;
304 exit( Interaction::EXIT_INTERNAL_ERROR
);
306 if( Interaction::theInMemorySources
.size( ) <= st_ino_
)
308 Interaction::theInMemorySources
.resize( st_ino_
);
310 Interaction::theInMemorySources
[ st_ino_
] = data
;
314 Ast::FileID::inMemoryIndex( ) const
316 if( kind_
!= IN_MEMORY
)
318 std::cerr
<< "Internal error: FileID::inMemoryIndex is only applicable to IN_MEMORY objects." << std::endl
;
319 exit( Interaction::EXIT_INTERNAL_ERROR
);
321 return static_cast< int >( st_ino_
);
325 Ast::FileID::build_fresh_inMemory( )
327 std::ostringstream fullName
;
328 ino_t index
= Interaction::theInMemorySources
.size( );
329 fullName
<< INPUTLIST_PREFIX
<< index
;
330 Interaction::theInMemorySources
.push_back( 0 );
331 return Ast::FileID::build_inMemory( index
, fullName
.str( ) );
335 Ast::FileID::inputlistPrefixLength( )
337 return strlen( INPUTLIST_PREFIX
);
340 Ast::SourceLocation::SourceLocation( )
341 : file_( Ast::FileID::build_internal( "*bison-uninitialized*" ) ), firstLine( 1 ), firstColumn( 0 ), lastLine( 1 ), lastColumn( 0 )
344 Ast::SourceLocation::SourceLocation( const Ast::FileID
* file
)
345 : file_( file
), firstLine( 1 ), firstColumn( 0 ), lastLine( 1 ), lastColumn( 0 )
348 Ast::SourceLocation::SourceLocation( const Ast::SourceLocation
& firstLoc
, const Ast::SourceLocation
& lastLoc
)
349 : file_( firstLoc
.file_
), firstLine( firstLoc
.firstLine
), firstColumn( firstLoc
.firstColumn
), lastLine( lastLoc
.lastLine
), lastColumn( lastLoc
.lastColumn
)
352 Ast::SourceLocation::SourceLocation( const Ast::SourceLocation
& orig
, bool dummy
)
353 : file_( orig
.file_
), firstLine( orig
.firstLine
), firstColumn( orig
.firstColumn
), lastLine( orig
.lastLine
), lastColumn( orig
.lastColumn
)
356 Ast::SourceLocation
&
357 Ast::SourceLocation::operator = ( const Ast::SourceLocation
& orig
)
360 firstLine
= orig
.firstLine
;
361 firstColumn
= orig
.firstColumn
;
362 lastLine
= orig
.lastLine
;
363 lastColumn
= orig
.lastColumn
;
368 Ast::SourceLocation::swap( SourceLocation
* other
)
370 SourceLocation
tmp( *this, bool( ) );
376 Ast::SourceLocation::isUnknown( ) const
378 return file_
== SourceLocation::UNKNOWN_FILE
;
382 Ast::SourceLocation::contains( const Ast::SourceLocation
& loc2
) const
387 ( firstLine
< loc2
.firstLine
||
388 ( firstLine
== loc2
.firstLine
&& firstColumn
<= loc2
.firstColumn
) )
390 ( lastLine
> loc2
.lastLine
||
391 ( lastLine
== loc2
.lastLine
&& lastColumn
>= loc2
.lastColumn
) )
396 Ast::operator << ( std::ostream
& os
, const Ast::SourceLocation
& self
)
398 if( self
.file_
== 0 )
400 os
<< "< unknown location >" ;
404 os
<< self
.file_
->name( ) ;
405 if( ! self
.file_
->hasPosition( ) )
410 if( Interaction::characterColumnInBytes
)
412 if( self
.firstLine
== self
.lastLine
)
414 os
<< self
.firstLine
<< "(" << self
.firstColumn
<< "-" << self
.lastColumn
<< ")" ;
418 os
<< self
.firstLine
<< "(" << self
.firstColumn
<< ")-" << self
.lastLine
<< "(" << self
.lastColumn
<< ")" ;
423 if( self
.firstLine
== self
.lastLine
)
425 os
<< self
.firstLine
<< "(" ;
427 size_t col
= self
.file_
->byteColumnToUTF8Column( self
.firstLine
, self
.firstColumn
);
428 if( col
!= std::numeric_limits
< size_t >::max( ) )
439 size_t col
= self
.file_
->byteColumnToUTF8Column( self
.lastLine
, self
.lastColumn
);
440 if( col
!= std::numeric_limits
< size_t >::max( ) )
453 os
<< self
.firstLine
<< "(" ;
455 size_t col
= self
.file_
->byteColumnToUTF8Column( self
.firstLine
, self
.firstColumn
);
456 if( col
!= std::numeric_limits
< size_t >::max( ) )
466 << self
.lastLine
<< "(" ;
468 size_t col
= self
.file_
->byteColumnToUTF8Column( self
.lastLine
, self
.lastColumn
);
469 if( col
!= std::numeric_limits
< size_t >::max( ) )
486 Ast::SourceLocation::copy( std::ostream
* os
) const
489 std::istringstream iss
;
490 std::istream
* is
= 0;
493 is
= file_
->open( & iFile
, & iss
);
495 catch( const Exceptions::FileID_OpenError
& ball
)
497 *os
<< "[UNABLE-TO-COPY-SOURCE]" ;
502 for( ; line
< firstLine
; ++line
)
504 is
->ignore( std::numeric_limits
< std::streamsize
>::max( ), '\n' );
506 is
->ignore( firstColumn
, '\0' );
508 size_t col1
= firstColumn
;
509 for( ; line
< lastLine
; ++line
)
511 getline( *is
, tmpLine
);
512 *os
<< tmpLine
<< std::endl
;
515 getline( *is
, tmpLine
);
516 *os
<< tmpLine
.substr( 0, lastColumn
- col1
) ;
520 Ast::SourceLocation::findExpression( ) const
522 typedef typeof file_
->nodes( ) ListType
;
523 ListType
& exprs
= file_
->nodes( );
524 for( ListType::iterator i
= exprs
.begin( ); i
!= exprs
.end( ); ++i
)
526 Ast::Expression
* tmp
= (*i
)->findExpressionSameFile( *this );
536 Ast::FileID::byteColumnToUTF8Column( size_t line
, size_t byteCol
) const
538 static const Ast::FileID
* fileInCache
= 0;
539 static size_t lineInCache
= 0;
540 static std::string cachedLine
;
547 if( this != fileInCache
||
548 line
!= lineInCache
)
552 std::istringstream iss
;
553 std::istream
* is
= open( & iFile
, & iss
);
554 for( lineInCache
= 1; lineInCache
< line
; ++lineInCache
)
556 is
->ignore( std::numeric_limits
< std::streamsize
>::max( ), '\n' );
560 std::ostringstream msg
;
561 msg
<< "Error in error message: Source location's line (" << line
<< ") is way beyond end of file: " << name( ) ;
562 Kernel::thePostCheckErrorsList
.push_back( new Exceptions::InternalError( msg
) );
565 getline( *is
, cachedLine
);
566 /* is->eof( ) is acceptable here, since it "just" means that there was no newline
567 * terminating the last line in the file.
571 // std::ostringstream msg;
572 // msg << "Error in error message: Source location's line (" << line << ") is one beyond end of file: " << filename ;
573 // Kernel::thePostCheckErrorsList.push_back( new Exceptions::InternalError( msg );
578 return byteColumnToUTF8Column( cachedLine
, byteCol
);
582 Ast::FileID::byteColumnToUTF8Column( const std::string
& line
, size_t byteCol
)
585 const char * end
= line
.c_str( ) + byteCol
;
586 for( const char * src
= line
.c_str( ); src
!= end
&& *src
!= '\0'; ++src
)
588 if( Helpers::utf8leadByte( *src
) )
597 Ast::FileID::UTF8ColumnTobyteColumn( size_t line
, size_t utf8Col
) const
600 std::istringstream iss
;
601 std::istream
* is
= open( & iFile
, & iss
);
602 for( size_t i
= 1; i
< line
; ++i
)
604 is
->ignore( std::numeric_limits
< std::streamsize
>::max( ), '\n' );
608 throw Exceptions::OutOfRange( "Line number is past end of file." );
610 std::string lineData
;
611 getline( *is
, lineData
);
612 return UTF8ColumnTobyteColumn( lineData
, utf8Col
);
616 Ast::FileID::UTF8ColumnTobyteColumn( const std::string
& line
, size_t utf8Col
)
618 const char * src
= line
.c_str( );
619 for( ; utf8Col
> 0 && *src
!= '\0'; ++src
)
621 if( Helpers::utf8leadByte( *src
) )
626 return src
- line
.c_str( );
630 Ast::SourceLocationFactory::SourceLocationFactory( )
633 Ast::SourceLocationFactory::~SourceLocationFactory( )
635 while( ! mem_
.empty( ) )
642 const Ast::SourceLocation
&
643 Ast::SourceLocationFactory::construct( const FileID
* file
)
645 Ast::SourceLocation
* loc
= new Ast::SourceLocation( file
);
646 mem_
.push_back( loc
);
650 const Ast::SourceLocation
&
651 Ast::SourceLocationFactory::construct_internal( const char * locationName
)
653 Ast::SourceLocation
* loc
= new Ast::SourceLocation( Ast::FileID::build_internal( locationName
) );
654 mem_
.push_back( loc
);