Commit 99c1a9a1 authored by Dick Hollenbeck's avatar Dick Hollenbeck

changes

parent dc35a18c
......@@ -220,6 +220,13 @@ extern const unsigned ${result}_keyword_count;
using namespace DSN; // enum ${enum} is in this namespace
/**
* Classs ${RESULT}_LEXER
* is an automatically generated class using the TokenList2DnsLexer.cmake
* technology, based on keywords provided by file:
* ${inputFile}
*/
class ${RESULT}_LEXER : public DSNLEXER
{
public:
......
......@@ -77,6 +77,11 @@ 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} )
add_executable( test_lpid
sch_lpid.cpp
)
target_link_libraries( test_lpid ${wxWidgets_LIBRARIES} )
make_lexer(
${CMAKE_CURRENT_SOURCE_DIR}/sch_lib_table.keywords
......@@ -84,4 +89,3 @@ make_lexer(
${CMAKE_CURRENT_SOURCE_DIR}/sch_lib_table_keywords.cpp
ELT_T
)
......@@ -443,4 +443,6 @@ public:
} // namespace SCH
/// @todo remove endsWithRev() in favor of EndsWithRev(), find home for it.
// EOF
......@@ -140,6 +140,7 @@ static const char* strrstr( const char* haystack, const char* needle )
return ret;
}
#if 1 // @todo switch over to EndsWithRev() global
static inline bool isDigit( char c )
{
......@@ -185,6 +186,8 @@ static inline const char* endsWithRev( const STRING& aPartName, char separator )
return endsWithRev( aPartName.c_str(), aPartName.c_str()+aPartName.size(), separator );
}
#endif
// see struct BY_REV
bool BY_REV::operator() ( const STRING& s1, const STRING& s2 ) const
......@@ -587,9 +590,9 @@ void DIR_LIB_SOURCE::cacheOneDir( const STRING& aCategory ) throw( IO_ERROR )
}
#if (1 || defined( TEST_DIR_LIB_SOURCE )) && defined(DEBUG)
#if 1 && defined(DEBUG)
int main( int argc, char** argv )
void DIR_LIB_SOURCE::Test( int argc, char** argv )
{
STRINGS partnames;
STRINGS sweets;
......@@ -601,7 +604,8 @@ int main( int argc, char** argv )
// DIR_LIB_SOURCE uut( argv[1] ? argv[1] : "", "" );
DIR_LIB_SOURCE uut( argv[1] ? argv[1] : "", "useVersioning" );
// initially, only the NAME_CACHE sweets and STRING categories are loaded:
// show the cached content, only the directory information is cached,
// parts are cached in class LIB, not down here.
uut.Show();
uut.GetCategoricalPartNames( &partnames, "lions" );
......@@ -649,7 +653,12 @@ int main( int argc, char** argv )
{
printf( "exception: %s\n", (const char*) wxConvertWX2MB( ioe.errorText ) );
}
}
int main( int argc, char** argv )
{
DIR_LIB_SOURCE::Test( argc, argv );
return 0;
}
......
......@@ -132,8 +132,7 @@ class DIR_LIB_SOURCE : public LIB_SOURCE
*/
STRING makeFileName( const STRING& aPartName );
//protected:
public:
protected:
/**
* Constructor DIR_LIB_SOURCE( const STRING& aDirectoryPath )
......@@ -151,8 +150,7 @@ public:
* tree, otherwise only a single version of each part is recognized, namely the
* one without the ".revN[N..]" trailer.
*/
DIR_LIB_SOURCE( const STRING& aDirectoryPath, const STRING& aOptions = "" )
throw( IO_ERROR );
DIR_LIB_SOURCE( const STRING& aDirectoryPath, const STRING& aOptions = "" ) throw( IO_ERROR );
~DIR_LIB_SOURCE();
......@@ -187,6 +185,10 @@ public:
* will output a debug dump of contents.
*/
void Show();
public:
static void Test( int argc, char** argv );
#endif
};
......
......@@ -25,29 +25,18 @@
#include <sch_lib_table.h>
#include <sch_lib_table_lexer.h>
#include <sch_lpid.h>
#include <set>
using namespace std;
//using namespace std; // screws up Doxygen
using namespace SCH;
LIB_TABLE::LIB_TABLE( LIB_TABLE* aFallBackTable ) :
fallBack( aFallBackTable )
{
/* not copying fall back, simply search aFallBackTable separately if "logicalName not found".
if( aFallBackTable )
{
const ROWS& t = aFallBackTable->rows;
for( ROWS_CITER it = t.begin(); it != t.end(); ++it )
{
// our rows are empty, expect no collisions here
auto_ptr<ROW> row( new ROW( *it->second ) );
row->owner = this;
insert( row );
}
}
*/
// not copying fall back, simply search aFallBackTable separately
// if "logicalName not found".
}
......@@ -56,9 +45,9 @@ void LIB_TABLE::Parse( SCH_LIB_TABLE_LEXER* in ) throw( IO_ERROR )
/* grammar:
(lib_table
(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"))
(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))
)
note: "(lib_table" has already been read in.
......@@ -66,7 +55,7 @@ void LIB_TABLE::Parse( SCH_LIB_TABLE_LEXER* in ) throw( IO_ERROR )
ELT_T tok;
while( (tok = in->NextTok() ) != T_RIGHT && tok != T_EOF )
while( ( tok = in->NextTok() ) != T_RIGHT && tok != T_EOF )
{
// (lib (logical "LOGICAL")(type "TYPE")(full_uri "FULL_URI")(options "OPTIONS"))
......@@ -83,7 +72,7 @@ void LIB_TABLE::Parse( SCH_LIB_TABLE_LEXER* in ) throw( IO_ERROR )
in->NeedSYMBOLorNUMBER();
auto_ptr<ROW> row( new ROW( this ) );
std::auto_ptr<ROW> row( new ROW( this ) );
row->SetLogicalName( in->CurText() );
......@@ -127,12 +116,12 @@ void LIB_TABLE::Parse( SCH_LIB_TABLE_LEXER* in ) throw( IO_ERROR )
row->SetOptions( in->CurText() );
in->NeedRIGHT();
in->NeedRIGHT(); // teriminate the (lib..)
in->NeedRIGHT(); // terminate the (lib..)
// all logicalNames within this table fragment must be unique, so we do not
// replace. However a fallBack table can have a conflicting logicalName
// and ours will supercede that one since in FindLib() we search this table
// before any fall back.
// use doReplace in InsertRow(). However a fallBack table can have a
// conflicting logicalName and ours will supercede that one since in
// FindLib() we search this table before any fall back.
if( !InsertRow( row ) )
{
STRING msg;
......@@ -144,7 +133,6 @@ void LIB_TABLE::Parse( SCH_LIB_TABLE_LEXER* in ) throw( IO_ERROR )
throw IO_ERROR( msg );
}
}
return;
}
......@@ -172,11 +160,12 @@ void LIB_TABLE::ROW::Format( OUTPUTFORMATTER* out, int nestLevel ) const
STRINGS LIB_TABLE::GetLogicalLibs()
{
// only return unique logical library names. Use std::set::insert() to
// quietly reject any duplicates, which can happen in the fall back table(s).
set<STRING> unique;
STRINGS ret;
// Only return unique logical library names. Use std::set::insert() to
// quietly reject any duplicates, which can happen when encountering a duplicate
// logical lib name from one of the fall back table(s).
std::set<STRING> unique;
STRINGS ret;
const LIB_TABLE* cur = this;
do
......@@ -188,8 +177,8 @@ STRINGS LIB_TABLE::GetLogicalLibs()
} while( ( cur = cur->fallBack ) != 0 );
// return a sorted, unique set of STRINGS to caller
for( set<STRING>::const_iterator it = unique.begin(); it!=unique.end(); ++it )
// return a sorted, unique set of logical lib name STRINGS to caller
for( std::set<STRING>::const_iterator it = unique.begin(); it!=unique.end(); ++it )
ret.push_back( *it );
return ret;
......@@ -228,7 +217,7 @@ const LIB_TABLE::ROW* LIB_TABLE::FindRow( const STRING& aLogicalName ) const
}
bool LIB_TABLE::InsertRow( auto_ptr<ROW>& aRow, bool doReplace )
bool LIB_TABLE::InsertRow( std::auto_ptr<ROW>& aRow, bool doReplace )
{
// this does not need to be super fast.
......
......@@ -41,6 +41,41 @@ class PART;
* Class LIB_TABLE
* holds LIB_TABLE::ROW records, and can be searched in a very high speed
* way based on logical library name.
* <p>
* This class owns the <b>library table</b>, which is like fstab in concept and maps logical
* library name to library URI, type, and options. It has the following columns:
* <ul>
* <li> Logical Library Name
* <li> Library Type
* <li> Library URI. The full URI to the library source, form dependent on Type.
* <li> Options, used for access, such as password
* </ul>
* <p>
* The Library Type can be one of:
* <ul>
* <li> "dir"
* <li> "schematic" i.e. a parts list from another schematic.
* <li> "subversion"
* <li> "http"
* </ul>
* <p>
* For now, the Library URI types needed to support the various types can be one of those
* shown below, which are typical of each type:
* <ul>
* <li> "file://C:/mylibdir"
* <li> "file://home/user/kicadwork/jtagboard.sch"
* <li> "svn://kicad.org/partlib/trunk"
* <li> "http://kicad.org/partlib"
* </ul>
* <p>
* The applicable library table is built up from several additive rows (table fragments),
* and the final table is a (conceptual) merging of the table fragments. Two
* anticipated sources of the rows are a personal table, and a schematic resident
* table. The schematic resident table rows are considered a higher priority in
* the final dynamically assembled library table. A row in the schematic
* contribution to the library table takes precedence over the personal table
* if there is a collision on logical library name, otherwise the rows simply
* combine without issue to make up the applicable library table.
*
* @author Dick Hollenbeck
*/
......@@ -168,7 +203,8 @@ public:
/**
* Constructor LIB_TABLE
* builds a library table from an s-expression form of the library 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.
*/
......@@ -176,16 +212,15 @@ public:
/**
* Function Parse
* fills this object from information in the input stream \a aLexer, which
* fills this table fragment from information in the input stream \a aLexer, which
* is a DSNLEXER customized for the grammar needed to describe instances of this object.
* The entire textual element spec is <br>
* (lib_table (logical _yourfieldname_)(value _yourvalue_) visible))
*
* <pre>
* (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)(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))
* </pre>
*
* When this function is called, the input token stream given by \a aLexer
......@@ -193,7 +228,7 @@ public:
* identifying keyword and before the content specifying stuff.<br>
* (lib_table ^ (....) )
*
* @param aSpec is the input token stream of keywords and symbols.
* @param aLexer is the input token stream of keywords and symbols.
*/
void Parse( SCH_LIB_TABLE_LEXER* aLexer ) throw( IO_ERROR );
......
......@@ -22,15 +22,248 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <cstring>
#include <sch_lpid.h>
using namespace SCH;
static inline bool isDigit( char c )
{
return c >= '0' && c <= '9';
}
const char* EndsWithRev( const char* start, const char* tail, char separator )
{
bool sawDigit = false;
while( tail > start && isDigit( *--tail ) )
{
sawDigit = true;
}
// if sawDigit, tail points to the 'v' here.
if( sawDigit && tail-3 >= start )
{
tail -= 3;
if( tail[0]==separator && tail[1]=='r' && tail[2]=='e' && tail[3]=='v' )
{
return tail+1; // omit separator, return "revN[N..]"
}
}
return 0;
}
LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR )
{
const char* rev = EndsWithRev( aLPID );
size_t revNdx;
size_t partNdx;
size_t baseNdx;
//=====<revision>=========================================
if( rev )
{
revNdx = rev - aLPID.c_str();
revision = aLPID.substr( revNdx );
--revNdx; // back up to omit the '/' which preceeds the rev
}
else
revNdx = aLPID.size();
//=====<logical>==========================================
if( ( partNdx = aLPID.find( ':' ) ) != aLPID.npos )
{
logical = aLPID.substr( 0, partNdx );
++partNdx; // skip ':'
}
else
partNdx = 0;
//=====<rawName && category>==============================
// "length limited" search:
const char* base = (const char*) memchr( aLPID.c_str() + partNdx, '/', revNdx - partNdx );
if( base )
{
baseNdx = base - aLPID.c_str();
category = aLPID.substr( partNdx, baseNdx - partNdx );
++baseNdx; // skip '/'
}
else
{
baseNdx = partNdx;
}
//=====<baseName>==========================================
baseName = aLPID.substr( baseNdx, revNdx - baseNdx );
}
STRING LPID::GetLogicalLib() const
{
return logical;
}
bool LPID::SetLogicalLib( const STRING& aLogical )
{
if( aLogical.find_first_of( ":/" ) == STRING::npos )
{
logical = aLogical;
return true;
}
return false;
}
STRING LPID::GetCategory() const
{
return category;
}
bool LPID::SetCategory( const STRING& aCategory )
{
if( aCategory.find_first_of( ":/" ) == STRING::npos )
{
category = aCategory;
return true;
}
return false;
}
STRING LPID::GetBaseName() const
{
return baseName;
}
bool LPID::SetBaseName( const STRING& aBaseName )
{
if( aBaseName.find_first_of( ":/" ) == STRING::npos )
{
baseName = aBaseName;
return true;
}
return false;
}
STRING LPID::GetPartName() const
{
STRING ret;
// return [category/]baseName
if( category.size() )
{
ret += category;
ret += '/';
}
ret += baseName;
return ret;
}
STRING LPID::GetRevision() const
{
return revision;
}
bool LPID::SetRevision( const STRING& aRevision )
{
STRING rev;
rev += "x/";
rev += aRevision;
if( EndsWithRev( rev ) )
{
revision = aRevision;
return true;
}
return false;
}
STRING LPID::GetFullText() const
{
STRING ret;
if( logical.size() )
{
ret += logical;
ret += ':';
}
if( category.size() )
{
ret += category;
ret += '/';
}
ret += baseName;
if( revision.size() )
{
ret += '/';
ret += revision;
}
return ret;
}
#if 1 && defined(DEBUG)
// build this with Debug CMAKE_BUILD_TYPE
void LPID::Test()
{
static const char* lpids[] = {
"me:passives/R/rev0",
"passives/R/rev2",
":passives/R/rev3",
"C/rev22",
"passives/C22",
"R",
"me:R",
// most difficult:
"me:/R/rev0",
"me:R/rev0",
};
for( unsigned i=0; i<sizeof(lpids)/sizeof(lpids[0]); ++i )
{
// test some round tripping
LPID lpid( lpids[i] ); // parse
// format
printf( "input:'%s' full:'%s' base:'%s' partName:'%s' cat:'%s'\n",
lpids[i],
lpid.GetFullText().c_str(),
lpid.GetBaseName().c_str(),
lpid.GetPartName().c_str(),
lpid.GetCategory().c_str()
);
}
}
int main( int argc, char** argv )
{
LPID::Test();
return 0;
}
#endif
......@@ -25,8 +25,7 @@
#ifndef SCH_LPID_H_
#define SCH_LPID_H_
//#include <wx/string.h>
#include <sch_lib.h> // STRING
/**
* Class LPID
......@@ -45,42 +44,7 @@
* <li> "rev6" is the revision number, which is optional. If missing then its
* delimiter should also not be present.
* </ul>
* <p>
* This class owns the <b>library table</b>, which is like fstab in concept and maps logical
* library name to library URI, type, and options. It has the following columns:
* <ul>
* <li> Logical Library Name
* <li> Library Type
* <li> Library URI. The full URI to the library source, form dependent on Type.
* <li> Options, used for access, such as password
* </ul>
* <p>
* For now, the Library Type can be one of:
* <ul>
* <li> "dir"
* <li> "schematic" i.e. a parts list from another schematic.
* <li> "subversion"
* <li> "bazaar"
* <li> "http"
* </ul>
* <p>
* For now, the Library URI types needed to support the various types can be one of those
* shown below, which are typical of each type:
* <ul>
* <li> "file://C:/mylibdir"
* <li> "file://home/user/kicadwork/jtagboard.sch"
* <li> "svn://kicad.org/partlib/trunk"
* <li> "http://kicad.org/partlib"
* </ul>
* <p>
* The applicable library table is built up from several additive rows (table fragments),
* and the final table is a merging of the table fragments. Two anticipated sources of
* the rows are a personal table, and a schematic resident table. The schematic
* resident table rows are considered a higher priority in the final dynamically
* assembled library table. A row in the schematic contribution to the library table
* will take precedence over the personal table if there is a collision on logical
* library name, otherwise the rows simply combine without issue to make up the
* applicable library table.
* @author Dick Hollenbeck
*/
class LPID // aka GUID
{
......@@ -95,12 +59,19 @@ public:
LPID( const STRING& aLPID ) throw( PARSE_ERROR );
/**
* Function GetLogLib
* Function GetLogicalLib
* returns the logical library portion of a LPID. There is not Set accessor
* for this portion since it comes from the library table and is considered
* read only here.
*/
STRING GetLogLib() const;
STRING GetLogicalLib() const;
/**
* 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.
*/
bool SetLogicalLib( const STRING& aLogical );
/**
* Function GetCategory
......@@ -113,8 +84,35 @@ 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.
*/
bool SetCategory( const STRING& aCategory );
/**
* Function GetBaseName
* returns the part name without the category.
*/
STRING GetBaseName() const;
/**
* Function SetBaseName
* overrides the base name portion of the LPID to @a aBaseName
* @return bool - true unless parameter has ':' or '/' in it.
*/
bool SetBaseName( const STRING& aBaseName );
/**
* Function GetBaseName
* 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 );
*/
void SetCategory( const STRING& aCategory );
/**
* Function GetRevision
......@@ -126,14 +124,43 @@ public:
* Function SetRevision
* overrides the revision portion of the LPID to @a aRevision and must
* be in the form "rev<num>" where "<num>" is "1", "2", etc.
* @return bool - true unless parameter is not of the form "revN]N..]"
*/
void SetRevision( const STRING& aRevision );
bool SetRevision( const STRING& aRevision );
/**
* Function GetFullText
* returns the full text of the LPID.
*/
STRING GetFullText() const;
#if defined(DEBUG)
static void Test();
#endif
protected:
STRING logical; ///< logical lib name or empty
STRING category; ///< or empty
STRING baseName; ///< excludes category
STRING revision; ///< "revN[N..]" or empty
};
/**
* Function EndsWithRev
* returns a pointer to the final string segment: "revN[N..]" or NULL if none.
* @param start is the beginning of string segment to test, the partname or
* any middle portion of it.
* @param tail is a pointer to the terminating nul, or one past inclusive end of
* segment, i.e. the string segment of interest is [start,tail)
* @param separator is the separating byte, expected: '.' or '/', depending on context.
*/
const char* EndsWithRev( const char* start, const char* tail, char separator = '/' );
static inline const char* EndsWithRev( const STRING& aPartName, char separator = '/' )
{
return EndsWithRev( aPartName.c_str(), aPartName.c_str()+aPartName.size(), separator );
}
#endif // SCH_LPID_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment