diff --git a/.bzrignore b/.bzrignore index 42800d2f87..3de1df8858 100644 --- a/.bzrignore +++ b/.bzrignore @@ -5,9 +5,6 @@ eeschema/cmp_library_lexer.h eeschema/cmp_library_keywords.* eeschema/template_fieldnames_keywords.* eeschema/template_fieldnames_lexer.h -new/html -new/sch_lib_table_keywords.cpp -new/sch_lib_table_lexer.h pcbnew/dialogs/dialog_freeroute_exchange_help_html.h Makefile CMakeFiles @@ -20,3 +17,8 @@ install_manifest.txt Documentation/doxygen *.cmake *.bak +new/html +new/sch_lib_table_keywords.cpp +new/sch_lib_table_lexer.h +new/sweet_keywords.cpp +new/sweet_lexer.h diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 212ae4fcfc..2274b75e75 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,6 +4,19 @@ KiCad ChangeLog 2010 Please add newer entries at the top, list the date and your name with email address. +2010-Dec-28 UPDATE Dick Hollenbeck +================================================================================ +++richio: + * Deleted kicad_exceptions, because it required the big #include and + that was slowing down compiling. Moved that stuff back into richio.h where + it came from. + * Enhanced IO_ERROR to format an errorText. + * Added THROW_IO_ERROR() and THROW_PARSE_ERROR() macros to capture the + the call site of the thrower. If you have problems compiling, it is probably + due to the definition of __LOC__ in richio.h. Some compilers may not support + __func__ in C++ yet. Find a macro that identifies your compiler, and we can + work out something in the #define of __LOC__. + 2010-Dec-28 UPDATE Dick Hollenbeck ================================================================================ ++new: diff --git a/common/dsnlexer.cpp b/common/dsnlexer.cpp index 8c91bed4d3..b1ded2154e 100644 --- a/common/dsnlexer.cpp +++ b/common/dsnlexer.cpp @@ -251,12 +251,14 @@ bool DSNLEXER::IsSymbol( int aTok ) void DSNLEXER::ThrowIOError( wxString aText, int charOffset ) throw( IO_ERROR ) { + // @todo convert this to THROW_PARSE_ERROR() + // append to aText, do not overwrite aText << wxT(" ") << _("in") << wxT(" \"") << CurSource() << wxT("\" ") << _("on line") << wxT(" ") << reader->LineNumber() << wxT(" ") << _("at offset") << wxT(" ") << charOffset; - throw IO_ERROR( aText ); + THROW_IO_ERROR( aText ); } diff --git a/common/richio.cpp b/common/richio.cpp index 0a526b5460..4fb458ec29 100644 --- a/common/richio.cpp +++ b/common/richio.cpp @@ -113,7 +113,7 @@ unsigned FILE_LINE_READER::ReadLine() throw( IO_ERROR ) length += strlen( line + length ); if( length == maxLineLength ) - throw IO_ERROR( _("Line length exceeded") ); + THROW_IO_ERROR( _("Line length exceeded") ); // a normal line breaks here, once through while loop if( length+1 < capacity || line[length-1] == '\n' ) @@ -164,7 +164,7 @@ unsigned STRING_LINE_READER::ReadLine() throw( IO_ERROR ) if( length ) { if( length >= maxLineLength ) - throw IO_ERROR( _("Line length exceeded") ); + THROW_IO_ERROR( _("Line length exceeded") ); if( length+1 > capacity ) // +1 for terminating nul expandCapacity( length+1 ); @@ -305,7 +305,7 @@ std::string OUTPUTFORMATTER::Quoted( const std::string& aWrapee ) throw( IO_ERRO // a decision was made to make all S-expression strings be on a single // line. You can embed \n (human readable) in the text but not // '\n' which is 0x0a. - throw IO_ERROR( _( "S-expression string has newline" ) ); + THROW_IO_ERROR( _( "S-expression string has newline" ) ); } } @@ -363,7 +363,7 @@ void STREAM_OUTPUTFORMATTER::write( const char* aOutBuf, int aCount ) throw( IO_ if( !os.IsOk() ) { - throw IO_ERROR( _( "OUTPUTSTREAM_OUTPUTFORMATTER write error" ) ); + THROW_IO_ERROR( _( "OUTPUTSTREAM_OUTPUTFORMATTER write error" ) ); } } } diff --git a/eeschema/sch_component.h b/eeschema/sch_component.h index 89c41dd9b5..49773edcf4 100644 --- a/eeschema/sch_component.h +++ b/eeschema/sch_component.h @@ -17,25 +17,6 @@ class LIB_PIN; class LIB_COMPONENT; -/** - * Holder of an error message and may be thrown from functions. - */ -struct Error -{ - wxString errorText; - - Error( const wxChar* aMsg ) : - errorText( aMsg ) - { - } - - - Error( const wxString& aMsg ) : - errorText( aMsg ) - { - } -}; - /// A container for several SCH_FIELD items typedef std::vector SCH_FIELDS; diff --git a/include/kicad_exceptions.h b/include/kicad_exceptions.h deleted file mode 100644 index 659f507690..0000000000 --- a/include/kicad_exceptions.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This program source code file is part of KICAD, a free EDA CAD application. - * - * Copyright (C) 2010 SoftPLC Corporation, - * Copyright (C) 2010 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 - */ - -#ifndef KICAD_EXCEPTIONS_H_ -#define KICAD_EXCEPTIONS_H_ - -/** - * @ingroup exception_types - * @{ - */ - - -#include -#include - -/** - * Struct IO_ERROR - * is a class used to hold an error message and may be used to throw exceptions - * containing meaningful error messages. - * @author Dick Hollenbeck - */ -struct IO_ERROR -{ - wxString errorText; - - /** - * Constructor ( const wxChar* ) - * handles the case where _() is passed as aMsg. - */ - IO_ERROR( const wxChar* aMsg ) : - errorText( aMsg ) - { - } - - IO_ERROR( const wxString& aMsg ) : - errorText( aMsg ) - { - } - - IO_ERROR( const std::string& aMsg ) : - errorText( wxConvertMB2WX( aMsg.c_str() ) ) - { - } -}; - - -/** - * Class PARSE_ERROR - * contains a filename or source description, a line number, a character offset, - * and an error message. - * @author Dick Hollenbeck - */ -struct PARSE_ERROR : public IO_ERROR -{ - wxString source; ///< filename typically, or other source - int lineNumber; - int byteIndex; ///< char offset, starting from 1, into the problem line. - - PARSE_ERROR( const wxString& aMsg, const wxString& aSource, - int aLineNumber, int aByteIndex ) : - IO_ERROR( aMsg ), - source( aSource ), - lineNumber( aLineNumber ) - { - } -}; - -/** @} exception_types */ - - -#endif // KICAD_EXCEPTIONS_H_ diff --git a/include/richio.h b/include/richio.h index fac82ed820..ce40141a20 100644 --- a/include/richio.h +++ b/include/richio.h @@ -38,7 +38,134 @@ // but the errorText needs to be wide char so wxString rules. #include #include -#include + + +/** + * @ingroup exception_types + * @{ + */ + + +#define IO_FORMAT _( "IO_ERROR: '%s'\n from %s : %s" ) +#define PARSE_FORMAT _( "PARSE_ERROR: '%s' in input/source '%s', line %d, offset %d\n from %s : %s" ) + +// references: +// http://stackoverflow.com/questions/2670816/how-can-i-use-the-compile-time-constant-line-in-a-string +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +// use one of the following __LOC__ defs, depending on whether your +// compiler supports __func__ or not, and how it handles __LINE__ +#define __LOC__ ((std::string(__func__) + " : ") + TOSTRING(__LINE__)).c_str() +//#define __LOC__ TOSTRING(__LINE__) + +/// macro which captures the "call site" values of __FILE_ & __LOC__ +#define THROW_IO_ERROR( msg ) throw IO_ERROR( __FILE__, __LOC__, msg ) + +/** + * Struct IO_ERROR + * is a class used to hold an error message and may be used to throw exceptions + * containing meaningful error messages. + * @author Dick Hollenbeck + */ +struct IO_ERROR // : std::exception +{ + wxString errorText; + + /** + * Constructor + * + * @param aThrowersFile is the __FILE__ preprocessor macro but generated + * at the source file of thrower. + * + * @param aThrowersLoc can be either a function name, such as __func__ + * or a stringified __LINE__ preprocessor macro but generated + * at the source function of the thrower, or concatonation. Use macro + * THROW_IO_ERROR() to wrap a call to this constructor at the call site. + * + * @param aMsg is error text that will be streamed through wxString.Printf() + * using the format string IO_FORMAT above. + */ + IO_ERROR( const char* aThrowersFile, + const char* aThrowersLoc, + const wxString& aMsg ) + { + init( aThrowersFile, aThrowersLoc, aMsg ); + } + + IO_ERROR( const char* aThrowersFile, + const char* aThrowersLoc, + const std::string& aMsg ) + { + init( aThrowersFile, aThrowersLoc, wxString::FromUTF8( aMsg.c_str() ) ); + } + + /** + * handles the case where _() is passed as aMsg. + */ + IO_ERROR( const char* aThrowersFile, + const char* aThrowersLoc, + const wxChar* aMsg ) + { + init( aThrowersFile, aThrowersLoc, wxString( aMsg ) ); + } + + void init( const char* aThrowersFile, const char* aThrowersLoc, const wxString& aMsg ) + { + errorText.Printf( IO_FORMAT, aMsg.GetData(), + wxString::FromUTF8( aThrowersFile ).GetData(), + wxString::FromUTF8( aThrowersLoc ).GetData() ); + } + + IO_ERROR() {} + + ~IO_ERROR() throw ( /*none*/ ){} +}; + + +#define THROW_PARSE_ERROR( msg, input, line, offset ) throw PARSE_ERROR( __FILE__, __LOC__, msg, input, line, offset ) + +/** + * Class PARSE_ERROR + * contains a filename or source description, a line number, a character offset, + * and an error message. + * @author Dick Hollenbeck + */ +struct PARSE_ERROR : public IO_ERROR +{ + // wxString errorText is still public from IO_ERROR + + int lineNumber; ///< at which line number, 1 based index. + int byteIndex; ///< at which character position within the line, 1 based index + + PARSE_ERROR( const char* aThrowersFile, const char* aThrowersLoc, + const wxString& aMsg, const wxString& aSource, + int aLineNumber, int aByteIndex ) : + IO_ERROR() + { + init( aThrowersFile, aThrowersLoc, aMsg, aSource, aLineNumber, aByteIndex ); + } + + void init( const char* aThrowersFile, const char* aThrowersLoc, + const wxString& aMsg, const wxString& aSource, + int aLineNumber, int aByteIndex ) + { + // save line and offset in binary for Sweet text editor, which will catch exceptions + lineNumber = aLineNumber; + byteIndex = aByteIndex; + + // #define PARSE_FORMAT _( "PARSE_ERROR: %s in source %s, line %d, offset %d\nfrom cpp:%s func:%s" ) + + errorText.Printf( PARSE_FORMAT, aMsg.GetData(), aSource.GetData(), + aLineNumber, aByteIndex, + wxString::FromUTF8( aThrowersFile ).GetData(), + wxString::FromUTF8( aThrowersLoc ).GetData() ); + } + + ~PARSE_ERROR() throw ( /*none*/ ){} +}; + +/** @} exception_types */ #define LINE_READER_LINE_DEFAULT_MAX 100000 diff --git a/new/CMakeLists.txt b/new/CMakeLists.txt index 1ea96fec40..b66a20ca24 100644 --- a/new/CMakeLists.txt +++ b/new/CMakeLists.txt @@ -86,6 +86,8 @@ add_executable( test_sch_lib_table sch_lib.cpp sch_lpid.cpp sch_dir_lib_source.cpp + sch_part.cpp + sweet_keywords.cpp ${PROJECT_SOURCE_DIR}/common/richio.cpp ${PROJECT_SOURCE_DIR}/common/dsnlexer.cpp ) @@ -98,3 +100,10 @@ make_lexer( ${CMAKE_CURRENT_SOURCE_DIR}/sch_lib_table_keywords.cpp ELT_T ) + +make_lexer( + ${CMAKE_CURRENT_SOURCE_DIR}/sweet.keywords + ${CMAKE_CURRENT_SOURCE_DIR}/sweet_lexer.h + ${CMAKE_CURRENT_SOURCE_DIR}/sweet_keywords.cpp + PART_T + ) diff --git a/new/sch_dir_lib_source.cpp b/new/sch_dir_lib_source.cpp index db70c6fdad..0e7f0a8f32 100644 --- a/new/sch_dir_lib_source.cpp +++ b/new/sch_dir_lib_source.cpp @@ -41,16 +41,10 @@ */ -#include -using namespace SCH; - -#include - #include #include #include #include - #include #include #include @@ -61,11 +55,8 @@ using namespace SCH; #include using namespace std; -/// This file extension is an implementation detail specific to this LIB_SOURCE -/// implementation, and to a corresponding LIB_SINK. -/// Core EESCHEMA should never have to see this. -#define SWEET_EXT ".part" -#define SWEET_EXTZ (sizeof(SWEET_EXT)-1) +#include +using namespace SCH; /* __func__ is C99 prescribed, but just in case: @@ -157,7 +148,7 @@ static inline bool isDigit( char c ) * segment, i.e. the string segment of interest is [start,tail) * @param separator is the separating byte, expected: '.' or '/', depending on context. */ -static const char* endsWithRev( const char* start, const char* tail, char separator ) +static const char* endsWithRev( const char* start, const char* tail, char separator = '/' ) { bool sawDigit = false; @@ -181,7 +172,7 @@ static const char* endsWithRev( const char* start, const char* tail, char separa return 0; } -static inline const char* endsWithRev( const STRING& aPartName, char separator ) +static inline const char* endsWithRev( const STRING& aPartName, char separator = '/' ) { return endsWithRev( aPartName.c_str(), aPartName.c_str()+aPartName.size(), separator ); } @@ -194,8 +185,8 @@ bool BY_REV::operator() ( const STRING& s1, const STRING& s2 ) const { // avoid instantiating new STRINGs, and thank goodness that c_str() is const. - const char* rev1 = endsWithRev( s1, '/' ); - const char* rev2 = endsWithRev( s2, '/' ); + const char* rev1 = endsWithRev( s1 ); + const char* rev2 = endsWithRev( s2 ); int rootLen1 = rev1 ? rev1 - s1.c_str() : s1.size(); int rootLen2 = rev2 ? rev2 - s2.c_str() : s2.size(); @@ -290,7 +281,7 @@ STRING DIR_LIB_SOURCE::makeFileName( const STRING& aPartName ) STRING fileName = sourceURI + "/"; - const char* rev = endsWithRev( aPartName, '/' ); + const char* rev = endsWithRev( aPartName ); if( rev ) { @@ -318,7 +309,7 @@ void DIR_LIB_SOURCE::readString( STRING* aResult, const STRING& aFileName ) thro { STRING msg = strerror( errno ); msg += "; cannot open(O_RDONLY) file " + aFileName; - throw( IO_ERROR( msg ) ); + THROW_IO_ERROR( msg ); } struct stat fs; @@ -330,9 +321,14 @@ void DIR_LIB_SOURCE::readString( STRING* aResult, const STRING& aFileName ) thro { STRING msg = aFileName; msg += " seems too big. ( > 1 mbyte )"; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } +#if 0 + // I read somewhere on the Internet that std::string chars are not guaranteed + // (over time) to be contiguous in future implementations of C++, so this + // strategy is here for that eventuality. We buffer through readBuffer here. + // reuse same readBuffer, which is not thread safe, but the API // is not advertising thread safe (yet, if ever). if( (int) fs.st_size > (int) readBuffer.size() ) @@ -343,13 +339,27 @@ void DIR_LIB_SOURCE::readString( STRING* aResult, const STRING& aFileName ) thro { STRING msg = strerror( errno ); msg += "; cannot read file " + aFileName; - throw( IO_ERROR( msg ) ); + THROW_IO_ERROR( msg ); } - // std::string chars are not guaranteed to be contiguous in - // future implementations of C++, so this is why we did not read into - // aResult directly. aResult->assign( &readBuffer[0], count ); +#else + + // read into the string directly + aResult->resize( fs.st_size ); + + int count = read( fw, &(*aResult)[0], fs.st_size ); + if( count != (int) fs.st_size ) + { + STRING msg = strerror( errno ); + msg += "; cannot read file " + aFileName; + THROW_IO_ERROR( msg ); + } + + // test trailing nul is there, which should have been put there with resize() above + // printf( "'%s'\n", aResult->c_str() ); // checked OK. +#endif + } @@ -371,7 +381,7 @@ DIR_LIB_SOURCE::DIR_LIB_SOURCE( const STRING& aDirectoryPath, if( sourceURI.size() == 0 ) { - throw( IO_ERROR( STRING("aDirectoryPath cannot be empty") ) ); + THROW_IO_ERROR( STRING("aDirectoryPath cannot be empty") ); } // remove any trailing separator, so we can add it back later without ambiguity @@ -406,7 +416,7 @@ void DIR_LIB_SOURCE::GetCategoricalPartNames( STRINGS* aResults, const STRING& a while( it != limit ) { - const char* rev = endsWithRev( *it, '/' ); + const char* rev = endsWithRev( *it ); // all cached partnames have a rev string in useVersioning mode assert( rev ); @@ -434,7 +444,13 @@ void DIR_LIB_SOURCE::ReadPart( STRING* aResult, const STRING& aPartName, const S throw( IO_ERROR ) { STRING partName = aPartName; // appended with aRev too if not empty - const char* rev = endsWithRev( partName, '/' ); + const char* rev = endsWithRev( partName ); + + if( !useVersioning && (aRev.size() || rev) ) + { + STRING msg = "this type 'dir' LIB_SOURCE not using 'useVersioning' option, cannot ask for a revision"; + THROW_IO_ERROR( msg ); + } if( aRev.size() ) { @@ -443,7 +459,7 @@ void DIR_LIB_SOURCE::ReadPart( STRING* aResult, const STRING& aPartName, const S partName += "/" + aRev; - rev = endsWithRev( partName, '/' ); + rev = endsWithRev( partName ); } // partName is the exact part name we need here, or if rev is NULL, @@ -456,7 +472,7 @@ void DIR_LIB_SOURCE::ReadPart( STRING* aResult, const STRING& aPartName, const S if( it == partnames.end() ) // part not found { partName += " not found."; - throw IO_ERROR( partName ); + THROW_IO_ERROR( partName ); } readString( aResult, makeFileName( partName ) ); @@ -474,8 +490,9 @@ void DIR_LIB_SOURCE::ReadPart( STRING* aResult, const STRING& aPartName, const S // some unrelated name that is larger. if( it == partnames.end() || it->compare( 0, search.size(), search ) != 0 ) { - partName += " rev not found."; - throw IO_ERROR( partName ); + partName.insert( partName.begin(), '\'' ); + partName += "' is not present without a revision."; + THROW_IO_ERROR( partName ); } readString( aResult, makeFileName( *it ) ); @@ -540,7 +557,7 @@ void DIR_LIB_SOURCE::cacheOneDir( const STRING& aCategory ) throw( IO_ERROR ) { STRING msg = strerror( errno ); msg += "; scanning directory " + curDir; - throw( IO_ERROR( msg ) ); + THROW_IO_ERROR( msg ); } struct stat fs; @@ -566,7 +583,7 @@ void DIR_LIB_SOURCE::cacheOneDir( const STRING& aCategory ) throw( IO_ERROR ) { STRING msg = partName; msg += " has already been encountered"; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } } @@ -649,9 +666,9 @@ void DIR_LIB_SOURCE::Test( int argc, char** argv ) printf( "std::exception\n" ); } - catch( IO_ERROR ioe ) + catch( IO_ERROR& ioe ) { - printf( "exception: %s\n", (const char*) wxConvertWX2MB( ioe.errorText ) ); + printf( "exception: %s\n", (const char*) ioe.errorText.ToUTF8() ) ); } } diff --git a/new/sch_dir_lib_source.h b/new/sch_dir_lib_source.h index 5e64ddec05..d7a7b9667c 100644 --- a/new/sch_dir_lib_source.h +++ b/new/sch_dir_lib_source.h @@ -26,11 +26,18 @@ #define DIR_LIB_SOURCE_H_ -#include - #include #include +#include + + +/// This file extension is an implementation detail specific to this LIB_SOURCE +/// and to a corresponding LIB_SINK. +/// Core EESCHEMA should never have to see this. +#define SWEET_EXT ".part" +#define SWEET_EXTZ (sizeof(SWEET_EXT)-1) + /** * struct BY_REV diff --git a/new/sch_lib.cpp b/new/sch_lib.cpp index 20b4a61bca..1770de0477 100644 --- a/new/sch_lib.cpp +++ b/new/sch_lib.cpp @@ -22,9 +22,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include // std::auto_ptr +#include + #include - - +#include +#include +#include +#include using namespace SCH; @@ -44,9 +49,40 @@ LIB::~LIB() } -PART* LIB::LookupPart( const LPID& aLPID ) throw( IO_ERROR ) +PART* LIB::LookupPart( const LPID& aLPID, LIB_TABLE* aLibTable ) throw( IO_ERROR ) { - return 0; + PART* part; + + // If part not already cached + if( 1 /* @todo test cache */ ) + { + // load it. + + part = new PART( this, aLPID.GetPartName(), aLPID.GetRevision() ); + + std::auto_ptr wrapped( part ); + + source->ReadPart( &part->body, aLPID.GetPartName(), aLPID.GetRevision() ); + +#if defined(DEBUG) + const STRING& body = part->body; + printf( "body: %s", body.c_str() ); + if( !body.size() || body[body.size()-1] != '\n' ) + printf( "\n" ); +#endif + + SWEET_LEXER sw( part->body, wxString::FromUTF8("body") /* @todo have ReadPart give better source */ ); + + part->Parse( &sw, aLibTable ); + + + // stuff the part into this LIBs cache: + // @todo + + wrapped.release(); + } + + return part; } diff --git a/new/sch_lib.h b/new/sch_lib.h index bcc1cff3dc..c05bfcf863 100644 --- a/new/sch_lib.h +++ b/new/sch_lib.h @@ -26,14 +26,14 @@ #define SCH_LIB_H_ #include -#include +#include namespace SCH { class LPID; class PART; - +class LIB_TABLE; /** * Class LIB_SOURCE @@ -69,7 +69,7 @@ protected: ///< derived classes must implement * Function ReadPart * fetches @a aPartName's s-expression into @a aResult after clear()ing aResult. */ - virtual void ReadPart( STRING* aResult, const STRING& aPartName, const STRING& aRev = "" ) + virtual void ReadPart( STR_UTF* aResult, const STRING& aPartName, const STRING& aRev = "" ) throw( IO_ERROR ) = 0; /** @@ -80,7 +80,7 @@ protected: ///< derived classes must implement * does not have a version string, then the most recent version is fetched. * @param aResults receives the s-expressions */ - virtual void ReadParts( STRINGS* aResults, const STRINGS& aPartNames ) + virtual void ReadParts( STR_UTFS* aResults, const STRINGS& aPartNames ) throw( IO_ERROR ) = 0; /** @@ -239,12 +239,15 @@ public: * @param aLPID is the part to lookup. The logicalLibName can be empty in it * since yes, we know which LIB is in play. * + * @param aLibTable is the LIB_TABLE view that is in effect for inheritance, + * and comes from the big containing SCHEMATIC object. + * * @return PART* - The desired PART and will never be NULL. No ownership is * given to caller. PARTs always reside in the cache that is a LIB. * * @throw IO_ERROR if the part cannot be found or loaded. */ - PART* LookupPart( const LPID& aLPID ) + PART* LookupPart( const LPID& aLPID, LIB_TABLE* aLibTable ) throw( IO_ERROR ); /** @@ -315,13 +318,13 @@ public: protected: - STRING fetch; // scratch, used to fetch things, grows to worst case size. - STRINGS vfetch; // scratch, used to fetch things. + STR_UTF fetch; // scratch, used to fetch things, grows to worst case size. + STR_UTFS vfetch; // scratch, used to fetch things. STRING name; LIB_SOURCE* source; LIB_SINK* sink; - STRING libraryURI; +// STRING libraryURI; STRINGS categories; diff --git a/new/sch_lib_table.cpp b/new/sch_lib_table.cpp index 4f3a05ed68..a5100c5d55 100644 --- a/new/sch_lib_table.cpp +++ b/new/sch_lib_table.cpp @@ -132,7 +132,7 @@ void LIB_TABLE::Parse( SCH_LIB_TABLE_LEXER* in ) throw( IO_ERROR ) msg += row->logicalName; msg += '\''; msg += " is a duplicate logical lib name"; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } } } @@ -187,53 +187,46 @@ STRINGS LIB_TABLE::GetLogicalLibs() } -PART* LIB_TABLE::LookupPart( const LPID& aLogicalPartID, LIB* aLocalLib ) +PART* LIB_TABLE::LookupPart( const LPID& aLPID, LIB* aLocalLib ) throw( IO_ERROR ) { - LIB* lib = lookupLib( aLogicalPartID, aLocalLib ); + LIB* lib = lookupLib( aLPID, aLocalLib ); - return lib->LookupPart( aLogicalPartID ); + return lib->LookupPart( aLPID, this ); } -LIB* LIB_TABLE::lookupLib( const LPID& aLogicalPartID, LIB* aLocalLib ) +LIB* LIB_TABLE::lookupLib( const LPID& aLPID, LIB* aFallBackLib ) throw( IO_ERROR ) { - if( aLocalLib ) + if( aLPID.GetLogicalLib().size() ) { - return aLocalLib; - } - else - { - const STRING& logName = aLogicalPartID.GetLogicalLib(); - - if( logName.size() ) + ROW* row = FindRow( aLPID.GetLogicalLib() ); + if( !row ) { - ROW* row = FindRow( logName ); - if( !row ) - { - STRING msg = "Unable to find logical lib "; - msg += logName; - throw IO_ERROR( msg ); - } - - if( !row->lib ) - { - loadLib( row ); - } - - assert( row->lib ); // loadLib() throws if cannot load - - return row->lib; + STRING msg = "lib table contains no logical lib '"; + msg += aLPID.GetLogicalLib(); + msg += '\''; + THROW_IO_ERROR( msg ); } - else + + if( !row->lib ) { - STRING msg = "No logicalLibName in LPID and no localLib"; - throw IO_ERROR( msg ); + loadLib( row ); } + + assert( row->lib ); // fix loadLib() to throw if cannot load + + return row->lib; } -// return NULL; never get here + if( aFallBackLib ) + { + return aFallBackLib; + } + + STRING msg = "lookupLib() requires logicalLibName or a fallback lib"; + THROW_IO_ERROR( msg ); } @@ -262,6 +255,7 @@ void LIB_TABLE::loadLib( ROW* aRow ) throw( IO_ERROR ) aRow->lib = new LIB( aRow->GetLogicalName(), source.release(), NULL ); } +/* else if( !libType.compare( "schematic" ) ) { // @todo code and load SCHEMATIC_LIB_SOURCE @@ -276,6 +270,14 @@ void LIB_TABLE::loadLib( ROW* aRow ) throw( IO_ERROR ) { // @todo code and load HTTP_LIB_SOURCE } +*/ + else + { + STRING msg = "cannot load unknown libType: '"; + msg += libType; + msg += '\''; + THROW_IO_ERROR( msg ); + } } @@ -334,44 +336,34 @@ bool LIB_TABLE::InsertRow( std::auto_ptr& aRow, bool doReplace ) } -#if 0 && defined(DEBUG) +#if 1 && defined(DEBUG) // build this with a Debug CMAKE_BUILD_TYPE void LIB_TABLE::Test() { - // the null string is not really a legal DSN token since any duplicated - // double quote ("") is assumed to be a single double quote ("). - // To pass an empty string, we can pass " " to (options " ") + // A pair of double quotes alone, is not really a legal DSN token since + // any duplicated double quote ("") is assumed to be a single double quote (") + // in any DSN lexer. + // To pass an empty string, we can pass " " to (options " "), or if you passed + // """" this would show up as "" with quotes present in the parser. The parser + // probably doesn't want a pair of double quotes, strlen() = 2. SCH_LIB_TABLE_LEXER slr( "(lib_table \n" " (lib (logical www) (type http) (full_uri http://kicad.org/libs) (options \" \"))\n" - " (lib (logical meparts) (type dir) (full_uri /tmp/eeschema-lib) (options \" \"))\n" + " (lib (logical meparts) (type dir) (full_uri /tmp/eeschema-lib) (options useVersioning))\n" " (lib (logical old-project) (type schematic)(full_uri /tmp/old-schematic.sch) (options \" \"))\n" , wxT( "inline text" ) // source ); - try - { - // read the "( lib_table" pair of tokens - slr.NextTok(); - slr.NextTok(); + // read the "( lib_table" pair of tokens + slr.NextTok(); + slr.NextTok(); - // parse the rest of input to slr - Parse( &slr ); - } - - catch( std::exception& ex ) - { - printf( "std::exception\n" ); - } - - catch( IO_ERROR ioe ) - { - printf( "exception: %s\n", (const char*) wxConvertWX2MB( ioe.errorText ) ); - } + // parse the rest of input to slr + Parse( &slr ); STRING_FORMATTER sf; @@ -401,6 +393,11 @@ void LIB_TABLE::Test() { printf( "logicalName: %s\n", it->c_str() ); } + + // find a part + LPID lpid( "meparts:tigers/ears/rev10" ); + + LookupPart( lpid ); } @@ -408,7 +405,18 @@ int main( int argc, char** argv ) { LIB_TABLE lib_table; - lib_table.Test(); + try + { + // test exceptions: + // THROW_PARSE_ERROR( wxT("test problem"), wxT("input"), 23, 46 ); + //THROW_IO_ERROR( wxT("io test") ); + + lib_table.Test(); + } + catch( IO_ERROR& ioe ) + { + printf( "%s\n", (const char*) ioe.errorText.ToUTF8() ); + } return 0; } diff --git a/new/sch_lib_table.h b/new/sch_lib_table.h index fcb8458bf3..1daa1b418d 100644 --- a/new/sch_lib_table.h +++ b/new/sch_lib_table.h @@ -350,14 +350,12 @@ private: * finds or loads a LIB based on @a aLogicalPartID or @a aFallBackLib. * If the LIB is already loaded then it is returned as is, else it is loaded. * - * @param aLogicalPartID may hold the logicalLibName. If - * logicalLibName is empty, then @a aLocalLib should not be NULL. + * @param aLogicalPartID holds the partName and may also hold the logicalLibName. If + * logicalLibName is empty, then @a aFallBackLib should not be NULL. * - * @param aLocalLib is used if not NULL, and should be supplied especiallly if - * aLogicalPartID has an empty logicalLibName. This is for the case when - * a partName must come from the same LIB as the referring content. - * For example, a PART extends another PART in the same LIB and the extends - * LPID has no logical lib name. + * @param aFallBackLib is used only if aLogicalPartID has an empty logicalLibName. + * This is for the case when an LPID has no logicalLibName because the LPID is using + * a partName from the same LIB as was the referring content. * * @return LIB* - this will never be NULL, and no ownership is transfered because * all LIBs live in the LIB_TABLEs. You only get to point to them in some LIB_TABLE. @@ -365,7 +363,7 @@ private: * * @throw IO_ERROR if any problem occurs or if the LIB cannot be found or cannot be loaded. */ - LIB* lookupLib( const LPID& aLogicalPartID, LIB* aLocalLib = NULL ) throw( IO_ERROR ); + LIB* lookupLib( const LPID& aLogicalPartID, LIB* aFallBackLib = NULL ) throw( IO_ERROR ); /** * Function loadLib diff --git a/new/sch_lpid.cpp b/new/sch_lpid.cpp index 8ec603e0c5..d80fb61a66 100644 --- a/new/sch_lpid.cpp +++ b/new/sch_lpid.cpp @@ -114,6 +114,7 @@ int LPID::Parse( const STRING& aLPID ) logical.clear(); category.clear(); baseName.clear(); + partName.clear(); revision.clear(); const char* rev = EndsWithRev( aLPID ); @@ -183,7 +184,7 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) if( offset != -1 ) { - throw PARSE_ERROR( + THROW_PARSE_ERROR( _( "Illegal character found in LPID string" ), wxConvertMB2WX( aLPID.c_str() ), 0, @@ -193,12 +194,6 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) } -const STRING& LPID::GetLogicalLib() const -{ - return logical; -} - - int LPID::SetLogicalLib( const STRING& aLogical ) { int offset = okLogical( aLogical ); @@ -210,63 +205,49 @@ int LPID::SetLogicalLib( const STRING& aLogical ) } -const STRING& LPID::GetCategory() const -{ - return category; -} - - int LPID::SetCategory( const STRING& aCategory ) { int offset = okCategory( aCategory ); if( offset == -1 ) { category = aCategory; + + // set the partName too + if( category.size() ) + { + partName = category; + partName += '/'; + partName += baseName; + } + else + partName = baseName; + } return offset; } -const STRING& LPID::GetBaseName() const -{ - return baseName; -} - - int LPID::SetBaseName( const STRING& aBaseName ) { int offset = okBase( aBaseName ); if( offset == -1 ) { baseName = aBaseName; + + // set the partName too + if( category.size() ) + { + partName = category; + partName += '/'; + partName += baseName; + } + else + partName = baseName; } return offset; } -STRING LPID::GetPartName() const -{ - STRING ret; - - // return [category/]baseName - if( category.size() ) - { - ret += category; - ret += '/'; - } - - ret += baseName; - - return ret; -} - - -const STRING& LPID::GetRevision() const -{ - return revision; -} - - int LPID::SetRevision( const STRING& aRevision ) { int offset = okRevision( aRevision ); @@ -306,7 +287,7 @@ STRING LPID::Format() const } -#if 1 && defined(DEBUG) +#if 0 && defined(DEBUG) // build this with Debug CMAKE_BUILD_TYPE diff --git a/new/sch_lpid.h b/new/sch_lpid.h index 7653c2a8a8..44d08a17a4 100644 --- a/new/sch_lpid.h +++ b/new/sch_lpid.h @@ -26,7 +26,7 @@ #define SCH_LPID_H_ #include -#include +#include namespace SCH { @@ -84,7 +84,10 @@ public: * for this portion since it comes from the library table and is considered * read only here. */ - const STRING& GetLogicalLib() const; + const STRING& GetLogicalLib() const + { + return logical; + } /** * Function SetCategory @@ -100,7 +103,10 @@ public: * returns the category of this part id, "passives" in the example at the * top of the class description. */ - const STRING& GetCategory() const; + const STRING& GetCategory() const + { + return category; + } /** * Function SetCategory @@ -116,7 +122,10 @@ public: * Function GetBaseName * returns the part name without the category. */ - const STRING& GetBaseName() const; + const STRING& GetBaseName() const + { + return baseName; + } /** * Function SetBaseName @@ -131,20 +140,19 @@ public: * Function GetPartName * returns the part name, i.e. category/baseName without revision. */ - STRING GetPartName() const; - - /** - * Function SetBaseName - * overrides the part name portion of the LPID to @a aPartName - not really needed, partname is an agreggate anyway, just parse a new one. - void SetPartName( const STRING& aPartName ); - */ + const STRING& GetPartName() const + { + return partName; + } /** * Function GetRevision * returns the revision portion of the LPID. */ - const STRING& GetRevision() const; + const STRING& GetRevision() const + { + return revision; + } /** * Function SetRevision @@ -171,6 +179,7 @@ protected: STRING category; ///< or empty STRING baseName; ///< without category STRING revision; ///< "revN[N..]" or empty + STRING partName; ///< cannot be set directory, set via SetBaseName() & SetCategory() }; } // namespace SCH diff --git a/new/sch_part.cpp b/new/sch_part.cpp index 12f726d759..ad88353adf 100644 --- a/new/sch_part.cpp +++ b/new/sch_part.cpp @@ -22,12 +22,18 @@ */ -#include "sch_part.h" +#include +#include +#include // _() + using namespace SCH; -void PART::Parse( LIB* aLexer ) throw( PARSE_ERROR ) + +void PART::Parse( SWEET_LEXER* aLexer, LIB_TABLE* aTable ) throw( PARSE_ERROR ) { + // Wayne's world, if he still wants it. + } diff --git a/new/sch_part.h b/new/sch_part.h index 9c7c1b7630..7cafcaf819 100644 --- a/new/sch_part.h +++ b/new/sch_part.h @@ -27,9 +27,31 @@ #include +class SWEET_LEXER; namespace SCH { +/** + * Enum PartBit + * is a set of bit positions that can be used to create flag bits within + * PART::contains to indicate what state the PART is in and what it contains, i.e. + * whether the PART has been parsed, and what the PART contains, categorically. + */ +enum PartBit +{ + PARSED, ///< have parsed this part already, otherwise 'body' text must be parsed + EXTENDS, ///< saw and "extends" keyword, inheriting from another PART + +}; + +/// Function PB +/// is a PartBit shifter for PART::contains field. +static inline const int PB( PartBit oneBitOnly ) +{ + return ( 1 << oneBitOnly ); +} + + /** * Class PART * will have to be unified with what Wayne is doing. I want a separate copy @@ -47,19 +69,23 @@ class PART /// be done by PARTS_LIST, a class derived from LIB. friend class LIB; - /// a private constructor, only a LIB can instantiate a PART. - PART() {} - + PART( LIB* aOwner, const STRING& aPartName, const STRING& aRevision ) : + owner( aOwner ), + contains( 0 ), + partName( aPartName ), + revision( aRevision ) + {} protected: // not likely to have C++ descendants, but protected none-the-less. - bool parsed; ///< true if the body as been parsed already. - LIB* owner; ///< which LIB am I a part of (pun if you want) + int contains; ///< has bits from Enum PartParts + STRING extends; ///< LPID of base part - STRING name; ///< example "passives/R", immutable. + STRING partName; ///< example "passives/R", immutable. + STRING revision; // @todo need a single search key, this won't do. /// s-expression text for the part, initially empty, and read in as this part /// actually becomes cached in RAM. @@ -81,7 +107,6 @@ protected: // not likely to have C++ descendants, but protected none-the-le // lots of other stuff, like the mandatory properties. - public: /** @@ -91,7 +116,6 @@ public: */ void Inherit( const PART& aBasePart ); - /** * Function Owner * returns the LIB* owner of this part. @@ -100,12 +124,23 @@ public: /** * Function Parse - * translates the \a body string into a binary form that is represented + * translates a Sweet string into a binary form that is represented * by the normal fields of this class. Parse is expected to call Inherit() * if this part extends any other. + * + * @param aLexer is an instance of SWEET_LEXER, rewound at the first line. + * + * @param aLibTable is the LIB_TABLE view that is in effect for inheritance, + * and comes from the big containing SCHEMATIC object. */ - void Parse( LIB* aLexer ) throw( PARSE_ERROR ); + void Parse( SWEET_LEXER* aLexer, LIB_TABLE* aTable ) throw( PARSE_ERROR ); +/* + void SetBody( const STR_UTF& aSExpression ) + { + body = aSExpression; + } +*/ }; } // namespace PART diff --git a/new/sweet.keywords b/new/sweet.keywords new file mode 100644 index 0000000000..697a0f9c53 --- /dev/null +++ b/new/sweet.keywords @@ -0,0 +1,2 @@ +part +extends diff --git a/new/utf8.h b/new/utf8.h index 94109c88ef..f633c48bcd 100644 --- a/new/utf8.h +++ b/new/utf8.h @@ -13,14 +13,19 @@ /** * Type STRING - * holds a sequence of 8 bit bytes that represent a sequence - * of variable multi-byte international characters, with unspecified encoding. + * holds a sequence of 8 bit bytes that represent a sequence of variable + * length multi-byte international characters, with unspecified encoding. */ typedef std::string STRING; +/** + * Type STRINGS + * is an "array like" list of STRINGs + */ +typedef std::deque STRINGS; /** - * Type STRING_UTF8 + * Type STR_UTF * holds a UTF8 encoded sequence of 8 bit bytes that represent a sequence * of variable multi-byte international characters. UTF8 is the chosen encoding * for all Kicad data files so that they can be transported from one nation to another @@ -29,19 +34,15 @@ typedef std::string STRING; * Filenames may be encoded on disk using an encoding chosen by the host operating * system. Nonetheless, Kicad data file _content_ is always UTF8 encoded, regardless * of host operating system. - * STRING_UTF is UTF8 encoded, by definition. + * STR_UTF is UTF8 encoded, by definition. */ -typedef STRING STRING_UTF8; - +typedef STRING STR_UTF; /** - * Type STRINGS_UTF8 - * is an "array like" list of STRING_UTF8s + * Type STR_UTFS + * is an "array like" list of STR_UTFs */ -typedef std::deque STRINGS_UTF8; - -typedef std::deque STRINGS; - +typedef std::deque STR_UTFS; /** @} string_types */ diff --git a/pcbnew/specctra.cpp b/pcbnew/specctra.cpp index cae5c4854a..5726de8f2a 100644 --- a/pcbnew/specctra.cpp +++ b/pcbnew/specctra.cpp @@ -542,7 +542,7 @@ void SPECCTRA_DB::ThrowIOError( const wxChar* fmt, ... ) throw( IO_ERROR ) errText.PrintfV( fmt, args ); va_end( args ); - throw IO_ERROR( errText ); + THROW_IO_ERROR( errText ); }