/new Miracles

This commit is contained in:
Dick Hollenbeck 2011-01-03 00:33:26 -06:00
parent 784c04203e
commit b942ebdc69
9 changed files with 497 additions and 81 deletions

View File

@ -400,7 +400,7 @@ DIR_LIB_SOURCE::~DIR_LIB_SOURCE()
void DIR_LIB_SOURCE::GetCategoricalPartNames( STRINGS* aResults, const STRING& aCategory )
throw( IO_ERROR )
{
PN_ITER limit = aCategory.size() ?
PN_ITER end = aCategory.size() ?
partnames.lower_bound( aCategory + char( '/' + 1 ) ) :
partnames.end();
@ -414,7 +414,7 @@ void DIR_LIB_SOURCE::GetCategoricalPartNames( STRINGS* aResults, const STRING& a
{
STRING partName;
while( it != limit )
while( it != end )
{
const char* rev = endsWithRev( *it );
@ -434,7 +434,32 @@ void DIR_LIB_SOURCE::GetCategoricalPartNames( STRINGS* aResults, const STRING& a
else
{
while( it != limit )
while( it != end )
aResults->push_back( *it++ );
}
}
void DIR_LIB_SOURCE::GetRevisions( STRINGS* aResults, const STRING& aPartName )
throw( IO_ERROR )
{
aResults->clear();
if( useVersioning )
{
STRING partName;
const char* rev = endsWithRev( aPartName );
if( rev )
// partName is substring which omits the rev and the separator
partName.assign( aPartName, 0, rev - aPartName.c_str() - 1 );
else
partName = aPartName;
PN_ITER it = partnames.upper_bound( partName +'/' );
PN_ITER end = partnames.lower_bound( partName + char( '/' +1 ) );
while( it != end )
aResults->push_back( *it++ );
}
}

View File

@ -174,10 +174,8 @@ protected:
void GetCategoricalPartNames( STRINGS* aResults, const STRING& aCategory = "" )
throw( IO_ERROR );
void GetRevisions( STRINGS* aResults, const STRING& aPartName ) throw( IO_ERROR )
{
// @todo
}
void GetRevisions( STRINGS* aResults, const STRING& aPartName )
throw( IO_ERROR );
void FindParts( STRINGS* aResults, const STRING& aQuery ) throw( IO_ERROR )
{

View File

@ -25,19 +25,95 @@
#include <memory> // std::auto_ptr
#include <wx/string.h>
#include <sch_lib.h>
#include <sch_lpid.h>
#include <sch_part.h>
#include <sweet_lexer.h>
#include <sch_lib_table.h>
#if 1
#include <map>
/*
The LIB part cache consist of a std::map of partnames without revisions at the top level.
Each top level map entry can point to another std::map which it owns and holds all the revisions
for that part name. At any point in the tree, there can be NULL pointers which
allow for lazy loading, including the very top most root pointer itself, which
is PARTS* parts. We use the key to hold the partName at one level, and revision
at the deeper nested level, and that key information may not be present within
right hand side of the map tuple.
1) Only things which are asked for are done.
2) Anything we learn we remember.
*/
namespace SCH {
class PART_REVS : public std::map< STRING, PART* >
{
// @todo provide an integer sort on revN.. strings here.
public:
~PART_REVS()
{
for( iterator it = begin(); it != end(); ++it )
{
delete it->second; // second may be NULL, no problem
}
}
};
class PARTS : public std::map< STRING, PART_REVS* >
{
public:
~PARTS()
{
for( iterator it = begin(); it != end(); ++it )
{
delete it->second; // second may be NULL, no problem
}
}
};
} // namespace SCH
#else // was nothing but grief:
#include <boost/ptr_container/ptr_map.hpp>
namespace SCH {
/// PARTS' key is revision, like "rev12", PART pointer may be null until loaded.
typedef boost::ptr_map< STRING, boost::nullable<PART> > PARTS;
typedef PARTS::iterator PARTS_ITER;
typedef PARTS::const_iterator PARTS_CITER;
/// PART_REVS' key is part name, w/o rev, PART pointer may be null until loaded.
typedef boost::ptr_map< STRING, boost::nullable<PARTS> > PART_REVS;
typedef PART_REVS::iterator PART_REVS_ITER;
typedef PART_REVS::const_iterator PART_REVS_CITER;
} // namespace SCH
#endif
using namespace SCH;
LIB::LIB( const STRING& aLogicalLibrary, LIB_SOURCE* aSource, LIB_SINK* aSink ) :
name( aLogicalLibrary ),
logicalName( aLogicalLibrary ),
source( aSource ),
sink( aSink )
sink( aSink ),
cachedCategories( false ),
parts( 0 )
{
}
@ -46,25 +122,99 @@ LIB::~LIB()
{
delete source;
delete sink;
delete parts;
}
const PART* LIB::findPart( const LPID& aLPID ) throw( IO_ERROR )
{
if( !parts )
{
parts = new PARTS;
source->GetCategoricalPartNames( &vfetch );
// insert a PART_REVS for each part name
for( STRINGS::const_iterator it = vfetch.begin(); it!=vfetch.end(); ++it )
{
// D(printf("findPart:%s\n", it->c_str() );)
(*parts)[*it] = new PART_REVS;
}
}
// load all the revisions for this part name
PARTS::iterator pi = parts->find( aLPID.GetPartName() );
PART_REVS* revs = pi != parts->end() ? pi->second : NULL;
// D(printf("revs:%p partName:%s\n", revs, aLPID.GetPartName().c_str() );)
// if the key for parts has no aLPID.GetPartName() the part is not in this lib
if( revs )
{
if( revs->size() == 0 )
{
// load all the revisions for this part.
source->GetRevisions( &vfetch, aLPID.GetPartName() );
// creat a PART_REV entry for revision, but leave the PART* NULL
for( STRINGS::const_iterator it = vfetch.begin(); it!=vfetch.end(); ++it )
{
// D(printf("findPartRev:%s\n", it->c_str() );)
(*revs)[*it] = 0;
}
}
PART_REVS::iterator result = revs->find( aLPID.GetPartNameAndRev() );
if( result != revs->end() )
{
if( !result->second ) // the PART has never been loaded before
{
result->second = new PART( this, aLPID.GetPartNameAndRev() );
}
return result->second;
}
// If caller did not say what revision, find the highest numbered one and return that.
// Otherwise he knew what he wanted specifically, and we do not have it.
if( !aLPID.GetRevision().size() && revs->size() )
{
result = revs->begin(); // sort order has highest rev first
if( !result->second ) // the PART has never been loaded before
{
result->second = new PART( this, LPID::Format( "", aLPID.GetPartName(), result->first ) );
}
return result->second;
}
}
return 0; // no such part name in this lib
}
PART* LIB::LookupPart( const LPID& aLPID, LIB_TABLE* aLibTable ) throw( IO_ERROR )
{
PART* part;
PART* part = (PART*) findPart( aLPID );
// If part not already cached
if( 1 /* @todo test cache */ )
if( !part ) // part does not exist in this lib
{
// load it.
part = new PART( this, aLPID.GetPartName(), aLPID.GetRevision() );
std::auto_ptr<PART> wrapped( part );
wxString msg = wxString::Format( _("part '%s' not found in lib %s" ),
wxString::FromUTF8( aLPID.GetPartNameAndRev().c_str() ).GetData(),
wxString::FromUTF8( logicalName.c_str() ).GetData() );
THROW_IO_ERROR( msg );
}
if( part->body.empty() )
{
// load body
source->ReadPart( &part->body, aLPID.GetPartName(), aLPID.GetRevision() );
#if defined(DEBUG)
#if 0 && defined(DEBUG)
const STRING& body = part->body;
printf( "body: %s", body.c_str() );
if( !body.size() || body[body.size()-1] != '\n' )
@ -74,12 +224,6 @@ PART* LIB::LookupPart( const LPID& aLPID, LIB_TABLE* aLibTable ) throw( IO_ERROR
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;

View File

@ -105,7 +105,9 @@ protected: ///< derived classes must implement
/**
* Function GetRevisions
* fetches all revisions for @a aPartName into @a aResults. Revisions are strings
* like "rev12", "rev279", and are library source agnostic. These
* like "rev12", "rev279", and are library source agnostic. These do not have to be
* in a contiguous order, but the first 3 characters must be "rev" and subsequent
* characters must consist of at least one decimal digit.
*/
virtual void GetRevisions( STRINGS* aResults, const STRING& aPartName )
throw( IO_ERROR ) = 0;
@ -183,6 +185,9 @@ protected:
};
class PARTS;
/**
* Class LIB
* is a cache of parts, and because the LIB_SOURCE is abstracted, there
@ -321,14 +326,31 @@ protected:
STR_UTF fetch; // scratch, used to fetch things, grows to worst case size.
STR_UTFS vfetch; // scratch, used to fetch things.
STRING name;
STRING logicalName;
LIB_SOURCE* source;
LIB_SINK* sink;
// STRING libraryURI;
STRINGS categories;
bool cachedCategories; /// < is true only after reading categories
/** parts are in various states of readiness:
* 1) not even loaded (if cachedParts is false)
* 2) present, but without member 'body' having been read() yet.
* 3) body has been read, but not parsed yet.
* 4) parsed and inheritance if any has been applied.
*/
PARTS* parts;
/**
* Function findPart
* finds a PART, returns NULL if cannot find.
* @throw IO_ERROR if there is some kind of communications error reading
* the original list of parts.
*/
const PART* findPart( const LPID& aLPID ) throw( IO_ERROR );
// PARTS parts;
};

View File

@ -375,7 +375,6 @@ private:
typedef boost::ptr_map<STRING, ROW> ROWS;
typedef ROWS::iterator ROWS_ITER;
typedef ROWS::const_iterator ROWS_CITER;
// typedef std::pair<ROWS_ITER, bool> ROW_PAIR;
ROWS rows;
LIB_TABLE* fallBack;

View File

@ -186,7 +186,7 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR )
{
THROW_PARSE_ERROR(
_( "Illegal character found in LPID string" ),
wxConvertMB2WX( aLPID.c_str() ),
wxString::FromUTF8( aLPID.c_str() ),
aLPID.c_str(),
0,
offset
@ -249,6 +249,36 @@ int LPID::SetBaseName( const STRING& aBaseName )
}
int LPID::SetPartName( const STRING& aPartName )
{
STRING category;
STRING base;
int offset;
int separation = int( aPartName.find_first_of( "/" ) );
if( separation != -1 )
{
category = aPartName.substr( 0, separation );
base = aPartName.substr( separation+1 );
}
else
{
// leave category empty
base = aPartName;
}
if( (offset = SetCategory( category )) != -1 )
return offset;
if( (offset = SetBaseName( base )) != -1 )
{
return offset + separation + 1;
}
return -1;
}
int LPID::SetRevision( const STRING& aRevision )
{
int offset = okRevision( aRevision );
@ -288,6 +318,121 @@ STRING LPID::Format() const
}
STRING LPID::GetPartNameAndRev() const
{
STRING ret;
if( category.size() )
{
ret += category;
ret += '/';
}
ret += baseName;
if( revision.size() )
{
ret += '/';
ret += revision;
}
return ret;
}
STRING LPID::Format( const STRING& aLogicalLib, const STRING& aPartName, const STRING& aRevision )
throw( PARSE_ERROR )
{
STRING ret;
int offset;
if( aLogicalLib.size() )
{
offset = okLogical( aLogicalLib );
if( offset != -1 )
{
THROW_PARSE_ERROR(
_( "Illegal character found in logical lib name" ),
wxString::FromUTF8( aLogicalLib.c_str() ),
aLogicalLib.c_str(),
0,
offset
);
}
ret += aLogicalLib;
ret += ':';
}
{
STRING category;
STRING base;
int separation = int( aPartName.find_first_of( "/" ) );
if( separation != -1 )
{
category = aPartName.substr( 0, separation );
base = aPartName.substr( separation+1 );
}
else
{
// leave category empty
base = aPartName;
}
if( (offset = okCategory( category )) != -1 )
{
THROW_PARSE_ERROR(
_( "Illegal character found in category" ),
wxString::FromUTF8( aRevision.c_str() ),
aRevision.c_str(),
0,
offset
);
}
if( (offset = okBase( base )) != -1 )
{
THROW_PARSE_ERROR(
_( "Illegal character found in base name" ),
wxString::FromUTF8( aRevision.c_str() ),
aRevision.c_str(),
0,
offset + separation + 1
);
}
if( category.size() )
{
ret += category;
ret += '/';
}
ret += base;
}
if( aRevision.size() )
{
offset = okRevision( aRevision );
if( offset != -1 )
{
THROW_PARSE_ERROR(
_( "Illegal character found in revision" ),
wxString::FromUTF8( aRevision.c_str() ),
aRevision.c_str(),
0,
offset
);
}
ret += '/';
ret += aRevision;
}
return ret;
}
#if 0 && defined(DEBUG)
// build this with Debug CMAKE_BUILD_TYPE

View File

@ -145,6 +145,23 @@ public:
return partName;
}
/**
* Function GetPartNameAndRev
* returns the part name with revision if any, i.e. [category/]baseName[/revN..]
*/
STRING GetPartNameAndRev() const;
/**
* Function SetPartName
* overrides the part name portion of the LPID to @a aPartName
* @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 more than one '/', or one or more ':', or is blank.
* A single '/' is allowed, since that is used to separate the category from the
* base name.
*/
int SetPartName( const STRING& aPartName );
/**
* Function GetRevision
* returns the revision portion of the LPID.
@ -170,6 +187,16 @@ public:
*/
STRING Format() const;
/**
* Function Format
* returns a STRING in the proper format as an LPID for a combination of
* aLogicalLib, aPartName, and aRevision.
* @throw PARSE_ERROR if any of the pieces are illegal.
*/
static STRING Format( const STRING& aLogicalLib, const STRING& aPartName, const STRING& aRevision="" )
throw( PARSE_ERROR );
#if defined(DEBUG)
static void Test();
#endif

View File

@ -31,9 +31,13 @@
using namespace SCH;
//-----<temporary home for PART sub objects, move after stable>------------------
#define MAX_INHERITANCE_NESTING 10 // no problem going larger
//-----<temporary home for PART sub objects, move after stable>------------------
struct XY {};
struct AT {};
//-----</temporary home for PART sub objects, move after stable>-----------------
@ -55,7 +59,8 @@ class PART_PARSER
{
SWEET_LEXER* in;
LIB_TABLE* libs;
int contains;
int contains; // separate from PART::contains until done
// so we can see what we inherited from base PART
public:
PART_PARSER( PART* aPart, SWEET_LEXER* aLexer, LIB_TABLE* aTable ) :
@ -66,35 +71,27 @@ public:
parsePart( aPart );
}
/// @param me = ja mir, the object getting stuffed, from its perspective
void parsePart( PART* me )
{
PART_T tok;
if( (tok = in->NextTok()) == T_LEFT )
tok = in->NextTok();
// a token "( part .." i.e. class PART
// Be flexible regarding the starting point of the stream.
// Caller may not have read the first two tokens out of the
// stream: T_LEFT and T_part, so ignore them if seen here.
// The 1st two tokens T_LEFT and T_part are then optional in the grammar.
if( tok == T_part )
void parseXY( XY* me )
{
in->NeedSYMBOLorNUMBER(); // read in part NAME_HINT, and toss
tok = in->NextTok();
}
// extends must be _first_ thing, if it is present at all, after NAME_HINT
if( tok == T_extends )
void parseAt( AT* me )
{
}
void parseExtends( PART* me )
{
PART* base;
int offset;
if( contains & PB(EXTENDS) )
in->Duplicate( tok );
in->Duplicate( T_extends );
in->NeedSYMBOLorNUMBER();
me->setExtends( new LPID() );
offset = me->extends->Parse( in->CurText() );
if( offset > -1 ) // -1 is success
THROW_PARSE_ERROR( _("invalid extends LPID"),
@ -102,15 +99,70 @@ public:
in->CurLine(),
in->CurLineNumber(),
in->CurOffset() + offset );
// we could be going in circles here, recursively, @todo add a max counter or stack chain
base = libs->LookupPart( *me->extends, me->Owner() );
// we could be going in circles here, recursively, or too deep, set limits
// and disallow extending from self (even indirectly)
int extendsDepth = 0;
for( PART* ancestor = base; ancestor && extendsDepth<MAX_INHERITANCE_NESTING;
++extendsDepth, ancestor = ancestor->base )
{
if( ancestor == me )
{
THROW_PARSE_ERROR( _("'extends' may not have self as any ancestor"),
in->CurSource(),
in->CurLine(),
in->CurLineNumber(),
in->CurOffset() );
}
}
if( extendsDepth == MAX_INHERITANCE_NESTING )
{
THROW_PARSE_ERROR( _("max allowed extends depth exceeded"),
in->CurSource(),
in->CurLine(),
in->CurLineNumber(),
in->CurOffset() );
}
me->inherit( *base );
me->base = base;
contains |= PB(EXTENDS);
}
/// @param me = ja mir, the object getting stuffed, from its perspective
void parsePart( PART* me )
{
PART_T tok = in->NextTok();
// Be flexible regarding the starting point of the stream.
// Caller may not have read the first two tokens out of the
// stream: T_LEFT and T_part, so ignore them if seen here.
// The 1st two tokens T_LEFT and T_part are then optional in the grammar.
if( tok == T_LEFT )
{
if( ( tok = in->NextTok() ) != T_part )
in->Expecting( T_part );
}
in->NeedSYMBOLorNUMBER(); // read in part NAME_HINT, and toss
tok = in->NextTok();
// extends must be _first_ thing, if it is present at all, after NAME_HINT
if( tok == T_extends )
{
parseExtends( me );
tok = in->NextTok();
}
for( ; tok!=T_RIGHT && tok!=T_EOF; tok = in->NextTok() )
for( ; tok!=T_RIGHT; tok = in->NextTok() )
{
if( tok==T_EOF )
in->Unexpected( _( "end of input" ) );
if( tok == T_LEFT )
tok = in->NextTok();
@ -212,22 +264,26 @@ public:
break;
}
}
contains |= PB(PARSED);
this->contains |= contains;
}
void parseAt( PART* me )
{
}
};
PART::PART( LIB* aOwner, const STRING& aPartName, const STRING& aRevision ) :
PART::PART( LIB* aOwner, const STRING& aPartNameAndRev ) :
owner( aOwner ),
contains( 0 ),
partName( aPartName ),
revision( aRevision ),
extends( 0 )
{}
partNameAndRev( aPartNameAndRev ),
extends( 0 ),
base( 0 )
{
// Our goal is to have class LIB only instantiate what is needed, so print here
// what it is doing. It is the only class where PART can be instantiated.
D(printf("PART::PART(%s)\n", aPartNameAndRev.c_str() );)
}
PART::~PART()
@ -254,9 +310,9 @@ void PART::inherit( const PART& other )
PART& PART::operator=( const PART& other )
{
owner = other.owner;
partName = other.partName;
revision = other.revision;
partNameAndRev = other.partNameAndRev;
body = other.body;
base = other.base;
setExtends( other.extends ? new LPID( *other.extends ) : 0 );

View File

@ -43,9 +43,8 @@ class LPID;
*/
enum PartBit
{
BODY, ///< body has been read in.
PARSED, ///< have parsed this part already, otherwise 'body' text must be parsed
EXTENDS, ///< saw and "extends" keyword, inheriting from another PART
EXTENDS, ///< saw "extends" keyword, inheriting from another PART
VALUE,
ANCHOR,
REFERENCE,
@ -55,6 +54,7 @@ enum PartBit
KEYWORDS,
};
/// Function PB
/// is a PartBit shifter for PART::contains field.
static inline const int PB( PartBit oneBitOnly )
@ -80,7 +80,7 @@ class PART
protected: // not likely to have C++ descendants, but protected none-the-less.
/// a protected constructor, only a LIB can instantiate a PART.
PART( LIB* aOwner, const STRING& aPartName, const STRING& aRevision );
PART( LIB* aOwner, const STRING& aPartNameAndRev );
/**
* Function inherit
@ -95,10 +95,10 @@ protected: // not likely to have C++ descendants, but protected none-the-le
LIB* owner; ///< which LIB am I a part of (pun if you want)
int contains; ///< has bits from Enum PartParts
STRING partName; ///< example "passives/R", immutable.
STRING revision; // @todo need a single search key, this won't do.
STRING partNameAndRev; ///< example "passives/R[/revN..]", immutable.
LPID* extends; ///< of base part, NULL if none, otherwise I own it.
PART* base; ///< which PART am I extending, if any. no ownership.
/// encapsulate the old version deletion, take ownership of @a aLPID
void setExtends( LPID* aLPID );
@ -107,6 +107,7 @@ protected: // not likely to have C++ descendants, but protected none-the-le
/// actually becomes cached in RAM.
STRING body;
// bool cachedRevisions; ///< allows lazy loading of revision of this same part name
// 3 separate lists for speed:
@ -136,7 +137,6 @@ public:
*/
LIB* Owner() { return owner; }
/**
* Function Parse
* translates a Sweet string into a binary form that is represented