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 2015 Henrik Tidefelt
19 #include "namespacedirectory.h"
20 #include "shapesexceptions.h"
21 #include "namespacescanner.h"
22 #include "shapesscanner.h"
31 using namespace Shapes
;
34 Ast::NamespaceDeclarations::NamespaceDeclarations( )
35 : encapsulated_( false )
39 Ast::NamespaceDeclarations::addPrelude( const std::string
& entry
)
41 preludeSet_
.insert( preludeSet_
.begin( ), entry
);
45 Ast::NamespaceDeclarations::addIgnore( const std::string
& entry
)
47 ignoreSet_
.insert( ignoreSet_
.begin( ), entry
);
51 Ast::NamespaceDeclarations::addPrecedes( const std::set
< std::string
> & x
, const std::set
< std::string
> & y
)
53 for( std::set
< std::string
>::const_iterator i
= x
.begin(); i
!= x
.end(); ++i
){
54 std::set
< std::string
> & s
= orderGraph_
[ *i
];
55 s
.insert( y
.begin( ), y
.end( ) );
61 std::list
< OrderGraphNode
* > predecessors_
;
62 Ast::NamespaceDirectoryEntry
* entry_
;
65 OrderGraphNode( Ast::NamespaceDirectoryEntry
* entry
)
66 : entry_( entry
), visited_( false )
68 void addPredecessor( OrderGraphNode
* pre
)
70 predecessors_
.push_back( pre
);
72 bool visited( ) const { return visited_
; }
73 std::string
name( ) const { return entry_
->name( ); }
74 /* Returns NULL if no cycle was detected.
75 * Otherwise, a pointer to a node on a cycle is returned.
77 OrderGraphNode
* depthFirstVisit( std::stack
< Ast::NamespaceDirectoryEntry
* > * loadOrderStack
)
84 typedef typeof predecessors_ ListType
;
85 for( ListType::iterator i
= predecessors_
.begin( ); i
!= predecessors_
.end( ); ++i
){
86 OrderGraphNode
* cycleNode
= (*i
)->depthFirstVisit( loadOrderStack
);
87 if( cycleNode
!= NULL
)
91 loadOrderStack
->push( entry_
);
98 Ast::NamespaceDeclarations::invalidOrderGraphEntries( const std::map
< std::string
, NamespaceDirectoryEntry
* > & entries
, std::set
< std::string
> * invalidEntries
) const
100 typedef typeof orderGraph_ OrderMapType
;
101 for( OrderMapType::const_iterator i
= orderGraph_
.begin( ); i
!= orderGraph_
.end( ); ++i
){
102 invalidEntries
->insert( invalidEntries
->begin( ), i
->first
);
103 invalidEntries
->insert( i
->second
.begin( ), i
->second
.end( ) );
105 typedef typeof entries EntriesMapType
;
106 for( EntriesMapType::const_iterator i
= entries
.begin( ); i
!= entries
.end( ); ++i
){
107 invalidEntries
->erase( i
->first
);
113 Ast::NamespaceDeclarations::orderEntries( const std::map
< std::string
, NamespaceDirectoryEntry
* > & entries
, std::vector
< NamespaceDirectoryEntry
* > * loadOrder
) const
115 PtrOwner_back_Access
< std::list
< OrderGraphNode
* > > nodeMemory
;
116 typedef std::map
< std::string
, OrderGraphNode
* > NodeMap
;
118 typedef typeof entries EntriesMapType
;
119 for( EntriesMapType::const_iterator i
= entries
.begin( ); i
!= entries
.end( ); ++i
){
120 OrderGraphNode
* node
= new OrderGraphNode( i
->second
);
121 nodeMemory
.push_back( node
);
122 nodes
[ i
->first
] = node
;
125 for( NodeMap::const_iterator i
= nodes
.begin( ); i
!= nodes
.end( ); ++i
){
126 typedef typeof orderGraph_ OrderMapType
;
127 OrderMapType::const_iterator j
= orderGraph_
.find( i
->first
);
128 if( j
== orderGraph_
.end( ) )
130 typedef typeof j
->second SetType
;
131 for( SetType::const_iterator k
= j
->second
.begin( ); k
!= j
->second
.end( ); ++j
){
132 NodeMap::iterator l
= nodes
.find( *k
);
133 if( l
== nodes
.end( ) ){
134 throw Exceptions::InternalError( "Ast::NamespaceDeclarations::orderEntries: Invalid entries should have been detected earlier." );
136 i
->second
->addPredecessor( l
->second
);
140 std::stack
< Ast::NamespaceDirectoryEntry
* > loadOrderStack
;
141 for( NodeMap::iterator i
= nodes
.begin( ); i
!= nodes
.end( ); ++i
){
142 OrderGraphNode
* node
= i
->second
;
143 if( node
->visited( ) )
145 OrderGraphNode
* cycleNode
= node
->depthFirstVisit( & loadOrderStack
);
146 if( cycleNode
!= NULL
)
147 return cycleNode
->name( );
151 loadOrder
->reserve( entries
.size( ) );
152 while( ! loadOrderStack
.empty( ) ){
153 loadOrder
->push_back( loadOrderStack
.top( ) );
154 loadOrderStack
.pop( );
157 return std::string( );
161 Ast::NamespaceDirectoryEntry::NamespaceDirectoryEntry( const std::string
& name
, const Ast::NamespaceDirectory
* parent
)
162 : name_( name
), parent_( parent
), prelude_( false )
165 Ast::NamespaceDirectoryEntry::~NamespaceDirectoryEntry( )
169 Ast::NamespaceDirectoryEntry::schedulePrelude( Ast::LoadStack
* loadStack
)
171 this->schedule_impl( loadStack
, true );
175 Ast::NamespaceDirectoryEntry::schedule( Ast::LoadStack
* loadStack
)
177 this->schedule_impl( loadStack
, false );
181 Ast::NamespaceDirectory::NamespaceDirectory( const RefCountPtr
< const Ast::NamespacePath
> & namespacePath
, const std::string
& filesystemPath
, const std::string
& name
, const Ast::NamespaceDirectory
* parent
)
182 : Ast::NamespaceDirectoryEntry( name
, parent
), initialized_( false ), namespacePath_( namespacePath
), filesystemPath_( filesystemPath
), encapsulated_( false )
185 Ast::NamespaceDirectory::~NamespaceDirectory( )
189 Ast::NamespaceDirectory::schedule_impl( Ast::LoadStack
* loadStack
, bool preludeOnly
)
193 typedef typeof loadOrder_ VecType
;
194 for( VecType::iterator i
= loadOrder_
.begin( ); i
!= loadOrder_
.end( ); ++i
){
195 if( preludeOnly
&& ! (*i
)->prelude( ) )
197 (*i
)->schedule_impl( loadStack
, preludeOnly
);
202 Ast::NamespaceDirectory::scheduleNamespace( const Ast::NamespacePath
& namespacePath
, const Ast::NamespacePath::const_iterator
& nsBegin
, Ast::LoadStack
* loadStack
)
206 if( nsBegin
== namespacePath
.end( ) ){
207 schedule( loadStack
);
211 Ast::NamespacePath::const_iterator
next( nsBegin
);
214 typedef typeof entries_ MapType
;
215 MapType::iterator i
= entries_
.find( *nsBegin
);
216 if( i
== entries_
.end( ) ){
217 throw Exceptions::NamespaceDirectoryLookupError( Exceptions::NamespaceDirectoryLookupError::NOT_FOUND
, namespacePath
.begin( ), next
);
220 NamespaceDirectory
* dir
= dynamic_cast< NamespaceDirectory
* >( i
->second
);
222 throw Exceptions::NamespaceDirectoryLookupError( Exceptions::NamespaceDirectoryLookupError::WRONG_TYPE
, namespacePath
.begin( ), next
);
225 dir
->scheduleNamespace( namespacePath
, next
, loadStack
);
229 Ast::NamespaceDirectory::resolveSingleFile( const Ast::NamespacePath
& namespacePath
, const Ast::NamespacePath::const_iterator
& nsBegin
, const std::string
& filename
, const Ast::SourceLocation
& loc
)
233 if( nsBegin
== namespacePath
.end( ) ){
235 typedef typeof entries_ MapType
;
236 MapType::iterator i
= entries_
.find( filename
);
237 if( i
== entries_
.end( ) ){
238 throw Exceptions::NamespaceDirectoryFileError( loc
, Exceptions::NamespaceDirectoryFileError::NOT_FOUND
, namespacePath
, filename
);
241 NamespaceFile
* fileEntry
= dynamic_cast< NamespaceFile
* >( i
->second
);
242 if( fileEntry
== NULL
){
243 throw Exceptions::NamespaceDirectoryFileError( loc
, Exceptions::NamespaceDirectoryFileError::WRONG_TYPE
, namespacePath
, filename
);
246 return fileEntry
->fileID();
250 Ast::NamespacePath::const_iterator
next( nsBegin
);
253 typedef typeof entries_ MapType
;
254 MapType::iterator i
= entries_
.find( *nsBegin
);
255 if( i
== entries_
.end( ) ){
256 throw Exceptions::NamespaceDirectoryLookupError( loc
, Exceptions::NamespaceDirectoryLookupError::NOT_FOUND
, namespacePath
.begin( ), next
);
259 NamespaceDirectory
* dir
= dynamic_cast< NamespaceDirectory
* >( i
->second
);
261 throw Exceptions::NamespaceDirectoryLookupError( loc
, Exceptions::NamespaceDirectoryLookupError::WRONG_TYPE
, namespacePath
.begin( ), next
);
264 return dir
->resolveSingleFile( namespacePath
, next
, filename
, loc
);
268 Ast::NamespaceDirectory::name( ) const
270 return filesystemPath_
;
274 Ast::NamespaceDirectory::initialize( )
280 std::string filename
= Ast::theShapesScanner
.needpathWithSuffix( filesystemPath_
+ "/", "Shapes-Namespace.txt" );
281 std::ifstream
iFile( filename
);
282 struct stat iFileStat
;
283 if( stat( filename
.c_str( ), & iFileStat
) != 0 || ! iFile
.is_open( ) ){
284 throw Exceptions::FileReadOpenError( Ast::THE_UNKNOWN_LOCATION
, strrefdup( filename
), NULL
, NULL
);
286 const Ast::FileID
* iFileID
= Ast::FileID::build_stat( iFileStat
, filename
);
288 NamespaceDeclarations declarations
;
289 NamespaceScanner
scanner( & declarations
, & iFile
, iFileID
);
290 int status
= scanner
.yylex( );
293 std::ostringstream oss
;
294 oss
<< "Namespace scanner returned with non-zero status: " << status
;
295 throw Exceptions::InternalError( strrefdup( oss
) );
298 encapsulated_
= declarations
.encapsulated( );
300 DIR* deDir
= opendir( filesystemPath_
.c_str( ) );
302 throw Exceptions::FileReadOpenError( Ast::THE_UNKNOWN_LOCATION
, strrefdup( filesystemPath_
), NULL
, NULL
);
305 for( struct dirent
* de
= readdir(deDir
); de
!= NULL
; de
= readdir(deDir
) ) {
306 if (strcmp(de
->d_name
, ".") == 0 || strcmp(de
->d_name
, "..") == 0)
308 std::string
name( de
->d_name
);
309 if( declarations
.inIgnoreSet( name
) )
311 std::string fullname
= filesystemPath_
+ "/" + name
;
313 if( stat( fullname
.c_str( ), & theStat
) != 0 )
314 continue; /* Strange directory entry, ignore it. */
315 if( ( theStat
.st_mode
& S_IFDIR
) != 0 ){
316 addDirectory( name
, fullname
, declarations
.inPreludeSet( name
) );
317 }else if( ( theStat
.st_mode
& S_IFREG
) != 0 ){
318 if( name
.compare( name
.size( ) - 6, std::string::npos
, ".shext" ) == 0 ){
319 const Ast::FileID
* fileID
= Ast::FileID::build_stat( theStat
, fullname
);
320 NamespaceFile
* entry
= new NamespaceFile( fileID
, name
, this );
321 if( declarations
.inPreludeSet( name
) )
322 entry
->setPrelude( );
323 entries_
[ name
] = entry
;
326 /* Other type of file, ignore it. */
331 std::set
< std::string
> invalidEntries
;
332 declarations
.invalidOrderGraphEntries( entries_
, & invalidEntries
);
333 if( ! invalidEntries
.empty( ) ){
334 throw Exceptions::NamespaceDirectoryInvalidEntries( namespacePath_
, invalidEntries
);
337 std::string cycleEntry
= declarations
.orderEntries( entries_
, & loadOrder_
);
338 if( ! cycleEntry
.empty( ) ){
339 throw Exceptions::NamespaceDirectoryCircularOrdering( namespacePath_
, cycleEntry
);
344 Ast::NamespaceDirectory::addDirectory( const std::string
& nsName
, const std::string
& dir
, bool prelude
)
346 std::string filename
= Ast::theShapesScanner
.needpathWithSuffix( dir
+ "/", "Shapes-Namespace.txt" );
347 std::ifstream
iFile( filename
);
348 if( ! iFile
.is_open( ) )
351 typedef typeof entries_ MapType
;
352 MapType::iterator i
= entries_
.find( nsName
);
353 if( i
!= entries_
.end( ) ){
354 throw Exceptions::AmbiguousNamespaceDirectory( i
->second
->name( ), dir
);
357 Ast::NamespacePath
* pathMem
= new Ast::NamespacePath( *namespacePath_
);
358 RefCountPtr
< const Ast::NamespacePath
> path( pathMem
);
359 /* Leaking memory with each strdup below, see 'Memory management notice' in identifier.h. */
361 pathMem
->push_back( strdup( Ast::theShapesScanner
.newEncapsulationName( ).getPtr( ) ) );
362 pathMem
->push_back( strdup( nsName
.c_str( ) ) );
363 NamespaceDirectory
* entry
= new NamespaceDirectory( path
, dir
, nsName
, this );
365 entry
->setPrelude( );
366 entries_
[ nsName
] = entry
;
372 Ast::NamespaceFile::NamespaceFile( const FileID
* fileID
, const std::string
& name
, const Ast::NamespaceDirectory
* parent
)
373 : Ast::NamespaceDirectoryEntry( name
, parent
), fileID_( fileID
)
376 Ast::NamespaceFile::~NamespaceFile( )
380 Ast::NamespaceFile::schedule_impl( Ast::LoadStack
* loadStack
, bool preludeOnly
)
382 if( ! prelude_
&& preludeOnly
){
383 throw Exceptions::InternalError( "Ast::NamespaceFile::schedule was called on a non-prelude file while loading the prelude." );
385 loadStack
->push( fileID_
, parent_
->namespacePath( ) );
389 Ast::NamespaceFile::name( ) const
391 return fileID_
->name( );
395 Ast::LoadStack::LoadStack( )
399 Ast::LoadStack::push( const FileID
* fileID
, const RefCountPtr
< const Ast::NamespacePath
> & namespacePath
)
401 fileIDs_
.push( fileID
);
402 namespacePaths_
.push( namespacePath
);
406 Ast::LoadStack::pop( )
409 namespacePaths_
.pop( );
415 Ast::NamespaceLoader::NamespaceLoader( )
418 Ast::NamespaceLoader::~NamespaceLoader( )
420 typedef typeof topEntries_ MapType
;
421 for( MapType::iterator i
= topEntries_
.begin( ); i
!= topEntries_
.end( ); ++i
){
427 Ast::NamespaceLoader::registerNeedDirectory( const std::string
& dir
)
430 if( stat( dir
.c_str( ), & theStat
) != 0 )
432 if( ( theStat
.st_mode
& S_IFDIR
) == 0 ){
433 /* Ignore non-directory */
437 /* See if the directory itself is a namespace directory. */
439 char* dirstr
= strdup( dir
.c_str( ) );
440 std::string
nsName( basename( dirstr
) );
442 if( addDirectory( nsName
, dir
) )
446 /* See if any of the subdirectories are namespace directories. */
447 DIR* deDir
= opendir( dir
.c_str( ) );
448 while (deDir
!= NULL
) {
449 struct dirent
* de
= readdir(deDir
);
450 if (de
== NULL
) break;
451 if (strcmp(de
->d_name
, ".") == 0 || strcmp(de
->d_name
, "..") == 0)
453 std::string
nsName( de
->d_name
);
454 std::string subdir
= dir
+ "/" + nsName
;
455 if( stat( subdir
.c_str( ), & theStat
) != 0 )
456 continue; /* Strange directory entry, ignore it. */
457 if( ( theStat
.st_mode
& S_IFDIR
) == 0 )
459 addDirectory( nsName
, subdir
);
465 Ast::NamespaceLoader::addDirectory( const std::string
& nsName
, const std::string
& dir
)
467 std::string filename
= Ast::theShapesScanner
.needpathWithSuffix( dir
+ "/", "Shapes-Namespace.txt" );
468 std::ifstream
iFile( filename
);
469 if( ! iFile
.is_open( ) )
472 typedef typeof topEntries_ MapType
;
473 MapType::iterator i
= topEntries_
.find( nsName
);
474 if( i
!= topEntries_
.end( ) ){
475 throw Exceptions::AmbiguousNamespaceDirectory( i
->second
->name( ), dir
);
478 Ast::NamespacePath
* pathMem
= new Ast::NamespacePath( );
479 RefCountPtr
< const Ast::NamespacePath
> path( pathMem
);
480 pathMem
->push_back( strdup( nsName
.c_str( ) ) ); /* Leaking memory, see 'Memory management notice' in identifier.h. */
481 topEntries_
[ nsName
] = new NamespaceDirectory( path
, dir
, nsName
, NULL
);
487 Ast::NamespaceLoader::pushPreludeFiles( LoadStack
* loadStack
)
489 typedef typeof topEntries_ MapType
;
490 for( MapType::iterator i
= topEntries_
.begin( ); i
!= topEntries_
.end( ); ++i
){
491 i
->second
->schedulePrelude( loadStack
); /* true means to only schedule the prelude. */
496 Ast::NamespaceLoader::pushNamespaceFiles( const Ast::NamespacePath
& namespacePath
, LoadStack
* loadStack
)
498 if( namespacePath
.empty() ){
499 throw Exceptions::NamespaceDirectoryLookupError( Exceptions::NamespaceDirectoryLookupError::GLOBAL
, namespacePath
.begin( ), namespacePath
.end( ) );
502 Ast::NamespacePath::const_iterator begin
= namespacePath
.begin( );
503 Ast::NamespacePath::const_iterator next
= begin
;
506 typedef typeof topEntries_ MapType
;
507 MapType::iterator i
= topEntries_
.find( *begin
);
508 if( i
== topEntries_
.end( ) ){
509 throw Exceptions::NamespaceDirectoryLookupError( Exceptions::NamespaceDirectoryLookupError::NOT_FOUND
, namespacePath
.begin( ), next
);
512 i
->second
->scheduleNamespace( namespacePath
, next
, loadStack
);
516 Ast::NamespaceLoader::resolveSingleFile( const Ast::NamespacePath
& namespacePath
, const std::string
& filename
, const Ast::SourceLocation
& loc
)
518 if( namespacePath
.empty() ){
519 throw Exceptions::NamespaceDirectoryLookupError( loc
, Exceptions::NamespaceDirectoryLookupError::GLOBAL
, namespacePath
.begin( ), namespacePath
.end( ) );
522 Ast::NamespacePath::const_iterator begin
= namespacePath
.begin( );
523 Ast::NamespacePath::const_iterator next
= begin
;
526 typedef typeof topEntries_ MapType
;
527 MapType::iterator i
= topEntries_
.find( *begin
);
528 if( i
== topEntries_
.end( ) ){
529 throw Exceptions::NamespaceDirectoryLookupError( loc
, Exceptions::NamespaceDirectoryLookupError::NOT_FOUND
, namespacePath
.begin( ), next
);
532 return i
->second
->resolveSingleFile( namespacePath
, next
, filename
, loc
);