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, 2010, 2013, 2014, 2015 Henrik Tidefelt
19 #include "shapesscanner.h"
22 #include "shapestypes.h"
26 #include "glyphlist.h"
28 #include "shapesexceptions.h"
30 using namespace Shapes
;
31 #include "shapesparser.hh"
38 operator < ( const NeededFile
& n1
, const NeededFile
& n2
)
40 int cmp
= Ast::compare( *(n1
.first
), *(n2
.first
) );
43 return *(n1
.second
) < *(n2
.second
);
47 ShapesScanner::ShapesScanner( )
48 : yyFlexLexer( 0, & std::cerr
), moreState_( false ), lastleng_( 0 ),
50 searchContext_( new Ast::SearchContext( Ast::THE_EMPTY_NAMESPACE_PATH
, Ast::SearchContext::makePrivateName( 0 ) ) ), uniqueNamespaceNumber_( 1 ),
51 showFiles_( false ), randSeedSet_( false ), interactive_( false ),
52 namespace_( Ast::NamespaceReference::ABSOLUTE
, new Ast::NamespacePath( ) )
54 shapeslloc
= Ast::THE_UNKNOWN_LOCATION
;
56 angleUnits_
[ "rad" ] = 1;
57 angleUnits_
[ "deg" ] = M_PI
/ 180;
58 angleUnits_
[ "°" ] = M_PI
/ 180;
59 angleUnits_
[ "grad" ] = M_PI
/ 200;
61 lengthUnits_
[ "bp" ] = 1;
62 lengthUnits_
[ "mm" ] = 0.1 * 72 / 2.54;
63 lengthUnits_
[ "cm" ] = 72 / 2.54;
64 lengthUnits_
[ "m" ] = 100 * 72 / 2.54;
65 lengthUnits_
[ "in" ] = 72;
67 namespaceLimits_
.push( 0 );
68 loadStackSizeStack_
.push( 0 );
69 stateStackSizeStack_
.push( 0 );
72 ShapesScanner::~ShapesScanner( )
74 while( ! locStack_
.empty( ) )
76 delete locStack_
.top( );
82 ShapesScanner::setInteractive( bool interactive
)
84 interactive_
= interactive
;
87 // The following method is placed in shapesyylex to access YY_BUF_SIZE
88 // ShapesScanner::start( )
91 ShapesScanner::setSourceDir( const std::string
& sourceDir
)
93 sourceDir_
= sourceDir
;
97 ShapesScanner::push_backNeedPath( const std::string
& path
)
100 Interaction::warn_or_push( new Exceptions::InvocationError( "Ignoring empty need entry on need path.", true ), & Ast::theAnalysisErrorsList
);
102 /* If the path ends in a '/', it is also a leading '/'. */
103 needSearchPath_
.push_back( path
);
104 loader_
.registerNeedDirectory( path
);
105 }else if( path
[ path
.size( ) - 1 ] == '/' ){
106 needSearchPath_
.push_back( path
);
107 loader_
.registerNeedDirectory( path
.substr( 0, path
.size( ) - 1 ) );
109 needSearchPath_
.push_back( path
+ "/" );
110 loader_
.registerNeedDirectory( path
);
115 ShapesScanner::push_frontNeedPath( const std::string
& path
)
118 path
[ path
.size( ) - 1 ] == '/' )
120 needSearchPath_
.push_front( path
);
124 needSearchPath_
.push_front( path
+ "/" );
129 ShapesScanner::pop_frontNeedPath( )
131 needSearchPath_
.pop_front( );
135 ShapesScanner::setShowFiles( bool showFiles
)
137 showFiles_
= showFiles
;
142 ShapesScanner::more( )
144 moreState_
= true; /* This one is for ourselves to use in doBeforeEachAction. */
145 yy_more_flag
= 1; /* This one is for flex, and will be reset before we reach doBeforeEachAction. */
150 ShapesScanner::doBeforeEachAction( )
154 shapeslloc
.lastColumn
+= yyleng
- lastleng_
;
158 shapeslloc
.firstLine
= shapeslloc
.lastLine
;
159 shapeslloc
.firstColumn
= shapeslloc
.lastColumn
;
160 shapeslloc
.lastColumn
= shapeslloc
.firstColumn
+ yyleng
;
166 ShapesScanner::lookupLengthUnitFactor( const char * name
) const
168 typedef typeof lengthUnits_ MapType
;
169 MapType::const_iterator i
= lengthUnits_
.find( name
);
170 if( i
== lengthUnits_
.end( ) )
174 return 1 / i
->second
;
178 ShapesScanner::strtoLength( const char * str
) const
181 double scalar
= strtod( str
, &endp
);
182 typedef typeof lengthUnits_ MapType
;
183 MapType::const_iterator i
= lengthUnits_
.find( endp
);
184 if( i
== lengthUnits_
.end( ) )
186 throw "Malformed length.";
188 return scalar
* i
->second
;
191 RefCountPtr
< const char >
192 ShapesScanner::newPrivateName( )
194 RefCountPtr
< const char > result
= Ast::SearchContext::makePrivateName( uniqueNamespaceNumber_
);
195 ++uniqueNamespaceNumber_
;
199 RefCountPtr
< const char >
200 ShapesScanner::newEncapsulationName( )
202 RefCountPtr
< const char > result
= Ast::SearchContext::makeEncapsulationName( uniqueNamespaceNumber_
);
203 ++uniqueNamespaceNumber_
;
208 ShapesScanner::setNamespace( char * str
)
212 Ast::NamespaceReference::Base base
= Ast::NamespaceReference::RELATIVE
;
215 base
= Ast::NamespaceReference::LOCAL
;
219 base
= Ast::NamespaceReference::ABSOLUTE
;
226 Ast::NamespacePath
* path
= new Ast::NamespacePath( );
228 for( char * tok
= strtok_r( src
, ".", & last
); tok
!= NULL
; tok
= strtok_r( NULL
, ".", & last
) ){
229 path
->push_back( strdup( tok
) );
232 namespace_
= Ast::NamespaceReference( base
, path
);
235 /* This internal helper function is only meant to work with well-formed identifiers.
238 ShapesScanner::prependNamespace( const char * idstr
) const
240 return new Ast::Identifier( searchContext_
, namespace_
, strdup( idstr
) );
243 Shapes::Ast::Identifier
*
244 ShapesScanner::simpleIdentifier( const char * idstr
) const
246 return new Ast::Identifier( searchContext_
, Ast::NamespaceReference::RELATIVE
, Ast::THE_EMPTY_NAMESPACE_PATH
, strdup( idstr
) );
249 Shapes::Ast::PlacedIdentifier
*
250 ShapesScanner::placedIdentifier( const char * idstr
) const
252 return new Ast::PlacedIdentifier( searchContext_
->lexicalPath( ), strdup( idstr
) );
255 RefCountPtr
< const Ast::NamespacePath
>
256 ShapesScanner::resolvedNamespace( ) const
258 if( namespace_
.base( ) == Ast::NamespaceReference::ABSOLUTE
)
259 return namespace_
.pathRef( );
261 Ast::NamespacePath
* resPtr( new Ast::NamespacePath( *(searchContext_
->lexicalPath( )) ) );
262 if( namespace_
.base( ) == Ast::NamespaceReference::LOCAL
){
263 while ( ! resPtr
->empty( ) && ! Ast::SearchContext::isEncapsulationName( resPtr
->back( ) ) )
267 for (Ast::NamespacePath::const_iterator i
= namespace_
.path( ).begin( ); i
!= namespace_
.path( ).end( ); ++i
){
268 resPtr
->push_back( *i
);
270 return RefCountPtr
< const Ast::NamespacePath
>( resPtr
);
274 ShapesScanner::searchFile( const std::string
& filename
, bool runtime
) const
276 struct stat theStatDummy
;
277 std::string res
= searchFile( filename
, & theStatDummy
, runtime
);
280 for( size_t i
= 0; i
< locStack_
.size( ); ++i
)
284 std::cerr
<< " " << res
<< std::endl
;
290 ShapesScanner::searchFile( const std::string
& filename
, struct stat
* dstStat
, bool runtime
) const
294 if( filename
.empty( ) )
298 throw strrefdup( "Empty file name." ); /* This string should be embedded in a catchable exception by the caller. */
302 throw Exceptions::InternalError( strrefdup( "ShapesScanner::searchFile called with empty argument." ) );
306 if( filename
[ 0 ] == '/' )
309 if( stat( res
.c_str( ), dstStat
) == 0 )
315 std::ostringstream msg
;
316 msg
<< "The absolute filename \"" << filename
<< "\" does not name a file." ;
317 throw strrefdup( msg
); /* This string should be embedded in a catchable exception by the caller. */
321 throw Exceptions::FileReadOpenError( shapeslloc
, strrefdup( filename
), 0, 0 );
325 if( needSearchPath_
.empty( ) )
329 std::ostringstream msg
;
330 msg
<< "The relative filename \"" << filename
<< "\" cannot be searched since the search path is empty." ;
331 throw strrefdup( msg
); /* This string should be embedded in a catchable exception by the caller. */
335 throw Exceptions::ScannerError( shapeslloc
, strrefdup( "Relative file inclusion impossible since search path is empty." ) );
339 typedef typeof needSearchPath_ ListType
;
340 for( ListType::const_iterator i
= needSearchPath_
.begin( ); i
!= needSearchPath_
.end( ); ++i
)
342 res
= needpathWithSuffix( *i
, filename
);
343 if( stat( res
.c_str( ), dstStat
) == 0 )
350 std::ostringstream msg
;
351 msg
<< "The relative filename \"" << filename
<< "\" did not name a file in the search path." ;
352 throw strrefdup( msg
); /* This string should be embedded in a catchable exception by the caller. */
356 throw Exceptions::FileReadOpenError( shapeslloc
, strrefdup( filename
), & sourceDir_
, & needSearchPath_
);
361 ShapesScanner::needpathWithSuffix( const std::string
& needpath
, const std::string
& suffix
) const
363 if( needpath
[0] == '/' )
365 return needpath
+ suffix
;
367 else if( needpath
== "./" )
369 return sourceDir_
+ suffix
;
373 return sourceDir_
+ needpath
+ suffix
;
378 ShapesScanner::rinseString( )
380 /* Both types of strings are terminated by a two byte sequence, ") or ´. */
381 size_t bytecount
= yyleng
- ( ( yyleng
>= 3 && yytext
[ yyleng
- 3 ] == '\n' ) ? 3 : 2 );
382 char * res
= new char[ bytecount
+ 1 ];
383 memcpy( res
, yytext
, bytecount
);
384 res
[ bytecount
] = '\0';
385 shapeslval
.Lang_String
= new Lang::String( RefCountPtr
< const char >( res
), bytecount
);
389 ShapesScanner::concatenateDataString( )
391 /* Remember: strcpy, strdup and friends may fail here, since the string may contain zeros. */
393 char * res
= new char[ dataStringTotalLength_
+ 1 ];
395 while( ! dataStringChunks_
.empty( ) )
397 char * ptr
= dataStringChunks_
.front( ).first
;
398 memcpy( dst
, ptr
, dataStringChunks_
.front( ).second
);
399 dst
+= dataStringChunks_
.front( ).second
;
401 dataStringChunks_
.pop_front( );
404 shapeslval
.Lang_String
= new Lang::String( RefCountPtr
< const char >( res
), dataStringTotalLength_
);
408 ShapesScanner::unicodeFromGlyphname( const char * name
)
410 FontMetrics::GlyphList::UnicodeType res
;
411 const FontMetrics::GlyphList
& glyphList
= Helpers::requireGlyphList( );
412 if( ! glyphList
.name_to_UCS4( name
, & res
) )
414 std::ostringstream msg
;
415 msg
<< "Unrecognized glyph name: " << name
;
416 Ast::theAnalysisErrorsList
.push_back( new Exceptions::ScannerError( shapeslloc
, strrefdup( msg
) ) );