Update procedures
[shapes.git] / source / sourcelocation.cc
blob2597af3685bcc7d776f682a57181cdc5dc0a9e89
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, 2013, 2014 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 /* 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." ) );
167 return false;
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 ) );
175 return false;
177 *fileDst = Ast::FileID::build_inMemory( ind, filename );
178 return true;
181 void
182 Ast::FileID::initInfo( const std::string & name ) const
184 Ast::FileInfo * res = Ast::theSourceFiles[ this ];
185 if( res == 0 )
187 Ast::theSourceFiles[ this ] = new Ast::FileInfo( name );
189 else
191 std::cerr << "Internal error. Ast::FileID::initInfo called multiple times for the file: " << name << std::endl ;
192 exit( Interaction::EXIT_INTERNAL_ERROR );
196 const char *
197 Ast::FileID::name( ) const
199 if( kind_ == INTERNAL )
201 return strData_;
203 Ast::FileInfo * res = Ast::theSourceFiles[ this ];
204 if( res == 0 )
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 ];
216 if( res == 0 )
218 std::cerr << "Internal error. Ast::FileID::nodes called before initializaiton." << std::endl ;
219 exit( Interaction::EXIT_INTERNAL_ERROR );
221 return res->topLevelNodes_;
224 bool
225 Ast::FileID::hasPosition( ) const
227 return kind_ != INTERNAL;
230 bool
231 Ast::FileID::operator < ( const Ast::FileID & other ) const
233 if( kind_ == NORMAL_FILE && other.kind_ != NORMAL_FILE )
235 return true;
237 if( kind_ != NORMAL_FILE && other.kind_ == NORMAL_FILE )
239 return false;
241 if( kind_ == NORMAL_FILE )
243 if( st_dev_ < other.st_dev_ )
245 return true;
247 if( st_dev_ > other.st_dev_ )
249 return false;
251 return st_ino_ < other.st_ino_;
253 if( kind_ == IN_MEMORY && other.kind_ != IN_MEMORY )
255 return true;
257 if( kind_ != IN_MEMORY && other.kind_ == IN_MEMORY )
259 return false;
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_;
269 std::istream *
270 Ast::FileID::open( std::ifstream * ifsMem, std::istringstream * issMem ) const
272 switch( kind_ )
274 case NORMAL_FILE:
276 ifsMem->open( strData_ );
277 if( ! ifsMem->is_open( ) )
279 throw Exceptions::FileID_OpenError( this, "Failed to open NORMAL file." );
281 return ifsMem;
283 case IN_MEMORY:
285 issMem->str( Interaction::theInMemorySources[ st_ino_ ] );
286 return issMem;
288 case SPECIAL:
289 case INTERNAL:
291 /* This is an error, see below. */
292 break;
295 throw Exceptions::FileID_OpenError( this, "FileID::open is not applicable to SPECIAL or INTERNAL files" );
298 void
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_ );
324 const Ast::FileID *
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( ) );
334 size_t
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 )
359 file_ = orig.file_;
360 firstLine = orig.firstLine;
361 firstColumn = orig.firstColumn;
362 lastLine = orig.lastLine;
363 lastColumn = orig.lastColumn;
364 return *this;
367 void
368 Ast::SourceLocation::swap( SourceLocation * other )
370 SourceLocation tmp( *this, bool( ) );
371 *this = *other;
372 *other = tmp;
375 bool
376 Ast::SourceLocation::isUnknown( ) const
378 return file_ == SourceLocation::UNKNOWN_FILE;
381 bool
382 Ast::SourceLocation::contains( const Ast::SourceLocation & loc2 ) const
384 return
385 file_ == loc2.file_
387 ( firstLine < loc2.firstLine ||
388 ( firstLine == loc2.firstLine && firstColumn <= loc2.firstColumn ) )
390 ( lastLine > loc2.lastLine ||
391 ( lastLine == loc2.lastLine && lastColumn >= loc2.lastColumn ) )
395 std::ostream &
396 Ast::operator << ( std::ostream & os, const Ast::SourceLocation & self )
398 if( self.file_ == 0 )
400 os << "< unknown location >" ;
401 return os;
404 os << self.file_->name( ) ;
405 if( ! self.file_->hasPosition( ) )
407 return os;
409 os << ":" ;
410 if( Interaction::characterColumnInBytes )
412 if( self.firstLine == self.lastLine )
414 os << self.firstLine << "(" << self.firstColumn << "-" << self.lastColumn << ")" ;
416 else
418 os << self.firstLine << "(" << self.firstColumn << ")-" << self.lastLine << "(" << self.lastColumn << ")" ;
421 else
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( ) )
430 os << col ;
432 else
434 os << "?" ;
437 os << "-" ;
439 size_t col = self.file_->byteColumnToUTF8Column( self.lastLine, self.lastColumn );
440 if( col != std::numeric_limits< size_t >::max( ) )
442 os << col ;
444 else
446 os << "?" ;
449 os << ")" ;
451 else
453 os << self.firstLine << "(" ;
455 size_t col = self.file_->byteColumnToUTF8Column( self.firstLine, self.firstColumn );
456 if( col != std::numeric_limits< size_t >::max( ) )
458 os << col ;
460 else
462 os << "?" ;
465 os << ")-"
466 << self.lastLine << "(" ;
468 size_t col = self.file_->byteColumnToUTF8Column( self.lastLine, self.lastColumn );
469 if( col != std::numeric_limits< size_t >::max( ) )
471 os << col ;
473 else
475 os << "?" ;
478 os << ")" ;
482 return os;
485 void
486 Ast::SourceLocation::copy( std::ostream * os ) const
488 std::ifstream iFile;
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]" ;
498 return;
501 size_t line = 1;
502 for( ; line < firstLine; ++line )
504 is->ignore( std::numeric_limits< std::streamsize >::max( ), '\n' );
506 is->ignore( firstColumn, '\0' );
507 std::string tmpLine;
508 size_t col1 = firstColumn;
509 for( ; line < lastLine; ++line )
511 getline( *is, tmpLine );
512 *os << tmpLine << std::endl ;
513 col1 = 0;
515 getline( *is, tmpLine );
516 *os << tmpLine.substr( 0, lastColumn - col1 ) ;
519 Ast::Expression *
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 );
527 if( tmp != 0 )
529 return tmp;
532 return 0;
535 size_t
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;
542 if( byteCol == 0 )
544 return 0;
547 if( this != fileInCache ||
548 line != lineInCache )
550 fileInCache = this;
551 std::ifstream iFile;
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' );
558 if( is->eof( ) )
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 ) );
563 return 0;
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.
569 // if( is->eof( ) )
570 // {
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 );
574 // return 0;
575 // }
578 return byteColumnToUTF8Column( cachedLine, byteCol );
581 size_t
582 Ast::FileID::byteColumnToUTF8Column( const std::string & line, size_t byteCol )
584 size_t count = 0;
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 ) )
590 ++count;
593 return count;
596 size_t
597 Ast::FileID::UTF8ColumnTobyteColumn( size_t line, size_t utf8Col ) const
599 std::ifstream iFile;
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' );
606 if( is->eof( ) )
608 throw Exceptions::OutOfRange( "Line number is past end of file." );
610 std::string lineData;
611 getline( *is, lineData );
612 return UTF8ColumnTobyteColumn( lineData, utf8Col );
615 size_t
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 ) )
623 --utf8Col;
626 return src - line.c_str( );
630 Ast::SourceLocationFactory::SourceLocationFactory( )
633 Ast::SourceLocationFactory::~SourceLocationFactory( )
635 while( ! mem_.empty( ) )
637 delete mem_.back( );
638 mem_.pop_back( );
642 const Ast::SourceLocation &
643 Ast::SourceLocationFactory::construct( const FileID * file )
645 Ast::SourceLocation * loc = new Ast::SourceLocation( file );
646 mem_.push_back( loc );
647 return *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 );
655 return *loc;