Commit 0d3fd5d1 authored by dickelbeck's avatar dickelbeck

more specctra work

parent 1afb0498
...@@ -156,6 +156,7 @@ const static KEYWORD tokens[] = { ...@@ -156,6 +156,7 @@ const static KEYWORD tokens[] = {
TOKDEF(host_cad), TOKDEF(host_cad),
TOKDEF(host_version), TOKDEF(host_version),
TOKDEF(image), TOKDEF(image),
TOKDEF(image_conductor),
TOKDEF(image_image), TOKDEF(image_image),
TOKDEF(image_image_spacing), TOKDEF(image_image_spacing),
TOKDEF(image_outline_clearance), TOKDEF(image_outline_clearance),
...@@ -464,6 +465,25 @@ int LINE_READER::ReadLine() throw (IOError) ...@@ -464,6 +465,25 @@ int LINE_READER::ReadLine() throw (IOError)
//-----<LEXER>------------------------------------------------------------- //-----<LEXER>-------------------------------------------------------------
LEXER::LEXER( FILE* aFile, const wxString& aFilename ) :
reader( aFile, 4096 )
{
curTok = T_END;
stringDelimiter = '"';
filename = aFilename;
space_in_quoted_tokens = false;
// "start" should never change until we change the reader. The DSN
// format spec supports an include file mechanism but we can add that later
// using a std::stack to hold a stack of LINE_READERs to track nesting.
start = (char*) reader;
limit = start;
next = start;
}
int LEXER::findToken( const std::string& tok ) int LEXER::findToken( const std::string& tok )
{ {
// convert to lower case once, this should be faster than using strcasecmp() // convert to lower case once, this should be faster than using strcasecmp()
...@@ -485,16 +505,70 @@ int LEXER::findToken( const std::string& tok ) ...@@ -485,16 +505,70 @@ int LEXER::findToken( const std::string& tok )
return -1; return -1;
} }
wxString LEXER::GetTokenText( DSN_T aTok )
{
wxString ret;
if( aTok < 0 )
{
switch( aTok )
{
case T_QUOTE_DEF:
ret << _("'quoted text delimiter'");
break;
case T_DASH:
ret << wxT( "'-'" );
break;
case T_SYMBOL:
ret << _("'symbol'");
break;
case T_NUMBER:
ret << _("'number'");
break;
case T_RIGHT:
ret << wxT( "')'" );
break;
case T_LEFT:
ret << wxT( "'('" );
break;
case T_STRING:
ret << _("\"quoted string\"");
break;
case T_EOF:
ret << _("'end of file'");
break;
default:
;
}
}
else
{
ret << wxT("'") << CONV_FROM_UTF8( tokens[aTok].name ) << wxT("'");
}
return ret;
}
void LEXER::ThrowIOError( wxString aText, int charOffset ) throw (IOError)
{
aText << wxT(" ") << _("in file") << wxT(" \"") << filename
<< wxT("\" ") << _("on line") << wxT(" ") << reader.LineNumber()
<< wxT(" ") << _("at offset") << wxT(" ") << charOffset;
throw IOError( aText );
}
DSN_T LEXER::NextTok() throw (IOError) DSN_T LEXER::NextTok() throw (IOError)
{ {
char* head; char* cur = next;
char* cur; char* head = cur;
lastTok = curTok; lastTok = curTok;
cur = next;
if( curTok != T_EOF ) if( curTok != T_EOF )
{ {
if( cur >= limit ) if( cur >= limit )
...@@ -520,15 +594,27 @@ L_read: ...@@ -520,15 +594,27 @@ L_read:
// switching the string_quote character // switching the string_quote character
if( lastTok == T_string_quote ) if( lastTok == T_string_quote )
{ {
static const wxString errtxt( _("String delimiter must be a single character of ', \", or $"));
char cc = *cur;
switch( cc )
{
case '\'':
case '$':
case '"':
break;
default:
ThrowIOError( errtxt, CurOffset() );
}
curText.clear(); curText.clear();
curText += *cur; curText += cc;
head = cur+1; head = cur+1;
if( head<limit && *head!=')' && *head!='(' && !isspace(*head) ) if( head<limit && *head!=')' && *head!='(' && !isspace(*head) )
{ {
wxString errtxt(_("String delimiter char must be a single char") ); ThrowIOError( errtxt, CurOffset() );
ThrowIOError( errtxt, cur-start+1 );
} }
curTok = T_QUOTE_DEF; curTok = T_QUOTE_DEF;
...@@ -578,17 +664,17 @@ L_read: ...@@ -578,17 +664,17 @@ L_read:
// a quoted string // a quoted string
else if( *cur == stringDelimiter ) else if( *cur == stringDelimiter )
{ {
++cur; // skip over the leading " ++cur; // skip over the leading delimiter: ",', or $
head = cur; head = cur;
while( head<limit && *head!=stringDelimiter ) while( head<limit && !isStringTerminator( *head ) )
++head; ++head;
if( head >= limit ) if( head >= limit )
{ {
wxString errtxt(_("Un-terminated delimited string") ); wxString errtxt(_("Un-terminated delimited string") );
ThrowIOError( errtxt, cur-start+1 ); ThrowIOError( errtxt, CurOffset() );
} }
curText.clear(); curText.clear();
...@@ -627,8 +713,10 @@ L_read: ...@@ -627,8 +713,10 @@ L_read:
} }
} }
} }
exit: // single point of exit
exit: // single point of exit curOffset = cur - start;
next = head; next = head;
...@@ -640,7 +728,7 @@ exit: // single point of exit ...@@ -640,7 +728,7 @@ exit: // single point of exit
#if defined(STANDALONE) #if 0 && defined(STANDALONE)
// stand alone testing // stand alone testing
......
...@@ -42,11 +42,10 @@ namespace DSN { ...@@ -42,11 +42,10 @@ namespace DSN {
enum DSN_T { enum DSN_T {
// the first few are special (the uppercase ones) // the first few are special (the uppercase ones)
T_QUOTE_DEF = -9, T_QUOTE_DEF = -8,
T_DASH = -8, T_DASH = -7,
T_SYMBOL = -7, T_SYMBOL = -6,
T_NUMBER = -6, T_NUMBER = -5,
T_NONE = -5, // not a token
T_RIGHT = -4, // right bracket, ')' T_RIGHT = -4, // right bracket, ')'
T_LEFT = -3, // left bracket, '(' T_LEFT = -3, // left bracket, '('
T_STRING = -2, // a quoted string, stripped of the quotes T_STRING = -2, // a quoted string, stripped of the quotes
...@@ -157,6 +156,7 @@ enum DSN_T { ...@@ -157,6 +156,7 @@ enum DSN_T {
T_host_cad, T_host_cad,
T_host_version, T_host_version,
T_image, T_image,
T_image_conductor,
T_image_image, T_image_image,
T_image_image_spacing, T_image_image_spacing,
T_image_outline_clearance, T_image_outline_clearance,
...@@ -513,8 +513,11 @@ class LEXER ...@@ -513,8 +513,11 @@ class LEXER
LINE_READER reader; LINE_READER reader;
int stringDelimiter; int stringDelimiter;
bool space_in_quoted_tokens; ///< blank spaces within quoted strings
wxString filename; wxString filename;
int lastTok; ///< curTok from previous NextTok() call. int lastTok; ///< curTok from previous NextTok() call.
int curOffset; ///< offset within current line of the current token
DSN_T curTok; ///< the current token obtained on last NextTok() DSN_T curTok; ///< the current token obtained on last NextTok()
std::string curText; ///< the text of the current token std::string curText; ///< the text of the current token
...@@ -543,24 +546,22 @@ class LEXER ...@@ -543,24 +546,22 @@ class LEXER
*/ */
int findToken( const std::string& tok ); int findToken( const std::string& tok );
bool isStringTerminator( char cc )
public:
LEXER( FILE* aFile, const wxString& aFilename ) :
reader( aFile, 4096 )
{ {
curTok = T_NONE; if( !space_in_quoted_tokens && cc==' ' )
stringDelimiter = '"'; return true;
filename = aFilename;
// "start" should never change until we change the reader. The DSN if( cc == stringDelimiter )
// format spec supports an include file mechanism but we can add that later return true;
// using a std::stack to hold a stack of LINE_READERs to track nesting.
start = (char*) reader;
limit = start; return false;
next = start;
} }
public:
LEXER( FILE* aFile, const wxString& aFilename );
/** /**
* Function SetStringDelimiter * Function SetStringDelimiter
* changes the string delimiter from the default " to some other character * changes the string delimiter from the default " to some other character
...@@ -574,6 +575,19 @@ public: ...@@ -574,6 +575,19 @@ public:
stringDelimiter = aStringDelimiter; stringDelimiter = aStringDelimiter;
return old; return old;
} }
/**
* Function SetSpaceInQuotedTokens
* changes the setting controlling whether a space in a quoted string is
* a terminator
*/
bool SetSpaceInQuotedTokens( bool val )
{
bool old = space_in_quoted_tokens;
space_in_quoted_tokens = val;
return old;
}
/** /**
* Function NextTok * Function NextTok
...@@ -590,15 +604,11 @@ public: ...@@ -590,15 +604,11 @@ public:
* encapsulates the formatting of an error message which contains the exact * encapsulates the formatting of an error message which contains the exact
* location within the input file of a lexical error. * location within the input file of a lexical error.
*/ */
void ThrowIOError( wxString aText, int charOffset ) throw (IOError) void ThrowIOError( wxString aText, int charOffset ) throw (IOError);
{
aText << wxT(" ") << _("in file") << wxT(" \"") << filename
<< wxT("\" ") << _("on line") << wxT(" ") << reader.LineNumber()
<< wxT(" ") << _("at offset") << wxT(" ") << charOffset;
throw IOError( aText );
}
wxString GetTokenText( DSN_T aTok );
/** /**
* Function CurText * Function CurText
...@@ -618,6 +628,16 @@ public: ...@@ -618,6 +628,16 @@ public:
{ {
return curTok; return curTok;
} }
/**
* Function CurOffset
* returns the char offset within the current line, using a 1 based index.
* @return int - a one based index into the current line.
*/
int CurOffset()
{
return curOffset + 1;
}
}; };
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#include <boost/ptr_container/ptr_vector.hpp> #include <boost/ptr_container/ptr_vector.hpp>
#include <wx/ffile.h>
#include "fctsys.h" #include "fctsys.h"
#include "pcbstruct.h" #include "pcbstruct.h"
...@@ -43,7 +43,6 @@ namespace DSN { ...@@ -43,7 +43,6 @@ namespace DSN {
* is a base class for any DSN element. It is not a parent node so it * is a base class for any DSN element. It is not a parent node so it
* cannot contain other elements but it can be extended to hold fields * cannot contain other elements but it can be extended to hold fields
* for any DSN element which contains no other elements, only fields. * for any DSN element which contains no other elements, only fields.
*/
class ELEM class ELEM
{ {
protected: protected:
...@@ -58,26 +57,24 @@ public: ...@@ -58,26 +57,24 @@ public:
virtual ~ELEM() virtual ~ELEM()
{ {
printf("~ELEM(%p %d)\n", this, Type() ); // printf("~ELEM(%p %d)\n", this, Type() );
} }
DSN_T Type() { return type; }
virtual void Test()
{
printf("virtual Test()\n" );
}
}; };
*/
/** /**
* Class PARENT * Class ELEM
* is a base class holder for any DSN element. It can contain other * is a holder for any DSN element. It can contain other
* elements, including elements derived from this class. * elements, including elements derived from this class.
*/ */
class PARENT : public ELEM class ELEM
{ {
protected:
DSN_T type;
// see http://www.boost.org/libs/ptr_container/doc/ptr_sequence_adapter.html // see http://www.boost.org/libs/ptr_container/doc/ptr_sequence_adapter.html
typedef boost::ptr_vector<ELEM> ELEM_ARRAY; typedef boost::ptr_vector<ELEM> ELEM_ARRAY;
...@@ -86,17 +83,52 @@ public: ...@@ -86,17 +83,52 @@ public:
public: public:
PARENT( DSN_T aType ) : ELEM( DSN_T aType ) :
ELEM( aType ) type( aType )
{ {
} }
virtual ~PARENT() virtual ~ELEM()
{ {
printf("~PARENT(%p %d)\n", this, Type() ); // printf("~ELEM(%p %d)\n", this, Type() );
} }
DSN_T Type() { return type; }
//-----< list operations >-------------------------------------------- //-----< list operations >--------------------------------------------
/**
* Function FindElem
* finds a particular instance number of a given type of ELEM.
* @param aType The type of ELEM to find
* @param instanceNum The instance number of to find: 0 for first, 1 for second, etc.
* @return int - The index into the kids array or -1 if not found.
*/
int FindElem( DSN_T aType, int instanceNum )
{
int repeats=0;
for( unsigned i=0; i<kids.size(); ++i )
{
if( kids[i].Type() == aType )
{
if( repeats == instanceNum )
return i;
++repeats;
}
}
return -1;
}
/**
* Function Length
* returns the number ELEMs in this ELEM.
* @return int - the count of children
*/
int Length() const
{
return kids.size();
}
void Append( ELEM* aElem ) void Append( ELEM* aElem )
{ {
...@@ -114,7 +146,8 @@ public: ...@@ -114,7 +146,8 @@ public:
ELEM_ARRAY::auto_type ret = kids.release( kids.begin()+aIndex ); ELEM_ARRAY::auto_type ret = kids.release( kids.begin()+aIndex );
return ret.release(); return ret.release();
} }
/*
ELEM& operator[]( int aIndex ) ELEM& operator[]( int aIndex )
{ {
return kids[aIndex]; return kids[aIndex];
...@@ -124,14 +157,17 @@ public: ...@@ -124,14 +157,17 @@ public:
{ {
return kids[aIndex]; return kids[aIndex];
} }
*/
void Insert( int aIndex, ELEM* aElem ) void Insert( int aIndex, ELEM* aElem )
{ {
kids.insert( kids.begin()+aIndex, aElem ); kids.insert( kids.begin()+aIndex, aElem );
} }
ELEM* At( int aIndex ) ELEM* operator[]( int aIndex )
{ {
// we have varying sized object and are using polymorphism, so we
// must return a pointer not a reference.
return &kids[aIndex]; return &kids[aIndex];
} }
...@@ -139,20 +175,20 @@ public: ...@@ -139,20 +175,20 @@ public:
{ {
kids.erase( kids.begin()+aIndex ); kids.erase( kids.begin()+aIndex );
} }
}; };
/** /**
* Class SPECCTRA_DB * Class SPECCTRA_DB
* holds a DSN data tree, usually coming from a DSN file. * holds a DSN data tree, usually coming from a DSN file.
*/ */
class SPECCTRA_DB class SPECCTRA_DB
{ {
FILE* fp; LEXER* lexer;
ELEM* tree;
FILE* fp;
/** /**
...@@ -164,15 +200,49 @@ class SPECCTRA_DB ...@@ -164,15 +200,49 @@ class SPECCTRA_DB
*/ */
void print( const char* fmt, ... ); void print( const char* fmt, ... );
/**
* Function nextTok
* returns the next token from the lexer.
*/
DSN_T nextTok();
void expecting( DSN_T ) throw( IOError );
void expecting( const wxChar* text ) throw( IOError );
void doPCB( ELEM* growth ) throw(IOError);
void doPARSER( ELEM* growth ) throw(IOError);
void doRESOLUTION( ELEM* growth ) throw(IOError);
public: public:
SPECCTRA_DB( FILE* aFile ) : SPECCTRA_DB()
fp( aFile )
{ {
lexer = 0;
tree = 0;
fp = 0;
} }
~SPECCTRA_DB()
{
delete lexer;
delete tree;
if( fp )
fclose( fp );
}
/**
* Function Load
* is a recursive descent parser for a DSN file.
* @param filename The name of the dsn file to load.
* @throw IOError if there is a lexer or parser error.
*/
void Load( const wxString& filename ) throw( IOError );
void ThrowIOError( const wxChar* fmt, ... ) throw( IOError );
/** /**
* Function Export * Function Export
...@@ -180,10 +250,261 @@ public: ...@@ -180,10 +250,261 @@ public:
* @param aBoard The BOARD to save. * @param aBoard The BOARD to save.
*/ */
void Export( BOARD* aBoard ); void Export( BOARD* aBoard );
}; };
void SPECCTRA_DB::ThrowIOError( const wxChar* fmt, ... ) throw( IOError )
{
wxString errText;
va_list args;
va_start( args, fmt );
errText.PrintfV( fmt, args );
va_end( args );
throw IOError( errText );
}
void SPECCTRA_DB::expecting( DSN_T aTok ) throw( IOError )
{
wxString errText( _("Expecting") );
errText << wxT(" ") << lexer->GetTokenText( aTok );
lexer->ThrowIOError( errText, lexer->CurOffset() );
}
void SPECCTRA_DB::expecting( const wxChar* text ) throw( IOError )
{
wxString errText( _("Expecting") );
errText << wxT(" '") << text << wxT("'");
lexer->ThrowIOError( errText, lexer->CurOffset() );
}
DSN_T SPECCTRA_DB::nextTok()
{
return lexer->NextTok();
}
void SPECCTRA_DB::Load( const wxString& filename ) throw( IOError )
{
wxFFile file( filename.c_str() );
if( !file.IsOpened() )
{
ThrowIOError( _("Unable to open file \"%s\""), filename.GetData() );
}
delete lexer;
lexer = 0;
lexer = new LEXER( file.fp(), filename );
if( nextTok() != T_LEFT )
expecting( T_LEFT );
if( nextTok() != T_pcb )
expecting( T_pcb );
tree = new ELEM( T_pcb );
doPCB( tree );
}
void SPECCTRA_DB::doPCB( ELEM* growth ) throw( IOError )
{
ELEM* child;
DSN_T tok = nextTok();
switch( tok )
{
case T_SYMBOL:
case T_STRING:
break;
default:
expecting( T_STRING );
}
while( (tok = nextTok()) != T_EOF )
{
if( tok != T_LEFT )
expecting( T_LEFT );
tok = nextTok();
switch( tok )
{
case T_parser:
child = new ELEM( T_parser );
growth->Append( child );
doPARSER( child );
break;
case T_unit:
child = new ELEM( T_unit );
growth->Append( child );
break;
case T_resolution:
child = new ELEM( T_resolution );
growth->Append( child );
doRESOLUTION( child );
break;
case T_structure:
case T_placement:
case T_library:
break;
default:
expecting( wxT("parser, unit, resolution, or structure") );
}
}
}
void SPECCTRA_DB::doPARSER( ELEM* growth ) throw( IOError )
{
DSN_T tok;
while( (tok = nextTok()) != T_RIGHT )
{
if( tok != T_LEFT )
expecting( T_LEFT );
tok = nextTok();
switch( tok )
{
case T_string_quote:
tok = nextTok();
if( tok != T_QUOTE_DEF )
expecting( T_QUOTE_DEF );
lexer->SetStringDelimiter( (unsigned char) *lexer->CurText() );
break;
case T_space_in_quoted_tokens:
tok = nextTok();
if( tok!=T_on && tok!=T_off )
expecting( _("on or off") );
lexer->SetSpaceInQuotedTokens( tok==T_on );
break;
case T_host_cad:
tok = nextTok();
if( tok!=T_STRING && tok!=T_SYMBOL )
expecting( T_SYMBOL );
// @todo
break;
case T_host_version:
tok = nextTok();
if( tok!=T_STRING && tok!=T_SYMBOL )
expecting( T_SYMBOL );
// @todo
break;
case T_constant:
tok = nextTok();
if( tok!=T_STRING && tok!=T_SYMBOL )
expecting( T_SYMBOL );
tok = nextTok();
if( tok!=T_STRING && tok!=T_SYMBOL )
expecting( T_SYMBOL );
// @todo
break;
case T_write_resolution: // [(writee_resolution {<character> <positive_integer >})]
while( (tok = nextTok()) != T_RIGHT )
{
if( tok!=T_SYMBOL )
expecting( T_SYMBOL );
tok = nextTok();
if( tok!=T_NUMBER )
expecting( T_NUMBER );
// @todo
}
continue; // we ate the T_RIGHT
case T_routes_include: // [(routes_include {[testpoint | guides | image_conductor]})]
while( (tok = nextTok()) != T_RIGHT )
{
switch( tok )
{
case T_testpoint:
case T_guide:
case T_image_conductor:
// @todo
break;
default:
expecting( _("testpoint, guides, or image_conductor") );
}
}
continue; // we ate the T_RIGHT
case T_wires_include: // [(wires_include testpoint)]
tok = nextTok();
if( tok != T_testpoint )
expecting( T_testpoint );
// @todo
break;
case T_case_sensitive:
tok = nextTok();
if( tok!=T_on && tok!=T_off )
expecting( _("on or off") );
// @todo
break;
case T_via_rotate_first: // [(via_rotate_first [on | off])]
tok = nextTok();
if( tok!=T_on && tok!=T_off )
expecting( _("on or off") );
// @todo
break;
default:
expecting( wxT("parser_descriptor contents") );
}
tok = nextTok();
if( tok != T_RIGHT )
expecting( T_RIGHT );
}
}
void SPECCTRA_DB::doRESOLUTION( ELEM* growth ) throw(IOError)
{
DSN_T tok = nextTok();
switch( tok )
{
case T_inch:
case T_mil:
case T_cm:
case T_mm:
case T_um:
// @todo
break;
default:
expecting( wxT("inch, mil, cm, mm, or um") );
}
tok = nextTok();
if( tok != T_NUMBER )
expecting( T_NUMBER );
tok = nextTok();
if( tok != T_RIGHT )
expecting( T_RIGHT );
}
void SPECCTRA_DB::print( const char* fmt, ... ) void SPECCTRA_DB::print( const char* fmt, ... )
{ {
...@@ -210,15 +531,36 @@ using namespace DSN; ...@@ -210,15 +531,36 @@ using namespace DSN;
int main( int argc, char** argv ) int main( int argc, char** argv )
{ {
PARENT parent( T_pcb ); #if 0
ELEM parent( T_pcb );
PARENT* child = new PARENT( T_absolute ); ELEM* child = new ELEM( T_absolute );
parent.Append( child ); parent.Append( child );
parent.At(0)->Test(); parent[0]->Test();
child->Append( new ELEM( T_absolute ) ); child->Append( new ELEM( T_absolute ) );
#else
wxString filename( wxT("/tmp/fpcroute/Sample_1sided/demo_1sided.dsn") );
// wxString filename( wxT("/tmp/testdesigns/test.dsn") );
SPECCTRA_DB db;
try
{
db.Load( filename );
}
catch( IOError ioe )
{
printf( "%s\n", CONV_TO_UTF8(ioe.errorText) );
exit(1);
}
printf("loaded OK\n");
#endif
} }
......
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