From 911bde45b6ad1b0aa43fb28740e55fe904c7c289 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Tue, 28 Dec 2010 19:18:07 -0600 Subject: [PATCH 1/3] changes --- new/sch_dir_lib_source.cpp | 6 -- new/sch_lpid.cpp | 145 +++++++++++++++++++++++++++++-------- new/sch_lpid.h | 40 +++++++--- 3 files changed, 144 insertions(+), 47 deletions(-) diff --git a/new/sch_dir_lib_source.cpp b/new/sch_dir_lib_source.cpp index 70cc3040da..cc9be1419b 100644 --- a/new/sch_dir_lib_source.cpp +++ b/new/sch_dir_lib_source.cpp @@ -148,12 +148,6 @@ static inline bool isDigit( char c ) } -static inline bool isDigit( char c ) -{ - return c >= '0' && c <= '9'; -} - - /** * Function endsWithRev * returns a pointer to the final string segment: "revN[N..]" or NULL if none. diff --git a/new/sch_lpid.cpp b/new/sch_lpid.cpp index 5d73680963..12e3dfd740 100644 --- a/new/sch_lpid.cpp +++ b/new/sch_lpid.cpp @@ -24,7 +24,7 @@ #include #include - +#include using namespace SCH; @@ -58,17 +58,74 @@ const char* EndsWithRev( const char* start, const char* tail, char separator ) } -LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) +//----------------------------------------- + +// These all return -1 on success, or >= 0 if there is an error at a +// particular character offset into their respectives arguments. + +static inline int okLogical( const STRING& aField ) { + // std::string::npos is largest positive number, casting to int makes it -1. + // Returning that means success. + return int( aField.find_first_of( ":/" ) ); +} + +static inline int okBase( const STRING& aField ) +{ + int offset = int( aField.find_first_of( ":/" ) ); + if( offset != -1 ) + return offset; + + // cannot be empty + if( !aField.size() ) + return 0; + + return offset; // ie. -1 +} + +static inline int okCategory( const STRING& aField ) +{ + return int( aField.find_first_of( ":/" ) ); +} + +static int okRevision( const STRING& aField ) +{ + char rev[32]; // C string for speed + + if( aField.size() >= 4 && aField.size() <= sizeof(rev)-3 ) + { + strcpy( rev, "x/" ); + strcat( rev, aField.c_str() ); + + if( EndsWithRev( rev ) == rev+2 ) + return -1; // success + } + + return 0; // first character position "is in error", is best we can do. +} + +//----------------------------------------- + + +int LPID::Parse( const STRING& aLPID ) +{ + logical.clear(); + category.clear(); + baseName.clear(); + revision.clear(); + const char* rev = EndsWithRev( aLPID ); size_t revNdx; size_t partNdx; size_t baseNdx; + int offset; //============================================== if( rev ) { - revNdx = rev - aLPID.c_str(); + revNdx = rev - aLPID.c_str(); + + // no need to check revision, EndsWithRev did that. revision = aLPID.substr( revNdx ); --revNdx; // back up to omit the '/' which preceeds the rev } @@ -78,7 +135,11 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) //=============================================== if( ( partNdx = aLPID.find( ':' ) ) != aLPID.npos ) { - logical = aLPID.substr( 0, partNdx ); + offset = SetLogicalLib( aLPID.substr( 0, partNdx ) ); + if( offset > -1 ) + { + return offset; + } ++partNdx; // skip ':' } else @@ -91,7 +152,11 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) if( base ) { baseNdx = base - aLPID.c_str(); - category = aLPID.substr( partNdx, baseNdx - partNdx ); + offset = SetCategory( aLPID.substr( partNdx, baseNdx - partNdx ) ); + if( offset > -1 ) + { + return offset + partNdx; + } ++baseNdx; // skip '/' } else @@ -100,7 +165,29 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) } //=============================================== - baseName = aLPID.substr( baseNdx, revNdx - baseNdx ); + offset = SetBaseName( aLPID.substr( baseNdx, revNdx - baseNdx ) ); + if( offset > -1 ) + { + return offset + baseNdx; + } + + return -1; +} + + +LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) +{ + int offset = Parse( aLPID ); + + if( offset != -1 ) + { + throw PARSE_ERROR( + _( "Illegal character found in LPID string" ), + wxConvertMB2WX( aLPID.c_str() ), + 0, + offset + ); + } } @@ -110,14 +197,14 @@ STRING LPID::GetLogicalLib() const } -bool LPID::SetLogicalLib( const STRING& aLogical ) +int LPID::SetLogicalLib( const STRING& aLogical ) { - if( aLogical.find_first_of( ":/" ) == STRING::npos ) + int offset = okLogical( aLogical ); + if( offset == -1 ) { logical = aLogical; - return true; } - return false; + return offset; } @@ -127,14 +214,14 @@ STRING LPID::GetCategory() const } -bool LPID::SetCategory( const STRING& aCategory ) +int LPID::SetCategory( const STRING& aCategory ) { - if( aCategory.find_first_of( ":/" ) == STRING::npos ) + int offset = okCategory( aCategory ); + if( offset == -1 ) { category = aCategory; - return true; } - return false; + return offset; } @@ -144,14 +231,14 @@ STRING LPID::GetBaseName() const } -bool LPID::SetBaseName( const STRING& aBaseName ) +int LPID::SetBaseName( const STRING& aBaseName ) { - if( aBaseName.find_first_of( ":/" ) == STRING::npos ) + int offset = okBase( aBaseName ); + if( offset == -1 ) { baseName = aBaseName; - return true; } - return false; + return offset; } @@ -178,23 +265,18 @@ STRING LPID::GetRevision() const } -bool LPID::SetRevision( const STRING& aRevision ) +int LPID::SetRevision( const STRING& aRevision ) { - STRING rev; - - rev += "x/"; - rev += aRevision; - - if( EndsWithRev( rev ) ) + int offset = okRevision( aRevision ); + if( offset == -1 ) { revision = aRevision; - return true; } - return false; + return offset; } -STRING LPID::GetFullText() const +STRING LPID::Format() const { STRING ret; @@ -248,12 +330,13 @@ void LPID::Test() LPID lpid( lpids[i] ); // parse // format - printf( "input:'%s' full:'%s' base:'%s' partName:'%s' cat:'%s'\n", + printf( "input:'%s' full:'%s' base:'%s' partName:'%s' cat:'%s' rev:'%s'\n", lpids[i], - lpid.GetFullText().c_str(), + lpid.Format().c_str(), lpid.GetBaseName().c_str(), lpid.GetPartName().c_str(), - lpid.GetCategory().c_str() + lpid.GetCategory().c_str(), + lpid.GetRevision().c_str() ); } } diff --git a/new/sch_lpid.h b/new/sch_lpid.h index bbbef6007b..1f6127cf6a 100644 --- a/new/sch_lpid.h +++ b/new/sch_lpid.h @@ -49,6 +49,10 @@ class LPID // aka GUID { public: + + LPID(); + + /** * Constructor LPID * takes aLPID string and parses it. A typical LPID string uses a logical @@ -58,6 +62,14 @@ public: */ LPID( const STRING& aLPID ) throw( PARSE_ERROR ); + /** + * Function Parse + * [re-]stuffs this LPID with the information from @a aLPID. + * @return int - minus 1 (i.e. -1) means success, >= 0 indicates the + * character offset into aLPID at which an error was detected. + */ + int Parse( const STRING& aLPID ); + /** * Function GetLogicalLib * returns the logical library portion of a LPID. There is not Set accessor @@ -69,9 +81,11 @@ public: /** * Function SetCategory * overrides the logical lib name portion of the LPID to @a aLogical, and can be empty. - * @return bool - true unless parameter has ':' or '/' in it. + * @return int - minus 1 (i.e. -1) means success, >= 0 indicates the + * character offset into the parameter at which an error was detected, usually + * because it contained '/' or ':'. */ - bool SetLogicalLib( const STRING& aLogical ); + int SetLogicalLib( const STRING& aLogical ); /** * Function GetCategory @@ -84,9 +98,11 @@ public: * Function SetCategory * overrides the category portion of the LPID to @a aCategory and is typically * either the empty string or a single word like "passives". - * @return bool - true unless parameter has ':' or '/' in it. + * @return int - minus 1 (i.e. -1) means success, >= 0 indicates the + * character offset into the parameter at which an error was detected, usually + * because it contained '/' or ':'. */ - bool SetCategory( const STRING& aCategory ); + int SetCategory( const STRING& aCategory ); /** * Function GetBaseName @@ -97,9 +113,11 @@ public: /** * Function SetBaseName * overrides the base name portion of the LPID to @a aBaseName - * @return bool - true unless parameter has ':' or '/' in it. + * @return int - minus 1 (i.e. -1) means success, >= 0 indicates the + * character offset into the parameter at which an error was detected, usually + * because it contained '/' or ':', or is blank. */ - bool SetBaseName( const STRING& aBaseName ); + int SetBaseName( const STRING& aBaseName ); /** * Function GetBaseName @@ -124,15 +142,17 @@ public: * Function SetRevision * overrides the revision portion of the LPID to @a aRevision and must * be in the form "rev" where "" is "1", "2", etc. - * @return bool - true unless parameter is not of the form "revN]N..]" + * @return int - minus 1 (i.e. -1) means success, >= 0 indicates the + * character offset into the parameter at which an error was detected, + * because it did not look like "rev23" */ - bool SetRevision( const STRING& aRevision ); + int SetRevision( const STRING& aRevision ); /** - * Function GetFullText + * Function Format * returns the full text of the LPID. */ - STRING GetFullText() const; + STRING Format() const; #if defined(DEBUG) static void Test(); From 69801630aae827418d8705120aba8d3c93cbaf76 Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Wed, 29 Dec 2010 14:30:45 -0600 Subject: [PATCH 2/3] changes --- CHANGELOG.txt | 4 +- include/kicad_exceptions.h | 9 +++- new/CMakeLists.txt | 27 ++++++---- new/design.h | 70 +++++++++++++++++-------- new/sch_dir_lib_source.cpp | 2 +- new/sch_dir_lib_source.h | 4 +- new/sch_lib.cpp | 21 ++++++++ new/sch_lib.h | 57 ++++++++++---------- new/sch_lib_table.cpp | 103 ++++++++++++++++++++++++++++++++++--- new/sch_lib_table.h | 64 +++++++++++++++++++---- new/sch_lpid.cpp | 16 +++--- new/sch_lpid.h | 37 ++++++++----- new/sch_part.cpp | 2 +- new/utf8.h | 58 +++++++++++++++++++++ 14 files changed, 366 insertions(+), 108 deletions(-) create mode 100644 new/utf8.h diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 53522cca1a..212ae4fcfc 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -7,8 +7,8 @@ email address. 2010-Dec-28 UPDATE Dick Hollenbeck ================================================================================ ++new: - Completed a good portion of /new class LIB_TABLE. - Starting LPID. + Completed most of /new class LIB_TABLE. + Completed all of /new class LPID. ++common: Tricked xnode.h into not issuing deprecation warnings. ++richio: diff --git a/include/kicad_exceptions.h b/include/kicad_exceptions.h index a05dbf268b..659f507690 100644 --- a/include/kicad_exceptions.h +++ b/include/kicad_exceptions.h @@ -25,8 +25,10 @@ #ifndef KICAD_EXCEPTIONS_H_ #define KICAD_EXCEPTIONS_H_ -/* Just exceptions -*/ +/** + * @ingroup exception_types + * @{ + */ #include @@ -84,4 +86,7 @@ struct PARSE_ERROR : public IO_ERROR } }; +/** @} exception_types */ + + #endif // KICAD_EXCEPTIONS_H_ diff --git a/new/CMakeLists.txt b/new/CMakeLists.txt index 518a45f54d..1ea96fec40 100644 --- a/new/CMakeLists.txt +++ b/new/CMakeLists.txt @@ -60,20 +60,14 @@ endif() include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) +#================================= +if( false ) + add_executable( test_dir_lib_source sch_dir_lib_source.cpp ) target_link_libraries( test_dir_lib_source ${wxWidgets_LIBRARIES} ) -add_executable( test_sch_lib_table - sch_lib_table.cpp - sch_lib_table_keywords.cpp - sch_lib.cpp - ${PROJECT_SOURCE_DIR}/common/richio.cpp - ${PROJECT_SOURCE_DIR}/common/dsnlexer.cpp - ) -target_link_libraries( test_sch_lib_table ${wxWidgets_LIBRARIES} ) - add_executable( test_sch_part sch_part.cpp ) target_link_libraries( test_sch_part ${wxWidgets_LIBRARIES} ) @@ -82,6 +76,21 @@ add_executable( test_lpid ) target_link_libraries( test_lpid ${wxWidgets_LIBRARIES} ) +endif( false ) + +#================================ + +add_executable( test_sch_lib_table + sch_lib_table.cpp + sch_lib_table_keywords.cpp + sch_lib.cpp + sch_lpid.cpp + sch_dir_lib_source.cpp + ${PROJECT_SOURCE_DIR}/common/richio.cpp + ${PROJECT_SOURCE_DIR}/common/dsnlexer.cpp + ) +target_link_libraries( test_sch_lib_table ${wxWidgets_LIBRARIES} ) + make_lexer( ${CMAKE_CURRENT_SOURCE_DIR}/sch_lib_table.keywords diff --git a/new/design.h b/new/design.h index 0aadf2ec57..831af35534 100644 --- a/new/design.h +++ b/new/design.h @@ -329,6 +329,10 @@ is used on the hood ornament. When aResults pointer is passed as an argument, I won't refer to this as 'returning' a value, but rather 'fetching' a result to distinguish between the two strategies. +

Functions named as lookupSomething() or LookupSomething() are to be understood +as "find and load if needed". This is different than a simple find operation only, in +that an implied load operation is also done, but only if needed. + @section architecture Architecture @@ -337,47 +341,59 @@ LIB_SOURCE. A library source is the backing to a library. The class name for a library in the new design is LIB.

-Show architecture here. - - Click here to see an architectural drawing. +You should see design architecture here */ /** - * \defgroup string_types STRING Types + * @defgroup string_types STRING Types * Provide some string types for use within the API. - * @{ */ -typedef std::string STRING; -typedef std::dequeue STRINGS; - -//typedef std::vector WSTRINGS; - - -const STRING StrEmpty = ""; - -/** @} string_types STRING Types */ /** - * \defgroup exception_types Exception Types + * @defgroup exception_types Exception Types * Provide some exception types for use within the API. - * @{ */ -/** @} exception_types Exception Types */ +/** + * Class HTTP_LIB_SOURCE + * implements a LIB_SOURCE to access a remote document root repository using http protocol. + */ +class HTTP_LIB_SOURCE : public LIB_SOURCE +{ + friend class LIB_TABLE; ///< constructor the LIB uses these functions. + +protected: + + /** + * Constructor ( const STRING& aSvnURL ) + * sets up a LIB_SOURCE using aSvnURI which points to a subversion + * repository. + * @see LIBS::GetLibrary(). + * + * @param aHttpURL is a full URL of a document root repo directory. Example might + * be "http://kicad.org/libs" + * + * @param aOptions is the options string from the library table. It can be any + * string that the LIB_SOURCE can parse and understand, perhaps using comma separation + * between options. Options and their syntax is LIB_SOURCE implementation specific. + */ + HTTP_LIB_SOURCE( const STRING& aHttpURL, const STRING& aOptions ) throws( IO_ERROR ); +}; /** * Class SVN_LIB_SOURCE - * implements a LIB_SOURCE in a subversion repository. + * implements a LIB_SOURCE to access a [remote or local] subversion repository + * using subversion client protocol. */ class SVN_LIB_SOURCE : public LIB_SOURCE { - friend class LIBS; ///< constructor the LIB uses these functions. + friend class LIB_TABLE; ///< constructor the LIB uses these functions. protected: @@ -389,8 +405,12 @@ protected: * * @param aSvnURL is a full URL of a subversion repo directory. Example might * be "svn://kicad.org/repos/library/trunk" + * + * @param aOptions is the options string from the library table. It can be any + * string that the LIB_SOURCE can parse and understand, perhaps using comma separation + * between options. Options and their syntax is LIB_SOURCE implementation specific. */ - SVN_LIB_SOURCE( const STRING& aSvnURL ) throws( IO_ERROR ); + SVN_LIB_SOURCE( const STRING& aSvnURL, const STRING& aOptions ) throws( IO_ERROR ); }; @@ -401,7 +421,7 @@ protected: */ class SCHEMATIC_LIB_SOURCE : public LIB_SOURCE { - friend class LIBS; ///< constructor the LIB uses these functions. + friend class LIB_TABLE; ///< constructor the LIB uses these functions. protected: @@ -414,8 +434,12 @@ protected: * * @param aSchematicFile is a full path and filename. Example: * "/home/user/kicadproject/design.sch" + * + * @param aOptions is the options string from the library table. It can be any + * string that the LIB_SOURCE can parse and understand, perhaps using comma separation + * between options. Options and their syntax is LIB_SOURCE implementation specific. */ - SCHEMATIC_LIB_SOURCE( const STRING& aSchematicFile ) throws( IO_ERROR ); + SCHEMATIC_LIB_SOURCE( const STRING& aSchematicFile, const STRING& aOptions ) throws( IO_ERROR ); }; @@ -445,4 +469,6 @@ public: /// @todo remove endsWithRev() in favor of EndsWithRev(), find home for it. +/// @todo add simple unit test infrastructure. + // EOF diff --git a/new/sch_dir_lib_source.cpp b/new/sch_dir_lib_source.cpp index cc9be1419b..db70c6fdad 100644 --- a/new/sch_dir_lib_source.cpp +++ b/new/sch_dir_lib_source.cpp @@ -590,7 +590,7 @@ void DIR_LIB_SOURCE::cacheOneDir( const STRING& aCategory ) throw( IO_ERROR ) } -#if 1 && defined(DEBUG) +#if 0 && defined(DEBUG) void DIR_LIB_SOURCE::Test( int argc, char** argv ) { diff --git a/new/sch_dir_lib_source.h b/new/sch_dir_lib_source.h index 1e6d09f38f..5e64ddec05 100644 --- a/new/sch_dir_lib_source.h +++ b/new/sch_dir_lib_source.h @@ -69,7 +69,7 @@ namespace SCH { */ class DIR_LIB_SOURCE : public LIB_SOURCE { - friend class LIBS; ///< LIBS::GetLib() can construct one. + friend class LIB_TABLE; ///< constructor is protected, LIB_TABLE can construct bool useVersioning; ///< use files with extension ".revNNN..", else not @@ -146,7 +146,7 @@ protected: * * @param aOptions is the options string from the library table, currently * the only supported option, that this LIB_SOURCE knows about is - * "userVersioning". If present means support versioning in the directory + * "useVersioning". If present means support versioning in the directory * tree, otherwise only a single version of each part is recognized, namely the * one without the ".revN[N..]" trailer. */ diff --git a/new/sch_lib.cpp b/new/sch_lib.cpp index 9a5f423da7..20b4a61bca 100644 --- a/new/sch_lib.cpp +++ b/new/sch_lib.cpp @@ -43,3 +43,24 @@ LIB::~LIB() delete sink; } + +PART* LIB::LookupPart( const LPID& aLPID ) throw( IO_ERROR ) +{ + return 0; +} + + +#if 0 && defined(DEBUG) + +void LIB::Test( int argc, char** argv ) throw( IO_ERROR ); +{ +} + +int main( int argc, char** argv ) +{ + LIB::Test( argc, argv ); + + return 0; +} + +#endif diff --git a/new/sch_lib.h b/new/sch_lib.h index fe907e0118..bcc1cff3dc 100644 --- a/new/sch_lib.h +++ b/new/sch_lib.h @@ -25,27 +25,13 @@ #ifndef SCH_LIB_H_ #define SCH_LIB_H_ -#include -#include - +#include #include -#ifdef DEBUG -#define D(x) x -#else -#define D(x) // nothing -#endif - - -typedef std::string STRING; -typedef std::deque STRINGS; -typedef STRINGS STRINGS; - -extern const STRING StrEmpty; - namespace SCH { +class LPID; class PART; @@ -83,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=StrEmpty ) + virtual void ReadPart( STRING* aResult, const STRING& aPartName, const STRING& aRev = "" ) throw( IO_ERROR ) = 0; /** @@ -113,7 +99,7 @@ protected: ///< derived classes must implement * * @param aResults is a place to put the fetched result, one category per STRING. */ - virtual void GetCategoricalPartNames( STRINGS* aResults, const STRING& aCategory=StrEmpty ) + virtual void GetCategoricalPartNames( STRINGS* aResults, const STRING& aCategory="" ) throw( IO_ERROR ) = 0; /** @@ -186,7 +172,7 @@ protected: ///< derived classes must implement * part is done, then LIB::ReloadPart() must be called on this same part * and all parts that inherit it must be reparsed. * @return STRING - if the LIB_SINK support revision numbering, then return a - * evision name that was next in the sequence, e.g. "rev22", else StrEmpty. + * revision name that was next in the sequence, e.g. "rev22", else "". */ virtual STRING WritePart( const STRING& aPartName, const STRING& aSExpression ) throw( IO_ERROR ) = 0; @@ -207,15 +193,15 @@ protected: */ class LIB { - friend class LIB_TABLE; ///< the LIB factory is LIB_TABLE + friend class LIB_TABLE; ///< protected constructor, LIB_TABLE may construct -protected: // constructor is not public, called from LIBS only. +protected: // constructor is not public, called from LIB_TABLE only. /** * Constructor LIB - * is not public and is only called from LIBS::GetLib() + * is not public and is only called from class LIB_TABLE * - * @param aLogicalLibrary is the name of a well know logical library, and is + * @param aLogicalLibrary is the name of a well known logical library, and is * known because it already exists in the library table. * * @param aSource is an open LIB_SOURCE whose ownership is @@ -246,13 +232,20 @@ public: //-------------------------------------- /** - * Function GetPart - * returns a PART given @a aPartName, such as "passives/R". - * @param aPartName is local to this LIB and does not have the logical - * library name prefixed. + * Function LookupPart + * returns a PART given @a aPartName, such as "passives/R". No ownership + * is given to the PART, it stays in the cache that is this LIB. + * + * @param aLPID is the part to lookup. The logicalLibName can be empty in it + * since yes, we know which LIB is in play. + * + * @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. */ - const PART* GetPart( const STRING& aPartName ) throw( IO_ERROR ); - + PART* LookupPart( const LPID& aLPID ) + throw( IO_ERROR ); /** * Function ReloadPart @@ -273,8 +266,7 @@ public: * creates cache entries for the very same parts if they do not already exist * in this LIB (i.e. cache). */ - STRINGS GetCategoricalPartNames( const STRING& aCategory=StrEmpty ) throw( IO_ERROR ); - + STRINGS GetCategoricalPartNames( const STRING& aCategory = "" ) throw( IO_ERROR ); //-----<.use delegates: source and sink>-------------------------------- @@ -317,6 +309,9 @@ public: return STRINGS(); } +#if defined(DEBUG) + static void Test( int argc, char** argv ) throw( IO_ERROR ); +#endif protected: diff --git a/new/sch_lib_table.cpp b/new/sch_lib_table.cpp index 5dd66ebfe0..4f3a05ed68 100644 --- a/new/sch_lib_table.cpp +++ b/new/sch_lib_table.cpp @@ -22,11 +22,13 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include -#include #include #include -#include +#include +#include //using namespace std; // screws up Doxygen using namespace SCH; @@ -185,14 +187,99 @@ STRINGS LIB_TABLE::GetLogicalLibs() } -PART* LIB_TABLE::GetPart( const LPID& aLogicalPartID ) throw( IO_ERROR ) +PART* LIB_TABLE::LookupPart( const LPID& aLogicalPartID, LIB* aLocalLib ) + throw( IO_ERROR ) { - // need LIPD done. - return 0; + LIB* lib = lookupLib( aLogicalPartID, aLocalLib ); + + return lib->LookupPart( aLogicalPartID ); } -const LIB_TABLE::ROW* LIB_TABLE::FindRow( const STRING& aLogicalName ) const +LIB* LIB_TABLE::lookupLib( const LPID& aLogicalPartID, LIB* aLocalLib ) + throw( IO_ERROR ) +{ + if( aLocalLib ) + { + return aLocalLib; + } + else + { + const STRING& logName = aLogicalPartID.GetLogicalLib(); + + if( logName.size() ) + { + 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; + } + else + { + STRING msg = "No logicalLibName in LPID and no localLib"; + throw IO_ERROR( msg ); + } + } + +// return NULL; never get here +} + + +void LIB_TABLE::loadLib( ROW* aRow ) throw( IO_ERROR ) +{ + assert( !aRow->lib ); // caller should know better. + + const STRING& libType = aRow->GetType(); + + if( !libType.compare( "dir" ) ) + { + // autor_ptr wrap source while we create sink, in case sink throws. + std::auto_ptr source( + new DIR_LIB_SOURCE( + aRow->GetFullURI(), + aRow->GetOptions() ) ); + + /* @todo load LIB_SINK + std::auto_ptr sink( + new DIR_LIB_SINK( + aRow->GetFullURI(), + aRow->GetOptions() ) ); + */ + + // LIB::LIB( const STRING& aLogicalLibrary, LIB_SOURCE* aSource, LIB_SINK* aSink = NULL ); + aRow->lib = new LIB( aRow->GetLogicalName(), source.release(), NULL ); + } + + else if( !libType.compare( "schematic" ) ) + { + // @todo code and load SCHEMATIC_LIB_SOURCE + } + + else if( !libType.compare( "subversion" ) ) + { + // @todo code and load SVN_LIB_SOURCE + } + + else if( !libType.compare( "http" ) ) + { + // @todo code and load HTTP_LIB_SOURCE + } +} + + +LIB_TABLE::ROW* LIB_TABLE::FindRow( const STRING& aLogicalName ) const { // this function must be *super* fast, so therefore should not instantiate // anything which would require using the heap. This function is the reason @@ -207,7 +294,7 @@ const LIB_TABLE::ROW* LIB_TABLE::FindRow( const STRING& aLogicalName ) const if( it != cur->rows.end() ) { // reference: http://myitcorner.com/blog/?p=361 - return (const LIB_TABLE::ROW*) it->second; // found + return (LIB_TABLE::ROW*) it->second; // found } // not found, search fall back table(s), if any @@ -247,7 +334,7 @@ bool LIB_TABLE::InsertRow( std::auto_ptr& aRow, bool doReplace ) } -#if 1 && defined(DEBUG) +#if 0 && defined(DEBUG) // build this with a Debug CMAKE_BUILD_TYPE diff --git a/new/sch_lib_table.h b/new/sch_lib_table.h index 5b860a37c1..fcb8458bf3 100644 --- a/new/sch_lib_table.h +++ b/new/sch_lib_table.h @@ -27,7 +27,9 @@ #include #include + #include +//#include class SCH_LIB_TABLE_LEXER; // outside namespace SCH, since make_lexer() Functions.cmake can't do namespace class OUTPUTFORMATTER; @@ -197,14 +199,14 @@ public: STRING fullURI; STRING options; - LIB* lib; + LIB* lib; ///< ownership of the loaded LIB is here }; - /** * Constructor LIB_TABLE * builds a library table by pre-pending this table fragment in front of * @a aFallBackTable. Loading of this table fragment is done by using Parse(). + * * @param aFallBackTable is another LIB_TABLE which is searched only when * a record is not found in this table. No ownership is taken of aFallBackTable. */ @@ -218,9 +220,9 @@ public: * *

      * (lib_table
-     *   (lib (logical LOGICAL)(type TYPE)(fullURI FULL_URI)(options OPTIONS))
-     *   (lib (logical LOGICAL)(type TYPE)(fullURI FULL_URI)(options OPTIONS))
-     *   (lib (logical LOGICAL)(type TYPE)(fullURI FULL_URI)(options OPTIONS))
+     *   (lib (logical LOGICAL)(type TYPE)(full_uri FULL_URI)(options OPTIONS))
+     *   (lib (logical LOGICAL)(type TYPE)(full_uri FULL_URI)(options OPTIONS))
+     *   (lib (logical LOGICAL)(type TYPE)(full_uri FULL_URI)(options OPTIONS))
      * 
* * When this function is called, the input token stream given by \a aLexer @@ -236,6 +238,7 @@ public: * Function Format * serializes this object as utf8 text to an OUTPUTFORMATTER, and tries to * make it look good using multiple lines and indentation. + * * @param out is an OUTPUTFORMATTER * @param nestLevel is the indentation level to base all lines of the output. * Actual indentation will be 2 spaces for each nestLevel. @@ -243,13 +246,26 @@ public: void Format( OUTPUTFORMATTER* out, int nestLevel ) const throw( IO_ERROR ); /** - * Function GetPart + * Function LookupPart * finds and loads a PART, and parses it. As long as the part is * accessible in any LIB_SOURCE, opened or not opened, this function * will find it and load it into its containing LIB, even if that means * having to open a LIB in this table that was not previously opened. + * + * @param aLogicalPartID holds the partName and may also hold the logicalLibName. If + * logicalLibName is empty, then @a aFallBackLib should not be NULL. + * + * @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 PART* - this will never be NULL, and no ownership is transfered because + * all PARTs live in LIBs. You only get to point to them in some LIB. If the PART + * cannot be found, then an exception is thrown. + * + * @throw IO_ERROR if any problem occurs or if the part cannot be found. */ - PART* GetPart( const LPID& aLogicalPartID ) throw( IO_ERROR ); + PART* LookupPart( const LPID& aLogicalPartID, LIB* aFallBackLib = NULL ) throw( IO_ERROR ); /** * Function GetLogicalLibs @@ -322,12 +338,42 @@ protected: // only a table editor can use these /** * Function FindRow - * returns a ROW* if aLogicalName is found in this table or in fallBack, else NULL. + * returns a ROW* if aLogicalName is found in this table or in any chained + * fallBack table fragment, else NULL. */ - const ROW* FindRow( const STRING& aLogicalName ) const; + ROW* FindRow( const STRING& aLogicalName ) const; private: + /** + * Function lookupLib + * 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 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. + * + * @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. + * If the LIB cannot be found, then an exception is thrown. + * + * @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 ); + + /** + * Function loadLib + * loads a LIB using information in @a aRow. Call only if LIB not + * already loaded. + */ + void loadLib( ROW* aRow ) throw( IO_ERROR ); + typedef boost::ptr_map ROWS; typedef ROWS::iterator ROWS_ITER; typedef ROWS::const_iterator ROWS_CITER; diff --git a/new/sch_lpid.cpp b/new/sch_lpid.cpp index 12e3dfd740..8ec603e0c5 100644 --- a/new/sch_lpid.cpp +++ b/new/sch_lpid.cpp @@ -23,8 +23,9 @@ */ #include +#include // _() + #include -#include using namespace SCH; @@ -61,7 +62,8 @@ const char* EndsWithRev( const char* start, const char* tail, char separator ) //----------------------------------------- // These all return -1 on success, or >= 0 if there is an error at a -// particular character offset into their respectives arguments. +// particular character offset into their respective arguments. If >=0, +// then that return value gives the character offset of the error. static inline int okLogical( const STRING& aField ) { @@ -97,7 +99,7 @@ static int okRevision( const STRING& aField ) strcpy( rev, "x/" ); strcat( rev, aField.c_str() ); - if( EndsWithRev( rev ) == rev+2 ) + if( EndsWithRev( rev, rev + strlen(rev) ) == rev+2 ) return -1; // success } @@ -191,7 +193,7 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) } -STRING LPID::GetLogicalLib() const +const STRING& LPID::GetLogicalLib() const { return logical; } @@ -208,7 +210,7 @@ int LPID::SetLogicalLib( const STRING& aLogical ) } -STRING LPID::GetCategory() const +const STRING& LPID::GetCategory() const { return category; } @@ -225,7 +227,7 @@ int LPID::SetCategory( const STRING& aCategory ) } -STRING LPID::GetBaseName() const +const STRING& LPID::GetBaseName() const { return baseName; } @@ -259,7 +261,7 @@ STRING LPID::GetPartName() const } -STRING LPID::GetRevision() const +const STRING& LPID::GetRevision() const { return revision; } diff --git a/new/sch_lpid.h b/new/sch_lpid.h index 1f6127cf6a..7653c2a8a8 100644 --- a/new/sch_lpid.h +++ b/new/sch_lpid.h @@ -25,14 +25,22 @@ #ifndef SCH_LPID_H_ #define SCH_LPID_H_ -#include // STRING +#include +#include + +namespace SCH { /** * Class LPID * (aka GUID) is a Logical Part ID and consists of various portions much like a URI. - * It relies heavily on a logical library name to hide where actual physical library - * sources reside. Its static functions serve as managers of the "library table" to - * map logical library names to actual library sources. + * It is a container for the separated portions of a logical part id STRING so they + * can be accessed individually. The various portions of an LPID are: + * logicalLibraryName, category, baseName, and revision. Only the baseName is + * mandatory. There is another construct called "partName" which consists of + * [category/]baseName. That is the category followed by a slash, but only if + * the category is not empty. + *

+ * partName = [category/]baseName *

* Example LPID string: * "kicad:passives/R/rev6". @@ -41,8 +49,9 @@ *

  • "kicad" is the logical library name. *
  • "passives" is the category. *
  • "passives/R" is the partname. - *
  • "rev6" is the revision number, which is optional. If missing then its - * delimiter should also not be present. + *
  • "rev6" is the revision, which is optional. If missing then its + * / delimiter should also not be present. A revision must begin with + * "rev" and be followed by at least one or more decimal digits. * * @author Dick Hollenbeck */ @@ -52,7 +61,6 @@ public: LPID(); - /** * Constructor LPID * takes aLPID string and parses it. A typical LPID string uses a logical @@ -76,7 +84,7 @@ public: * for this portion since it comes from the library table and is considered * read only here. */ - STRING GetLogicalLib() const; + const STRING& GetLogicalLib() const; /** * Function SetCategory @@ -92,7 +100,7 @@ public: * returns the category of this part id, "passives" in the example at the * top of the class description. */ - STRING GetCategory() const; + const STRING& GetCategory() const; /** * Function SetCategory @@ -108,7 +116,7 @@ public: * Function GetBaseName * returns the part name without the category. */ - STRING GetBaseName() const; + const STRING& GetBaseName() const; /** * Function SetBaseName @@ -120,7 +128,7 @@ public: int SetBaseName( const STRING& aBaseName ); /** - * Function GetBaseName + * Function GetPartName * returns the part name, i.e. category/baseName without revision. */ STRING GetPartName() const; @@ -134,9 +142,9 @@ public: /** * Function GetRevision - * returns the revision portion of the LPID or StrEmpty if none. + * returns the revision portion of the LPID. */ - STRING GetRevision() const; + const STRING& GetRevision() const; /** * Function SetRevision @@ -161,10 +169,11 @@ public: protected: STRING logical; ///< logical lib name or empty STRING category; ///< or empty - STRING baseName; ///< excludes category + STRING baseName; ///< without category STRING revision; ///< "revN[N..]" or empty }; +} // namespace SCH /** * Function EndsWithRev diff --git a/new/sch_part.cpp b/new/sch_part.cpp index 9a93109811..12f726d759 100644 --- a/new/sch_part.cpp +++ b/new/sch_part.cpp @@ -31,7 +31,7 @@ void PART::Parse( LIB* aLexer ) throw( PARSE_ERROR ) } -#if 1 +#if 0 && defined(DEBUG) int main( int argc, char** argv ) { diff --git a/new/utf8.h b/new/utf8.h new file mode 100644 index 0000000000..94109c88ef --- /dev/null +++ b/new/utf8.h @@ -0,0 +1,58 @@ + +#ifndef UTF8_H_ +#define UTF8_H_ + +#include +#include + +/** + * @ingroup string_types + * @{ + */ + + +/** + * Type STRING + * holds a sequence of 8 bit bytes that represent a sequence + * of variable multi-byte international characters, with unspecified encoding. + */ +typedef std::string STRING; + + +/** + * Type STRING_UTF8 + * 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 + * without ambiguity. Data files are those where Kicad controls the content. + * This is not the same thing as filenames, which are not file content. + * 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. + */ +typedef STRING STRING_UTF8; + + +/** + * Type STRINGS_UTF8 + * is an "array like" list of STRING_UTF8s + */ +typedef std::deque STRINGS_UTF8; + +typedef std::deque STRINGS; + + +/** @} string_types */ + + +// @todo this does not belong here +#ifndef D +#ifdef DEBUG +#define D(x) x +#else +#define D(x) // nothing +#endif +#endif + +#endif // UTF8_H_ From d2cc77ac7118d667cafa75685b1b35e51406330b Mon Sep 17 00:00:00 2001 From: Dick Hollenbeck Date: Thu, 30 Dec 2010 16:15:53 -0600 Subject: [PATCH 3/3] see CHANGELOG.txt --- .bzrignore | 8 ++- CHANGELOG.txt | 13 ++++ common/dsnlexer.cpp | 4 +- common/richio.cpp | 8 +-- eeschema/sch_component.h | 19 ------ include/kicad_exceptions.h | 92 -------------------------- include/richio.h | 129 ++++++++++++++++++++++++++++++++++++- new/CMakeLists.txt | 9 +++ new/sch_dir_lib_source.cpp | 83 ++++++++++++++---------- new/sch_dir_lib_source.h | 11 +++- new/sch_lib.cpp | 44 +++++++++++-- new/sch_lib.h | 19 +++--- new/sch_lib_table.cpp | 122 +++++++++++++++++++---------------- new/sch_lib_table.h | 14 ++-- new/sch_lpid.cpp | 67 +++++++------------ new/sch_lpid.h | 35 ++++++---- new/sch_part.cpp | 10 ++- new/sch_part.h | 55 +++++++++++++--- new/sweet.keywords | 2 + new/utf8.h | 25 +++---- pcbnew/specctra.cpp | 2 +- 21 files changed, 458 insertions(+), 313 deletions(-) delete mode 100644 include/kicad_exceptions.h create mode 100644 new/sweet.keywords 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 ); }