Updating the changelog in the VERSION file, and version_sync.
[shapes.git] / source / sourcelocation.cc
blob015f2fd11598667c36d319c016cfcb50157101e0
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
6 * any later version.
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"
22 #include "ast.h"
23 #include "globals.h"
24 #include "utf8tools.h"
25 #include "config.h"
27 #include <cerrno>
28 #include <fstream>
29 #include <sstream>
30 #include <limits>
31 #include <iomanip>
32 #include <cstring>
34 using namespace Shapes;
36 namespace Shapes
38 namespace Ast
40 std::map< const Ast::FileID *, Ast::FileInfo *, Ast::FileIDPtrLess > theSourceFiles;
43 namespace Interaction
45 /* Make sure this is initialized before any SPECIAL file is initialized!
47 std::vector< const char * > theInMemorySources;
50 namespace Exceptions
52 class FileID_OpenError : public Exception
54 private:
55 const Ast::FileID * fileID_;
56 const char * msg_;
57 public:
58 FileID_OpenError( const Ast::FileID * fileID, const char * msg )
59 : fileID_( fileID ), msg_( msg )
60 { }
61 virtual ~FileID_OpenError( )
62 { }
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 )
77 : name_( name )
78 { }
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 )
83 { }
85 const Ast::FileID *
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( ) )
93 delete tmp;
94 return i->first;
96 const_cast< FileID * >( tmp )->strData_ = strdup( filename.c_str( ) );
97 Ast::theSourceFiles.insert( MapType::value_type( tmp, new Ast::FileInfo( filename ) ) );
98 return tmp;
101 const Ast::FileID *
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( ) )
109 delete tmp;
110 return i->first;
112 Ast::theSourceFiles.insert( MapType::value_type( tmp, new Ast::FileInfo( sourceName ) ) );
113 return tmp;
116 const Ast::FileID *
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( ) )
124 delete tmp;
125 return i->first;
127 Ast::theSourceFiles.insert( MapType::value_type( tmp, 0 ) );
128 tmp->initInfo( specialName );
129 return tmp;
132 const Ast::FileID *
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 );
144 bool
145 Ast::FileID::is_inMemory( const std::string & filename, const FileID ** fileDst )
147 if( strncmp( filename.c_str( ), INPUTLIST_PREFIX, INPUTLIST_PREFIX_LENGTH ) != 0 )
149 return 0;
151 char * end;
152 ino_t ind = strtol( filename.c_str( ) + INPUTLIST_PREFIX_LENGTH, & end, 10 );
153 if( *end != '\0' )
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 ) );
158 return false;
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 ) );
167 return false;
169 *fileDst = Ast::FileID::build_inMemory( ind, filename );
170 return true;
173 void
174 Ast::FileID::initInfo( const std::string & name ) const
176 Ast::FileInfo * res = Ast::theSourceFiles[ this ];
177 if( res == 0 )
179 Ast::theSourceFiles[ this ] = new Ast::FileInfo( name );
181 else
183 std::cerr << "Internal error. Ast::FileID::initInfo called multiple times for the file: " << name << std::endl ;
184 exit( Interaction::EXIT_INTERNAL_ERROR );
188 const char *
189 Ast::FileID::name( ) const
191 if( kind_ == INTERNAL )
193 return strData_;
195 Ast::FileInfo * res = Ast::theSourceFiles[ this ];
196 if( res == 0 )
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 ];
208 if( res == 0 )
210 std::cerr << "Internal error. Ast::FileID::expressions called before initializaiton." << std::endl ;
211 exit( Interaction::EXIT_INTERNAL_ERROR );
213 return res->topLevelNodes_;
216 bool
217 Ast::FileID::hasPosition( ) const
219 return kind_ != INTERNAL;
222 bool
223 Ast::FileID::operator < ( const Ast::FileID & other ) const
225 if( kind_ == NORMAL_FILE && other.kind_ != NORMAL_FILE )
227 return true;
229 if( kind_ != NORMAL_FILE && other.kind_ == NORMAL_FILE )
231 return false;
233 if( kind_ == NORMAL_FILE )
235 if( st_dev_ < other.st_dev_ )
237 return true;
239 if( st_dev_ > other.st_dev_ )
241 return false;
243 return st_ino_ < other.st_ino_;
245 if( kind_ == IN_MEMORY && other.kind_ != IN_MEMORY )
247 return true;
249 if( kind_ != IN_MEMORY && other.kind_ == IN_MEMORY )
251 return false;
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_;
261 std::istream *
262 Ast::FileID::open( std::ifstream * ifsMem, std::istringstream * issMem ) const
264 switch( kind_ )
266 case NORMAL_FILE:
268 ifsMem->open( strData_ );
269 if( ! ifsMem->is_open( ) )
271 throw Exceptions::FileID_OpenError( this, "Failed to open NORMAL file." );
273 return ifsMem;
275 case IN_MEMORY:
277 issMem->str( Interaction::theInMemorySources[ st_ino_ ] );
278 return issMem;
280 case SPECIAL:
281 case INTERNAL:
283 /* This is an error, see below. */
284 break;
287 throw Exceptions::FileID_OpenError( this, "FileID::open is not applicable to SPECIAL or INTERNAL files" );
290 void
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_ );
316 const Ast::FileID *
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( ) );
326 size_t
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 )
348 bool
349 Ast::SourceLocation::isUnknown( ) const
351 return file_ == SourceLocation::UNKNOWN_FILE;
354 bool
355 Ast::SourceLocation::contains( const Ast::SourceLocation & loc2 ) const
357 return
358 file_ == loc2.file_
360 ( firstLine < loc2.firstLine ||
361 ( firstLine == loc2.firstLine && firstColumn <= loc2.firstColumn ) )
363 ( lastLine > loc2.lastLine ||
364 ( lastLine == loc2.lastLine && lastColumn >= loc2.lastColumn ) )
368 std::ostream &
369 Ast::operator << ( std::ostream & os, const Ast::SourceLocation & self )
371 if( self.file_ == 0 )
373 os << "< unknown location >" ;
374 return os;
377 os << self.file_->name( ) ;
378 if( ! self.file_->hasPosition( ) )
380 return os;
382 os << ":" ;
383 if( Interaction::characterColumnInBytes )
385 if( self.firstLine == self.lastLine )
387 os << self.firstLine << "(" << self.firstColumn << "-" << self.lastColumn << ")" ;
389 else
391 os << self.firstLine << "(" << self.firstColumn << ")-" << self.lastLine << "(" << self.lastColumn << ")" ;
394 else
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( ) )
403 os << col ;
405 else
407 os << "?" ;
410 os << "-" ;
412 size_t col = self.file_->byteColumnToUTF8Column( self.lastLine, self.lastColumn );
413 if( col != std::numeric_limits< size_t >::max( ) )
415 os << col ;
417 else
419 os << "?" ;
422 os << ")" ;
424 else
426 os << self.firstLine << "(" ;
428 size_t col = self.file_->byteColumnToUTF8Column( self.firstLine, self.firstColumn );
429 if( col != std::numeric_limits< size_t >::max( ) )
431 os << col ;
433 else
435 os << "?" ;
438 os << ")-"
439 << self.lastLine << "(" ;
441 size_t col = self.file_->byteColumnToUTF8Column( self.lastLine, self.lastColumn );
442 if( col != std::numeric_limits< size_t >::max( ) )
444 os << col ;
446 else
448 os << "?" ;
451 os << ")" ;
455 return os;
458 void
459 Ast::SourceLocation::copy( std::ostream * os ) const
461 std::ifstream iFile;
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]" ;
471 return;
474 size_t line = 1;
475 for( ; line < firstLine; ++line )
477 is->ignore( std::numeric_limits< std::streamsize >::max( ), '\n' );
479 is->ignore( firstColumn, '\0' );
480 std::string tmpLine;
481 size_t col1 = firstColumn;
482 for( ; line < lastLine; ++line )
484 getline( *is, tmpLine );
485 *os << tmpLine << std::endl ;
486 col1 = 0;
488 getline( *is, tmpLine );
489 *os << tmpLine.substr( 0, lastColumn - col1 ) ;
492 Ast::Expression *
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 );
500 if( tmp != 0 )
502 return tmp;
505 return 0;
508 size_t
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;
515 if( byteCol == 0 )
517 return 0;
520 if( this != fileInCache ||
521 line != lineInCache )
523 fileInCache = this;
524 std::ifstream iFile;
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' );
531 if( is->eof( ) )
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 ) );
536 return 0;
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.
542 // if( is->eof( ) )
543 // {
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 );
547 // return 0;
548 // }
551 return byteColumnToUTF8Column( cachedLine, byteCol );
554 size_t
555 Ast::FileID::byteColumnToUTF8Column( const std::string & line, size_t byteCol )
557 size_t count = 0;
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 ) )
563 ++count;
566 return count;
569 size_t
570 Ast::FileID::UTF8ColumnTobyteColumn( size_t line, size_t utf8Col ) const
572 std::ifstream iFile;
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' );
579 if( is->eof( ) )
581 throw Exceptions::OutOfRange( "Line number is past end of file." );
583 std::string lineData;
584 getline( *is, lineData );
585 return UTF8ColumnTobyteColumn( lineData, utf8Col );
588 size_t
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 ) )
596 --utf8Col;
599 return src - line.c_str( );