/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2007-2008 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2007 Kicad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include // bsearch() #include #include "dsnlexer.h" #include "fctsys.h" #include "pcbnew.h" static int compare( const void* a1, const void* a2 ) { const KEYWORD* k1 = (const KEYWORD*) a1; const KEYWORD* k2 = (const KEYWORD*) a2; int ret = strcmp( k1->name, k2->name ); return ret; } //------------------------------------------------------------------ DSNLEXER::DSNLEXER( FILE* aFile, const wxString& aFilename, const KEYWORD* aKeywordTable, unsigned aKeywordCount ) : reader( aFile, 4096 ) { keywords = aKeywordTable; keywordCount = aKeywordCount; curTok = DSN_NONE; stringDelimiter = '"'; filename = aFilename; space_in_quoted_tokens = true; // "start" should never change until we change the reader. The DSN // format spec supports an include file mechanism but we can add that later // using a std::stack to hold a stack of LINE_READERs to track nesting. start = (char*) reader; limit = start; next = start; } int DSNLEXER::findToken( const std::string& tok ) { // convert to lower case once, this should be faster than using strcasecmp() // for each test in compare(). lowercase.clear(); for( std::string::const_iterator iter = tok.begin(); iter!=tok.end(); ++iter ) lowercase += (char) tolower( *iter ); KEYWORD search; search.name = lowercase.c_str(); // a boost hashtable might be a few percent faster, depending on // hashtable size and quality of the hash function. const KEYWORD* findings = (const KEYWORD*) bsearch( &search, keywords, keywordCount, sizeof(KEYWORD), compare ); if( findings ) return findings->token; else return -1; } const char* DSNLEXER::Syntax( int aTok ) { const char* ret; switch( aTok ) { case DSN_NONE: ret = "NONE"; break; case DSN_STRING_QUOTE: ret = "string_quote"; // a special DSN syntax token, see specctra spec. break; case DSN_QUOTE_DEF: ret = "quoted text delimiter"; break; case DSN_DASH: ret = "-"; break; case DSN_SYMBOL: ret = "symbol"; break; case DSN_NUMBER: ret = "number"; break; case DSN_RIGHT: ret = ")"; break; case DSN_LEFT: ret = "("; break; case DSN_STRING: ret = "quoted string"; break; case DSN_EOF: ret = "end of file"; break; default: ret = "???"; } return ret; } const char* DSNLEXER::GetTokenText( int aTok ) { const char* ret; if( aTok < 0 ) { return Syntax( aTok ); } else if( (unsigned) aTok < keywordCount ) { ret = keywords[aTok].name; } else ret = "token too big"; return ret; } void DSNLEXER::ThrowIOError( wxString aText, int charOffset ) throw (IOError) { // append to aText, do not overwrite aText << wxT(" ") << _("in file") << wxT(" \"") << filename << wxT("\" ") << _("on line") << wxT(" ") << reader.LineNumber() << wxT(" ") << _("at offset") << wxT(" ") << charOffset; throw IOError( aText ); } /** * Function isspace * strips the upper bits of the int to ensure the value passed to ::isspace() is * in the range of 0-255 */ static inline bool isSpace( int cc ) { // make sure int passed to ::isspace() is 0-255 return ::isspace( cc & 0xff ); } int DSNLEXER::NextTok() throw (IOError) { char* cur = next; char* head = cur; prevTok = curTok; if( curTok != DSN_EOF ) { if( cur >= limit ) { L_read: // blank lines are returned as "\n" and will have a len of 1. // EOF will have a len of 0 and so is detectable. int len = readLine(); if( len == 0 ) { curTok = DSN_EOF; goto exit; } cur = start; // skip leading whitespace while( cur= limit ) goto L_read; // switching the string_quote character if( prevTok == DSN_STRING_QUOTE ) { static const wxString errtxt( _("String delimiter must be a single character of ', \", or $")); char cc = *cur; switch( cc ) { case '\'': case '$': case '"': break; default: ThrowIOError( errtxt, CurOffset() ); } curText = cc; head = cur+1; if( head which is embedded for example like: U2-14 or "U2"-"14" This is detectable by a non-space immediately preceeding the dash. */ if( *cur == '-' && cur>start && !isSpace( cur[-1] ) ) { curText = '-'; curTok = DSN_DASH; head = cur+1; goto exit; } // handle DSN_NUMBER if( strchr( "+-.0123456789", *cur ) ) { head = cur+1; while( head= limit ) { wxString errtxt(_("Un-terminated delimited string") ); ThrowIOError( errtxt, CurOffset() ); } curText.clear(); curText.append( cur, head ); ++head; // skip over the trailing delimiter curTok = DSN_STRING; goto exit; } // Maybe it is a token we will find in the token table. // If not, then call it a DSN_SYMBOL. { head = cur+1; while( head