Update suitable examples and tests to use blank mode
[shapes.git] / source / shapesscanner.cc
blob561ca7ebff50eac6507838d790a26cc015060a9f
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, 2010, 2013, 2014, 2015 Henrik Tidefelt
19 #include "shapesscanner.h"
20 #include "yyltype.h"
22 #include "shapestypes.h"
23 #include "ast.h"
24 #include "astflow.h"
25 #include "astclass.h"
26 #include "glyphlist.h"
27 #include "globals.h"
28 #include "shapesexceptions.h"
30 using namespace Shapes;
31 #include "shapesparser.hh"
33 #include <iostream>
35 using namespace std;
37 bool
38 operator < ( const NeededFile & n1, const NeededFile & n2 )
40 int cmp = Ast::compare( *(n1.first), *(n2.first) );
41 if( cmp != 0 )
42 return cmp < 0;
43 return *(n1.second) < *(n2.second);
47 ShapesScanner::ShapesScanner( )
48 : yyFlexLexer( 0, & std::cerr ), moreState_( false ), lastleng_( 0 ),
49 bracketDepth_( 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( );
77 locStack_.pop( );
81 void
82 ShapesScanner::setInteractive( bool interactive )
84 interactive_ = interactive;
87 // The following method is placed in shapesyylex to access YY_BUF_SIZE
88 // ShapesScanner::start( )
90 void
91 ShapesScanner::setSourceDir( const std::string & sourceDir )
93 sourceDir_ = sourceDir;
96 void
97 ShapesScanner::push_backNeedPath( const std::string & path )
99 if( path.empty( ) ){
100 Interaction::warn_or_push( new Exceptions::InvocationError( "Ignoring empty need entry on need path.", true ), & Ast::theAnalysisErrorsList );
101 }if( path == "/" ){
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 ) );
108 }else{
109 needSearchPath_.push_back( path + "/" );
110 loader_.registerNeedDirectory( path );
114 void
115 ShapesScanner::push_frontNeedPath( const std::string & path )
117 if( path.empty( ) ||
118 path[ path.size( ) - 1 ] == '/' )
120 needSearchPath_.push_front( path );
122 else
124 needSearchPath_.push_front( path + "/" );
128 void
129 ShapesScanner::pop_frontNeedPath( )
131 needSearchPath_.pop_front( );
134 void
135 ShapesScanner::setShowFiles( bool showFiles )
137 showFiles_ = showFiles;
141 void
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. */
146 lastleng_ = yyleng;
149 void
150 ShapesScanner::doBeforeEachAction( )
152 if( moreState_ )
154 shapeslloc.lastColumn += yyleng - lastleng_;
156 else
158 shapeslloc.firstLine = shapeslloc.lastLine;
159 shapeslloc.firstColumn = shapeslloc.lastColumn;
160 shapeslloc.lastColumn = shapeslloc.firstColumn + yyleng;
162 moreState_ = false;
165 double
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( ) )
172 return -1;
174 return 1 / i->second;
177 Concrete::Length
178 ShapesScanner::strtoLength( const char * str ) const
180 char * endp;
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_;
196 return result;
199 RefCountPtr< const char >
200 ShapesScanner::newEncapsulationName( )
202 RefCountPtr< const char > result = Ast::SearchContext::makeEncapsulationName( uniqueNamespaceNumber_ );
203 ++uniqueNamespaceNumber_;
204 return result;
207 void
208 ShapesScanner::setNamespace( char * str )
210 char * src = str;
212 Ast::NamespaceReference::Base base = Ast::NamespaceReference::RELATIVE;
213 switch( *src ){
214 case '^':
215 base = Ast::NamespaceReference::LOCAL;
216 src += 2;
217 break;
218 case '.':
219 base = Ast::NamespaceReference::ABSOLUTE;
220 src += 2;
221 break;
222 default:
223 break;
226 Ast::NamespacePath * path = new Ast::NamespacePath( );
227 char * last;
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.
237 Ast::Identifier *
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( ) ) )
264 resPtr->pop_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 );
273 std::string
274 ShapesScanner::searchFile( const std::string & filename, bool runtime ) const
276 struct stat theStatDummy;
277 std::string res = searchFile( filename, & theStatDummy, runtime );
278 if( showFiles_ )
280 for( size_t i = 0; i < locStack_.size( ); ++i )
282 std::cerr << " " ;
284 std::cerr << " " << res << std::endl ;
286 return res;
289 std::string
290 ShapesScanner::searchFile( const std::string & filename, struct stat * dstStat, bool runtime ) const
292 std::string res;
294 if( filename.empty( ) )
296 if( runtime )
298 throw strrefdup( "Empty file name." ); /* This string should be embedded in a catchable exception by the caller. */
300 else
302 throw Exceptions::InternalError( strrefdup( "ShapesScanner::searchFile called with empty argument." ) );
306 if( filename[ 0 ] == '/' )
308 res = filename;
309 if( stat( res.c_str( ), dstStat ) == 0 )
311 return res;
313 if( runtime )
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. */
319 else
321 throw Exceptions::FileReadOpenError( shapeslloc, strrefdup( filename ), 0, 0 );
325 if( needSearchPath_.empty( ) )
327 if( runtime )
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. */
333 else
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 )
345 return res;
348 if( runtime )
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. */
354 else
356 throw Exceptions::FileReadOpenError( shapeslloc, strrefdup( filename ), & sourceDir_, & needSearchPath_ );
360 std::string
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;
371 else
373 return sourceDir_ + needpath + suffix;
377 void
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 );
388 void
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 ];
394 char * dst = res;
395 while( ! dataStringChunks_.empty( ) )
397 char * ptr = dataStringChunks_.front( ).first;
398 memcpy( dst, ptr, dataStringChunks_.front( ).second );
399 dst += dataStringChunks_.front( ).second;
400 delete ptr;
401 dataStringChunks_.pop_front( );
403 *dst = '\0';
404 shapeslval.Lang_String = new Lang::String( RefCountPtr< const char >( res ), dataStringTotalLength_ );
407 uint32_t
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 ) ) );
418 return res;