Commit 0d3fd5d1 authored by dickelbeck's avatar dickelbeck
Browse files

more specctra work

parent 1afb0498
Loading
Loading
Loading
Loading
+100 −12
Original line number Original line Diff line number Diff line
@@ -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)
//-----<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()
@@ -486,15 +506,69 @@ int LEXER::findToken( const std::string& tok )
}
}




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:
        // 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:
        // 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();
@@ -630,6 +716,8 @@ L_read:


exit:                   // single point of exit
exit:                   // single point of exit
    
    
    curOffset = cur - start;
    
    next = head;
    next = head;
    
    
    return curTok;
    return curTok;
@@ -640,7 +728,7 @@ exit: // single point of exit






#if defined(STANDALONE)
#if 0 && defined(STANDALONE)


// stand alone testing
// stand alone testing


+46 −26
Original line number Original line Diff line number Diff line
@@ -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 {
    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
    
    
    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
     */ 
     */ 
    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
@@ -575,6 +576,19 @@ public:
        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
     * returns the next token found in the input file or T_EOF when reaching
     * returns the next token found in the input file or T_EOF when reaching
@@ -590,14 +604,10 @@ 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 );


    
    
    /**
    /**
@@ -619,6 +629,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;
    }
    
};
};




+370 −28
Original line number Original line Diff line number Diff line
@@ -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 {
 * 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:
    
    
    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,18 +83,53 @@ 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 )
    {
    {
        kids.push_back( aElem );
        kids.push_back( aElem );
@@ -115,6 +147,7 @@ public:
        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:
    {
    {
        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:
    {
    {
        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
     */
     */
    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:
     * @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;


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
    
    
}
}