1959 lines
48 KiB
C++
1959 lines
48 KiB
C++
|
|
/*
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2007-2008 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
|
* Copyright (C) 2007 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
|
|
*/
|
|
|
|
|
|
/* This source file implements export and import capabilities to the
|
|
specctra dsn file format. The grammar for that file format is documented
|
|
fairly well. There are classes for each major type of descriptor in the
|
|
spec.
|
|
|
|
Since there are so many classes in here, it may be helpful to generate
|
|
the Doxygen directory:
|
|
|
|
$ cd <kicadSourceRoot>
|
|
$ doxygen
|
|
Then you can view the html documentation in the <kicadSourceRoot>/doxygen
|
|
directory.
|
|
*/
|
|
|
|
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
|
|
#include <boost/ptr_container/ptr_vector.hpp>
|
|
|
|
#include <wx/ffile.h>
|
|
#include "fctsys.h"
|
|
#include "pcbstruct.h"
|
|
#include "dsn.h"
|
|
|
|
|
|
|
|
#define EDA_BASE // build_version.h behavior
|
|
#undef COMMON_GLOBL
|
|
#define COMMON_GLOBL // build_version.h behavior
|
|
#include "build_version.h"
|
|
|
|
|
|
namespace DSN {
|
|
|
|
|
|
class SPECCTRA_DB;
|
|
class PCB;
|
|
|
|
|
|
/**
|
|
* Class OUTPUTFORMATTER
|
|
* is an interface (abstract class) used to output ASCII text.
|
|
*/
|
|
class OUTPUTFORMATTER
|
|
{
|
|
public:
|
|
|
|
/**
|
|
* Function print
|
|
* formats and writes text to the output stream.
|
|
*
|
|
* @param nestLevel The multiple of spaces to preceed the output with.
|
|
* @param fmt A printf style format string.
|
|
* @param ... a variable list of parameters that will get blended into
|
|
* the output under control of the format string.
|
|
* @throw IOError if there is a problem outputting, such as a full disk.
|
|
*/
|
|
virtual void Print( int nestLevel, const char* fmt, ... ) throw( IOError ) = 0;
|
|
|
|
/**
|
|
* Function GetQuoteChar
|
|
* returns the quote character as a single character string for a given
|
|
* input wrapee string. Often the return value is "" the null string if
|
|
* there are no delimiters in the input string. If you want the quote_char
|
|
* to be assuredly not "", then pass in "(" as the wrappee.
|
|
* @param wrapee A string might need wrapping on each end.
|
|
* @return const char* - the quote_char as a single character string, or ""
|
|
* if the wrapee does not need to be wrapped.
|
|
*/
|
|
virtual const char* GetQuoteChar( const char* wrapee ) = 0;
|
|
};
|
|
|
|
|
|
struct POINT
|
|
{
|
|
float x;
|
|
float y;
|
|
|
|
POINT() { x=0.0; y=0.0; }
|
|
};
|
|
|
|
typedef std::vector<std::string> STRINGS;
|
|
|
|
|
|
|
|
/**
|
|
* Class ELEM
|
|
* is a holder for any DSN element. It can contain other
|
|
* elements, including elements derived from this class.
|
|
*/
|
|
class ELEM
|
|
{
|
|
protected:
|
|
DSN_T type;
|
|
|
|
ELEM* parent;
|
|
|
|
// see http://www.boost.org/libs/ptr_container/doc/ptr_sequence_adapter.html
|
|
typedef boost::ptr_vector<ELEM> ELEM_ARRAY;
|
|
|
|
ELEM_ARRAY kids; ///< ELEM pointers
|
|
|
|
public:
|
|
|
|
ELEM( DSN_T aType, ELEM* aParent = 0 );
|
|
|
|
virtual ~ELEM();
|
|
|
|
DSN_T Type() { return type; }
|
|
|
|
|
|
/**
|
|
* Function GetUnits
|
|
* returns the units for this section. Derived classes may override this
|
|
* to check for section specific overrides.
|
|
* @return DSN_T - one of the allowed values to <unit_descriptor>
|
|
*/
|
|
virtual DSN_T GetUnits()
|
|
{
|
|
if( parent )
|
|
return parent->GetUnits();
|
|
|
|
return T_inch;
|
|
}
|
|
|
|
|
|
/**
|
|
* Function Save
|
|
* writes this object out in ASCII form according to the SPECCTRA DSN format.
|
|
* @param out The formatter to write to.
|
|
* @param nestLevel A multiple of the number of spaces to preceed the output with.
|
|
* @throw IOError if a system error writing the output, such as a full disk.
|
|
*/
|
|
virtual void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError );
|
|
|
|
|
|
//-----< 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 = 0 );
|
|
|
|
|
|
/**
|
|
* Function Length
|
|
* returns the number of ELEMs in this ELEM.
|
|
* @return int - the count of children
|
|
*/
|
|
int Length() const
|
|
{
|
|
return kids.size();
|
|
}
|
|
|
|
void Append( ELEM* aElem )
|
|
{
|
|
kids.push_back( aElem );
|
|
}
|
|
|
|
ELEM* Replace( int aIndex, ELEM* aElem )
|
|
{
|
|
ELEM_ARRAY::auto_type ret = kids.replace( aIndex, aElem );
|
|
return ret.release();
|
|
}
|
|
|
|
ELEM* Remove( int aIndex )
|
|
{
|
|
ELEM_ARRAY::auto_type ret = kids.release( kids.begin()+aIndex );
|
|
return ret.release();
|
|
}
|
|
|
|
void Insert( int aIndex, ELEM* aElem )
|
|
{
|
|
kids.insert( kids.begin()+aIndex, aElem );
|
|
}
|
|
|
|
ELEM* At( int aIndex )
|
|
{
|
|
// we have varying sized objects and are using polymorphism, so we
|
|
// must return a pointer not a reference.
|
|
return &kids[aIndex];
|
|
}
|
|
|
|
ELEM* operator[]( int aIndex )
|
|
{
|
|
return At( aIndex );
|
|
}
|
|
|
|
void Delete( int aIndex )
|
|
{
|
|
kids.erase( kids.begin()+aIndex );
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Class PARSER
|
|
* is simply a configuration record per the SPECCTRA DSN file spec.
|
|
* It is not actually a parser.
|
|
*/
|
|
class PARSER : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
char string_quote;
|
|
bool space_in_quoted_tokens;
|
|
bool case_sensitive;
|
|
bool wires_include_testpoint;
|
|
bool routes_include_testpoint;
|
|
bool routes_include_guides;
|
|
bool routes_include_image_conductor;
|
|
bool via_rotate_first;
|
|
|
|
std::string const_id1;
|
|
std::string const_id2;
|
|
|
|
std::string host_cad;
|
|
std::string host_version;
|
|
|
|
|
|
public:
|
|
|
|
PARSER( ELEM* aParent ) :
|
|
ELEM( T_parser, aParent )
|
|
{
|
|
string_quote = '"';
|
|
space_in_quoted_tokens = false;
|
|
|
|
case_sensitive = false;
|
|
wires_include_testpoint = false;
|
|
routes_include_testpoint = false;
|
|
routes_include_guides = false;
|
|
routes_include_image_conductor = false;
|
|
via_rotate_first = true;
|
|
|
|
host_cad = "Kicad's PCBNEW";
|
|
host_version = CONV_TO_UTF8(g_BuildVersion);
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError );
|
|
};
|
|
|
|
|
|
class RESOLUTION : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
DSN_T units;
|
|
int value;
|
|
|
|
public:
|
|
RESOLUTION( ELEM* aParent ) :
|
|
ELEM( T_resolution, aParent )
|
|
{
|
|
units = T_inch;
|
|
value = 2540000;
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s %s %d)\n", LEXER::GetTokenText( Type() ),
|
|
LEXER::GetTokenText(units), value );
|
|
}
|
|
|
|
DSN_T GetUnits()
|
|
{
|
|
return units;
|
|
}
|
|
};
|
|
|
|
|
|
class UNIT : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
DSN_T units;
|
|
|
|
public:
|
|
UNIT( ELEM* aParent ) :
|
|
ELEM( T_unit, aParent )
|
|
{
|
|
units = T_inch;
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s %s)\n", LEXER::GetTokenText( Type() ),
|
|
LEXER::GetTokenText(units) );
|
|
}
|
|
|
|
DSN_T GetUnits()
|
|
{
|
|
return units;
|
|
}
|
|
};
|
|
|
|
|
|
class RECTANGLE : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
std::string layer_id;
|
|
|
|
POINT point0;
|
|
POINT point1;
|
|
|
|
public:
|
|
|
|
RECTANGLE( ELEM* aParent ) :
|
|
ELEM( T_rect, aParent )
|
|
{
|
|
}
|
|
|
|
~RECTANGLE()
|
|
{
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
const char* quote = out->GetQuoteChar( layer_id.c_str() );
|
|
|
|
out->Print( nestLevel, "(%s %s%s%s %f %f %f %f)\n",
|
|
LEXER::GetTokenText( Type() ),
|
|
quote, layer_id.c_str(), quote,
|
|
point0.x, point0.y,
|
|
point1.x, point1.y );
|
|
}
|
|
};
|
|
|
|
|
|
class PATH : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
std::string layer_id;
|
|
double aperture_width;
|
|
|
|
typedef std::vector<POINT> POINTS;
|
|
|
|
POINTS points;
|
|
|
|
DSN_T aperture_type;
|
|
|
|
public:
|
|
|
|
PATH( ELEM* aParent ) :
|
|
ELEM( T_path, aParent )
|
|
{
|
|
aperture_width = 0.0;
|
|
aperture_type = T_round;
|
|
}
|
|
|
|
~PATH()
|
|
{
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
const char* quote = out->GetQuoteChar( layer_id.c_str() );
|
|
|
|
out->Print( nestLevel, "(%s %s%s%s %f\n", LEXER::GetTokenText( Type() ),
|
|
quote, layer_id.c_str(), quote,
|
|
aperture_width );
|
|
|
|
for( unsigned i=0; i<points.size(); ++i )
|
|
{
|
|
out->Print( nestLevel+1, "%f %f\n", points[i].x, points[i].y );
|
|
}
|
|
|
|
if( aperture_type == T_square )
|
|
out->Print( nestLevel+1, "(aperture_type square)\n" );
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
};
|
|
|
|
|
|
class BOUNDARY : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
// see http://www.boost.org/libs/ptr_container/doc/ptr_sequence_adapter.html
|
|
typedef boost::ptr_vector<PATH> PATHS;
|
|
|
|
PATHS paths;
|
|
RECTANGLE* rectangle;
|
|
|
|
public:
|
|
|
|
BOUNDARY( ELEM* aParent, DSN_T aType = T_boundary ) :
|
|
ELEM( aType, aParent )
|
|
{
|
|
rectangle = 0;
|
|
}
|
|
|
|
~BOUNDARY()
|
|
{
|
|
delete rectangle;
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s\n", LEXER::GetTokenText( Type() ) );
|
|
|
|
if( rectangle )
|
|
rectangle->Save( out, nestLevel+1 );
|
|
else
|
|
{
|
|
for( unsigned i=0; i<paths.size(); ++i )
|
|
{
|
|
paths[i].Save( out, nestLevel+1 );
|
|
}
|
|
}
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
};
|
|
|
|
|
|
class VIA : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
STRINGS padstacks;
|
|
STRINGS spares;
|
|
|
|
public:
|
|
|
|
VIA( ELEM* aParent ) :
|
|
ELEM( T_via, aParent )
|
|
{
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s\n", LEXER::GetTokenText( Type() ) );
|
|
|
|
for( STRINGS::const_iterator i=padstacks.begin();
|
|
i!=padstacks.end(); ++i )
|
|
{
|
|
const char* quote = out->GetQuoteChar( i->c_str() );
|
|
|
|
out->Print( nestLevel+1, "%s%s%s\n", quote, i->c_str(), quote );
|
|
}
|
|
|
|
if( spares.size() )
|
|
{
|
|
out->Print( nestLevel+1, "(spare\n" );
|
|
|
|
for( STRINGS::const_iterator i=spares.begin();
|
|
i!=spares.end(); ++i )
|
|
{
|
|
const char* quote = out->GetQuoteChar( i->c_str() );
|
|
|
|
out->Print( nestLevel+2, "%s%s%s\n", quote, i->c_str(), quote );
|
|
}
|
|
|
|
out->Print( nestLevel+1, ")\n" );
|
|
}
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
};
|
|
|
|
|
|
class CONTROL : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
bool via_at_smd;
|
|
bool via_at_smd_grid_on;
|
|
|
|
|
|
public:
|
|
|
|
CONTROL( ELEM* aParent ) :
|
|
ELEM( T_control, aParent )
|
|
{
|
|
via_at_smd = false;
|
|
via_at_smd_grid_on = false;
|
|
}
|
|
|
|
~CONTROL()
|
|
{
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s\n", LEXER::GetTokenText( Type() ) );
|
|
|
|
//if( via_at_smd )
|
|
{
|
|
out->Print( nestLevel+1, "(via_at_smd %s", via_at_smd ? "on" : "off" );
|
|
if( via_at_smd_grid_on )
|
|
out->Print( 0, " grid %s", via_at_smd_grid_on ? "on" : "off" );
|
|
|
|
out->Print( 0, ")\n" );
|
|
}
|
|
|
|
for( int i=0; i<Length(); ++i )
|
|
{
|
|
At(i)->Save( out, nestLevel+1 );
|
|
}
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Class RULES
|
|
* corresponds to the <rule_descriptor> in the specctra dsn spec.
|
|
*/
|
|
class RULES : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
STRINGS rules; ///< rules are saved in std::string form.
|
|
|
|
public:
|
|
|
|
RULES( ELEM* aParent ) :
|
|
ELEM( T_rule, aParent )
|
|
{
|
|
}
|
|
|
|
~RULES()
|
|
{
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s\n", LEXER::GetTokenText( Type() ) );
|
|
|
|
for( STRINGS::const_iterator i = rules.begin(); i!=rules.end(); ++i )
|
|
out->Print( nestLevel+1, "%s\n", i->c_str() );
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
};
|
|
|
|
|
|
class LAYER : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
std::string name;
|
|
DSN_T layer_type; ///< one of: T_signal, T_power, T_mixed, T_jumper
|
|
int direction;
|
|
int cost; ///< [forbidden | high | medium | low | free | <positive_integer > | -1]
|
|
int cost_type; ///< T_length | T_way
|
|
RULES* rules;
|
|
STRINGS use_net;
|
|
|
|
struct PROPERTY
|
|
{
|
|
std::string name;
|
|
std::string value;
|
|
};
|
|
|
|
typedef std::vector<PROPERTY> PROPERTYS;
|
|
|
|
PROPERTYS propertys;
|
|
|
|
public:
|
|
|
|
LAYER( ELEM* aParent ) :
|
|
ELEM( T_layer, aParent )
|
|
{
|
|
layer_type = T_signal;
|
|
direction = -1;
|
|
cost = -1;
|
|
cost_type = -1;
|
|
|
|
rules = 0;
|
|
}
|
|
|
|
~LAYER()
|
|
{
|
|
delete rules;
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
const char* quote = out->GetQuoteChar( name.c_str() );
|
|
|
|
out->Print( nestLevel, "(%s %s%s%s\n", LEXER::GetTokenText( Type() ),
|
|
quote, name.c_str(), quote );
|
|
|
|
out->Print( nestLevel+1, "(type %s)\n", LEXER::GetTokenText( layer_type ) );
|
|
|
|
if( propertys.size() )
|
|
{
|
|
out->Print( nestLevel+1, "(property \n" );
|
|
|
|
for( PROPERTYS::const_iterator i = propertys.begin();
|
|
i != propertys.end(); ++i )
|
|
{
|
|
const char* quoteName = out->GetQuoteChar( i->name.c_str() );
|
|
const char* quoteValue = out->GetQuoteChar( i->value.c_str() );
|
|
|
|
out->Print( nestLevel+2, "(%s%s%s %s%s%s)\n",
|
|
quoteName, i->name.c_str(), quoteName,
|
|
quoteValue, i->value.c_str(), quoteValue );
|
|
}
|
|
out->Print( nestLevel+1, ")\n" );
|
|
}
|
|
|
|
if( direction != -1 )
|
|
out->Print( nestLevel+1, "(direction %s)\n",
|
|
LEXER::GetTokenText( (DSN_T)direction ) );
|
|
|
|
if( rules )
|
|
rules->Save( out, nestLevel+1 );
|
|
|
|
if( cost != -1 )
|
|
{
|
|
if( cost < 0 )
|
|
out->Print( nestLevel+1, "(cost %d", -cost ); // positive integer, stored as negative
|
|
else
|
|
out->Print( nestLevel+1, "(cost %s", LEXER::GetTokenText( (DSN_T)cost ) );
|
|
|
|
if( cost_type != -1 )
|
|
out->Print( 0, " (type %s)", LEXER::GetTokenText( (DSN_T)cost_type ) );
|
|
|
|
out->Print( 0, ")\n" );
|
|
}
|
|
|
|
if( use_net.size() )
|
|
{
|
|
out->Print( nestLevel+1, "(use_net" );
|
|
for( STRINGS::const_iterator i = use_net.begin(); i!=use_net.end(); ++i )
|
|
{
|
|
const char* quote = out->GetQuoteChar( i->c_str() );
|
|
out->Print( 0, " %s%s%s",
|
|
quote, i->c_str(), quote );
|
|
}
|
|
out->Print( 0, ")\n" );
|
|
}
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
};
|
|
|
|
|
|
class STRUCTURE : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
UNIT* unit;
|
|
BOUNDARY* boundary;
|
|
BOUNDARY* place_boundary;
|
|
VIA* via;
|
|
CONTROL* control;
|
|
RULES* rules;
|
|
|
|
typedef boost::ptr_vector<LAYER> LAYERS;
|
|
LAYERS layers;
|
|
|
|
public:
|
|
|
|
STRUCTURE( ELEM* aParent ) :
|
|
ELEM( T_structure, aParent )
|
|
{
|
|
unit = 0;
|
|
boundary = 0;
|
|
place_boundary = 0;
|
|
via = 0;
|
|
control = 0;
|
|
rules = 0;
|
|
}
|
|
|
|
~STRUCTURE()
|
|
{
|
|
delete unit;
|
|
delete boundary;
|
|
delete place_boundary;
|
|
delete via;
|
|
delete control;
|
|
delete rules;
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s\n", LEXER::GetTokenText( Type() ) );
|
|
|
|
if( unit )
|
|
unit->Save( out, nestLevel+1 );
|
|
|
|
for( unsigned i=0; i<layers.size(); ++i )
|
|
layers[i].Save( out, nestLevel+1 );
|
|
|
|
if( boundary )
|
|
boundary->Save( out, nestLevel+1 );
|
|
|
|
if( place_boundary )
|
|
place_boundary->Save( out, nestLevel+1 );
|
|
|
|
if( via )
|
|
via->Save( out, nestLevel+1 );
|
|
|
|
if( control )
|
|
control->Save( out, nestLevel+1 );
|
|
|
|
for( int i=0; i<Length(); ++i )
|
|
{
|
|
At(i)->Save( out, nestLevel+1 );
|
|
}
|
|
|
|
if( rules )
|
|
rules->Save( out, nestLevel+1 );
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
|
|
DSN_T GetUnits()
|
|
{
|
|
if( unit )
|
|
return unit->GetUnits();
|
|
|
|
return ELEM::GetUnits();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Class TOKPROP
|
|
* is a container for a single property whose value is another token.
|
|
* The name of the property is obtained from the DSN_T Type().
|
|
*/
|
|
class TOKPROP : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
DSN_T value;
|
|
|
|
public:
|
|
|
|
TOKPROP( ELEM* aParent, DSN_T aType ) :
|
|
ELEM( aType, aParent )
|
|
{
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s %s)\n", LEXER::GetTokenText( Type() ),
|
|
LEXER::GetTokenText( value ) );
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Class STRINGPROP
|
|
* is a container for a single property whose value is a string.
|
|
* The name of the property is obtained from the DSN_T.
|
|
*/
|
|
class STRINGPROP : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
std::string value;
|
|
|
|
public:
|
|
|
|
STRINGPROP( ELEM* aParent, DSN_T aType ) :
|
|
ELEM( aType, aParent )
|
|
{
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
const char* quote_char = out->GetQuoteChar( value.c_str() );
|
|
|
|
out->Print( nestLevel, "(%s %s%s%s)\n", LEXER::GetTokenText( Type() ),
|
|
quote_char, value.c_str(), quote_char );
|
|
}
|
|
};
|
|
|
|
|
|
class PCB : public ELEM
|
|
{
|
|
friend class SPECCTRA_DB;
|
|
|
|
std::string pcbname;
|
|
PARSER* parser;
|
|
RESOLUTION* resolution;
|
|
UNIT* unit;
|
|
STRUCTURE* structure;
|
|
|
|
public:
|
|
|
|
PCB() :
|
|
ELEM( T_pcb )
|
|
{
|
|
parser = 0;
|
|
resolution = 0;
|
|
unit = 0;
|
|
structure = 0;
|
|
}
|
|
|
|
~PCB()
|
|
{
|
|
delete parser;
|
|
delete resolution;
|
|
delete unit;
|
|
delete structure;
|
|
}
|
|
|
|
void Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
const char* quote = out->GetQuoteChar( pcbname.c_str() );
|
|
|
|
out->Print( nestLevel, "(%s %s%s%s\n", LEXER::GetTokenText( Type() ),
|
|
quote, pcbname.c_str(), quote );
|
|
|
|
if( parser )
|
|
parser->Save( out, nestLevel+1 );
|
|
|
|
if( resolution )
|
|
resolution->Save( out, nestLevel+1 );
|
|
|
|
if( unit )
|
|
unit->Save( out, nestLevel+1 );
|
|
|
|
if( structure )
|
|
structure->Save( out, nestLevel+1 );
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
|
|
DSN_T GetUnits()
|
|
{
|
|
if( unit )
|
|
return unit->GetUnits();
|
|
|
|
if( resolution )
|
|
return resolution->GetUnits();
|
|
|
|
return ELEM::GetUnits();
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Class SPECCTRA_DB
|
|
* holds a DSN data tree, usually coming from a DSN file.
|
|
*/
|
|
class SPECCTRA_DB : public OUTPUTFORMATTER
|
|
{
|
|
LEXER* lexer;
|
|
|
|
PCB* tree;
|
|
|
|
FILE* fp;
|
|
|
|
wxString filename;
|
|
|
|
std::string quote_char;
|
|
|
|
|
|
/**
|
|
* Function nextTok
|
|
* returns the next token from the lexer.
|
|
*/
|
|
DSN_T nextTok();
|
|
|
|
|
|
static bool isSymbol( DSN_T aTok )
|
|
{
|
|
// if aTok is >= 0, then it might be a coincidental match to a keyword.
|
|
return aTok==T_SYMBOL || aTok==T_STRING || aTok>=0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Function expecting
|
|
* throws an IOError exception with an input file specific error message.
|
|
* @param DSN_T The token type which was expected at the current input location.
|
|
* @throw IOError with the location within the input file of the problem.
|
|
*/
|
|
void expecting( DSN_T ) throw( IOError );
|
|
void expecting( const wxChar* text ) throw( IOError );
|
|
void unexpected( DSN_T aTok ) throw( IOError );
|
|
void unexpected( const char* text ) throw( IOError );
|
|
|
|
void doPCB( PCB* growth ) throw(IOError);
|
|
void doPARSER( PARSER* growth ) throw(IOError);
|
|
void doRESOLUTION( RESOLUTION* growth ) throw(IOError);
|
|
void doUNIT( UNIT* growth ) throw( IOError );
|
|
void doSTRUCTURE( STRUCTURE* growth ) throw( IOError );
|
|
void doBOUNDARY( BOUNDARY* growth ) throw( IOError );
|
|
void doRECTANGLE( RECTANGLE* growth ) throw( IOError );
|
|
void doPATH( PATH* growth ) throw( IOError );
|
|
void doSTRINGPROP( STRINGPROP* growth ) throw( IOError );
|
|
void doTOKPROP( TOKPROP* growth ) throw( IOError );
|
|
void doVIA( VIA* growth ) throw( IOError );
|
|
void doCONTROL( CONTROL* growth ) throw( IOError );
|
|
void doLAYER( LAYER* growth ) throw( IOError );
|
|
void doRULES( RULES* growth ) throw( IOError );
|
|
|
|
public:
|
|
|
|
SPECCTRA_DB()
|
|
{
|
|
lexer = 0;
|
|
tree = 0;
|
|
fp = 0;
|
|
quote_char += '"';
|
|
}
|
|
|
|
~SPECCTRA_DB()
|
|
{
|
|
delete lexer;
|
|
delete tree;
|
|
|
|
if( fp )
|
|
fclose( fp );
|
|
}
|
|
|
|
|
|
//-----<OUTPUTFORMATTER>-------------------------------------------------
|
|
void Print( int nestLevel, const char* fmt, ... ) throw( IOError );
|
|
const char* GetQuoteChar( const char* wrapee );
|
|
//-----</OUTPUTFORMATTER>------------------------------------------------
|
|
|
|
/**
|
|
* Function MakePCB
|
|
* makes a PCB with all the default ELEMs and parts on the heap.
|
|
*/
|
|
static PCB* MakePCB();
|
|
|
|
|
|
/**
|
|
* Function SetPCB
|
|
* deletes any existing PCB and replaces it with the given one.
|
|
*/
|
|
void SetPCB( const PCB* aPcb )
|
|
{
|
|
delete tree;
|
|
tree = (PCB*) aPcb;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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
|
|
* writes the given BOARD out as a SPECTRA DSN format file.
|
|
* @param aBoard The BOARD to save.
|
|
*/
|
|
void Export( wxString, BOARD* aBoard );
|
|
};
|
|
|
|
|
|
|
|
//-----<SPECCTRA_DB>-------------------------------------------------
|
|
|
|
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::GetTokenString( 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() );
|
|
}
|
|
|
|
void SPECCTRA_DB::unexpected( DSN_T aTok ) throw( IOError )
|
|
{
|
|
wxString errText( _("Unexpected") );
|
|
errText << wxT(" ") << LEXER::GetTokenString( aTok );
|
|
lexer->ThrowIOError( errText, lexer->CurOffset() );
|
|
}
|
|
|
|
void SPECCTRA_DB::unexpected( const char* text ) throw( IOError )
|
|
{
|
|
wxString errText( _("Unexpected") );
|
|
errText << wxT(" '") << CONV_FROM_UTF8(text) << wxT("'");
|
|
lexer->ThrowIOError( errText, lexer->CurOffset() );
|
|
}
|
|
|
|
|
|
DSN_T SPECCTRA_DB::nextTok()
|
|
{
|
|
DSN_T ret = lexer->NextTok();
|
|
return ret;
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::Load( const wxString& filename ) throw( IOError )
|
|
{
|
|
wxFFile file;
|
|
|
|
FILE* fp = wxFopen( filename, wxT("r") );
|
|
|
|
if( !fp )
|
|
{
|
|
ThrowIOError( _("Unable to open file \"%s\""), filename.GetData() );
|
|
}
|
|
|
|
file.Attach( fp ); // "exception safe" way to close the file.
|
|
|
|
delete lexer;
|
|
lexer = 0;
|
|
|
|
lexer = new LEXER( file.fp(), filename );
|
|
|
|
if( nextTok() != T_LEFT )
|
|
expecting( T_LEFT );
|
|
|
|
if( nextTok() != T_pcb )
|
|
expecting( T_pcb );
|
|
|
|
SetPCB( new PCB() );
|
|
|
|
doPCB( tree );
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doPCB( PCB* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
if( !isSymbol(tok) )
|
|
expecting( T_SYMBOL );
|
|
|
|
growth->pcbname = lexer->CurText();
|
|
|
|
while( (tok = nextTok()) != T_RIGHT )
|
|
{
|
|
if( tok != T_LEFT )
|
|
expecting( T_LEFT );
|
|
|
|
tok = nextTok();
|
|
switch( tok )
|
|
{
|
|
case T_parser:
|
|
growth->parser = new PARSER( growth );
|
|
doPARSER( growth->parser );
|
|
break;
|
|
|
|
case T_unit:
|
|
growth->unit = new UNIT( growth );
|
|
doUNIT( growth->unit );
|
|
break;
|
|
|
|
case T_resolution:
|
|
growth->resolution = new RESOLUTION( growth );
|
|
doRESOLUTION( growth->resolution );
|
|
break;
|
|
|
|
case T_structure:
|
|
growth->structure = new STRUCTURE( growth );
|
|
doSTRUCTURE( growth->structure );
|
|
break;
|
|
|
|
case T_placement:
|
|
case T_library:
|
|
break;
|
|
|
|
default:
|
|
unexpected( lexer->CurText() );
|
|
}
|
|
}
|
|
|
|
tok = nextTok();
|
|
if( tok != T_EOF )
|
|
expecting( T_EOF );
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doPARSER( PARSER* 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() );
|
|
growth->string_quote = *lexer->CurText();
|
|
quote_char = lexer->CurText();
|
|
break;
|
|
|
|
case T_space_in_quoted_tokens:
|
|
tok = nextTok();
|
|
if( tok!=T_on && tok!=T_off )
|
|
expecting( wxT("on|off") );
|
|
lexer->SetSpaceInQuotedTokens( tok==T_on );
|
|
growth->space_in_quoted_tokens = (tok==T_on);
|
|
break;
|
|
|
|
case T_host_cad:
|
|
tok = nextTok();
|
|
if( tok!=T_STRING && tok!=T_SYMBOL )
|
|
expecting( T_SYMBOL );
|
|
growth->host_cad = lexer->CurText();
|
|
break;
|
|
|
|
case T_host_version:
|
|
tok = nextTok();
|
|
if( tok!=T_STRING && tok!=T_SYMBOL )
|
|
expecting( T_SYMBOL );
|
|
growth->host_version = lexer->CurText();
|
|
break;
|
|
|
|
case T_constant:
|
|
tok = nextTok();
|
|
if( tok!=T_STRING && tok!=T_SYMBOL )
|
|
expecting( T_SYMBOL );
|
|
growth->const_id1 = lexer->CurText();
|
|
tok = nextTok();
|
|
if( tok!=T_STRING && tok!=T_SYMBOL )
|
|
expecting( T_SYMBOL );
|
|
growth->const_id2 = lexer->CurText();
|
|
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:
|
|
growth->routes_include_testpoint = true;
|
|
break;
|
|
case T_guide:
|
|
growth->routes_include_guides = true;
|
|
break;
|
|
case T_image_conductor:
|
|
growth->routes_include_image_conductor = true;
|
|
break;
|
|
default:
|
|
expecting( wxT("testpoint|guides|image_conductor") );
|
|
}
|
|
}
|
|
continue; // we ate the T_RIGHT
|
|
|
|
case T_wires_include: // [(wires_include testpoint)]
|
|
tok = nextTok();
|
|
if( tok != T_testpoint )
|
|
expecting( T_testpoint );
|
|
growth->routes_include_testpoint = true;
|
|
break;
|
|
|
|
case T_case_sensitive:
|
|
tok = nextTok();
|
|
if( tok!=T_on && tok!=T_off )
|
|
expecting( wxT("on|off") );
|
|
growth->case_sensitive = (tok==T_on);
|
|
break;
|
|
|
|
case T_via_rotate_first: // [(via_rotate_first [on | off])]
|
|
tok = nextTok();
|
|
if( tok!=T_on && tok!=T_off )
|
|
expecting( wxT("on|off") );
|
|
growth->via_rotate_first = (tok==T_on);
|
|
break;
|
|
|
|
default:
|
|
unexpected( lexer->CurText() );
|
|
}
|
|
|
|
tok = nextTok();
|
|
if( tok != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
}
|
|
|
|
void SPECCTRA_DB::doRESOLUTION( RESOLUTION* growth ) throw(IOError)
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
switch( tok )
|
|
{
|
|
case T_inch:
|
|
case T_mil:
|
|
case T_cm:
|
|
case T_mm:
|
|
case T_um:
|
|
growth->units = tok;
|
|
break;
|
|
default:
|
|
expecting( wxT("inch | mil | cm | mm | um") );
|
|
}
|
|
|
|
tok = nextTok();
|
|
if( tok != T_NUMBER )
|
|
expecting( T_NUMBER );
|
|
|
|
growth->value = atoi( lexer->CurText() );
|
|
|
|
tok = nextTok();
|
|
if( tok != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
|
|
void SPECCTRA_DB::doUNIT( UNIT* growth ) throw(IOError)
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
switch( tok )
|
|
{
|
|
case T_inch:
|
|
case T_mil:
|
|
case T_cm:
|
|
case T_mm:
|
|
case T_um:
|
|
growth->units = tok;
|
|
break;
|
|
default:
|
|
expecting( wxT("inch | mil | cm | mm | um") );
|
|
}
|
|
|
|
tok = nextTok();
|
|
if( tok != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
|
|
void SPECCTRA_DB::doSTRUCTURE( STRUCTURE* growth ) throw(IOError)
|
|
{
|
|
DSN_T tok;
|
|
|
|
while( (tok = nextTok()) != T_RIGHT )
|
|
{
|
|
if( tok != T_LEFT )
|
|
expecting( T_LEFT );
|
|
|
|
tok = nextTok();
|
|
switch( tok )
|
|
{
|
|
case T_unit:
|
|
if( growth->unit )
|
|
unexpected( T_unit );
|
|
growth->unit = new UNIT( growth );
|
|
doUNIT( growth->unit );
|
|
break;
|
|
|
|
case T_place_boundary:
|
|
L_place:
|
|
if( growth->place_boundary )
|
|
unexpected( T_place_boundary );
|
|
growth->place_boundary = new BOUNDARY( growth, T_place_boundary );
|
|
doBOUNDARY( growth->place_boundary );
|
|
break;
|
|
|
|
case T_boundary:
|
|
if( growth->boundary )
|
|
{
|
|
if( growth->place_boundary )
|
|
unexpected( T_boundary );
|
|
goto L_place;
|
|
}
|
|
growth->boundary = new BOUNDARY( growth );
|
|
doBOUNDARY( growth->boundary );
|
|
break;
|
|
|
|
case T_snap_angle:
|
|
STRINGPROP* stringprop;
|
|
stringprop = new STRINGPROP( growth, T_snap_angle );
|
|
growth->Append( stringprop );
|
|
doSTRINGPROP( stringprop );
|
|
break;
|
|
|
|
case T_via:
|
|
growth->via = new VIA( growth );
|
|
doVIA( growth->via );
|
|
break;
|
|
|
|
case T_control:
|
|
growth->control = new CONTROL( growth );
|
|
doCONTROL( growth->control );
|
|
break;
|
|
|
|
case T_layer:
|
|
LAYER* layer;
|
|
layer = new LAYER( growth );
|
|
growth->layers.push_back( layer );
|
|
doLAYER( layer );
|
|
break;
|
|
|
|
case T_rule:
|
|
growth->rules = new RULES( growth );
|
|
doRULES( growth->rules );
|
|
break;
|
|
|
|
default:
|
|
unexpected( lexer->CurText() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doBOUNDARY( BOUNDARY* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
if( tok != T_LEFT )
|
|
expecting( T_LEFT );
|
|
|
|
tok = nextTok();
|
|
if( tok == T_rect )
|
|
{
|
|
growth->rectangle = new RECTANGLE( growth );
|
|
doRECTANGLE( growth->rectangle );
|
|
if( nextTok() != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
else if( tok == T_path )
|
|
{
|
|
for(;;)
|
|
{
|
|
if( tok != T_path )
|
|
expecting( T_path );
|
|
|
|
PATH* path = new PATH( growth ) ;
|
|
growth->paths.push_back( path );
|
|
|
|
doPATH( path );
|
|
|
|
tok = nextTok();
|
|
if( tok == T_RIGHT )
|
|
break;
|
|
|
|
if( tok != T_LEFT )
|
|
expecting(T_LEFT);
|
|
|
|
tok = nextTok();
|
|
}
|
|
}
|
|
else
|
|
expecting( wxT("rect|path") );
|
|
}
|
|
|
|
void SPECCTRA_DB::doPATH( PATH* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
switch( tok )
|
|
{
|
|
case T_SYMBOL:
|
|
case T_STRING:
|
|
|
|
case T_pcb: // reserved layer names
|
|
case T_power:
|
|
case T_signal:
|
|
growth->layer_id = lexer->CurText();
|
|
break;
|
|
|
|
default:
|
|
expecting( T_SYMBOL );
|
|
}
|
|
|
|
if( nextTok() != T_NUMBER )
|
|
expecting( wxT("<aperture_width>") );
|
|
|
|
growth->aperture_width = strtod( lexer->CurText(), NULL );
|
|
|
|
POINT ptTemp;
|
|
|
|
tok = nextTok();
|
|
|
|
do
|
|
{
|
|
if( tok != T_NUMBER )
|
|
expecting( T_NUMBER );
|
|
ptTemp.x = strtod( lexer->CurText(), NULL );
|
|
|
|
if( nextTok() != T_NUMBER )
|
|
expecting( T_NUMBER );
|
|
ptTemp.y = strtod( lexer->CurText(), NULL );
|
|
|
|
growth->points.push_back( ptTemp );
|
|
|
|
} while( (tok = nextTok())!=T_RIGHT && tok!=T_LEFT );
|
|
|
|
if( tok == T_LEFT )
|
|
{
|
|
if( nextTok() != T_aperture_type )
|
|
expecting( T_aperture_type );
|
|
|
|
tok = nextTok();
|
|
if( tok!=T_round && tok!=T_square )
|
|
expecting( wxT("round|square") );
|
|
|
|
growth->aperture_type = tok;
|
|
|
|
if( nextTok() != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doRECTANGLE( RECTANGLE* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
if( !isSymbol( tok ) )
|
|
expecting( T_SYMBOL );
|
|
|
|
growth->layer_id = lexer->CurText();
|
|
|
|
if( nextTok() != T_NUMBER )
|
|
expecting( T_NUMBER );
|
|
growth->point0.x = strtod( lexer->CurText(), NULL );
|
|
|
|
if( nextTok() != T_NUMBER )
|
|
expecting( T_NUMBER );
|
|
growth->point0.y = strtod( lexer->CurText(), NULL );
|
|
|
|
if( nextTok() != T_NUMBER )
|
|
expecting( T_NUMBER );
|
|
growth->point1.x = strtod( lexer->CurText(), NULL );
|
|
|
|
if( nextTok() != T_NUMBER )
|
|
expecting( T_NUMBER );
|
|
growth->point1.y = strtod( lexer->CurText(), NULL );
|
|
|
|
if( nextTok() != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doSTRINGPROP( STRINGPROP* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
if( !isSymbol( tok ) )
|
|
expecting( T_SYMBOL );
|
|
|
|
growth->value = lexer->CurText();
|
|
|
|
if( nextTok() != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doTOKPROP( TOKPROP* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
if( tok<0 )
|
|
unexpected( lexer->CurText() );
|
|
|
|
growth->value = tok;
|
|
|
|
if( nextTok() != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doVIA( VIA* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok;
|
|
|
|
while( (tok = nextTok()) != T_RIGHT )
|
|
{
|
|
if( tok == T_LEFT )
|
|
{
|
|
if( nextTok() != T_spare )
|
|
expecting( T_spare );
|
|
|
|
while( (tok = nextTok()) != T_RIGHT )
|
|
{
|
|
if( !isSymbol( tok ) )
|
|
expecting( T_SYMBOL );
|
|
|
|
growth->spares.push_back( lexer->CurText() );
|
|
}
|
|
}
|
|
else if( isSymbol( tok ) )
|
|
{
|
|
growth->padstacks.push_back( lexer->CurText() );
|
|
}
|
|
else
|
|
unexpected( lexer->CurText() );
|
|
}
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doCONTROL( CONTROL* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok;
|
|
|
|
while( (tok = nextTok()) != T_RIGHT )
|
|
{
|
|
if( tok != T_LEFT )
|
|
expecting( T_LEFT );
|
|
|
|
tok = nextTok();
|
|
switch( tok )
|
|
{
|
|
case T_via_at_smd:
|
|
tok = nextTok();
|
|
if( tok!=T_on && tok!=T_off )
|
|
expecting( wxT("on|off") );
|
|
growth->via_at_smd = (tok==T_on);
|
|
if( nextTok() != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
break;
|
|
|
|
case T_off_grid:
|
|
case T_route_to_fanout_only:
|
|
case T_force_to_terminal_point:
|
|
case T_same_net_checking:
|
|
case T_checking_trim_by_pin:
|
|
case T_noise_calculation:
|
|
case T_noise_accumulation:
|
|
case T_include_pins_in_crosstalk:
|
|
case T_bbv_ctr2ctr:
|
|
case T_average_pair_length:
|
|
case T_crosstalk_model:
|
|
case T_roundoff_rotation:
|
|
case T_microvia:
|
|
case T_reroute_order_viols:
|
|
TOKPROP* tokprop;
|
|
tokprop = new TOKPROP( growth, tok ) ;
|
|
growth->Append( tokprop );
|
|
doTOKPROP( tokprop );
|
|
break;
|
|
|
|
default:
|
|
unexpected( lexer->CurText() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doLAYER( LAYER* growth ) throw( IOError )
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
if( !isSymbol(tok) )
|
|
expecting(T_SYMBOL);
|
|
|
|
growth->name = lexer->CurText();
|
|
|
|
while( (tok = nextTok()) != T_RIGHT )
|
|
{
|
|
if( tok != T_LEFT )
|
|
expecting( T_LEFT );
|
|
|
|
tok = nextTok();
|
|
switch( tok )
|
|
{
|
|
case T_type:
|
|
tok = nextTok();
|
|
if( tok!=T_signal && tok!=T_power && tok!=T_mixed && tok!=T_jumper )
|
|
expecting( wxT("signal|power|mixed|jumper") );
|
|
growth->layer_type = tok;
|
|
if( nextTok()!=T_RIGHT )
|
|
expecting(T_RIGHT);
|
|
break;
|
|
|
|
case T_rule:
|
|
growth->rules = new RULES( growth );
|
|
doRULES( growth->rules );
|
|
break;
|
|
|
|
case T_property:
|
|
{
|
|
LAYER::PROPERTY property; // construct it once here, append multiple times.
|
|
|
|
while( (tok = nextTok()) != T_RIGHT )
|
|
{
|
|
if( tok != T_LEFT )
|
|
expecting( T_LEFT );
|
|
|
|
tok = nextTok();
|
|
if( !isSymbol(tok) )
|
|
expecting( T_SYMBOL );
|
|
property.name = lexer->CurText();
|
|
|
|
tok = nextTok();
|
|
if( !isSymbol(tok) )
|
|
expecting( T_SYMBOL );
|
|
property.value = lexer->CurText();
|
|
|
|
growth->propertys.push_back( property );
|
|
|
|
if( nextTok() != T_RIGHT )
|
|
expecting( T_RIGHT );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case T_direction:
|
|
tok = nextTok();
|
|
switch( tok )
|
|
{
|
|
case T_horizontal:
|
|
case T_vertical:
|
|
case T_orthogonal:
|
|
case T_positive_diagonal:
|
|
case T_negative_diagonal:
|
|
case T_diagonal:
|
|
case T_off:
|
|
growth->direction = tok;
|
|
break;
|
|
default:
|
|
expecting( wxT("horizontal|vertical|orthogonal|positive_diagonal|negative_diagonal|diagonal|off") );
|
|
}
|
|
if( nextTok()!=T_RIGHT )
|
|
expecting(T_RIGHT);
|
|
break;
|
|
|
|
case T_cost:
|
|
tok = nextTok();
|
|
switch( tok )
|
|
{
|
|
case T_forbidden:
|
|
case T_high:
|
|
case T_medium:
|
|
case T_low:
|
|
case T_free:
|
|
growth->cost = tok;
|
|
break;
|
|
case T_NUMBER:
|
|
// store as negative so we can differentiate between
|
|
// DSN_T (positive) and T_NUMBER (negative)
|
|
growth->cost = -atoi( lexer->CurText() );
|
|
break;
|
|
default:
|
|
expecting( wxT("forbidden|high|medium|low|free|<positive_integer>|-1") );
|
|
}
|
|
tok = nextTok();
|
|
if( tok == T_LEFT )
|
|
{
|
|
if( nextTok() != T_type )
|
|
unexpected( lexer->CurText() );
|
|
|
|
tok = nextTok();
|
|
if( tok!=T_length && tok!=T_way )
|
|
expecting( wxT("length|way") );
|
|
|
|
growth->cost_type = tok;
|
|
if( nextTok()!=T_RIGHT )
|
|
expecting(T_RIGHT);
|
|
|
|
tok = nextTok();
|
|
}
|
|
if( tok!=T_RIGHT )
|
|
expecting(T_RIGHT);
|
|
break;
|
|
|
|
case T_use_net:
|
|
while( (tok = nextTok()) != T_RIGHT )
|
|
{
|
|
if( !isSymbol(tok) )
|
|
expecting( T_SYMBOL );
|
|
|
|
growth->use_net.push_back( lexer->CurText() );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
unexpected( lexer->CurText() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::doRULES( RULES* growth ) throw( IOError )
|
|
{
|
|
std::string builder;
|
|
int bracketNesting = 1; // we already saw the opening T_LEFT
|
|
|
|
while( bracketNesting != 0 )
|
|
{
|
|
DSN_T tok = nextTok();
|
|
|
|
switch( tok )
|
|
{
|
|
case T_LEFT: ++bracketNesting; break;
|
|
case T_RIGHT: --bracketNesting; break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
if( bracketNesting >= 1 )
|
|
{
|
|
if( lexer->PrevTok() != T_LEFT && tok!=T_RIGHT )
|
|
builder += ' ';
|
|
|
|
builder += lexer->CurText();
|
|
}
|
|
|
|
if( bracketNesting == 1 )
|
|
{
|
|
growth->rules.push_back( builder );
|
|
builder.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SPECCTRA_DB::Print( int nestLevel, const char* fmt, ... ) throw( IOError )
|
|
{
|
|
va_list args;
|
|
|
|
va_start( args, fmt );
|
|
|
|
int ret = 0;
|
|
|
|
for( int i=0; i<nestLevel; ++i )
|
|
{
|
|
ret = fprintf( fp, " " );
|
|
if( ret < 0 )
|
|
break;
|
|
}
|
|
|
|
if( ret<0 || vfprintf( fp, fmt, args )<0 )
|
|
ThrowIOError( _("System file error writing to file \"%s\""), filename.GetData() );
|
|
|
|
va_end( args );
|
|
}
|
|
|
|
|
|
const char* SPECCTRA_DB::GetQuoteChar( const char* wrapee )
|
|
{
|
|
while( *wrapee )
|
|
{
|
|
// if the string to be wrapped (wrapee) has a delimiter in it,
|
|
// return the quote_char so caller wraps the wrapee.
|
|
if( strchr( "\t ()", *wrapee++ ) )
|
|
return quote_char.c_str();
|
|
}
|
|
return ""; // can use an unwrapped string.
|
|
}
|
|
|
|
|
|
void SPECCTRA_DB::Export( wxString filename, BOARD* aBoard )
|
|
{
|
|
fp = wxFopen( filename, wxT("w") );
|
|
|
|
if( !fp )
|
|
{
|
|
ThrowIOError( _("Unable to open file \"%s\""), filename.GetData() );
|
|
}
|
|
|
|
tree->Save( this, 0 );
|
|
}
|
|
|
|
|
|
PCB* SPECCTRA_DB::MakePCB()
|
|
{
|
|
PCB* pcb = new PCB();
|
|
|
|
pcb->parser = new PARSER( pcb );
|
|
|
|
return pcb;
|
|
}
|
|
|
|
|
|
//-----<ELEM>---------------------------------------------------------------
|
|
|
|
ELEM::ELEM( DSN_T aType, ELEM* aParent ) :
|
|
type( aType ),
|
|
parent( aParent )
|
|
{
|
|
}
|
|
|
|
|
|
ELEM::~ELEM()
|
|
{
|
|
}
|
|
|
|
|
|
void ELEM::Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s\n", LEXER::GetTokenText( Type() ) );
|
|
|
|
for( int i=0; i<Length(); ++i )
|
|
{
|
|
At(i)->Save( out, nestLevel+1 );
|
|
}
|
|
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
|
|
|
|
int ELEM::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;
|
|
}
|
|
|
|
|
|
//-----<PARSER>-----------------------------------------------------------
|
|
|
|
void PARSER::Save( OUTPUTFORMATTER* out, int nestLevel ) throw( IOError )
|
|
{
|
|
out->Print( nestLevel, "(%s\n", LEXER::GetTokenText( Type() ) );
|
|
out->Print( nestLevel+1, "(string_quote %c)\n", string_quote );
|
|
out->Print( nestLevel+1, "(space_in_quoted_tokens %s)\n", space_in_quoted_tokens ? "on" : "off" );
|
|
out->Print( nestLevel+1, "(host_cad \"%s\")\n", host_cad.c_str() );
|
|
out->Print( nestLevel+1, "(host_version \"%s\")\n", host_version.c_str() );
|
|
|
|
if( const_id1.length()>0 || const_id2.length()>0 )
|
|
out->Print( nestLevel+1, "(constant %c%s%c %c%s%c)\n",
|
|
string_quote, const_id1.c_str(), string_quote,
|
|
string_quote, const_id2.c_str(), string_quote );
|
|
|
|
if( routes_include_testpoint || routes_include_guides || routes_include_image_conductor )
|
|
out->Print( nestLevel+1, "(routes_include%s%s%s)\n",
|
|
routes_include_testpoint ? " testpoint" : "",
|
|
routes_include_guides ? " guides" : "",
|
|
routes_include_image_conductor ? " image_conductor" : "");
|
|
|
|
if( wires_include_testpoint )
|
|
out->Print( nestLevel+1, "(wires_include testpoint)\n" );
|
|
|
|
if( !via_rotate_first )
|
|
out->Print( nestLevel+1, "(via_rotate_first off)\n" );
|
|
|
|
out->Print( nestLevel+1, "(case_sensitive %s)\n", case_sensitive ? "on" : "off" );
|
|
out->Print( nestLevel, ")\n" );
|
|
}
|
|
|
|
|
|
} // namespace DSN
|
|
|
|
|
|
using namespace DSN;
|
|
|
|
// a test to verify some of the list management functions.
|
|
|
|
int main( int argc, char** argv )
|
|
{
|
|
#if 0
|
|
ELEM parent( T_pcb );
|
|
|
|
ELEM* child = new ELEM( T_absolute );
|
|
|
|
parent.Append( child );
|
|
|
|
parent[0]->Test();
|
|
|
|
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;
|
|
bool failed = false;
|
|
|
|
try
|
|
{
|
|
db.Load( filename );
|
|
}
|
|
catch( IOError ioe )
|
|
{
|
|
printf( "%s\n", CONV_TO_UTF8(ioe.errorText) );
|
|
failed = true;
|
|
}
|
|
|
|
if( !failed )
|
|
printf("loaded OK\n");
|
|
|
|
// db.SetPCB( SPECCTRA_DB::MakePCB() );
|
|
|
|
db.Export( wxT("/tmp/export.dsn"), 0 );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
//EOF
|