kicad/common/io/eagle/eagle_parser.h

1074 lines
23 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2012-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017 CERN
*
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
*
* 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
*/
#ifndef _EAGLE_PARSER_H_
#define _EAGLE_PARSER_H_
#include <map>
#include <memory>
#include <unordered_map>
#include <wx/xml/xml.h>
#include <wx/string.h>
#include <wx/filename.h>
#include <layer_ids.h>
#include <trigo.h>
#include <core/wx_stl_compat.h>
class FOOTPRINT;
struct EINSTANCE;
struct EPART;
struct ETEXT;
typedef std::unordered_map<wxString, wxXmlNode*> NODE_MAP;
typedef std::map<wxString, EINSTANCE*> EINSTANCE_MAP;
typedef std::map<wxString, std::unique_ptr<EPART>> EPART_MAP;
///< Translates Eagle special characters to their counterparts in KiCad.
wxString escapeName( const wxString& aNetName );
///< Interprets special characters in Eagle text and converts them to KiCAD notation.
wxString interpretText( const wxString& aText );
///< Translates Eagle special text reference to a KiCad variable reference
bool substituteVariable( wxString* aText );
///< Converts Eagle's HTML description into KiCad description format
wxString convertDescription( wxString aDescr );
static inline wxXmlNode* getChildrenNodes( NODE_MAP& aMap, const wxString& aName )
{
auto it = aMap.find( aName );
return it == aMap.end() ? nullptr : it->second->GetChildren();
}
/**
* Implement a simple wrapper around runtime_error to isolate the errors thrown by the
* Eagle XML parser.
*/
struct XML_PARSER_ERROR : std::runtime_error
{
/**
* Build an XML error by just calling its parent class constructor, std::runtime_error, with
* the passed message.
*
* @param aMessage is an explanatory error message.
*/
XML_PARSER_ERROR( const wxString& aMessage ) noexcept :
std::runtime_error( "XML parser failed - " + aMessage.ToStdString() )
{}
};
/// segment (element) of our XPATH into the Eagle XML document tree in PTREE form.
struct TRIPLET
{
const char* element;
const char* attribute;
const char* value;
TRIPLET( const char* aElement, const char* aAttribute = "", const char* aValue = "" ) :
element( aElement ),
attribute( aAttribute ),
value( aValue )
{}
};
/**
* Keep track of what we are working on within a PTREE.
*
* Then if an exception is thrown, the place within the tree that gave us
* grief can be reported almost accurately. To minimally impact
* speed, merely assign const char* pointers during the tree walking
* expedition. The const char* pointers must be to C strings residing either in
* the data or code segment (i.e. "compiled in") or within the XML document, but
* not on the stack, since the stack is unwound during the throwing of the
* exception. The XML document will not immediately vanish since we capture
* the xpath (using function Contents()) before the XML document tree (PTREE)
* is destroyed.
*/
class XPATH
{
std::vector<TRIPLET> p;
public:
void push( const char* aPathSegment, const char* aAttribute="" )
{
p.emplace_back( aPathSegment, aAttribute );
}
void clear() { p.clear(); }
void pop() { p.pop_back(); }
/// modify the last path node's value
void Value( const char* aValue )
{
p.back().value = aValue;
}
/// modify the last path node's attribute
void Attribute( const char* aAttribute )
{
p.back().attribute = aAttribute;
}
/// return the contents of the XPATH as a single string
wxString Contents()
{
typedef std::vector<TRIPLET>::const_iterator CITER_TRIPLET;
wxString ret;
for( CITER_TRIPLET it = p.begin(); it != p.end(); ++it )
{
if( it != p.begin() )
ret += '.';
ret += it->element;
if( it->attribute[0] && it->value[0] )
{
ret += '[';
ret += it->attribute;
ret += '=';
ret += it->value;
ret += ']';
}
}
return ret;
}
};
/**
* Convert a wxString to a generic type T.
*
* @param aValue is a wxString containing the value that will be converted to type T.
* @throw XML_PARSER_ERROR - an exception is thrown if the parsing fails or if the conversion to
* type T is unknown.
*/
template<typename T>
T Convert( const wxString& aValue )
{
throw XML_PARSER_ERROR( "Conversion failed. Unknown type." );
}
template <>
wxString Convert<wxString>( const wxString& aValue );
/**
* Model an optional XML attribute.
*
* This was implemented as an alternative to std::optional. This class should be replaced with a
* simple typedef per type using std::optional when C++17 is published.
*/
template <typename T>
class OPTIONAL_XML_ATTRIBUTE
{
private:
/// A boolean indicating if the data is present or not.
bool m_isAvailable;
/// The actual data if m_isAvailable is true; otherwise, garbage.
T m_data;
public:
/**
* Construct a default OPTIONAL_XML_ATTRIBUTE, whose data is not available.
*/
OPTIONAL_XML_ATTRIBUTE() :
m_isAvailable( false ),
m_data( T() )
{}
/**
* @param aData is a wxString containing the value that should be converted to type T. If
* aData is empty, the attribute is understood as unavailable; otherwise, the
* conversion to T is tried.
*/
OPTIONAL_XML_ATTRIBUTE( const wxString& aData )
{
m_data = T();
m_isAvailable = !aData.IsEmpty();
if( m_isAvailable )
Set( aData );
}
/**
* @param aData is the value of the XML attribute. If this constructor is called, the
* attribute is available.
*/
template<typename V = T>
OPTIONAL_XML_ATTRIBUTE( T aData ) :
m_isAvailable( true ),
m_data( aData )
{}
/**
* @return bool the availability of the attribute.
*/
operator bool() const
{
return m_isAvailable;
}
/**
* Assign to a string (optionally) containing the data.
*
* @param aData is a wxString that should be converted to T. If the string is empty, the
* attribute is set to unavailable.
*/
OPTIONAL_XML_ATTRIBUTE<T>& operator =( const wxString& aData )
{
m_isAvailable = !aData.IsEmpty();
if( m_isAvailable )
Set( aData );
return *this;
}
/**
* Assign to an object of the base type containing the data.
*
* @param aData is the actual value of the attribute. Calling this assignment, the attribute
* is automatically made available.
*/
OPTIONAL_XML_ATTRIBUTE<T>& operator =( T aData )
{
m_data = aData;
m_isAvailable = true;
return *this;
}
/**
* @param aOther is the object of the base type that should be compared with this one.
*/
bool operator ==( const T& aOther ) const
{
return m_isAvailable && ( aOther == m_data );
}
/**
* Attempt to convert a string to the base type.
*
* @param aString is the string that will be converted to the base type.
*/
void Set( const wxString& aString )
{
m_data = Convert<T>( aString );
m_isAvailable = !aString.IsEmpty();
}
/**
* Return a reference to the value of the attribute assuming it is available.
*
* @return T& - the value of the attribute.
*/
T& Get()
{
assert( m_isAvailable );
return m_data;
}
/**
* Return a constant reference to the value of the attribute assuming it is available.
*
* @return const T& - the value of the attribute.
*/
const T& CGet() const
{
assert( m_isAvailable );
return m_data;
}
/**
* Return a reference to the value of the attribute assuming it is available.
*
* @return T& - the value of the attribute.
*/
T& operator*()
{
return Get();
}
/**
* Return a constant reference to the value of the attribute assuming it is available.
*
* @return const T& - the value of the attribute.
*/
const T& operator*() const
{
return CGet();
}
/**
* Return a pointer to the value of the attribute assuming it is available.
*
* @return T* - the value of the attribute.
*/
T* operator->()
{
return &Get();
}
/**
* Return a constant pointer to the value of the attribute assuming it is available.
*
* @return const T* - the value of the attribute.
*/
const T* operator->() const
{
return &CGet();
}
};
/**
* Provide an easy access to the children of an XML node via their names.
*
* @param currentNode is a pointer to a wxXmlNode, whose children will be mapped.
* @return NODE_MAP is a map linking the name of each children to the children itself (via a
* wxXmlNode*)
*/
NODE_MAP MapChildren( wxXmlNode* aCurrentNode );
///< Convert an Eagle curve end to a KiCad center for S_ARC
VECTOR2I ConvertArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double aAngle );
// Pre-declare for typedefs
struct EROT;
struct ECOORD;
typedef OPTIONAL_XML_ATTRIBUTE<wxString> opt_wxString;
typedef OPTIONAL_XML_ATTRIBUTE<int> opt_int;
typedef OPTIONAL_XML_ATTRIBUTE<double> opt_double;
typedef OPTIONAL_XML_ATTRIBUTE<bool> opt_bool;
typedef OPTIONAL_XML_ATTRIBUTE<EROT> opt_erot;
typedef OPTIONAL_XML_ATTRIBUTE<ECOORD> opt_ecoord;
// All of the 'E'STRUCTS below merely hold Eagle XML information verbatim, in binary.
// For maintenance and troubleshooting purposes, it was thought that we'd need to
// separate the conversion process into distinct steps. There is no intent to have KiCad
// forms of information in these 'E'STRUCTS. They are only binary forms
// of the Eagle information in the corresponding Eagle XML nodes.
// Eagle coordinates
struct ECOORD
{
enum EAGLE_UNIT
{
EU_NM, ///< nanometers
EU_MM, ///< millimeters
EU_INCH, ///< inches
EU_MIL, ///< mils/thous
};
///< Value expressed in nanometers
long long int value;
///< Unit used for the value field
static constexpr EAGLE_UNIT ECOORD_UNIT = EU_NM;
ECOORD()
: value( 0 )
{
}
ECOORD( int aValue, enum EAGLE_UNIT aUnit )
: value( ConvertToNm( aValue, aUnit ) )
{
}
ECOORD( const wxString& aValue, enum EAGLE_UNIT aUnit );
int ToMils() const
{
return value / 25400;
}
int To100NanoMeters() const
{
return value / 100;
}
int ToNanoMeters() const
{
return value;
}
float ToMm() const
{
return value / 1000000.0;
}
int ToSchUnits() const { return To100NanoMeters(); }
int ToPcbUnits() const { return ToNanoMeters(); }
ECOORD operator+( const ECOORD& aOther ) const
{
return ECOORD( value + aOther.value, ECOORD_UNIT );
}
ECOORD operator-( const ECOORD& aOther ) const
{
return ECOORD( value - aOther.value, ECOORD_UNIT );
}
bool operator==( const ECOORD& aOther ) const
{
return value == aOther.value;
}
///< Converts a size expressed in a certain unit to nanometers.
static long long int ConvertToNm( int aValue, enum EAGLE_UNIT aUnit );
};
/// Eagle net
struct ENET
{
int netcode;
wxString netname;
ENET( int aNetCode, const wxString& aNetName ) :
netcode( aNetCode ),
netname( aNetName )
{}
ENET() :
netcode( 0 )
{}
};
/// Eagle rotation
struct EROT
{
bool mirror;
bool spin;
double degrees;
EROT() :
mirror( false ),
spin( false ),
degrees( 0 )
{}
EROT( double aDegrees ) :
mirror( false ),
spin( false ),
degrees( aDegrees )
{}
};
/// Eagle wire
struct EWIRE
{
ECOORD x1;
ECOORD y1;
ECOORD x2;
ECOORD y2;
ECOORD width;
int layer;
// for style: (continuous | longdash | shortdash | dashdot)
enum { CONTINUOUS,
LONGDASH,
SHORTDASH,
DASHDOT };
opt_int style;
opt_double curve; ///< range is -359.9..359.9
// for cap: (flat | round)
enum { FLAT,
ROUND };
opt_int cap;
EWIRE( wxXmlNode* aWire );
};
/// Eagle Junction
struct EJUNCTION
{
ECOORD x;
ECOORD y;
EJUNCTION( wxXmlNode* aJunction);
};
/// Eagle label
struct ELABEL
{
ECOORD x;
ECOORD y;
ECOORD size;
int layer;
opt_erot rot;
opt_wxString xref;
wxString netname;
ELABEL( wxXmlNode* aLabel, const wxString& aNetName );
};
/// Eagle via
struct EVIA
{
ECOORD x;
ECOORD y;
int layer_front_most; /// < extent
int layer_back_most; /// < inclusive
ECOORD drill;
opt_ecoord diam;
opt_wxString shape;
EVIA( wxXmlNode* aVia );
};
/// Eagle circle
struct ECIRCLE
{
ECOORD x;
ECOORD y;
ECOORD radius;
ECOORD width;
int layer;
ECIRCLE( wxXmlNode* aCircle );
};
/// Eagle XML rectangle in binary
struct ERECT
{
ECOORD x1;
ECOORD y1;
ECOORD x2;
ECOORD y2;
int layer;
opt_erot rot;
ERECT( wxXmlNode* aRect );
};
/**
* Parse an Eagle "attribute" XML element.
*
* @note An attribute element is different than an XML element attribute. The attribute element
* is a full XML node in and of itself, and has attributes of its own. Blame Eagle.
*/
struct EATTR
{
wxString name;
opt_wxString value;
opt_ecoord x;
opt_ecoord y;
opt_ecoord size;
opt_int layer;
opt_double ratio;
opt_erot rot;
enum { // for 'display'
Off,
VALUE,
NAME,
BOTH,
};
opt_int display;
opt_int align;
EATTR( wxXmlNode* aTree );
EATTR() {}
};
/// Eagle dimension element
struct EDIMENSION
{
ECOORD x1;
ECOORD y1;
ECOORD x2;
ECOORD y2;
ECOORD x3;
ECOORD y3;
opt_ecoord textsize;
int layer;
opt_wxString dimensionType;
EDIMENSION( wxXmlNode* aDimension );
};
/// Eagle text element
struct ETEXT
{
wxString text;
ECOORD x;
ECOORD y;
ECOORD size;
int layer;
opt_wxString font;
opt_double ratio;
opt_erot rot;
enum { // for align
CENTER = 0,
CENTER_LEFT,
TOP_CENTER,
TOP_LEFT,
TOP_RIGHT,
// opposites are -1 x above, used by code tricks in here
CENTER_RIGHT = -CENTER_LEFT,
BOTTOM_CENTER = -TOP_CENTER,
BOTTOM_LEFT = -TOP_RIGHT,
BOTTOM_RIGHT = -TOP_LEFT,
};
opt_int align;
ETEXT( wxXmlNode* aText );
/// Calculate text size based on font type and size
VECTOR2I ConvertSize() const;
};
/**
* Parse an Eagle frame element.
*/
struct EFRAME
{
ECOORD x1;
ECOORD y1;
ECOORD x2;
ECOORD y2;
int columns;
int rows;
int layer;
opt_bool border_left;
opt_bool border_top;
opt_bool border_right;
opt_bool border_bottom;
EFRAME( wxXmlNode* aFrameNode );
};
/// Structure holding common properties for through-hole and SMD pads
struct EPAD_COMMON
{
wxString name;
ECOORD x, y;
opt_erot rot;
opt_bool stop;
opt_bool thermals;
EPAD_COMMON( wxXmlNode* aPad );
};
/// Eagle thru hole pad
struct EPAD : public EPAD_COMMON
{
ECOORD drill;
opt_ecoord diameter;
// for shape: (square | round | octagon | long | offset)
enum {
UNDEF = -1,
SQUARE,
ROUND,
OCTAGON,
LONG,
OFFSET,
};
opt_int shape;
opt_bool first;
EPAD( wxXmlNode* aPad );
};
/// Eagle SMD pad
struct ESMD : public EPAD_COMMON
{
ECOORD dx;
ECOORD dy;
int layer;
opt_int roundness;
opt_bool cream;
ESMD( wxXmlNode* aSMD );
};
/// Eagle pin element
struct EPIN
{
wxString name;
ECOORD x;
ECOORD y;
opt_wxString visible;
opt_wxString length;
opt_wxString direction;
opt_wxString function;
opt_int swaplevel;
opt_erot rot;
EPIN( wxXmlNode* aPin );
};
/// Eagle vertex
struct EVERTEX
{
ECOORD x;
ECOORD y;
opt_double curve; ///< range is -359.9..359.9
EVERTEX( wxXmlNode* aVertex );
};
/// Eagle polygon, without vertices which are parsed as needed
struct EPOLYGON
{
ECOORD width;
int layer;
opt_ecoord spacing;
// KiCad priority is opposite of Eagle rank, that is:
// - Eagle Low rank drawn first
// - KiCad high priority drawn first
// So since Eagle has an upper limit we define this, used for the cases
// where no rank is specified.
static const int max_priority = 6;
enum { // for pour
SOLID,
HATCH,
CUTOUT,
};
int pour;
opt_ecoord isolate;
opt_bool orphans;
opt_bool thermals;
opt_int rank;
EPOLYGON( wxXmlNode* aPolygon );
};
/// Eagle hole element
struct EHOLE
{
ECOORD x;
ECOORD y;
ECOORD drill;
EHOLE( wxXmlNode* aHole );
};
/// Eagle element element
struct EELEMENT
{
wxString name;
wxString library;
wxString package;
wxString value;
ECOORD x;
ECOORD y;
opt_bool locked;
opt_bool smashed;
opt_erot rot;
EELEMENT( wxXmlNode* aElement );
};
struct ELAYER
{
int number;
wxString name;
int color;
int fill;
opt_bool visible;
opt_bool active;
ELAYER( wxXmlNode* aLayer );
};
struct EAGLE_LAYER
{
enum
{
TOP = 1,
ROUTE2 = 2,
ROUTE3 = 3,
ROUTE4 = 4,
ROUTE5 = 5,
ROUTE6 = 6,
ROUTE7 = 7,
ROUTE8 = 8,
ROUTE9 = 9,
ROUTE10 = 10,
ROUTE11 = 11,
ROUTE12 = 12,
ROUTE13 = 13,
ROUTE14 = 14,
ROUTE15 = 15,
BOTTOM = 16,
PADS = 17,
VIAS = 18,
UNROUTED = 19,
DIMENSION = 20,
TPLACE = 21,
BPLACE = 22,
TORIGINS = 23,
BORIGINS = 24,
TNAMES = 25,
BNAMES = 26,
TVALUES = 27,
BVALUES = 28,
TSTOP = 29,
BSTOP = 30,
TCREAM = 31,
BCREAM = 32,
TFINISH = 33,
BFINISH = 34,
TGLUE = 35,
BGLUE = 36,
TTEST = 37,
BTEST = 38,
TKEEPOUT = 39,
BKEEPOUT = 40,
TRESTRICT = 41,
BRESTRICT = 42,
VRESTRICT = 43,
DRILLS = 44,
HOLES = 45,
MILLING = 46,
MEASURES = 47,
DOCUMENT = 48,
REFERENCELC = 49,
REFERENCELS = 50,
TDOCU = 51,
BDOCU = 52,
NETS = 91,
BUSSES = 92,
PINS = 93,
SYMBOLS = 94,
NAMES = 95,
VALUES = 96,
INFO = 97,
GUIDE = 98,
USERLAYER1 = 160,
USERLAYER2 = 161
};
};
struct EPART
{
/*
* <!ELEMENT part (attribute*, variant*)>
* <!ATTLIST part
* name %String; #REQUIRED
* library %String; #REQUIRED
* deviceset %String; #REQUIRED
* device %String; #REQUIRED
* technology %String; ""
* value %String; #IMPLIED
* >
*/
wxString name;
wxString library;
wxString deviceset;
wxString device;
opt_wxString technology;
opt_wxString value;
std::map<std::string,std::string> attribute;
std::map<std::string,std::string> variant;
EPART( wxXmlNode* aPart );
};
struct EINSTANCE
{
/*
* <!ELEMENT instance (attribute)*>
* <!ATTLIST instance
* part %String; #REQUIRED
* gate %String; #REQUIRED
* x %Coord; #REQUIRED
* y %Coord; #REQUIRED
* smashed %Bool; "no"
* rot %Rotation; "R0"
* >
*/
wxString part;
wxString gate;
ECOORD x;
ECOORD y;
opt_bool smashed;
opt_erot rot;
EINSTANCE( wxXmlNode* aInstance );
};
struct EGATE
{
/*
* <!ELEMENT gate EMPTY>
* <!ATTLIST gate
* name %String; #REQUIRED
* symbol %String; #REQUIRED
* x %Coord; #REQUIRED
* y %Coord; #REQUIRED
* addlevel %GateAddLevel; "next"
* swaplevel %Int; "0"
* >
*/
wxString name;
wxString symbol;
ECOORD x;
ECOORD y;
opt_int addlevel;
opt_int swaplevel;
enum
{
MUST,
CAN,
NEXT,
REQUEST,
ALWAYS
};
EGATE( wxXmlNode* aGate );
};
struct ECONNECT
{
/*
* <!ELEMENT connect EMPTY>
* <!ATTLIST connect
* gate %String; #REQUIRED
* pin %String; #REQUIRED
* pad %String; #REQUIRED
* route %ContactRoute; "all"
* >
*/
wxString gate;
wxString pin;
wxString pad;
//int contactroute; // TODO
ECONNECT( wxXmlNode* aConnect );
};
struct EDEVICE
{
/*
* <!ELEMENT device (connects?, technologies?)>
* <!ATTLIST device
* name %String; ""
* package %String; #IMPLIED
* >
*/
wxString name;
opt_wxString package;
std::vector<ECONNECT> connects;
EDEVICE( wxXmlNode* aDevice );
};
struct EDEVICE_SET
{
/*
<!ELEMENT deviceset (description?, gates, devices)>
<!ATTLIST deviceset
name %String; #REQUIRED
prefix %String; ""
uservalue %Bool; "no"
>
*/
wxString name;
opt_wxString prefix;
opt_bool uservalue;
//std::vector<EDEVICE> devices;
//std::vector<EGATE> gates;
EDEVICE_SET( wxXmlNode* aDeviceSet );
};
struct ECLASS
{
wxString number;
wxString name;
std::map<wxString, ECOORD> clearanceMap;
ECLASS( wxXmlNode* aClass );
};
#endif // _EAGLE_PARSER_H_