summaryrefslogtreecommitdiff
path: root/pcbnew/pcb_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/pcb_parser.cpp')
-rw-r--r--pcbnew/pcb_parser.cpp3073
1 files changed, 3073 insertions, 0 deletions
diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp
new file mode 100644
index 0000000..45d6c69
--- /dev/null
+++ b/pcbnew/pcb_parser.cpp
@@ -0,0 +1,3073 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 CERN
+ * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.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
+ */
+
+/**
+ * @file pcb_parser.cpp
+ * @brief Pcbnew s-expression file format parser implementation.
+ */
+
+#include <errno.h>
+#include <common.h>
+#include <confirm.h>
+#include <macros.h>
+#include <convert_from_iu.h>
+#include <trigo.h>
+#include <3d_struct.h>
+#include <class_title_block.h>
+
+#include <class_board.h>
+#include <class_dimension.h>
+#include <class_drawsegment.h>
+#include <class_edge_mod.h>
+#include <class_mire.h>
+#include <class_module.h>
+#include <class_netclass.h>
+#include <class_pad.h>
+#include <class_track.h>
+#include <class_zone.h>
+#include <kicad_plugin.h>
+#include <pcb_plot_params_parser.h>
+#include <pcb_plot_params.h>
+#include <zones.h>
+#include <pcb_parser.h>
+
+#include <boost/make_shared.hpp>
+
+using namespace PCB_KEYS_T;
+
+
+void PCB_PARSER::init()
+{
+ m_tooRecent = false;
+ m_requiredVersion = 0;
+ m_layerIndices.clear();
+ m_layerMasks.clear();
+
+ // Add untranslated default (i.e. english) layernames.
+ // Some may be overridden later if parsing a board rather than a footprint.
+ // The english name will survive if parsing only a footprint.
+ for( LAYER_NUM layer = 0; layer < LAYER_ID_COUNT; ++layer )
+ {
+ std::string untranslated = TO_UTF8( wxString( LSET::Name( LAYER_ID( layer ) ) ) );
+
+ m_layerIndices[ untranslated ] = LAYER_ID( layer );
+ m_layerMasks[ untranslated ] = LSET( LAYER_ID( layer ) );
+ }
+
+ m_layerMasks[ "*.Cu" ] = LSET::AllCuMask();
+ m_layerMasks[ "F&B.Cu" ] = LSET( 2, F_Cu, B_Cu );
+ m_layerMasks[ "*.Adhes" ] = LSET( 2, B_Adhes, F_Adhes );
+ m_layerMasks[ "*.Paste" ] = LSET( 2, B_Paste, F_Paste );
+ m_layerMasks[ "*.Mask" ] = LSET( 2, B_Mask, F_Mask );
+ m_layerMasks[ "*.SilkS" ] = LSET( 2, B_SilkS, F_SilkS );
+ m_layerMasks[ "*.Fab" ] = LSET( 2, B_Fab, F_Fab );
+ m_layerMasks[ "*.CrtYd" ] = LSET( 2, B_CrtYd, F_CrtYd );
+
+ // This is for the first pretty & *.kicad_pcb formats, which had
+ // Inner1_Cu - Inner14_Cu with the numbering sequence
+ // reversed from the subsequent format's In1_Cu - In30_Cu numbering scheme.
+ // The newer format brought in an additional 16 Cu layers and flipped the cu stack but
+ // kept the gap between one of the outside layers and the last cu internal.
+
+ for( int i=1; i<=14; ++i )
+ {
+ std::string key = StrPrintf( "Inner%d.Cu", i );
+
+ m_layerMasks[ key ] = LSET( LAYER_ID( In15_Cu - i ) );
+ }
+
+#if defined(DEBUG) && 0
+ printf( "m_layerMasks:\n" );
+ for( LSET_MAP::const_iterator it = m_layerMasks.begin(); it != m_layerMasks.end(); ++it )
+ {
+ printf( " [%s] == 0x%s\n", it->first.c_str(), it->second.FmtHex().c_str() );
+ }
+
+ printf( "m_layerIndices:\n" );
+ for( LAYER_ID_MAP::const_iterator it = m_layerIndices.begin(); it != m_layerIndices.end(); ++it )
+ {
+ printf( " [%s] == %d\n", it->first.c_str(), it->second );
+ }
+#endif
+
+}
+
+
+void PCB_PARSER::pushValueIntoMap( int aIndex, int aValue )
+{
+ // Add aValue in netcode mapping (m_netCodes) at index aNetCode
+ // ensure there is room in m_netCodes for that, and add room if needed.
+
+ if( (int)m_netCodes.size() <= aIndex )
+ m_netCodes.resize( aIndex+1 );
+
+ m_netCodes[aIndex] = aValue;
+}
+
+double PCB_PARSER::parseDouble() throw( IO_ERROR )
+{
+ char* tmp;
+
+ errno = 0;
+
+ double fval = strtod( CurText(), &tmp );
+
+ if( errno )
+ {
+ wxString error;
+ error.Printf( _( "invalid floating point number in\nfile: <%s>\nline: %d\noffset: %d" ),
+ GetChars( CurSource() ), CurLineNumber(), CurOffset() );
+
+ THROW_IO_ERROR( error );
+ }
+
+ if( CurText() == tmp )
+ {
+ wxString error;
+ error.Printf( _( "missing floating point number in\nfile: <%s>\nline: %d\noffset: %d" ),
+ GetChars( CurSource() ), CurLineNumber(), CurOffset() );
+
+ THROW_IO_ERROR( error );
+ }
+
+ return fval;
+}
+
+
+bool PCB_PARSER::parseBool() throw( PARSE_ERROR )
+{
+ T token = NextTok();
+
+ if( token == T_yes )
+ return true;
+ else if( token == T_no )
+ return false;
+ else
+ Expecting( "yes or no" );
+
+ return false;
+}
+
+
+int PCB_PARSER::parseVersion() throw( IO_ERROR, PARSE_ERROR )
+{
+ if( NextTok() != T_version )
+ Expecting( GetTokenText( T_version ) );
+
+ int pcb_version = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
+
+ NeedRIGHT();
+
+ return pcb_version;
+}
+
+
+wxString PCB_PARSER::GetRequiredVersion()
+{
+ int year, month, day;
+
+ year = m_requiredVersion / 10000;
+ month = ( m_requiredVersion / 100 ) - ( year * 100 );
+ day = m_requiredVersion - ( year * 10000 ) - ( month * 100 );
+
+ // wx throws an assertion, not a catchable exception, when the date is invalid.
+ // User input shouldn't give wx asserts, so check manually and throw a proper
+ // error instead
+ if( day <= 0 || month <= 0 || month > 12 ||
+ day > wxDateTime::GetNumberOfDays( (wxDateTime::Month)( month - 1 ), year ) )
+ {
+ wxString err;
+ err.Printf( _( "cannot interpret date code %d" ), m_requiredVersion );
+ THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
+ }
+
+ wxDateTime date( day, (wxDateTime::Month)( month - 1 ), year, 0, 0, 0, 0 );
+ return date.FormatDate();
+}
+
+
+wxPoint PCB_PARSER::parseXY() throw( PARSE_ERROR, IO_ERROR )
+{
+ if( CurTok() != T_LEFT )
+ NeedLEFT();
+
+ wxPoint pt;
+ T token = NextTok();
+
+ if( token != T_xy )
+ Expecting( T_xy );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+
+ NeedRIGHT();
+
+ return pt;
+}
+
+
+void PCB_PARSER::parseXY( int* aX, int* aY ) throw( PARSE_ERROR, IO_ERROR )
+{
+ wxPoint pt = parseXY();
+
+ if( aX )
+ *aX = pt.x;
+
+ if( aY )
+ *aY = pt.y;
+}
+
+
+void PCB_PARSER::parseEDA_TEXT( EDA_TEXT* aText ) throw( PARSE_ERROR, IO_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_effects,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDA_TEXT." ) );
+
+ T token;
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_font:
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ continue;
+
+ switch( token )
+ {
+ case T_size:
+ {
+ wxSize sz;
+ sz.SetHeight( parseBoardUnits( "text height" ) );
+ sz.SetWidth( parseBoardUnits( "text width" ) );
+ aText->SetSize( sz );
+ NeedRIGHT();
+ }
+ break;
+
+ case T_thickness:
+ aText->SetThickness( parseBoardUnits( "text thickness" ) );
+ NeedRIGHT();
+ break;
+
+ case T_bold:
+ aText->SetBold( true );
+ break;
+
+ case T_italic:
+ aText->SetItalic( true );
+ break;
+
+ default:
+ Expecting( "size, bold, or italic" );
+ }
+ }
+
+ break;
+
+ case T_justify:
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ continue;
+
+ switch( token )
+ {
+ case T_left:
+ aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
+ break;
+
+ case T_right:
+ aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
+ break;
+
+ case T_top:
+ aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
+ break;
+
+ case T_bottom:
+ aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
+ break;
+
+ case T_mirror:
+ aText->SetMirrored( true );
+ break;
+
+ default:
+ Expecting( "left, right, top, bottom, or mirror" );
+ }
+
+ }
+ break;
+
+ case T_hide:
+ aText->SetVisible( false );
+ break;
+
+ default:
+ Expecting( "font, justify, or hide" );
+ }
+ }
+}
+
+
+S3D_MASTER* PCB_PARSER::parse3DModel() throw( PARSE_ERROR, IO_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_model, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as S3D_MASTER." ) );
+
+ T token;
+
+ std::auto_ptr< S3D_MASTER > n3D( new S3D_MASTER( NULL ) );
+
+ NeedSYMBOLorNUMBER();
+ n3D->SetShape3DName( FromUTF8() );
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_at:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_xyz )
+ Expecting( T_xyz );
+
+ n3D->m_MatPosition.x = parseDouble( "x value" );
+ n3D->m_MatPosition.y = parseDouble( "y value" );
+ n3D->m_MatPosition.z = parseDouble( "z value" );
+ NeedRIGHT();
+ break;
+
+ case T_scale:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_xyz )
+ Expecting( T_xyz );
+
+ n3D->m_MatScale.x = parseDouble( "x value" );
+ n3D->m_MatScale.y = parseDouble( "y value" );
+ n3D->m_MatScale.z = parseDouble( "z value" );
+ NeedRIGHT();
+ break;
+
+ case T_rotate:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_xyz )
+ Expecting( T_xyz );
+
+ n3D->m_MatRotation.x = parseDouble( "x value" );
+ n3D->m_MatRotation.y = parseDouble( "y value" );
+ n3D->m_MatRotation.z = parseDouble( "z value" );
+ NeedRIGHT();
+ break;
+
+ default:
+ Expecting( "at, scale, or rotate" );
+ }
+
+ NeedRIGHT();
+ }
+
+ return n3D.release();
+}
+
+
+BOARD_ITEM* PCB_PARSER::Parse() throw( IO_ERROR, PARSE_ERROR )
+{
+ T token;
+ BOARD_ITEM* item;
+ LOCALE_IO toggle;
+
+ // MODULEs can be prefixed with an initial block of single line comments and these
+ // are kept for Format() so they round trip in s-expression form. BOARDs might
+ // eventually do the same, but currently do not.
+ std::auto_ptr<wxArrayString> initial_comments( ReadCommentLines() );
+
+ token = CurTok();
+
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ switch( NextTok() )
+ {
+ case T_kicad_pcb:
+ if( m_board == NULL )
+ m_board = new BOARD();
+
+ item = (BOARD_ITEM*) parseBOARD();
+ break;
+
+ case T_module:
+ item = (BOARD_ITEM*) parseMODULE( initial_comments.release() );
+ break;
+
+ default:
+ wxString err;
+ err.Printf( _( "unknown token \"%s\"" ), GetChars( FromUTF8() ) );
+ THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
+ }
+
+ return item;
+}
+
+
+BOARD* PCB_PARSER::parseBOARD() throw( IO_ERROR, PARSE_ERROR, FUTURE_FORMAT_ERROR )
+{
+ try
+ {
+ return parseBOARD_unchecked();
+ }
+ catch( const PARSE_ERROR& parse_error )
+ {
+ if( m_tooRecent )
+ throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
+ else
+ throw;
+ }
+}
+
+
+BOARD* PCB_PARSER::parseBOARD_unchecked() throw( IO_ERROR, PARSE_ERROR )
+{
+ T token;
+
+ parseHeader();
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_general:
+ parseGeneralSection();
+ break;
+
+ case T_page:
+ parsePAGE_INFO();
+ break;
+
+ case T_title_block:
+ parseTITLE_BLOCK();
+ break;
+
+ case T_layers:
+ parseLayers();
+ break;
+
+ case T_setup:
+ parseSetup();
+ break;
+
+ case T_net:
+ parseNETINFO_ITEM();
+ break;
+
+ case T_net_class:
+ parseNETCLASS();
+ break;
+
+ case T_gr_arc:
+ case T_gr_circle:
+ case T_gr_curve:
+ case T_gr_line:
+ case T_gr_poly:
+ m_board->Add( parseDRAWSEGMENT(), ADD_APPEND );
+ break;
+
+ case T_gr_text:
+ m_board->Add( parseTEXTE_PCB(), ADD_APPEND );
+ break;
+
+ case T_dimension:
+ m_board->Add( parseDIMENSION(), ADD_APPEND );
+ break;
+
+ case T_module:
+ m_board->Add( parseMODULE(), ADD_APPEND );
+ break;
+
+ case T_segment:
+ m_board->Add( parseTRACK(), ADD_APPEND );
+ break;
+
+ case T_via:
+ m_board->Add( parseVIA(), ADD_APPEND );
+ break;
+
+ case T_zone:
+ m_board->Add( parseZONE_CONTAINER(), ADD_APPEND );
+ break;
+
+ case T_target:
+ m_board->Add( parsePCB_TARGET(), ADD_APPEND );
+ break;
+
+ default:
+ wxString err;
+ err.Printf( _( "unknown token \"%s\"" ), GetChars( FromUTF8() ) );
+ THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
+ }
+ }
+
+ return m_board;
+}
+
+
+void PCB_PARSER::parseHeader() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_kicad_pcb,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
+
+ NeedLEFT();
+
+ T tok = NextTok();
+ if( tok == T_version )
+ {
+ m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
+ m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
+ NeedRIGHT();
+
+ // Skip the host name and host build version information.
+ NeedLEFT();
+ NeedSYMBOL();
+ NeedSYMBOL();
+ NeedSYMBOL();
+ NeedRIGHT();
+ }
+ else
+ {
+ m_requiredVersion = SEXPR_BOARD_FILE_VERSION;
+ m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
+
+ // Skip the host name and host build version information.
+ NeedSYMBOL();
+ NeedSYMBOL();
+ NeedRIGHT();
+ }
+
+ m_board->SetFileFormatVersionAtLoad( m_requiredVersion );
+}
+
+
+void PCB_PARSER::parseGeneralSection() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_general,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
+ wxT( " as a general section." ) );
+
+ T token;
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_thickness:
+ m_board->GetDesignSettings().SetBoardThickness( parseBoardUnits( T_thickness ) );
+ NeedRIGHT();
+ break;
+
+ case T_nets:
+ m_netCodes.resize( parseInt( "nets number" ) );
+ NeedRIGHT();
+ break;
+
+ case T_no_connects:
+ m_board->SetUnconnectedNetCount( parseInt( "no connect count" ) );
+ NeedRIGHT();
+ break;
+
+ default: // Skip everything but the board thickness.
+ wxLogDebug( wxT( "Skipping general section token %s " ),
+ GetChars( GetTokenString( token ) ) );
+
+ while( ( token = NextTok() ) != T_RIGHT )
+ {
+ if( !IsSymbol( token ) && token != T_NUMBER )
+ Expecting( "symbol or number" );
+ }
+ }
+ }
+}
+
+
+void PCB_PARSER::parsePAGE_INFO() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_page,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
+
+ T token;
+ PAGE_INFO pageInfo;
+
+ NeedSYMBOL();
+
+ wxString pageType = FromUTF8();
+
+ if( !pageInfo.SetType( pageType ) )
+ {
+ wxString err;
+ err.Printf( _( "page type \"%s\" is not valid " ), GetChars( FromUTF8() ) );
+ THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
+ }
+
+ if( pageType == PAGE_INFO::Custom )
+ {
+ double width = parseDouble( "width" ); // width in mm
+
+ // Perform some controls to avoid crashes if the size is edited by hands
+ if( width < 100.0 )
+ width = 100.0;
+ else if( width > 1200.0 )
+ width = 1200.0;
+
+ double height = parseDouble( "height" ); // height in mm
+
+ if( height < 100.0 )
+ height = 100.0;
+ else if( height > 1200.0 )
+ height = 1200.0;
+
+ pageInfo.SetWidthMils( Mm2mils( width ) );
+ pageInfo.SetHeightMils( Mm2mils( height ) );
+ }
+
+ token = NextTok();
+
+ if( token == T_portrait )
+ {
+ pageInfo.SetPortrait( true );
+ NeedRIGHT();
+ }
+ else if( token != T_RIGHT )
+ {
+ Expecting( "portrait|)" );
+ }
+
+ m_board->SetPageSettings( pageInfo );
+}
+
+
+void PCB_PARSER::parseTITLE_BLOCK() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_title_block,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
+ wxT( " as TITLE_BLOCK." ) );
+
+ T token;
+ TITLE_BLOCK titleBlock;
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_title:
+ NextTok();
+ titleBlock.SetTitle( FromUTF8() );
+ break;
+
+ case T_date:
+ NextTok();
+ titleBlock.SetDate( FromUTF8() );
+ break;
+
+ case T_rev:
+ NextTok();
+ titleBlock.SetRevision( FromUTF8() );
+ break;
+
+ case T_company:
+ NextTok();
+ titleBlock.SetCompany( FromUTF8() );
+ break;
+
+ case T_comment:
+ {
+ int commentNumber = parseInt( "comment" );
+
+ switch( commentNumber )
+ {
+ case 1:
+ NextTok();
+ titleBlock.SetComment1( FromUTF8() );
+ break;
+
+ case 2:
+ NextTok();
+ titleBlock.SetComment2( FromUTF8() );
+ break;
+
+ case 3:
+ NextTok();
+ titleBlock.SetComment3( FromUTF8() );
+ break;
+
+ case 4:
+ NextTok();
+ titleBlock.SetComment4( FromUTF8() );
+ break;
+
+ default:
+ wxString err;
+ err.Printf( wxT( "%d is not a valid title block comment number" ), commentNumber );
+ THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
+ }
+ }
+ break;
+
+ default:
+ Expecting( "title, date, rev, company, or comment" );
+ }
+
+ NeedRIGHT();
+ }
+
+ m_board->SetTitleBlock( titleBlock );
+}
+
+
+void PCB_PARSER::parseLayer( LAYER* aLayer ) throw( IO_ERROR, PARSE_ERROR )
+{
+ T token;
+
+ std::string name;
+ std::string type;
+ bool isVisible = true;
+
+ aLayer->clear();
+
+ if( CurTok() != T_LEFT )
+ Expecting( T_LEFT );
+
+ // this layer_num is not used, we DO depend on LAYER_T however.
+ LAYER_NUM layer_num = parseInt( "layer index" );
+
+ NeedSYMBOLorNUMBER();
+ name = CurText();
+
+ NeedSYMBOL();
+ type = CurText();
+
+ token = NextTok();
+
+ if( token == T_hide )
+ {
+ isVisible = false;
+ NeedRIGHT();
+ }
+ else if( token != T_RIGHT )
+ {
+ Expecting( "hide or )" );
+ }
+
+ aLayer->m_name = FROM_UTF8( name.c_str() );
+ aLayer->m_type = LAYER::ParseType( type.c_str() );
+ aLayer->m_number = layer_num;
+ aLayer->m_visible = isVisible;
+}
+
+
+
+void PCB_PARSER::parseLayers() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_layers,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layers." ) );
+
+ T token;
+ LSET visibleLayers;
+ LSET enabledLayers;
+ int copperLayerCount = 0;
+ LAYER layer;
+
+ std::vector<LAYER> cu;
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ parseLayer( &layer );
+
+ if( layer.m_type == LT_UNDEFINED ) // it's a non-copper layer
+ break;
+
+ cu.push_back( layer ); // it's copper
+ }
+
+ // All Cu layers are parsed, but not the non-cu layers here.
+
+ // The original *.kicad_pcb file format and the inverted
+ // Cu stack format both have all the Cu layers first, so use this
+ // trick to handle either. The layer number in the (layers ..)
+ // s-expression element are ignored.
+ if( cu.size() )
+ {
+ // Rework the layer numbers, which changed when the Cu stack
+ // was flipped. So we instead use position in the list.
+ cu[cu.size()-1].m_number = B_Cu;
+
+ for( unsigned i=0; i < cu.size()-1; ++i )
+ {
+ cu[i].m_number = i;
+ }
+
+ for( std::vector<LAYER>::const_iterator it = cu.begin(); it<cu.end(); ++it )
+ {
+ enabledLayers.set( it->m_number );
+
+ if( it->m_visible )
+ visibleLayers.set( it->m_number );
+
+ m_board->SetLayerDescr( LAYER_ID( it->m_number ), *it );
+
+ UTF8 name = it->m_name;
+
+ m_layerIndices[ name ] = LAYER_ID( it->m_number );
+ m_layerMasks[ name ] = LSET( LAYER_ID( it->m_number ) );
+ }
+
+ copperLayerCount = cu.size();
+ }
+
+ // process non-copper layers
+ while( token != T_RIGHT )
+ {
+ LAYER_ID_MAP::const_iterator it = m_layerIndices.find( UTF8( layer.m_name ) );
+
+ if( it == m_layerIndices.end() )
+ {
+ wxString error = wxString::Format(
+ _( "Layer '%s' in file '%s' at line %d, is not in fixed layer hash" ),
+ GetChars( layer.m_name ),
+ GetChars( CurSource() ),
+ CurLineNumber(),
+ CurOffset()
+ );
+
+ THROW_IO_ERROR( error );
+ }
+
+ layer.m_number = it->second;
+
+ enabledLayers.set( layer.m_number );
+
+ if( layer.m_visible )
+ visibleLayers.set( layer.m_number );
+
+ // DBG( printf( "aux m_visible:%s\n", layer.m_visible ? "true" : "false" );)
+
+ m_board->SetLayerDescr( it->second, layer );
+
+ token = NextTok();
+
+ if( token != T_LEFT )
+ break;
+
+ parseLayer( &layer );
+ }
+
+ // We need at least 2 copper layers and there must be an even number of them.
+ if( copperLayerCount < 2 || (copperLayerCount % 2) != 0 )
+ {
+ wxString err = wxString::Format(
+ _( "%d is not a valid layer count" ), copperLayerCount );
+
+ THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
+ }
+
+ m_board->SetCopperLayerCount( copperLayerCount );
+ m_board->SetEnabledLayers( enabledLayers );
+
+ // call SetEnabledLayers before SetVisibleLayers()
+ m_board->SetVisibleLayers( visibleLayers );
+}
+
+
+template<class T, class M>
+T PCB_PARSER::lookUpLayer( const M& aMap ) throw( PARSE_ERROR, IO_ERROR )
+{
+ // avoid constructing another std::string, use lexer's directly
+ typename M::const_iterator it = aMap.find( curText );
+
+ if( it == aMap.end() )
+ {
+#if 0 && defined(DEBUG)
+ // dump the whole darn table, there's something wrong with it.
+ for( it = aMap.begin(); it != aMap.end(); ++it )
+ {
+ wxLogDebug( &aMap == (void*)&m_layerIndices ? wxT( "lm[%s] = %d" ) :
+ wxT( "lm[%s] = %08X" ), it->first.c_str(), it->second );
+ }
+#endif
+
+ wxString error = wxString::Format( _(
+ "Layer '%s' in file\n"
+ "'%s'\n"
+ "at line %d, position %d\n"
+ "was not defined in the layers section"
+ ),
+ GetChars( FROM_UTF8( CurText() ) ),
+ GetChars( CurSource() ),
+ CurLineNumber(), CurOffset() );
+
+ THROW_IO_ERROR( error );
+ }
+
+ return it->second;
+}
+
+
+LAYER_ID PCB_PARSER::parseBoardItemLayer() throw( PARSE_ERROR, IO_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_layer, UNDEFINED_LAYER,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layer." ) );
+
+ NextTok();
+
+ LAYER_ID layerIndex = lookUpLayer<LAYER_ID>( m_layerIndices );
+
+ // Handle closing ) in object parser.
+
+ return layerIndex;
+}
+
+
+LSET PCB_PARSER::parseBoardItemLayersAsMask() throw( PARSE_ERROR, IO_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_layers, LSET(),
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
+ wxT( " as item layer mask." ) );
+
+ LSET layerMask;
+
+ for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ LSET mask = lookUpLayer<LSET>( m_layerMasks );
+ layerMask |= mask;
+ }
+
+ return layerMask;
+}
+
+
+void PCB_PARSER::parseSetup() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_setup,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as setup." ) );
+
+ T token;
+ NETCLASSPTR defaultNetClass = m_board->GetDesignSettings().GetDefault();
+ // TODO Orson: is it really necessary to first operate on a copy and then apply it?
+ // would not it be better to use reference here and apply all the changes instantly?
+ BOARD_DESIGN_SETTINGS designSettings = m_board->GetDesignSettings();
+ ZONE_SETTINGS zoneSettings = m_board->GetZoneSettings();
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_last_trace_width: // not used now
+ /* lastTraceWidth =*/ parseBoardUnits( T_last_trace_width );
+ NeedRIGHT();
+ break;
+
+ case T_user_trace_width:
+ designSettings.m_TrackWidthList.push_back( parseBoardUnits( T_user_trace_width ) );
+ NeedRIGHT();
+ break;
+
+ case T_trace_clearance:
+ defaultNetClass->SetClearance( parseBoardUnits( T_trace_clearance ) );
+ NeedRIGHT();
+ break;
+
+ case T_zone_clearance:
+ zoneSettings.m_ZoneClearance = parseBoardUnits( T_zone_clearance );
+ NeedRIGHT();
+ break;
+
+ case T_zone_45_only:
+ zoneSettings.m_Zone_45_Only = parseBool();
+ NeedRIGHT();
+ break;
+
+ case T_trace_min:
+ designSettings.m_TrackMinWidth = parseBoardUnits( T_trace_min );
+ NeedRIGHT();
+ break;
+
+ case T_segment_width:
+ designSettings.m_DrawSegmentWidth = parseBoardUnits( T_segment_width );
+ NeedRIGHT();
+ break;
+
+ case T_edge_width:
+ designSettings.m_EdgeSegmentWidth = parseBoardUnits( T_edge_width );
+ NeedRIGHT();
+ break;
+
+ case T_via_size:
+ defaultNetClass->SetViaDiameter( parseBoardUnits( T_via_size ) );
+ NeedRIGHT();
+ break;
+
+ case T_via_drill:
+ defaultNetClass->SetViaDrill( parseBoardUnits( T_via_drill ) );
+ NeedRIGHT();
+ break;
+
+ case T_via_min_size:
+ designSettings.m_ViasMinSize = parseBoardUnits( T_via_min_size );
+ NeedRIGHT();
+ break;
+
+ case T_via_min_drill:
+ designSettings.m_ViasMinDrill = parseBoardUnits( T_via_min_drill );
+ NeedRIGHT();
+ break;
+
+ case T_user_via:
+ {
+ int viaSize = parseBoardUnits( "user via size" );
+ int viaDrill = parseBoardUnits( "user via drill" );
+ designSettings.m_ViasDimensionsList.push_back( VIA_DIMENSION( viaSize, viaDrill ) );
+ NeedRIGHT();
+ }
+ break;
+
+ case T_uvia_size:
+ defaultNetClass->SetuViaDiameter( parseBoardUnits( T_uvia_size ) );
+ NeedRIGHT();
+ break;
+
+ case T_uvia_drill:
+ defaultNetClass->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
+ NeedRIGHT();
+ break;
+
+ case T_uvias_allowed:
+ designSettings.m_MicroViasAllowed = parseBool();
+ NeedRIGHT();
+ break;
+
+ case T_blind_buried_vias_allowed:
+ designSettings.m_BlindBuriedViaAllowed = parseBool();
+ NeedRIGHT();
+ break;
+
+ case T_uvia_min_size:
+ designSettings.m_MicroViasMinSize = parseBoardUnits( T_uvia_min_size );
+ NeedRIGHT();
+ break;
+
+ case T_uvia_min_drill:
+ designSettings.m_MicroViasMinDrill = parseBoardUnits( T_uvia_min_drill );
+ NeedRIGHT();
+ break;
+
+ case T_pcb_text_width:
+ designSettings.m_PcbTextWidth = parseBoardUnits( T_pcb_text_width );
+ NeedRIGHT();
+ break;
+
+ case T_pcb_text_size:
+ designSettings.m_PcbTextSize.x = parseBoardUnits( "pcb text width" );
+ designSettings.m_PcbTextSize.y = parseBoardUnits( "pcb text height" );
+ NeedRIGHT();
+ break;
+
+ case T_mod_edge_width:
+ designSettings.m_ModuleSegmentWidth = parseBoardUnits( T_mod_edge_width );
+ NeedRIGHT();
+ break;
+
+ case T_mod_text_size:
+ designSettings.m_ModuleTextSize.x = parseBoardUnits( "module text width" );
+ designSettings.m_ModuleTextSize.y = parseBoardUnits( "module text height" );
+ NeedRIGHT();
+ break;
+
+ case T_mod_text_width:
+ designSettings.m_ModuleTextWidth = parseBoardUnits( T_mod_text_width );
+ NeedRIGHT();
+ break;
+
+ case T_pad_size:
+ {
+ wxSize sz;
+ sz.SetWidth( parseBoardUnits( "master pad width" ) );
+ sz.SetHeight( parseBoardUnits( "master pad height" ) );
+ designSettings.m_Pad_Master.SetSize( sz );
+ NeedRIGHT();
+ }
+ break;
+
+ case T_pad_drill:
+ {
+ int drillSize = parseBoardUnits( T_pad_drill );
+ designSettings.m_Pad_Master.SetDrillSize( wxSize( drillSize, drillSize ) );
+ NeedRIGHT();
+ }
+ break;
+
+ case T_pad_to_mask_clearance:
+ designSettings.m_SolderMaskMargin = parseBoardUnits( T_pad_to_mask_clearance );
+ NeedRIGHT();
+ break;
+
+ case T_solder_mask_min_width:
+ designSettings.m_SolderMaskMinWidth = parseBoardUnits( T_solder_mask_min_width );
+ NeedRIGHT();
+ break;
+
+ case T_pad_to_paste_clearance:
+ designSettings.m_SolderPasteMargin = parseBoardUnits( T_pad_to_paste_clearance );
+ NeedRIGHT();
+ break;
+
+ case T_pad_to_paste_clearance_ratio:
+ designSettings.m_SolderPasteMarginRatio = parseDouble( T_pad_to_paste_clearance_ratio );
+ NeedRIGHT();
+ break;
+
+ case T_aux_axis_origin:
+ {
+ int x = parseBoardUnits( "auxiliary origin X" );
+ int y = parseBoardUnits( "auxiliary origin Y" );
+ // m_board->SetAuxOrigin( wxPoint( x, y ) ); gets overwritten via SetDesignSettings below
+ designSettings.m_AuxOrigin = wxPoint( x, y );
+ NeedRIGHT();
+ }
+ break;
+
+ case T_grid_origin:
+ {
+ int x = parseBoardUnits( "grid origin X" );
+ int y = parseBoardUnits( "grid origin Y" );
+ // m_board->SetGridOrigin( wxPoint( x, y ) ); gets overwritten SetDesignSettings below
+ designSettings.m_GridOrigin = wxPoint( x, y );
+ NeedRIGHT();
+ }
+ break;
+
+ case T_visible_elements:
+ designSettings.SetVisibleElements( parseHex() | MIN_VISIBILITY_MASK );
+ NeedRIGHT();
+ break;
+
+ case T_pcbplotparams:
+ {
+ PCB_PLOT_PARAMS plotParams;
+ PCB_PLOT_PARAMS_PARSER parser( reader );
+ // parser must share the same current line as our current PCB parser
+ // synchronize it.
+ parser.SyncLineReaderWith( *this );
+
+ plotParams.Parse( &parser );
+ SyncLineReaderWith( parser );
+
+ m_board->SetPlotOptions( plotParams );
+ }
+ break;
+
+ default:
+ Unexpected( CurText() );
+ }
+ }
+
+ m_board->SetDesignSettings( designSettings );
+ m_board->SetZoneSettings( zoneSettings );
+}
+
+
+void PCB_PARSER::parseNETINFO_ITEM() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_net,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net." ) );
+
+ int netCode = parseInt( "net number" );
+
+ NeedSYMBOLorNUMBER();
+ wxString name = FromUTF8();
+
+ NeedRIGHT();
+
+ // net 0 should be already in list, so store this net
+ // if it is not the net 0, or if the net 0 does not exists.
+ // (TODO: a better test.)
+ if( netCode > 0 || m_board->FindNet( 0 ) == NULL )
+ {
+ NETINFO_ITEM* net = new NETINFO_ITEM( m_board, name, netCode );
+ m_board->AppendNet( net );
+
+ // Store the new code mapping
+ pushValueIntoMap( netCode, net->GetNet() );
+ }
+}
+
+
+void PCB_PARSER::parseNETCLASS() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_RET( CurTok() == T_net_class,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net class." ) );
+
+ T token;
+
+ NETCLASSPTR nc = boost::make_shared<NETCLASS>( wxEmptyString );
+
+ // Read netclass name (can be a name or just a number like track width)
+ NeedSYMBOLorNUMBER();
+ nc->SetName( FromUTF8() );
+ NeedSYMBOL();
+ nc->SetDescription( FromUTF8() );
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_clearance:
+ nc->SetClearance( parseBoardUnits( T_clearance ) );
+ break;
+
+ case T_trace_width:
+ nc->SetTrackWidth( parseBoardUnits( T_trace_width ) );
+ break;
+
+ case T_via_dia:
+ nc->SetViaDiameter( parseBoardUnits( T_via_dia ) );
+ break;
+
+ case T_via_drill:
+ nc->SetViaDrill( parseBoardUnits( T_via_drill ) );
+ break;
+
+ case T_uvia_dia:
+ nc->SetuViaDiameter( parseBoardUnits( T_uvia_dia ) );
+ break;
+
+ case T_uvia_drill:
+ nc->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
+ break;
+
+ case T_add_net:
+ NeedSYMBOLorNUMBER();
+ nc->Add( FromUTF8() );
+ break;
+
+ default:
+ Expecting( "clearance, trace_width, via_dia, via_drill, uvia_dia, uvia_drill, or add_net" );
+ }
+
+ NeedRIGHT();
+ }
+
+ if( !m_board->GetDesignSettings().m_NetClasses.Add( nc ) )
+ {
+ // Must have been a name conflict, this is a bad board file.
+ // User may have done a hand edit to the file.
+
+ // auto_ptr will delete nc on this code path
+
+ wxString error;
+ error.Printf( _( "duplicate NETCLASS name '%s' in file <%s> at line %d, offset %d" ),
+ nc->GetName().GetData(), CurSource().GetData(), CurLineNumber(), CurOffset() );
+ THROW_IO_ERROR( error );
+ }
+}
+
+
+DRAWSEGMENT* PCB_PARSER::parseDRAWSEGMENT() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_gr_arc || CurTok() == T_gr_circle || CurTok() == T_gr_curve ||
+ CurTok() == T_gr_line || CurTok() == T_gr_poly, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DRAWSEGMENT." ) );
+
+ T token;
+ wxPoint pt;
+ std::auto_ptr< DRAWSEGMENT > segment( new DRAWSEGMENT( NULL ) );
+
+ switch( CurTok() )
+ {
+ case T_gr_arc:
+ segment->SetShape( S_ARC );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_start )
+ Expecting( T_start );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetStart( pt );
+ NeedRIGHT();
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_end )
+ Expecting( T_end );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetEnd( pt );
+ NeedRIGHT();
+ break;
+
+ case T_gr_circle:
+ segment->SetShape( S_CIRCLE );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_center )
+ Expecting( T_center );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetStart( pt );
+ NeedRIGHT();
+ NeedLEFT();
+
+ token = NextTok();
+
+ if( token != T_end )
+ Expecting( T_end );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetEnd( pt );
+ NeedRIGHT();
+ break;
+
+ case T_gr_curve:
+ segment->SetShape( S_CURVE );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ segment->SetStart( parseXY() );
+ segment->SetBezControl1( parseXY() );
+ segment->SetBezControl2( parseXY() );
+ segment->SetEnd( parseXY() );
+ NeedRIGHT();
+ break;
+
+ case T_gr_line:
+ // Default DRAWSEGMENT type is S_SEGMENT.
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_start )
+ Expecting( T_start );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetStart( pt );
+ NeedRIGHT();
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_end )
+ Expecting( T_end );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetEnd( pt );
+ NeedRIGHT();
+ break;
+
+ case T_gr_poly:
+ {
+ segment->SetShape( S_POLYGON );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ std::vector< wxPoint > pts;
+
+ while( (token = NextTok()) != T_RIGHT )
+ pts.push_back( parseXY() );
+
+ segment->SetPolyPoints( pts );
+ }
+ break;
+
+ default:
+ Expecting( "gr_arc, gr_circle, gr_curve, gr_line, or gr_poly" );
+ }
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_angle:
+ segment->SetAngle( parseDouble( "segment angle" ) * 10.0 );
+ break;
+
+ case T_layer:
+ segment->SetLayer( parseBoardItemLayer() );
+ break;
+
+ case T_width:
+ segment->SetWidth( parseBoardUnits( T_width ) );
+ break;
+
+ case T_tstamp:
+ segment->SetTimeStamp( parseHex() );
+ break;
+
+ case T_status:
+ segment->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
+ break;
+
+ default:
+ Expecting( "layer, width, tstamp, or status" );
+ }
+
+ NeedRIGHT();
+ }
+
+ return segment.release();
+}
+
+
+TEXTE_PCB* PCB_PARSER::parseTEXTE_PCB() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_gr_text, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as TEXTE_PCB." ) );
+
+ T token;
+
+ std::auto_ptr<TEXTE_PCB> text( new TEXTE_PCB( m_board ) );
+ NeedSYMBOLorNUMBER();
+
+ text->SetText( FromUTF8() );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_at )
+ Expecting( T_at );
+
+ wxPoint pt;
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ text->SetTextPosition( pt );
+
+ // If there is no orientation defined, then it is the default value of 0 degrees.
+ token = NextTok();
+
+ if( token == T_NUMBER )
+ {
+ text->SetOrientation( parseDouble() * 10.0 );
+ NeedRIGHT();
+ }
+ else if( token != T_RIGHT )
+ {
+ Unexpected( CurText() );
+ }
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_layer:
+ text->SetLayer( parseBoardItemLayer() );
+ NeedRIGHT();
+ break;
+
+ case T_tstamp:
+ text->SetTimeStamp( parseHex() );
+ NeedRIGHT();
+ break;
+
+ case T_effects:
+ parseEDA_TEXT( (EDA_TEXT*) text.get() );
+ break;
+
+ default:
+ Expecting( "layer, tstamp or effects" );
+ }
+ }
+
+ return text.release();
+}
+
+
+DIMENSION* PCB_PARSER::parseDIMENSION() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_dimension, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DIMENSION." ) );
+
+ T token;
+
+ std::auto_ptr<DIMENSION> dimension( new DIMENSION( NULL ) );
+
+ dimension->SetValue( parseBoardUnits( "dimension value" ) );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_width )
+ Expecting( T_width );
+
+ dimension->SetWidth( parseBoardUnits( "dimension width value" ) );
+ NeedRIGHT();
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_layer:
+ dimension->SetLayer( parseBoardItemLayer() );
+ NeedRIGHT();
+ break;
+
+ case T_tstamp:
+ dimension->SetTimeStamp( parseHex() );
+ NeedRIGHT();
+ break;
+
+ case T_gr_text:
+ {
+ TEXTE_PCB* text = parseTEXTE_PCB();
+ dimension->Text() = *text;
+ dimension->SetPosition( text->GetTextPosition() );
+ delete text;
+ break;
+ }
+
+ case T_feature1:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ parseXY( &dimension->m_featureLineDO.x, &dimension->m_featureLineDO.y );
+ parseXY( &dimension->m_featureLineDF.x, &dimension->m_featureLineDF.y );
+ dimension->UpdateHeight();
+ NeedRIGHT();
+ NeedRIGHT();
+ break;
+
+ case T_feature2:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ parseXY( &dimension->m_featureLineGO.x, &dimension->m_featureLineGO.y );
+ parseXY( &dimension->m_featureLineGF.x, &dimension->m_featureLineGF.y );
+ dimension->UpdateHeight();
+ NeedRIGHT();
+ NeedRIGHT();
+ break;
+
+
+ case T_crossbar:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ parseXY( &dimension->m_crossBarO.x, &dimension->m_crossBarO.y );
+ parseXY( &dimension->m_crossBarF.x, &dimension->m_crossBarF.y );
+ dimension->UpdateHeight();
+ NeedRIGHT();
+ NeedRIGHT();
+ break;
+
+ case T_arrow1a:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ parseXY( &dimension->m_crossBarF.x, &dimension->m_crossBarF.y );
+ parseXY( &dimension->m_arrowD1F.x, &dimension->m_arrowD1F.y );
+ NeedRIGHT();
+ NeedRIGHT();
+ break;
+
+ case T_arrow1b:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ parseXY( &dimension->m_crossBarF.x, &dimension->m_crossBarF.y );
+ parseXY( &dimension->m_arrowD2F.x, &dimension->m_arrowD2F.y );
+ NeedRIGHT();
+ NeedRIGHT();
+ break;
+
+ case T_arrow2a:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ parseXY( &dimension->m_crossBarO.x, &dimension->m_crossBarO.y );
+ parseXY( &dimension->m_arrowG1F.x, &dimension->m_arrowG1F.y );
+ NeedRIGHT();
+ NeedRIGHT();
+ break;
+
+ case T_arrow2b:
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ parseXY( &dimension->m_crossBarO.x, &dimension->m_crossBarO.y );
+ parseXY( &dimension->m_arrowG2F.x, &dimension->m_arrowG2F.y );
+ NeedRIGHT();
+ NeedRIGHT();
+ break;
+
+ default:
+ Expecting( "layer, tstamp, gr_text, feature1, feature2 crossbar, arrow1a, "
+ "arrow1b, arrow2a, or arrow2b" );
+ }
+ }
+
+ return dimension.release();
+}
+
+
+MODULE* PCB_PARSER::parseMODULE( wxArrayString* aInitialComments )
+ throw( IO_ERROR, PARSE_ERROR, FUTURE_FORMAT_ERROR )
+{
+ try
+ {
+ return parseMODULE_unchecked( aInitialComments );
+ }
+ catch( const PARSE_ERROR& parse_error )
+ {
+ if( m_tooRecent )
+ throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
+ else
+ throw;
+ }
+}
+
+
+MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments )
+ throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_module, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as MODULE." ) );
+
+ wxString name;
+ wxPoint pt;
+ T token;
+ FPID fpid;
+
+ std::auto_ptr<MODULE> module( new MODULE( m_board ) );
+
+ module->SetInitialComments( aInitialComments );
+
+ token = NextTok();
+
+ if( !IsSymbol( token ) && token != T_NUMBER )
+ Expecting( "symbol|number" );
+
+ name = FromUTF8();
+
+ if( !name.IsEmpty() && fpid.Parse( FromUTF8() ) >= 0 )
+ {
+ wxString error;
+ error.Printf( _( "invalid footprint ID in\nfile: <%s>\nline: %d\noffset: %d" ),
+ GetChars( CurSource() ), CurLineNumber(), CurOffset() );
+ THROW_IO_ERROR( error );
+ }
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_version:
+ {
+ // Theoretically a module nested in a PCB could declare its own version, though
+ // as of writing this comment we don't do that. Just in case, take the greater
+ // version.
+ int this_version = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
+ NeedRIGHT();
+ m_requiredVersion = std::max( m_requiredVersion, this_version );
+ m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
+ break;
+ }
+
+ case T_locked:
+ module->SetLocked( true );
+ break;
+
+ case T_placed:
+ module->SetIsPlaced( true );
+ break;
+
+ case T_layer:
+ {
+ // Footprints can be only on the front side or the back side.
+ // but because we can find some stupid layer in file, ensure a
+ // acceptable layer is set for the footprint
+ LAYER_ID layer = parseBoardItemLayer();
+ module->SetLayer( layer == B_Cu ? B_Cu : F_Cu );
+ }
+ NeedRIGHT();
+ break;
+
+ case T_tedit:
+ module->SetLastEditTime( parseHex() );
+ NeedRIGHT();
+ break;
+
+ case T_tstamp:
+ module->SetTimeStamp( parseHex() );
+ NeedRIGHT();
+ break;
+
+ case T_at:
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ module->SetPosition( pt );
+ token = NextTok();
+
+ if( token == T_NUMBER )
+ {
+ module->SetOrientation( parseDouble() * 10.0 );
+ NeedRIGHT();
+ }
+ else if( token != T_RIGHT )
+ {
+ Expecting( T_RIGHT );
+ }
+
+ break;
+
+ case T_descr:
+ NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
+ module->SetDescription( FromUTF8() );
+ NeedRIGHT();
+ break;
+
+ case T_tags:
+ NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
+ module->SetKeywords( FromUTF8() );
+ NeedRIGHT();
+ break;
+
+ case T_path:
+ NeedSYMBOLorNUMBER(); // Paths can be numerical so a number is also a symbol here
+ module->SetPath( FromUTF8() );
+ NeedRIGHT();
+ break;
+
+ case T_autoplace_cost90:
+ module->SetPlacementCost90( parseInt( "auto place cost at 90 degrees" ) );
+ NeedRIGHT();
+ break;
+
+ case T_autoplace_cost180:
+ module->SetPlacementCost180( parseInt( "auto place cost at 180 degrees" ) );
+ NeedRIGHT();
+ break;
+
+ case T_solder_mask_margin:
+ module->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_solder_paste_margin:
+ module->SetLocalSolderPasteMargin(
+ parseBoardUnits( "local solder paste margin value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_solder_paste_ratio:
+ module->SetLocalSolderPasteMarginRatio(
+ parseDouble( "local solder paste margin ratio value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_clearance:
+ module->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_zone_connect:
+ module->SetZoneConnection( (ZoneConnection) parseInt( "zone connection value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_thermal_width:
+ module->SetThermalWidth( parseBoardUnits( "thermal width value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_thermal_gap:
+ module->SetThermalGap( parseBoardUnits( "thermal gap value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_attr:
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ switch( token )
+ {
+ case T_smd:
+ module->SetAttributes( module->GetAttributes() | MOD_CMS );
+ break;
+
+ case T_virtual:
+ module->SetAttributes( module->GetAttributes() | MOD_VIRTUAL );
+ break;
+
+ default:
+ Expecting( "smd and/or virtual" );
+ }
+ }
+
+ break;
+
+ case T_fp_text:
+ {
+ TEXTE_MODULE* text = parseTEXTE_MODULE();
+ text->SetParent( module.get() );
+ double orientation = text->GetOrientation();
+ orientation -= module->GetOrientation();
+ text->SetOrientation( orientation );
+ text->SetDrawCoord();
+
+ switch( text->GetType() )
+ {
+ case TEXTE_MODULE::TEXT_is_REFERENCE:
+ module->Reference() = *text;
+ delete text;
+ break;
+
+ case TEXTE_MODULE::TEXT_is_VALUE:
+ module->Value() = *text;
+ delete text;
+ break;
+
+ default:
+ module->GraphicalItems().PushBack( text );
+ }
+ }
+ break;
+
+ case T_fp_arc:
+ case T_fp_circle:
+ case T_fp_curve:
+ case T_fp_line:
+ case T_fp_poly:
+ {
+ EDGE_MODULE* em = parseEDGE_MODULE();
+ em->SetParent( module.get() );
+ em->SetDrawCoord();
+ module->GraphicalItems().PushBack( em );
+ }
+ break;
+
+ case T_pad:
+ {
+ D_PAD* pad = parseD_PAD( module.get() );
+ wxPoint pt = pad->GetPos0();
+
+ RotatePoint( &pt, module->GetOrientation() );
+ pad->SetPosition( pt + module->GetPosition() );
+ module->Add( pad, ADD_APPEND );
+ }
+ break;
+
+ case T_model:
+ module->Add3DModel( parse3DModel() );
+ break;
+
+ default:
+ Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, "
+ "autoplace_cost90, autoplace_cost180, solder_mask_margin, "
+ "solder_paste_margin, solder_paste_ratio, clearance, "
+ "zone_connect, thermal_width, thermal_gap, attr, fp_text, "
+ "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, pad, or model" );
+ }
+ }
+
+ module->SetFPID( fpid );
+ module->CalculateBoundingBox();
+
+ return module.release();
+}
+
+
+TEXTE_MODULE* PCB_PARSER::parseTEXTE_MODULE() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_fp_text, NULL,
+ wxString::Format( wxT( "Cannot parse %s as TEXTE_MODULE at line %d, offset %d." ),
+ GetChars( GetTokenString( CurTok() ) ),
+ CurLineNumber(), CurOffset() ) );
+
+ T token = NextTok();
+
+ std::auto_ptr<TEXTE_MODULE> text( new TEXTE_MODULE( NULL ) );
+
+ switch( token )
+ {
+ case T_reference:
+ text->SetType( TEXTE_MODULE::TEXT_is_REFERENCE );
+ break;
+
+ case T_value:
+ text->SetType( TEXTE_MODULE::TEXT_is_VALUE );
+ break;
+
+ case T_user:
+ break; // Default type is user text.
+
+ default:
+ THROW_IO_ERROR( wxString::Format( _( "cannot handle footprint text type %s" ),
+ GetChars( FromUTF8() ) ) );
+ }
+
+ NeedSYMBOLorNUMBER();
+
+ text->SetText( FromUTF8() );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_at )
+ Expecting( T_at );
+
+ wxPoint pt;
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ text->SetPos0( pt );
+ token = NextTok();
+
+ // If there is no orientation defined, then it is the default value of 0 degrees.
+ if( token == T_NUMBER )
+ {
+ text->SetOrientation( parseDouble() * 10.0 );
+ NeedRIGHT();
+ }
+ else if( token != T_RIGHT )
+ {
+ Unexpected( CurText() );
+ }
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_layer:
+ text->SetLayer( parseBoardItemLayer() );
+ NeedRIGHT();
+ break;
+
+ case T_hide:
+ text->SetVisible( false );
+ break;
+
+ case T_effects:
+ parseEDA_TEXT( (EDA_TEXT*) text.get() );
+ // Due to a double definition (a bug) of SetVisible (one in EDA_TEXT and
+ // one in TEXTE_MODULE), verify if the EDA_TEXT is set to invisible
+ // This allows compatibility with files which could be created
+ // by pcbnew version janv 25, 2017, which fix a old bug (this one)
+ // This compatibility is especially important for footprint files.
+ if( !text->EDA_TEXT::IsVisible() )
+ {
+ // Only if the EDA_TEXT visibility is false, set the
+ // TEXTE_MODULE visibility to false.
+ // in files created by 4.05 version and older, the EDA_TEXT
+ // visibility is always true (the default), due to this bug
+ text->SetVisible( false );
+ // If we want a full compatibiliy with previous stable version,
+ // this ugly workaround avoids writing "hide" token
+ // in "effects" section, when the file comes from some recent pcbnew version
+ text->EDA_TEXT::SetVisible( true );
+ }
+ break;
+
+ default:
+ Expecting( "hide or effects" );
+ }
+ }
+
+ return text.release();
+}
+
+
+EDGE_MODULE* PCB_PARSER::parseEDGE_MODULE() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_fp_arc || CurTok() == T_fp_circle || CurTok() == T_fp_curve ||
+ CurTok() == T_fp_line || CurTok() == T_fp_poly, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDGE_MODULE." ) );
+
+ wxPoint pt;
+ T token;
+
+ std::auto_ptr< EDGE_MODULE > segment( new EDGE_MODULE( NULL ) );
+
+ switch( CurTok() )
+ {
+ case T_fp_arc:
+ segment->SetShape( S_ARC );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_start )
+ Expecting( T_start );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetStart0( pt );
+ NeedRIGHT();
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_end )
+ Expecting( T_end );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetEnd0( pt );
+ NeedRIGHT();
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_angle )
+ Expecting( T_angle );
+
+ segment->SetAngle( parseDouble( "segment angle" ) * 10.0 );
+ NeedRIGHT();
+ break;
+
+ case T_fp_circle:
+ segment->SetShape( S_CIRCLE );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_center )
+ Expecting( T_center );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetStart0( pt );
+ NeedRIGHT();
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_end )
+ Expecting( T_end );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetEnd0( pt );
+ NeedRIGHT();
+ break;
+
+ case T_fp_curve:
+ segment->SetShape( S_CURVE );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ segment->SetStart0( parseXY() );
+ segment->SetBezControl1( parseXY() );
+ segment->SetBezControl2( parseXY() );
+ segment->SetEnd0( parseXY() );
+ NeedRIGHT();
+ break;
+
+ case T_fp_line:
+ // Default DRAWSEGMENT type is S_SEGMENT.
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_start )
+ Expecting( T_start );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetStart0( pt );
+
+ NeedRIGHT();
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_end )
+ Expecting( T_end );
+
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ segment->SetEnd0( pt );
+ NeedRIGHT();
+ break;
+
+ case T_fp_poly:
+ {
+ segment->SetShape( S_POLYGON );
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ std::vector< wxPoint > pts;
+
+ while( (token = NextTok()) != T_RIGHT )
+ pts.push_back( parseXY() );
+
+ segment->SetPolyPoints( pts );
+ }
+ break;
+
+ default:
+ Expecting( "fp_arc, fp_circle, fp_curve, fp_line, or fp_poly" );
+ }
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_layer:
+ segment->SetLayer( parseBoardItemLayer() );
+ break;
+
+ case T_width:
+ segment->SetWidth( parseBoardUnits( T_width ) );
+ break;
+
+ case T_tstamp:
+ segment->SetTimeStamp( parseHex() );
+ break;
+
+ case T_status:
+ segment->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
+ break;
+
+ default:
+ Expecting( "layer or width" );
+ }
+
+ NeedRIGHT();
+ }
+
+ return segment.release();
+}
+
+
+D_PAD* PCB_PARSER::parseD_PAD( MODULE* aParent ) throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_pad, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as D_PAD." ) );
+
+ wxSize sz;
+ wxPoint pt;
+
+ std::auto_ptr< D_PAD > pad( new D_PAD( aParent ) );
+
+ NeedSYMBOLorNUMBER();
+ pad->SetPadName( FromUTF8() );
+
+ T token = NextTok();
+
+ switch( token )
+ {
+ case T_thru_hole:
+ pad->SetAttribute( PAD_ATTRIB_STANDARD );
+ break;
+
+ case T_smd:
+ pad->SetAttribute( PAD_ATTRIB_SMD );
+
+ // Default D_PAD object is thru hole with drill.
+ // SMD pads have no hole
+ pad->SetDrillSize( wxSize( 0, 0 ) );
+ break;
+
+ case T_connect:
+ pad->SetAttribute( PAD_ATTRIB_CONN );
+
+ // Default D_PAD object is thru hole with drill.
+ // CONN pads have no hole
+ pad->SetDrillSize( wxSize( 0, 0 ) );
+ break;
+
+ case T_np_thru_hole:
+ pad->SetAttribute( PAD_ATTRIB_HOLE_NOT_PLATED );
+ break;
+
+ default:
+ Expecting( "thru_hole, smd, connect, or np_thru_hole" );
+ }
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_circle:
+ pad->SetShape( PAD_SHAPE_CIRCLE );
+ break;
+
+ case T_rect:
+ pad->SetShape( PAD_SHAPE_RECT );
+ break;
+
+ case T_oval:
+ pad->SetShape( PAD_SHAPE_OVAL );
+ break;
+
+ case T_trapezoid:
+ pad->SetShape( PAD_SHAPE_TRAPEZOID );
+ break;
+
+ default:
+ Expecting( "circle, rectangle, oval, or trapezoid" );
+ }
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_size:
+ sz.SetWidth( parseBoardUnits( "width value" ) );
+ sz.SetHeight( parseBoardUnits( "height value" ) );
+ pad->SetSize( sz );
+ NeedRIGHT();
+ break;
+
+ case T_at:
+ pt.x = parseBoardUnits( "X coordinate" );
+ pt.y = parseBoardUnits( "Y coordinate" );
+ pad->SetPos0( pt );
+ token = NextTok();
+
+ if( token == T_NUMBER )
+ {
+ pad->SetOrientation( parseDouble() * 10.0 );
+ NeedRIGHT();
+ }
+ else if( token != T_RIGHT )
+ {
+ Expecting( ") or angle value" );
+ }
+
+ break;
+
+ case T_rect_delta:
+ {
+ wxSize delta;
+ delta.SetWidth( parseBoardUnits( "rectangle delta width" ) );
+ delta.SetHeight( parseBoardUnits( "rectangle delta height" ) );
+ pad->SetDelta( delta );
+ NeedRIGHT();
+ }
+ break;
+
+ case T_drill:
+ {
+ bool haveWidth = false;
+ wxSize drillSize = pad->GetDrillSize();
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_oval:
+ pad->SetDrillShape( PAD_DRILL_SHAPE_OBLONG );
+ break;
+
+ case T_NUMBER:
+ {
+ if( !haveWidth )
+ {
+ drillSize.SetWidth( parseBoardUnits() );
+
+ // If height is not defined the width and height are the same.
+ drillSize.SetHeight( drillSize.GetWidth() );
+ haveWidth = true;
+ }
+ else
+ {
+ drillSize.SetHeight( parseBoardUnits() );
+ }
+
+ }
+ break;
+
+ case T_offset:
+ pt.x = parseBoardUnits( "drill offset x" );
+ pt.y = parseBoardUnits( "drill offset y" );
+ pad->SetOffset( pt );
+ NeedRIGHT();
+ break;
+
+ default:
+ Expecting( "oval, size, or offset" );
+ }
+ }
+
+ // This fixes a bug caused by setting the default D_PAD drill size to a value
+ // other than 0 used to fix a bunch of debug assertions even though it is defined
+ // as a through hole pad. Wouldn't a though hole pad with no drill be a surface
+ // mount pad (or a conn pad which is a smd pad with no solder paste)?
+ if( ( pad->GetAttribute() != PAD_ATTRIB_SMD ) && ( pad->GetAttribute() != PAD_ATTRIB_CONN ) )
+ pad->SetDrillSize( drillSize );
+ else
+ pad->SetDrillSize( wxSize( 0, 0 ) );
+
+ }
+ break;
+
+ case T_layers:
+ {
+ LSET layerMask = parseBoardItemLayersAsMask();
+ pad->SetLayerSet( layerMask );
+ }
+ break;
+
+ case T_net:
+ if( ! pad->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
+ THROW_IO_ERROR(
+ wxString::Format( _( "invalid net ID in\nfile: <%s>\nline: %d\noffset: %d" ),
+ GetChars( CurSource() ), CurLineNumber(), CurOffset() )
+ );
+ NeedSYMBOLorNUMBER();
+ if( m_board && FromUTF8() != m_board->FindNet( pad->GetNetCode() )->GetNetname() )
+ THROW_IO_ERROR(
+ wxString::Format( _( "invalid net ID in\nfile: <%s>\nline: %d\noffset: %d" ),
+ GetChars( CurSource() ), CurLineNumber(), CurOffset() )
+ );
+ NeedRIGHT();
+ break;
+
+ case T_die_length:
+ pad->SetPadToDieLength( parseBoardUnits( T_die_length ) );
+ NeedRIGHT();
+ break;
+
+ case T_solder_mask_margin:
+ pad->SetLocalSolderMaskMargin( parseBoardUnits( T_solder_mask_margin ) );
+ NeedRIGHT();
+ break;
+
+ case T_solder_paste_margin:
+ pad->SetLocalSolderPasteMargin( parseBoardUnits( T_solder_paste_margin ) );
+ NeedRIGHT();
+ break;
+
+ case T_solder_paste_margin_ratio:
+ pad->SetLocalSolderPasteMarginRatio(
+ parseDouble( "pad local solder paste margin ratio value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_clearance:
+ pad->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_zone_connect:
+ pad->SetZoneConnection( (ZoneConnection) parseInt( "zone connection value" ) );
+ NeedRIGHT();
+ break;
+
+ case T_thermal_width:
+ pad->SetThermalWidth( parseBoardUnits( T_thermal_width ) );
+ NeedRIGHT();
+ break;
+
+ case T_thermal_gap:
+ pad->SetThermalGap( parseBoardUnits( T_thermal_gap ) );
+ NeedRIGHT();
+ break;
+
+ default:
+ Expecting( "at, drill, layers, net, die_length, solder_mask_margin, "
+ "solder_paste_margin, solder_paste_margin_ratio, clearance, "
+ "zone_connect, thermal_width, or thermal_gap" );
+ }
+ }
+
+ return pad.release();
+}
+
+
+TRACK* PCB_PARSER::parseTRACK() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_segment, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as TRACK." ) );
+
+ wxPoint pt;
+ T token;
+
+ std::auto_ptr< TRACK > track( new TRACK( m_board ) );
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_start:
+ pt.x = parseBoardUnits( "start x" );
+ pt.y = parseBoardUnits( "start y" );
+ track->SetStart( pt );
+ break;
+
+ case T_end:
+ pt.x = parseBoardUnits( "end x" );
+ pt.y = parseBoardUnits( "end y" );
+ track->SetEnd( pt );
+ break;
+
+ case T_width:
+ track->SetWidth( parseBoardUnits( "width" ) );
+ break;
+
+ case T_layer:
+ track->SetLayer( parseBoardItemLayer() );
+ break;
+
+ case T_net:
+ if( ! track->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
+ THROW_IO_ERROR(
+ wxString::Format( _( "invalid net ID in\nfile: <%s>\nline: %d\noffset: %d" ),
+ GetChars( CurSource() ), CurLineNumber(), CurOffset() )
+ );
+ break;
+
+ case T_tstamp:
+ track->SetTimeStamp( parseHex() );
+ break;
+
+ case T_status:
+ track->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
+ break;
+
+ default:
+ Expecting( "start, end, width, layer, net, tstamp, or status" );
+ }
+
+ NeedRIGHT();
+ }
+
+ return track.release();
+}
+
+
+VIA* PCB_PARSER::parseVIA() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_via, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as VIA." ) );
+
+ wxPoint pt;
+ T token;
+
+ std::auto_ptr< VIA > via( new VIA( m_board ) );
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_blind:
+ via->SetViaType( VIA_BLIND_BURIED );
+ break;
+
+ case T_micro:
+ via->SetViaType( VIA_MICROVIA );
+ break;
+
+ case T_at:
+ pt.x = parseBoardUnits( "start x" );
+ pt.y = parseBoardUnits( "start y" );
+ via->SetStart( pt );
+ via->SetEnd( pt );
+ NeedRIGHT();
+ break;
+
+ case T_size:
+ via->SetWidth( parseBoardUnits( "via width" ) );
+ NeedRIGHT();
+ break;
+
+ case T_drill:
+ via->SetDrill( parseBoardUnits( "drill diameter" ) );
+ NeedRIGHT();
+ break;
+
+ case T_layers:
+ {
+ LAYER_ID layer1, layer2;
+ NextTok();
+ layer1 = lookUpLayer<LAYER_ID>( m_layerIndices );
+ NextTok();
+ layer2 = lookUpLayer<LAYER_ID>( m_layerIndices );
+ via->SetLayerPair( layer1, layer2 );
+ NeedRIGHT();
+ }
+ break;
+
+ case T_net:
+ if(! via->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true))
+ THROW_IO_ERROR(
+ wxString::Format( _( "invalid net ID in\nfile: <%s>\nline: %d\noffset: %d" ),
+ GetChars( CurSource() ), CurLineNumber(), CurOffset() )
+ );
+ NeedRIGHT();
+ break;
+
+ case T_tstamp:
+ via->SetTimeStamp( parseHex() );
+ NeedRIGHT();
+ break;
+
+ case T_status:
+ via->SetStatus( static_cast<STATUS_FLAGS>( parseHex() ) );
+ NeedRIGHT();
+ break;
+
+ default:
+ Expecting( "blind, micro, at, size, drill, layers, net, tstamp, or status" );
+ }
+ }
+
+ return via.release();
+}
+
+
+ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_zone, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
+ wxT( " as ZONE_CONTAINER." ) );
+
+ CPolyLine::HATCH_STYLE hatchStyle = CPolyLine::NO_HATCH;
+
+ int hatchPitch = Mils2iu( CPolyLine::GetDefaultHatchPitchMils() );
+ wxPoint pt;
+ T token;
+ int tmp;
+ wxString netnameFromfile; // the zone net name find in file
+
+ // bigger scope since each filled_polygon is concatenated in here
+ SHAPE_POLY_SET pts;
+
+ std::auto_ptr< ZONE_CONTAINER > zone( new ZONE_CONTAINER( m_board ) );
+
+ zone->SetPriority( 0 );
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_net:
+ // Init the net code only, not the netname, to be sure
+ // the zone net name is the name read in file.
+ // (When mismatch, the user will be prompted in DRC, to fix the actual name)
+ tmp = getNetCode( parseInt( "net number" ) );
+
+ if( tmp < 0 )
+ tmp = 0;
+
+ if( ! zone->SetNetCode( tmp, /* aNoAssert */ true ) )
+ THROW_IO_ERROR(
+ wxString::Format( _( "invalid net ID in\nfile: <%s>\nline: %d\noffset: %d" ),
+ GetChars( CurSource() ), CurLineNumber(), CurOffset() )
+ );
+
+ NeedRIGHT();
+ break;
+
+ case T_net_name:
+ NeedSYMBOLorNUMBER();
+ netnameFromfile = FromUTF8();
+ NeedRIGHT();
+ break;
+
+ case T_layer:
+ zone->SetLayer( parseBoardItemLayer() );
+ NeedRIGHT();
+ break;
+
+ case T_tstamp:
+ zone->SetTimeStamp( parseHex() );
+ NeedRIGHT();
+ break;
+
+ case T_hatch:
+ token = NextTok();
+
+ if( token != T_none && token != T_edge && token != T_full )
+ Expecting( "none, edge, or full" );
+
+ switch( token )
+ {
+ default:
+ case T_none: hatchStyle = CPolyLine::NO_HATCH; break;
+ case T_edge: hatchStyle = CPolyLine::DIAGONAL_EDGE; break;
+ case T_full: hatchStyle = CPolyLine::DIAGONAL_FULL;
+ }
+
+ hatchPitch = parseBoardUnits( "hatch pitch" );
+ NeedRIGHT();
+ break;
+
+ case T_priority:
+ zone->SetPriority( parseInt( "zone priority" ) );
+ NeedRIGHT();
+ break;
+
+ case T_connect_pads:
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_yes:
+ zone->SetPadConnection( PAD_ZONE_CONN_FULL );
+ break;
+
+ case T_no:
+ zone->SetPadConnection( PAD_ZONE_CONN_NONE );
+ break;
+
+ case T_thru_hole_only:
+ zone->SetPadConnection( PAD_ZONE_CONN_THT_THERMAL );
+ break;
+
+ case T_clearance:
+ zone->SetZoneClearance( parseBoardUnits( "zone clearance" ) );
+ NeedRIGHT();
+ break;
+
+ default:
+ Expecting( "yes, no, or clearance" );
+ }
+ }
+
+ break;
+
+ case T_min_thickness:
+ zone->SetMinThickness( parseBoardUnits( T_min_thickness ) );
+ NeedRIGHT();
+ break;
+
+ case T_fill:
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_yes:
+ zone->SetIsFilled( true );
+ break;
+
+ case T_mode:
+ token = NextTok();
+
+ if( token != T_segment && token != T_polygon )
+ Expecting( "segment or polygon" );
+
+ // @todo Create an enum for fill modes.
+ zone->SetFillMode( token == T_polygon ? 0 : 1 );
+ NeedRIGHT();
+ break;
+
+ case T_arc_segments:
+ zone->SetArcSegmentCount( parseInt( "arc segment count" ) );
+ NeedRIGHT();
+ break;
+
+ case T_thermal_gap:
+ zone->SetThermalReliefGap( parseBoardUnits( T_thermal_gap ) );
+ NeedRIGHT();
+ break;
+
+ case T_thermal_bridge_width:
+ zone->SetThermalReliefCopperBridge( parseBoardUnits( T_thermal_bridge_width ) );
+ NeedRIGHT();
+ break;
+
+ case T_smoothing:
+ switch( NextTok() )
+ {
+ case T_none:
+ zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_NONE );
+ break;
+
+ case T_chamfer:
+ if( !zone->GetIsKeepout() ) // smoothing has meaning only for filled zones
+ zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_CHAMFER );
+ break;
+
+ case T_fillet:
+ if( !zone->GetIsKeepout() ) // smoothing has meaning only for filled zones
+ zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_FILLET );
+ break;
+
+ default:
+ Expecting( "none, chamfer, or fillet" );
+ }
+ NeedRIGHT();
+ break;
+
+ case T_radius:
+ tmp = parseBoardUnits( "corner radius" );
+ if( !zone->GetIsKeepout() ) // smoothing has meaning only for filled zones
+ zone->SetCornerRadius( tmp );
+ NeedRIGHT();
+ break;
+
+ default:
+ Expecting( "mode, arc_segments, thermal_gap, thermal_bridge_width, "
+ "smoothing, or radius" );
+ }
+ }
+ break;
+
+ case T_keepout:
+ zone->SetIsKeepout( true );
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_tracks:
+ token = NextTok();
+
+ if( token != T_allowed && token != T_not_allowed )
+ Expecting( "allowed or not_allowed" );
+ zone->SetDoNotAllowTracks( token == T_not_allowed );
+ break;
+
+ case T_vias:
+ token = NextTok();
+
+ if( token != T_allowed && token != T_not_allowed )
+ Expecting( "allowed or not_allowed" );
+ zone->SetDoNotAllowVias( token == T_not_allowed );
+ break;
+
+ case T_copperpour:
+ token = NextTok();
+
+ if( token != T_allowed && token != T_not_allowed )
+ Expecting( "allowed or not_allowed" );
+ zone->SetDoNotAllowCopperPour( token == T_not_allowed );
+ break;
+
+ default:
+ Expecting( "tracks, vias or copperpour" );
+ }
+
+ NeedRIGHT();
+ }
+
+ break;
+
+ case T_polygon:
+ {
+ std::vector< wxPoint > corners;
+
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ corners.push_back( parseXY() );
+ }
+
+ NeedRIGHT();
+ zone->AddPolygon( corners );
+ }
+ break;
+
+ case T_filled_polygon:
+ {
+ // "(filled_polygon (pts"
+ NeedLEFT();
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ pts.NewOutline();
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ pts.Append( parseXY() );
+ }
+
+ NeedRIGHT();
+ }
+ break;
+
+ case T_fill_segments:
+ {
+ std::vector< SEGMENT > segs;
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token != T_LEFT )
+ Expecting( T_LEFT );
+
+ token = NextTok();
+
+ if( token != T_pts )
+ Expecting( T_pts );
+
+ SEGMENT segment( parseXY(), parseXY() );
+ NeedRIGHT();
+ segs.push_back( segment );
+ }
+
+ zone->AddFillSegments( segs );
+ }
+ break;
+
+ default:
+ Expecting( "net, layer, tstamp, hatch, priority, connect_pads, min_thickness, "
+ "fill, polygon, filled_polygon, or fill_segments" );
+ }
+ }
+
+ if( zone->GetNumCorners() > 2 )
+ {
+ if( !zone->IsOnCopperLayer() )
+ {
+ zone->SetFillMode( 0 );
+ zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
+ }
+
+ // Set hatch here, after outlines corners are read
+ zone->Outline()->SetHatch( hatchStyle, hatchPitch, true );
+ }
+
+ if( !pts.IsEmpty() )
+ zone->AddFilledPolysList( pts );
+
+ // Ensure keepout and non copper zones do not have a net
+ // (which have no sense for these zones)
+ // the netcode 0 is used for these zones
+ bool zone_has_net = zone->IsOnCopperLayer() && !zone->GetIsKeepout();
+
+ if( !zone_has_net )
+ zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
+
+ // Ensure the zone net name is valid, and matches the net code, for copper zones
+ if( zone_has_net && ( zone->GetNet()->GetNetname() != netnameFromfile ) )
+ {
+ // Can happens which old boards, with nonexistent nets ...
+ // or after being edited by hand
+ // We try to fix the mismatch.
+ NETINFO_ITEM* net = m_board->FindNet( netnameFromfile );
+
+ if( net ) // An existing net has the same net name. use it for the zone
+ zone->SetNetCode( net->GetNet() );
+ else // Not existing net: add a new net to keep trace of the zone netname
+ {
+ int newnetcode = m_board->GetNetCount();
+ net = new NETINFO_ITEM( m_board, netnameFromfile, newnetcode );
+ m_board->AppendNet( net );
+
+ // Store the new code mapping
+ pushValueIntoMap( newnetcode, net->GetNet() );
+ // and update the zone netcode
+ zone->SetNetCode( net->GetNet() );
+
+ // Prompt the user
+ wxString msg;
+ msg.Printf( _( "There is a zone that belongs to a not existing net\n"
+ "\"%s\"\n"
+ "you should verify and edit it (run DRC test)." ),
+ GetChars( netnameFromfile ) );
+ DisplayError( NULL, msg );
+ }
+ }
+
+ return zone.release();
+}
+
+
+PCB_TARGET* PCB_PARSER::parsePCB_TARGET() throw( IO_ERROR, PARSE_ERROR )
+{
+ wxCHECK_MSG( CurTok() == T_target, NULL,
+ wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TARGET." ) );
+
+ wxPoint pt;
+ T token;
+
+ std::auto_ptr< PCB_TARGET > target( new PCB_TARGET( NULL ) );
+
+ for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+ {
+ if( token == T_LEFT )
+ token = NextTok();
+
+ switch( token )
+ {
+ case T_x:
+ target->SetShape( 1 );
+ break;
+
+ case T_plus:
+ target->SetShape( 0 );
+ break;
+
+ case T_at:
+ pt.x = parseBoardUnits( "target x position" );
+ pt.y = parseBoardUnits( "target y position" );
+ target->SetPosition( pt );
+ NeedRIGHT();
+ break;
+
+ case T_size:
+ target->SetSize( parseBoardUnits( "target size" ) );
+ NeedRIGHT();
+ break;
+
+ case T_width:
+ target->SetWidth( parseBoardUnits( "target thickness" ) );
+ NeedRIGHT();
+ break;
+
+ case T_layer:
+ target->SetLayer( parseBoardItemLayer() );
+ NeedRIGHT();
+ break;
+
+ case T_tstamp:
+ target->SetTimeStamp( parseHex() );
+ NeedRIGHT();
+ break;
+
+ default:
+ Expecting( "x, plus, at, size, width, layer or tstamp" );
+ }
+ }
+
+ return target.release();
+}