/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2007-2011 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2007-2021 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 */ /* * 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 * $ doxygen * * Then you can view the html documentation in the /doxygen * directory. The main class in this file is SPECCTRA_DB and its main * functions are LoadPCB(), LoadSESSION(), and ExportPCB(). * * Wide use is made of boost::ptr_vector<> and std::vector<> template classes. * If the contained object is small, then std::vector tends to be used. * If the contained object is large, variable size, or would require writing * an assignment operator() or copy constructor, then boost::ptr_vector * cannot be beat. */ #include #include #include #include #include #include "specctra.h" #include namespace DSN { #define NESTWIDTH 2 ///< how many spaces per nestLevel //------------------------------------------------------ const char* GetTokenText( T aTok ) { return SPECCTRA_LEXER::TokenName( aTok ); } void SPECCTRA_DB::buildLayerMaps( BOARD* aBoard ) { // specctra wants top physical layer first, then going down to the // bottom most physical layer in physical sequence. // Same as KiCad now except for B_Cu unsigned layerCount = aBoard->GetCopperLayerCount(); m_layerIds.clear(); m_pcbLayer2kicad.resize( layerCount ); m_kicadLayer2pcb.resize( B_Cu + 1 ); #if 0 // was: for( LAYER_NUM kiNdx = layerCount - 1, pcbNdx=FIRST_LAYER; kiNdx >= 0; --kiNdx, ++pcbNdx ) { LAYER_NUM kilayer = (kiNdx>0 && kiNdx==layerCount-1) ? F_Cu : kiNdx; // establish bi-directional mapping between KiCad's BOARD layer and PCB layer pcbLayer2kicad[pcbNdx] = kilayer; kicadLayer2pcb[kilayer] = pcbNdx; // save the specctra layer name in SPECCTRA_DB::layerIds for later. layerIds.push_back( TO_UTF8( aBoard->GetLayerName( ToLAYER_ID( kilayer ) ) ) ); } #else // establish bi-directional mapping between KiCad's BOARD layer and PCB layer for( unsigned i = 0; i < m_kicadLayer2pcb.size(); ++i ) { if( i < layerCount-1 ) m_kicadLayer2pcb[i] = i; else m_kicadLayer2pcb[i] = layerCount - 1; } for( unsigned i = 0; i < m_pcbLayer2kicad.size(); ++i ) { PCB_LAYER_ID id = ( i < layerCount-1 ) ? ToLAYER_ID( i ) : B_Cu; m_pcbLayer2kicad[i] = id; // save the specctra layer name in SPECCTRA_DB::layerIds for later. m_layerIds.push_back(TO_UTF8( aBoard->GetLayerName( id ) ) ); } #endif } int SPECCTRA_DB::findLayerName( const std::string& aLayerName ) const { for( int i = 0; i < int( m_layerIds.size() ); ++i ) { if( 0 == aLayerName.compare( m_layerIds[i] ) ) return i; } return -1; } void SPECCTRA_DB::readCOMPnPIN( std::string* component_id, std::string* pin_id ) { T tok; static const char pin_def[] = "::=-"; if( !IsSymbol( (T) CurTok() ) ) Expecting( pin_def ); // case for: A12-14, i.e. no wrapping quotes. This should be a single // token, so split it. if( CurTok() != T_STRING ) { const char* toktext = CurText(); const char* dash = strchr( toktext, '-' ); if( !dash ) Expecting( pin_def ); while( toktext != dash ) *component_id += *toktext++; ++toktext; // skip the dash while( *toktext ) *pin_id += *toktext++; } else // quoted string: "U12"-"14" or "U12"-14, 3 tokens in either case { *component_id = CurText(); tok = NextTok(); if( tok!=T_DASH ) Expecting( pin_def ); NextTok(); // accept anything after the dash. *pin_id = CurText(); } } void SPECCTRA_DB::readTIME( time_t* time_stamp ) { T tok; struct tm mytime; static const char time_toks[] = " : : "; static const char* months[] = { // index 0 = Jan "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", nullptr }; NeedSYMBOL(); // month const char* ptok = CurText(); mytime.tm_mon = 0; // remains if we don't find a month match. for( int m = 0; months[m]; ++m ) { if( !strcasecmp( months[m], ptok ) ) { mytime.tm_mon = m; break; } } tok = NextTok(); // day if( tok != T_NUMBER ) Expecting( time_toks ); mytime.tm_mday = atoi( CurText() ); tok = NextTok(); // hour if( tok != T_NUMBER ) Expecting( time_toks ); mytime.tm_hour = atoi( CurText() ); // : colon NeedSYMBOL(); if( *CurText() != ':' || strlen( CurText() )!=1 ) Expecting( time_toks ); tok = NextTok(); // minute if( tok != T_NUMBER ) Expecting( time_toks ); mytime.tm_min = atoi( CurText() ); // : colon NeedSYMBOL(); if( *CurText() != ':' || strlen( CurText() )!=1 ) Expecting( time_toks ); tok = NextTok(); // second if( tok != T_NUMBER ) Expecting( time_toks ); mytime.tm_sec = atoi( CurText() ); tok = NextTok(); // year if( tok != T_NUMBER ) Expecting( time_toks ); mytime.tm_year = atoi( CurText() ) - 1900; *time_stamp = mktime( &mytime ); } void SPECCTRA_DB::LoadPCB( const wxString& aFilename ) { FILE_LINE_READER curr_reader( aFilename ); PushReader( &curr_reader ); if( NextTok() != T_LEFT ) Expecting( T_LEFT ); if( NextTok() != T_pcb ) Expecting( T_pcb ); SetPCB( new PCB() ); doPCB( m_pcb ); PopReader(); } void SPECCTRA_DB::LoadSESSION( const wxString& aFilename ) { FILE_LINE_READER curr_reader( aFilename ); PushReader( &curr_reader ); if( NextTok() != T_LEFT ) Expecting( T_LEFT ); if( NextTok() != T_session ) Expecting( T_session ); SetSESSION( new SESSION() ); doSESSION( m_session ); PopReader(); } void SPECCTRA_DB::doPCB( PCB* growth ) { T tok; /* ::= (pcb [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ | ] [ | ] [ | ] [ | ] [ | ] [ | ] [ ] [ ] ) */ NeedSYMBOL(); growth->pcbname = CurText(); while( (tok = NextTok()) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_parser: if( growth->parser ) Unexpected( tok ); growth->parser = new PARSER( growth ); doPARSER( growth->parser ); break; case T_unit: if( growth->unit ) Unexpected( tok ); growth->unit = new UNIT_RES( growth, tok ); doUNIT( growth->unit ); break; case T_resolution: if( growth->resolution ) Unexpected( tok ); growth->resolution = new UNIT_RES( growth, tok ); doRESOLUTION( growth->resolution ); break; case T_structure: if( growth->structure ) Unexpected( tok ); growth->structure = new STRUCTURE( growth ); doSTRUCTURE( growth->structure ); break; case T_placement: if( growth->placement ) Unexpected( tok ); growth->placement = new PLACEMENT( growth ); doPLACEMENT( growth->placement ); break; case T_library: if( growth->library ) Unexpected( tok ); growth->library = new LIBRARY( growth ); doLIBRARY( growth->library ); break; case T_network: if( growth->network ) Unexpected( tok ); growth->network = new NETWORK( growth ); doNETWORK( growth->network ); break; case T_wiring: if( growth->wiring ) Unexpected( tok ); growth->wiring = new WIRING( growth ); doWIRING( growth->wiring ); break; default: Unexpected( CurText() ); } } tok = NextTok(); if( tok != T_EOF ) Expecting( T_EOF ); } void SPECCTRA_DB::doPARSER( PARSER* growth ) { T tok; std::string const1; std::string const2; /* ::= (parser [(string_quote )] (space_in_quoted_tokens [on | off]) [(host_cad )] [(host_version )] [{(constant )}] [(write_resolution] { })] [(routes_include {[testpoint | guides | image_conductor]})] [(wires_include testpoint)] [(case_sensitive [on | off])] [(via_rotate_first [on | off])] ) */ 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 ); SetStringDelimiter( (unsigned char) *CurText() ); growth->string_quote = *CurText(); m_quote_char = CurText(); NeedRIGHT(); break; case T_space_in_quoted_tokens: tok = NextTok(); if( tok!=T_on && tok!=T_off ) Expecting( "on|off" ); SetSpaceInQuotedTokens( tok==T_on ); growth->space_in_quoted_tokens = (tok==T_on); NeedRIGHT(); break; case T_host_cad: NeedSYMBOL(); growth->host_cad = CurText(); NeedRIGHT(); break; case T_host_version: NeedSYMBOLorNUMBER(); growth->host_version = CurText(); NeedRIGHT(); break; case T_constant: NeedSYMBOLorNUMBER(); const1 = CurText(); NeedSYMBOLorNUMBER(); const2 = CurText(); NeedRIGHT(); growth->constants.push_back( const1 ); growth->constants.push_back( const2 ); break; case T_write_resolution: // [(writee_resolution { })] while( (tok = NextTok()) != T_RIGHT ) { if( tok!=T_SYMBOL ) Expecting( T_SYMBOL ); tok = NextTok(); if( tok!=T_NUMBER ) Expecting( T_NUMBER ); // @todo } break; 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( "testpoint|guides|image_conductor" ); } } break; case T_wires_include: // [(wires_include testpoint)] tok = NextTok(); if( tok != T_testpoint ) Expecting( T_testpoint ); growth->routes_include_testpoint = true; NeedRIGHT(); break; case T_case_sensitive: tok = NextTok(); if( tok!=T_on && tok!=T_off ) Expecting( "on|off" ); growth->case_sensitive = (tok==T_on); NeedRIGHT(); break; case T_via_rotate_first: // [(via_rotate_first [on | off])] tok = NextTok(); if( tok!=T_on && tok!=T_off ) Expecting( "on|off" ); growth->via_rotate_first = (tok==T_on); NeedRIGHT(); break; case T_generated_by_freeroute: growth->generated_by_freeroute = true; NeedRIGHT(); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doRESOLUTION( UNIT_RES* growth ) { 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( "inch|mil|cm|mm|um" ); } tok = NextTok(); if( tok != T_NUMBER ) Expecting( T_NUMBER ); growth->value = atoi( CurText() ); NeedRIGHT(); } void SPECCTRA_DB::doUNIT( UNIT_RES* growth ) { 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( "inch|mil|cm|mm|um" ); } NeedRIGHT(); } void SPECCTRA_DB::doSPECCTRA_LAYER_PAIR( SPECCTRA_LAYER_PAIR* growth ) { NeedSYMBOL(); growth->layer_id0 = CurText(); NeedSYMBOL(); growth->layer_id1 = CurText(); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->layer_weight = strtod( CurText(), 0 ); NeedRIGHT(); } void SPECCTRA_DB::doLAYER_NOISE_WEIGHT( LAYER_NOISE_WEIGHT* growth ) { T tok; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); if( NextTok() != T_layer_pair ) Expecting( T_layer_pair ); SPECCTRA_LAYER_PAIR* layer_pair = new SPECCTRA_LAYER_PAIR( growth ); growth->layer_pairs.push_back( layer_pair ); doSPECCTRA_LAYER_PAIR( layer_pair ); } } void SPECCTRA_DB::doSTRUCTURE( STRUCTURE* growth ) { 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( tok ); growth->unit = new UNIT_RES( growth, tok ); doUNIT( growth->unit ); break; case T_resolution: if( growth->unit ) Unexpected( tok ); growth->unit = new UNIT_RES( growth, tok ); doRESOLUTION( growth->unit ); break; case T_layer_noise_weight: if( growth->layer_noise_weight ) Unexpected( tok ); growth->layer_noise_weight = new LAYER_NOISE_WEIGHT( growth ); doLAYER_NOISE_WEIGHT( growth->layer_noise_weight ); break; case T_place_boundary: L_place: if( growth->place_boundary ) Unexpected( tok ); 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( tok ); goto L_place; } growth->boundary = new BOUNDARY( growth ); doBOUNDARY( growth->boundary ); break; case T_plane: COPPER_PLANE* plane; plane = new COPPER_PLANE( growth ); growth->planes.push_back( plane ); doKEEPOUT( plane ); break; case T_region: REGION* region; region = new REGION( growth ); growth->regions.push_back( region ); doREGION( region ); break; case T_snap_angle: STRINGPROP* stringprop; stringprop = new STRINGPROP( growth, T_snap_angle ); growth->Append( stringprop ); doSTRINGPROP( stringprop ); break; case T_via: if( growth->via ) Unexpected( tok ); growth->via = new VIA( growth ); doVIA( growth->via ); break; case T_control: if( growth->control ) Unexpected( tok ); 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: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; case T_place_rule: if( growth->place_rules ) Unexpected( tok ); growth->place_rules = new RULE( growth, T_place_rule ); doRULE( growth->place_rules ); break; case T_keepout: case T_place_keepout: case T_via_keepout: case T_wire_keepout: case T_bend_keepout: case T_elongate_keepout: KEEPOUT* keepout; keepout = new KEEPOUT( growth, tok ); growth->keepouts.push_back( keepout ); doKEEPOUT( keepout ); break; case T_grid: GRID* grid; grid = new GRID( growth ); growth->grids.push_back( grid ); doGRID( grid ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doSTRUCTURE_OUT( STRUCTURE_OUT* growth ) { /* ::= (structure_out { } [ ] ) */ T tok = NextTok(); while( tok != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_layer: LAYER* layer; layer = new LAYER( growth ); growth->layers.push_back( layer ); doLAYER( layer ); break; case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; default: Unexpected( CurText() ); } tok = NextTok(); } } void SPECCTRA_DB::doKEEPOUT( KEEPOUT* growth ) { T tok = NextTok(); if( IsSymbol(tok) ) { growth->name = CurText(); tok = NextTok(); } if( tok!=T_LEFT ) Expecting( T_LEFT ); while( tok != T_RIGHT ) { if( tok!=T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_sequence_number: if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->sequence_number = atoi( CurText() ); NeedRIGHT(); break; case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; case T_place_rule: if( growth->place_rules ) Unexpected( tok ); growth->place_rules = new RULE( growth, T_place_rule ); doRULE( growth->place_rules ); break; case T_rect: if( growth->shape ) Unexpected( tok ); growth->shape = new RECTANGLE( growth ); doRECTANGLE( (RECTANGLE*) growth->shape ); break; case T_circle: if( growth->shape ) Unexpected( tok ); growth->shape = new CIRCLE( growth ); doCIRCLE( (CIRCLE*) growth->shape ); break; case T_polyline_path: tok = T_path; KI_FALLTHROUGH; case T_path: case T_polygon: if( growth->shape ) Unexpected( tok ); growth->shape = new PATH( growth, tok ); doPATH( (PATH*) growth->shape ); break; case T_qarc: if( growth->shape ) Unexpected( tok ); growth->shape = new QARC( growth ); doQARC( (QARC*) growth->shape ); break; case T_window: WINDOW* window; window = new WINDOW( growth ); growth->windows.push_back( window ); doWINDOW( window ); break; default: Unexpected( CurText() ); } tok = NextTok(); } } void SPECCTRA_DB::doCONNECT( CONNECT* growth ) { /* from page 143 of specctra spec: (connect {(terminal [ ])} ) */ T tok = NextTok(); while( tok != T_RIGHT ) { if( tok!=T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_terminal: // since we do not use the terminal information, simply toss it. while( ( tok = NextTok() ) != T_RIGHT && tok != T_EOF ) ; break; default: Unexpected( CurText() ); } tok = NextTok(); } } void SPECCTRA_DB::doWINDOW( WINDOW* growth ) { T tok = NextTok(); while( tok != T_RIGHT ) { if( tok!=T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_rect: if( growth->shape ) Unexpected( tok ); growth->shape = new RECTANGLE( growth ); doRECTANGLE( (RECTANGLE*) growth->shape ); break; case T_circle: if( growth->shape ) Unexpected( tok ); growth->shape = new CIRCLE( growth ); doCIRCLE( (CIRCLE*) growth->shape ); break; case T_polyline_path: tok = T_path; KI_FALLTHROUGH; case T_path: case T_polygon: if( growth->shape ) Unexpected( tok ); growth->shape = new PATH( growth, tok ); doPATH( (PATH*) growth->shape ); break; case T_qarc: if( growth->shape ) Unexpected( tok ); growth->shape = new QARC( growth ); doQARC( (QARC*) growth->shape ); break; default: Unexpected( CurText() ); } tok = NextTok(); } } void SPECCTRA_DB::doBOUNDARY( BOUNDARY* growth ) { T tok = NextTok(); if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); if( tok == T_rect ) { if( growth->paths.size() ) Unexpected( "rect when path already encountered" ); growth->rectangle = new RECTANGLE( growth ); doRECTANGLE( growth->rectangle ); NeedRIGHT(); } else if( tok == T_path ) { if( growth->rectangle ) Unexpected( "path when rect already encountered" ); for(;;) { if( tok != T_path ) Expecting( T_path ); PATH* path = new PATH( growth, T_path ); 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( "rect|path" ); } } void SPECCTRA_DB::doPATH( PATH* growth ) { T tok = NextTok(); if( !IsSymbol( tok ) && tok != T_NUMBER ) // a layer name can be like a number like +12 Expecting( "layer_id" ); growth->layer_id = CurText(); if( NextTok() != T_NUMBER ) Expecting( "aperture_width" ); growth->aperture_width = strtod( CurText(), nullptr ); POINT ptTemp; tok = NextTok(); do { if( tok != T_NUMBER ) Expecting( T_NUMBER ); ptTemp.x = strtod( CurText(), nullptr ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); ptTemp.y = strtod( CurText(), nullptr ); 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( "round|square" ); growth->aperture_type = tok; NeedRIGHT(); } } void SPECCTRA_DB::doRECTANGLE( RECTANGLE* growth ) { NeedSYMBOL(); growth->layer_id = CurText(); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->point0.x = strtod( CurText(), nullptr ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->point0.y = strtod( CurText(), nullptr ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->point1.x = strtod( CurText(), nullptr ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->point1.y = strtod( CurText(), nullptr ); NeedRIGHT(); } void SPECCTRA_DB::doCIRCLE( CIRCLE* growth ) { T tok; NeedSYMBOLorNUMBER(); growth->layer_id = CurText(); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->diameter = strtod( CurText(), 0 ); tok = NextTok(); if( tok == T_NUMBER ) { growth->vertex.x = strtod( CurText(), 0 ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->vertex.y = strtod( CurText(), 0 ); tok = NextTok(); } if( tok != T_RIGHT ) Expecting( T_RIGHT ); } void SPECCTRA_DB::doQARC( QARC* growth ) { NeedSYMBOL(); growth->layer_id = CurText(); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->aperture_width = strtod( CurText(), 0 ); for( int i = 0; i < 3; ++i ) { if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->vertex[i].x = strtod( CurText(), 0 ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->vertex[i].y = strtod( CurText(), 0 ); } NeedRIGHT(); } void SPECCTRA_DB::doSTRINGPROP( STRINGPROP* growth ) { NeedSYMBOL(); growth->value = CurText(); NeedRIGHT(); } void SPECCTRA_DB::doTOKPROP( TOKPROP* growth ) { T tok = NextTok(); if( tok<0 ) Unexpected( CurText() ); growth->value = tok; NeedRIGHT(); } void SPECCTRA_DB::doVIA( VIA* growth ) { 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( CurText() ); } } else if( IsSymbol( tok ) ) { growth->padstacks.push_back( CurText() ); } else { Unexpected( CurText() ); } } } void SPECCTRA_DB::doCONTROL( CONTROL* growth ) { 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( "on|off" ); growth->via_at_smd = (tok==T_on); NeedRIGHT(); 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( CurText() ); } } } void SPECCTRA_DB::doPROPERTIES( PROPERTIES* growth ) { T tok; PROPERTY property; // construct it once here, append multiple times. while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); NeedSYMBOLorNUMBER(); property.name = CurText(); NeedSYMBOLorNUMBER(); property.value = CurText(); growth->push_back( property ); NeedRIGHT(); } } void SPECCTRA_DB::doLAYER( LAYER* growth ) { T tok = NextTok(); if( !IsSymbol( tok ) ) Expecting( T_SYMBOL ); growth->name = 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( "signal|power|mixed|jumper" ); growth->layer_type = tok; if( NextTok()!=T_RIGHT ) Expecting(T_RIGHT); break; case T_rule: growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; case T_property: doPROPERTIES( &growth->properties ); 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: // the spec has an example show an abbreviation of the "horizontal" keyword. Ouch. if( !strcmp( "hori", CurText() ) ) { growth->direction = T_horizontal; break; } else if( !strcmp( "vert", CurText() ) ) { growth->direction = T_vertical; break; } Expecting( "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 // T (positive) and T_NUMBER (negative) growth->cost = -atoi( CurText() ); break; default: Expecting( "forbidden|high|medium|low|free||-1" ); } tok = NextTok(); if( tok == T_LEFT ) { if( NextTok() != T_type ) Unexpected( CurText() ); tok = NextTok(); if( tok!=T_length && tok!=T_way ) Expecting( "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( CurText() ); } break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doRULE( RULE* growth ) { std::string builder; int bracketNesting = 1; // we already saw the opening T_LEFT T tok = T_NONE; while( bracketNesting != 0 && tok != T_EOF ) { tok = NextTok(); if( tok==T_LEFT) ++bracketNesting; else if( tok==T_RIGHT ) --bracketNesting; if( bracketNesting >= 1 ) { if( PrevTok() != T_LEFT && tok != T_RIGHT && ( tok != T_LEFT || bracketNesting > 2 ) ) builder += ' '; if( tok == T_STRING ) builder += m_quote_char; builder += CurText(); if( tok == T_STRING ) builder += m_quote_char; } // When the nested rule is closed with a T_RIGHT and we are back down // to bracketNesting == 1, (inside the but outside // the last rule). Then save the last rule and clear the string builder. if( bracketNesting == 1 ) { growth->rules.push_back( builder ); builder.clear(); } } if( tok==T_EOF ) Unexpected( T_EOF ); } #if 0 void SPECCTRA_DB::doPLACE_RULE( PLACE_RULE* growth, bool expect_object_type ) { /* (place_rule [ ] {[ | | | ]} ) */ T tok = NextTok(); if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); if( tok == T_object_type ) { if( !expect_object_type ) Unexpected( tok ); /* [(object_type [pcb | image_set [large | small | discrete | capacitor | resistor] [(image_type [smd | pin])]] )] */ tok = NextTok(); switch( tok ) { case T_pcb: growth->object_type = tok; break; case T_image_set: tok = NextTok(); switch( tok ) { case T_large: case T_small: case T_discrete: case T_capacitor: case T_resistor: growth->object_type = tok; break; default: Unexpected( CurText() ); } break; default: Unexpected( CurText() ); } tok = NextTok(); if( tok == T_LEFT ) { tok = NextTok(); if( tok != T_image_type ) Expecting( T_image_type ); tok = NextTok(); if( tok!=T_smd && tok!=T_pin ) Expecting( "smd|pin" ); NeedRIGHT(); tok = NextTok(); } if( tok != T_RIGHT ) Expecting( T_RIGHT ); tok = NextTok(); } /* {[ | | | ]} */ doRULE( growth ); } #endif void SPECCTRA_DB::doREGION( REGION* growth ) { T tok = NextTok(); if( IsSymbol( tok ) ) { growth->region_id = CurText(); tok = NextTok(); } for(;;) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_rect: if( growth->rectangle ) Unexpected( tok ); growth->rectangle = new RECTANGLE( growth ); doRECTANGLE( growth->rectangle ); break; case T_polygon: if( growth->polygon ) Unexpected( tok ); growth->polygon = new PATH( growth, T_polygon ); doPATH( growth->polygon ); break; case T_region_net: case T_region_class: STRINGPROP* stringprop; stringprop = new STRINGPROP( growth, tok ); growth->Append( stringprop ); doSTRINGPROP( stringprop ); break; case T_region_class_class: CLASS_CLASS* class_class; class_class = new CLASS_CLASS( growth, tok ); growth->Append( class_class ); doCLASS_CLASS( class_class ); break; case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; default: Unexpected( CurText() ); } tok = NextTok(); if( tok == T_RIGHT ) { if( !growth->rules ) Expecting( T_rule ); break; } } } void SPECCTRA_DB::doCLASS_CLASS( CLASS_CLASS* growth ) { T tok = NextTok(); if( tok != T_LEFT ) Expecting( T_LEFT ); while( ( tok = NextTok() ) != T_RIGHT ) { switch( tok ) { case T_classes: if( growth->classes ) Unexpected( tok ); growth->classes = new CLASSES( growth ); doCLASSES( growth->classes ); break; case T_rule: // only T_class_class takes a T_rule if( growth->Type() == T_region_class_class ) Unexpected( tok ); RULE* rule; rule = new RULE( growth, T_rule ); growth->Append( rule ); doRULE( rule ); break; case T_layer_rule: // only T_class_class takes a T_layer_rule if( growth->Type() == T_region_class_class ) Unexpected( tok ); LAYER_RULE* layer_rule; layer_rule = new LAYER_RULE( growth ); growth->Append( layer_rule ); doLAYER_RULE( layer_rule ); break; default: Unexpected( tok ); } } } void SPECCTRA_DB::doCLASSES( CLASSES* growth ) { T tok = NextTok(); // require at least 2 class_ids if( !IsSymbol( tok ) ) Expecting( "class_id" ); growth->class_ids.push_back( CurText() ); do { tok = NextTok(); if( !IsSymbol( tok ) ) Expecting( "class_id" ); growth->class_ids.push_back( CurText() ); } while( ( tok = NextTok() ) != T_RIGHT ); } void SPECCTRA_DB::doGRID( GRID* growth ) { T tok = NextTok(); switch( tok ) { case T_via: case T_wire: case T_via_keepout: case T_snap: case T_place: growth->grid_type = tok; if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->dimension = strtod( CurText(), 0 ); tok = NextTok(); if( tok == T_LEFT ) { while( ( tok = NextTok() ) != T_RIGHT ) { if( tok == T_direction ) { if( growth->grid_type == T_place ) Unexpected( tok ); tok = NextTok(); if( tok != T_x && tok != T_y ) Unexpected( CurText() ); growth->direction = tok; if( NextTok() != T_RIGHT ) Expecting(T_RIGHT); } else if( tok == T_offset ) { if( growth->grid_type == T_place ) Unexpected( tok ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->offset = strtod( CurText(), 0 ); if( NextTok() != T_RIGHT ) Expecting( T_RIGHT ); } else if( tok == T_image_type ) { if( growth->grid_type != T_place ) Unexpected( tok ); tok = NextTok(); if( tok != T_smd && tok != T_pin ) Unexpected( CurText() ); growth->image_type = tok; if( NextTok() != T_RIGHT ) Expecting( T_RIGHT ); } } } break; default: Unexpected( tok ); } } void SPECCTRA_DB::doLAYER_RULE( LAYER_RULE* growth ) { T tok; NeedSYMBOL(); do { growth->layer_ids.push_back( CurText() ); } while( IsSymbol( tok = NextTok() ) ); if( tok != T_LEFT ) Expecting( T_LEFT ); if( NextTok() != T_rule ) Expecting( T_rule ); growth->rule = new RULE( growth, T_rule ); doRULE( growth->rule ); NeedRIGHT(); } void SPECCTRA_DB::doPLACE( PLACE* growth ) { T tok = NextTok(); if( !IsSymbol( tok ) ) Expecting( "component_id" ); growth->component_id = CurText(); tok = NextTok(); if( tok == T_NUMBER ) { POINT point; point.x = strtod( CurText(), 0 ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); point.y = strtod( CurText(), 0 ); growth->SetVertex( point ); tok = NextTok(); if( tok != T_front && tok != T_back ) Expecting( "front|back" ); growth->side = tok; if( NextTok() != T_NUMBER ) Expecting( "rotation" ); growth->SetRotation( strtod( CurText(), 0 ) ); } while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_mirror: tok = NextTok(); if( tok == T_x || tok == T_y || tok == T_xy || tok == T_off ) growth->mirror = tok; else Expecting( "x|y|xy|off" ); break; case T_status: tok = NextTok(); if( tok==T_added || tok==T_deleted || tok==T_substituted ) growth->status = tok; else Expecting("added|deleted|substituted"); break; case T_logical_part: if( growth->logical_part.size() ) Unexpected( tok ); tok = NextTok(); if( !IsSymbol( tok ) ) Expecting( "logical_part_id"); growth->logical_part = CurText(); break; case T_place_rule: if( growth->place_rules ) Unexpected( tok ); growth->place_rules = new RULE( growth, T_place_rule ); doRULE( growth->place_rules ); break; case T_property: if( growth->properties.size() ) Unexpected( tok ); doPROPERTIES( &growth->properties ); break; case T_lock_type: tok = NextTok(); if( tok == T_position || tok == T_gate || tok == T_subgate || tok == T_pin ) growth->lock_type = tok; else Expecting( "position|gate|subgate|pin" ); break; case T_rule: if( growth->rules || growth->region ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; case T_region: if( growth->rules || growth->region ) Unexpected( tok ); growth->region = new REGION( growth ); doREGION( growth->region ); break; case T_pn: if( growth->part_number.size() ) Unexpected( tok ); NeedSYMBOLorNUMBER(); growth->part_number = CurText(); NeedRIGHT(); break; default: Unexpected( tok ); } } } void SPECCTRA_DB::doCOMPONENT( COMPONENT* growth ) { T tok = NextTok(); if( !IsSymbol( tok ) && tok != T_NUMBER ) Expecting( "image_id" ); growth->image_id = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_place: PLACE* place; place = new PLACE( growth ); growth->places.push_back( place ); doPLACE( place ); break; default: Unexpected( tok ); } } } void SPECCTRA_DB::doPLACEMENT( PLACEMENT* growth ) { T tok; while( ( tok = NextTok() ) != T_RIGHT ) { if( tok == T_EOF ) Unexpected( T_EOF ); if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_unit: case T_resolution: growth->unit = new UNIT_RES( growth, tok ); if( tok == T_resolution ) doRESOLUTION( growth->unit ); else doUNIT( growth->unit ); break; case T_place_control: NeedRIGHT(); tok = NextTok(); if( tok != T_flip_style ) Expecting( T_flip_style ); tok = NextTok(); if( tok == T_mirror_first || tok == T_rotate_first ) growth->flip_style = tok; else Expecting( "mirror_first|rotate_first" ); NeedRIGHT(); NeedRIGHT(); break; case T_component: COMPONENT* component; component = new COMPONENT( growth ); growth->components.push_back( component ); doCOMPONENT( component ); break; default: Unexpected( tok ); } } } void SPECCTRA_DB::doPADSTACK( PADSTACK* growth ) { T tok = NextTok(); /* (padstack [ ] {(shape [ ] [(connect [on | off])] [{ }] )} [ ] [{ }] [(rotate [on | off])] [(absolute [on | off])] [(rule )]) */ // padstack_id may be a number if( !IsSymbol( tok ) && tok != T_NUMBER ) Expecting( "padstack_id" ); growth->padstack_id = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_unit: if( growth->unit ) Unexpected( tok ); growth->unit = new UNIT_RES( growth, tok ); doUNIT( growth->unit ); break; case T_rotate: tok = NextTok(); if( tok != T_on && tok != T_off ) Expecting( "on|off" ); growth->rotate = tok; NeedRIGHT(); break; case T_absolute: tok = NextTok(); if( tok != T_on && tok != T_off ) Expecting( "on|off" ); growth->absolute = tok; NeedRIGHT(); break; case T_shape: SHAPE* shape; shape = new SHAPE( growth ); growth->Append( shape ); doSHAPE( shape ); break; case T_attach: tok = NextTok(); if( tok != T_off && tok != T_on ) Expecting( "off|on" ); growth->attach = tok; tok = NextTok(); if( tok == T_LEFT ) { if( NextTok() != T_use_via ) Expecting( T_use_via ); NeedSYMBOL(); growth->via_id = CurText(); NeedRIGHT(); NeedRIGHT(); } break; /* case T_via_site: not supported break; */ case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doSHAPE( SHAPE* growth ) { T tok; /* (shape [ ] [(connect [on | off])] [{ }]) */ while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_polyline_path: tok = T_path; KI_FALLTHROUGH; case T_rect: case T_circle: case T_path: case T_polygon: case T_qarc: L_done_that: if( growth->shape ) Unexpected( tok ); break; default: // the example in the spec uses "circ" instead of "circle". Bad! if( !strcmp( "circ", CurText() ) ) { tok = T_circle; goto L_done_that; } } switch( tok ) { case T_rect: growth->shape = new RECTANGLE( growth ); doRECTANGLE( (RECTANGLE*) growth->shape ); break; case T_circle: growth->shape = new CIRCLE( growth ); doCIRCLE( (CIRCLE*)growth->shape ); break; case T_path: case T_polygon: growth->shape = new PATH( growth, tok ); doPATH( (PATH*)growth->shape ); break; case T_qarc: growth->shape = new QARC( growth ); doQARC( (QARC*)growth->shape ); break; case T_connect: tok = NextTok(); if( tok!=T_on && tok!=T_off ) Expecting( "on|off" ); growth->connect = tok; NeedRIGHT(); break; case T_window: WINDOW* window; window = new WINDOW( growth ); growth->windows.push_back( window ); doWINDOW( window ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doIMAGE( IMAGE* growth ) { T tok = NextTok(); /* ::= (image [(side [front | back | both])] [ ] [ ] {(pin [(rotate )] [ | ] [ ])} [{ }] [{ }] [ ] [ ] [{ }] [ ] ) */ if( !IsSymbol( tok ) && tok != T_NUMBER ) Expecting( "image_id" ); growth->image_id = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_unit: if( growth->unit ) Unexpected( tok ); growth->unit = new UNIT_RES( growth, tok ); doUNIT( growth->unit ); break; case T_side: tok = NextTok(); if( tok != T_front && tok != T_back && tok != T_both ) Expecting( "front|back|both" ); growth->side = tok; NeedRIGHT(); break; case T_outline: SHAPE* outline; outline = new SHAPE( growth, T_outline ); // use SHAPE for T_outline growth->Append( outline ); doSHAPE( outline ); break; case T_pin: PIN* pin; pin = new PIN( growth ); growth->pins.push_back( pin ); doPIN( pin ); break; case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, tok ); doRULE( growth->rules ); break; case T_place_rule: if( growth->place_rules ) Unexpected( tok ); growth->place_rules = new RULE( growth, tok ); doRULE( growth->place_rules ); break; case T_keepout: case T_place_keepout: case T_via_keepout: case T_wire_keepout: case T_bend_keepout: case T_elongate_keepout: KEEPOUT* keepout; keepout = new KEEPOUT( growth, tok ); growth->keepouts.push_back( keepout ); doKEEPOUT( keepout ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doPIN( PIN* growth ) { T tok = NextTok(); /* (pin [(rotate )] [ | ] [ ]) */ // a padstack_id may be a number if( !IsSymbol( tok ) && tok!=T_NUMBER ) Expecting( "padstack_id" ); growth->padstack_id = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok == T_LEFT ) { tok = NextTok(); if( tok != T_rotate ) Expecting( T_rotate ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->SetRotation( strtod( CurText(), 0 ) ); NeedRIGHT(); } else { if( !IsSymbol( tok ) && tok != T_NUMBER ) Expecting( "pin_id" ); growth->pin_id = CurText(); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->vertex.x = strtod( CurText(), 0 ); if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->vertex.y = strtod( CurText(), 0 ); } } } void SPECCTRA_DB::doLIBRARY( LIBRARY* growth ) { T tok; /* ::= (library [ ] { } [{ }] { } { } [ ] [ ] [{ }] [{ }] ) */ while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_unit: if( growth->unit ) Unexpected( tok ); growth->unit = new UNIT_RES( growth, tok ); doUNIT( growth->unit ); break; case T_padstack: PADSTACK* padstack; padstack = new PADSTACK(); growth->AddPadstack( padstack ); doPADSTACK( padstack ); break; case T_image: IMAGE* image; image = new IMAGE( growth ); growth->images.push_back( image ); doIMAGE( image ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doNET( NET* growth ) { T tok = NextTok(); PIN_REFS* pin_refs; /* ::= (net [(unassigned)] [(net_number )] [(pins { }) | (order { })] [ ] [(type [fix | normal])] [ ] [ ] [ ] [{ }] [ ] [(expose { })] [(noexpose { })] [(source { })] [(load { })] [(terminator { })] [(supply [power | ground])] ) */ if( !IsSymbol( tok ) ) Expecting( "net_id" ); growth->net_id = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_unassigned: growth->unassigned = true; NeedRIGHT(); break; case T_net_number: if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->net_number = atoi( CurText() ); NeedRIGHT(); break; case T_pins: case T_order: growth->pins_type = tok; pin_refs = &growth->pins; goto L_pins; case T_expose: pin_refs = &growth->expose; goto L_pins; case T_noexpose: pin_refs = &growth->noexpose; goto L_pins; case T_source: pin_refs = &growth->source; goto L_pins; case T_load: pin_refs = &growth->load; goto L_pins; case T_terminator: pin_refs = &growth->terminator; //goto L_pins; L_pins: { PIN_REF empty( growth ); while( ( tok = NextTok() ) != T_RIGHT ) { // copy the empty one, then fill its copy later thru pin_ref. pin_refs->push_back( empty ); PIN_REF* pin_ref = &pin_refs->back(); readCOMPnPIN( &pin_ref->component_id, &pin_ref->pin_id ); } } break; case T_comp_order: if( growth->comp_order ) Unexpected( tok ); growth->comp_order = new COMP_ORDER( growth ); doCOMP_ORDER( growth->comp_order ); break; case T_type: tok = NextTok(); if( tok!=T_fix && tok!=T_normal ) Expecting( "fix|normal" ); growth->type = tok; NeedRIGHT(); break; /* @todo case T_circuit: break; */ case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; case T_layer_rule: LAYER_RULE* layer_rule; layer_rule = new LAYER_RULE( growth ); growth->layer_rules.push_back( layer_rule ); doLAYER_RULE( layer_rule ); break; case T_fromto: FROMTO* fromto; fromto = new FROMTO( growth ); growth->fromtos.push_back( fromto ); doFROMTO( fromto ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doTOPOLOGY( TOPOLOGY* growth ) { T tok; /* ::= (topology {[ | ]}) */ while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_fromto: FROMTO* fromto; fromto = new FROMTO( growth ); growth->fromtos.push_back( fromto ); doFROMTO( fromto ); break; case T_comp_order: COMP_ORDER* comp_order; comp_order = new COMP_ORDER( growth ); growth->comp_orders.push_back( comp_order ); doCOMP_ORDER( comp_order ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doCLASS( CLASS* growth ) { T tok; /* ::= (class {[{} | { }]} [ ] [ ] [{ }] [ ] ) */ NeedSYMBOL(); growth->class_id = CurText(); // do net_ids, do not support s at this time while( IsSymbol( tok = NextTok() ) ) { growth->net_ids.push_back( CurText() ); } while( tok != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; case T_layer_rule: LAYER_RULE* layer_rule; layer_rule = new LAYER_RULE( growth ); growth->layer_rules.push_back( layer_rule ); doLAYER_RULE( layer_rule ); break; case T_topology: if( growth->topology ) Unexpected( tok ); growth->topology = new TOPOLOGY( growth ); doTOPOLOGY( growth->topology ); break; case T_circuit: // handle all the circuit_descriptor here as strings { std::string builder; int bracketNesting = 1; // we already saw the opening T_LEFT tok = T_NONE; while( bracketNesting != 0 && tok != T_EOF ) { tok = NextTok(); if( tok == T_LEFT ) ++bracketNesting; else if( tok == T_RIGHT ) --bracketNesting; if( bracketNesting >= 1 ) { T previousTok = (T) PrevTok(); if( previousTok != T_LEFT && previousTok != T_circuit && tok != T_RIGHT ) builder += ' '; if( tok == T_STRING ) builder += m_quote_char; builder += CurText(); if( tok == T_STRING ) builder += m_quote_char; } // When the nested rule is closed with a T_RIGHT and we are back down // to bracketNesting == 0, then save the builder and break; if( bracketNesting == 0 ) { growth->circuit.push_back( builder ); break; } } if( tok == T_EOF ) Unexpected( T_EOF ); break; } // scope bracket default: Unexpected( CurText() ); } // switch tok = NextTok(); } // while } void SPECCTRA_DB::doNETWORK( NETWORK* growth ) { T tok; /* ::= (network {} [{ }] [{ }] [{ }] [{ }] [{ }] [{ }] ) */ while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_net: NET* net; net = new NET( growth ); growth->nets.push_back( net ); doNET( net ); break; case T_class: CLASS* myclass; myclass = new CLASS( growth ); growth->classes.push_back( myclass ); doCLASS( myclass ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doCOMP_ORDER( COMP_ORDER* growth ) { T tok; /* ::= (comp_order { }) */ while( IsSymbol( tok = NextTok() ) ) { growth->placement_ids.push_back( CurText() ); } if( tok != T_RIGHT ) Expecting( T_RIGHT ); } void SPECCTRA_DB::doFROMTO( FROMTO* growth ) { T tok; /* ::= {(fromto [ | ] | ] [ | | ] [(type [fix | normal | soft])] [(net )] [ ] [ ] [{ }] )} */ // read the first two grammar items in as 2 single tokens, i.e. do not // split apart the s into 3 separate tokens. Do this by // turning off the string delimiter in the lexer. char old = SetStringDelimiter( 0 ); if( !IsSymbol(NextTok() ) ) { SetStringDelimiter( old ); Expecting( T_SYMBOL ); } growth->fromText = CurText(); if( !IsSymbol(NextTok() ) ) { SetStringDelimiter( old ); Expecting( T_SYMBOL ); } growth->toText = CurText(); SetStringDelimiter( old ); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_type: tok = NextTok(); if( tok != T_fix && tok != T_normal && tok != T_soft ) Expecting( "fix|normal|soft" ); growth->fromto_type = tok; NeedRIGHT(); break; case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, T_rule ); doRULE( growth->rules ); break; case T_layer_rule: LAYER_RULE* layer_rule; layer_rule = new LAYER_RULE( growth ); growth->layer_rules.push_back( layer_rule ); doLAYER_RULE( layer_rule ); break; case T_net: if( growth->net_id.size() ) Unexpected( tok ); NeedSYMBOL(); growth->net_id = CurText(); NeedRIGHT(); break; // circuit descriptor not supported at this time default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doWIRE( WIRE* growth ) { T tok; /* ::= (wire [(net )] [(turret )] [(type [fix | route | normal | protect])] [(attr [test | fanout | bus | jumper])] [(shield )] [{ }] [(connect (terminal [ ]) (terminal [ ]) )] [(supply)] ) */ while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_rect: if( growth->shape ) Unexpected( tok ); growth->shape = new RECTANGLE( growth ); doRECTANGLE( (RECTANGLE*) growth->shape ); break; case T_circle: if( growth->shape ) Unexpected( tok ); growth->shape = new CIRCLE( growth ); doCIRCLE( (CIRCLE*) growth->shape ); break; case T_polyline_path: tok = T_path; KI_FALLTHROUGH; case T_path: case T_polygon: if( growth->shape ) Unexpected( tok ); growth->shape = new PATH( growth, tok ); doPATH( (PATH*) growth->shape ); break; case T_qarc: if( growth->shape ) Unexpected( tok ); growth->shape = new QARC( growth ); doQARC( (QARC*) growth->shape ); break; case T_net: NeedSYMBOLorNUMBER(); growth->net_id = CurText(); NeedRIGHT(); break; case T_turret: if( NextTok() != T_NUMBER ) Expecting( T_NUMBER ); growth->turret = atoi( CurText() ); NeedRIGHT(); break; case T_type: tok = NextTok(); if( tok != T_fix && tok != T_route && tok != T_normal && tok != T_protect ) Expecting( "fix|route|normal|protect" ); growth->wire_type = tok; NeedRIGHT(); break; case T_attr: tok = NextTok(); if( tok != T_test && tok != T_fanout && tok != T_bus && tok != T_jumper ) Expecting( "test|fanout|bus|jumper" ); growth->attr = tok; NeedRIGHT(); break; case T_shield: NeedSYMBOL(); growth->shield = CurText(); NeedRIGHT(); break; case T_window: WINDOW* window; window = new WINDOW( growth ); growth->windows.push_back( window ); doWINDOW( window ); break; case T_connect: if( growth->connect ) Unexpected( tok ); growth->connect = new CONNECT( growth ); doCONNECT( growth->connect ); break; case T_supply: growth->supply = true; NeedRIGHT(); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doWIRE_VIA( WIRE_VIA* growth ) { T tok; POINT point; /* ::= (via { } [(net )] [(via_number )] [(type [fix | route | normal | protect])] [(attr [test | fanout | jumper | virtual_pin ])] [(contact {})] [(supply)] ) (virtual_pin (net ) ) */ NeedSYMBOL(); growth->padstack_id = CurText(); while( ( tok = NextTok() ) == T_NUMBER ) { point.x = strtod( CurText(), 0 ); if( NextTok() != T_NUMBER ) Expecting( "vertex.y" ); point.y = strtod( CurText(), 0 ); growth->vertexes.push_back( point ); } while( tok != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_net: NeedSYMBOL(); growth->net_id = CurText(); NeedRIGHT(); break; case T_via_number: if( NextTok() != T_NUMBER ) Expecting( "" ); growth->via_number = atoi( CurText() ); NeedRIGHT(); break; case T_type: tok = NextTok(); if( tok != T_fix && tok != T_route && tok != T_normal && tok != T_protect ) Expecting( "fix|route|normal|protect" ); growth->via_type = tok; NeedRIGHT(); break; case T_attr: tok = NextTok(); if( tok != T_test && tok != T_fanout && tok != T_jumper && tok != T_virtual_pin ) Expecting( "test|fanout|jumper|virtual_pin" ); growth->attr = tok; if( tok == T_virtual_pin ) { NeedSYMBOL(); growth->virtual_pin_name = CurText(); } NeedRIGHT(); break; case T_contact: NeedSYMBOL(); tok = T_SYMBOL; while( IsSymbol( tok ) ) { growth->contact_layers.push_back( CurText() ); tok = NextTok(); } if( tok != T_RIGHT ) Expecting( T_RIGHT ); break; case T_supply: growth->supply = true; NeedRIGHT(); break; default: Unexpected( CurText() ); } tok = NextTok(); } } void SPECCTRA_DB::doWIRING( WIRING* growth ) { T tok; /* ::= (wiring [ | | null] { } [ ] {[ ]} ) */ while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_unit: if( growth->unit ) Unexpected( tok ); growth->unit = new UNIT_RES( growth, tok ); doUNIT( growth->unit ); break; case T_resolution: if( growth->unit ) Unexpected( tok ); growth->unit = new UNIT_RES( growth, tok ); doRESOLUTION( growth->unit ); break; case T_wire: WIRE* wire; wire = new WIRE( growth ); growth->wires.push_back( wire ); doWIRE( wire ); break; case T_via: WIRE_VIA* wire_via; wire_via = new WIRE_VIA( growth ); growth->wire_vias.push_back( wire_via ); doWIRE_VIA( wire_via ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doANCESTOR( ANCESTOR* growth ) { T tok; /* ::= (ancestor (created_time ) [(comment )]) */ NeedSYMBOL(); growth->filename = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_created_time: readTIME( &growth->time_stamp ); NeedRIGHT(); break; case T_comment: NeedSYMBOL(); growth->comment = CurText(); NeedRIGHT(); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doHISTORY( HISTORY* growth ) { T tok; /* ::= (history [{ }] ) */ while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_ancestor: ANCESTOR* ancestor; ancestor = new ANCESTOR( growth ); growth->ancestors.push_back( ancestor ); doANCESTOR( ancestor ); break; case T_self: while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_created_time: readTIME( &growth->time_stamp ); NeedRIGHT(); break; case T_comment: NeedSYMBOL(); growth->comments.push_back( CurText() ); NeedRIGHT(); break; default: Unexpected( CurText() ); } } break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doSESSION( SESSION* growth ) { T tok; /* ::= (session (base_design ) [ ] [ ] [ ] [ ] [ ] [ ] ] [ ] ) */ NeedSYMBOL(); growth->session_id = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_base_design: NeedSYMBOL(); growth->base_design = CurText(); NeedRIGHT(); break; case T_history: if( growth->history ) Unexpected( tok ); growth->history = new HISTORY( growth ); doHISTORY( growth->history ); break; case T_structure: if( growth->structure ) Unexpected( tok ); growth->structure = new STRUCTURE( growth ); doSTRUCTURE( growth->structure ); break; case T_placement: if( growth->placement ) Unexpected( tok ); growth->placement = new PLACEMENT( growth ); doPLACEMENT( growth->placement ); break; case T_was_is: if( growth->was_is ) Unexpected( tok ); growth->was_is = new WAS_IS( growth ); doWAS_IS( growth->was_is ); break; case T_routes: if( growth->route ) Unexpected( tok ); growth->route = new ROUTE( growth ); doROUTE( growth->route ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doWAS_IS( WAS_IS* growth ) { T tok; PIN_PAIR empty( growth ); PIN_PAIR* pin_pair; /* ::= (was_is {(pins )}) */ // none of the pins is ok too while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_pins: // copy the empty one, then fill its copy later thru pin_pair. growth->pin_pairs.push_back( empty ); pin_pair= &growth->pin_pairs.back(); NeedSYMBOL(); // readCOMPnPIN() expects 1st token to have been read readCOMPnPIN( &pin_pair->was.component_id, &pin_pair->was.pin_id ); NeedSYMBOL(); // readCOMPnPIN() expects 1st token to have been read readCOMPnPIN( &pin_pair->is.component_id, &pin_pair->is.pin_id ); NeedRIGHT(); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doROUTE( ROUTE* growth ) { T tok; /* ::= (routes ) */ while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_resolution: if( growth->resolution ) Unexpected( tok ); growth->resolution = new UNIT_RES( growth, tok ); doRESOLUTION( growth->resolution ); break; case T_parser: if( growth->parser ) { #if 0 // Electra 2.9.1 emits two (parser ) elements in a row. // Work around their bug for now. Unexpected( tok ); #else delete growth->parser; #endif } growth->parser = new PARSER( growth ); doPARSER( growth->parser ); break; case T_structure_out: if( growth->structure_out ) Unexpected( tok ); growth->structure_out = new STRUCTURE_OUT( growth ); doSTRUCTURE_OUT( growth->structure_out ); break; case T_library_out: if( growth->library ) Unexpected( tok ); growth->library = new LIBRARY( growth, tok ); doLIBRARY( growth->library ); break; case T_network_out: while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); if( tok != T_net ) // it is class NET_OUT, but token T_net Unexpected( CurText() ); NET_OUT* net_out; net_out = new NET_OUT( growth ); growth->net_outs.push_back( net_out ); doNET_OUT( net_out ); } break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doNET_OUT( NET_OUT* growth ) { T tok; /* ::= (net [(net_number )] [ ] {[ | | | ]} {[ ]} ) */ NeedSYMBOLorNUMBER(); growth->net_id = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( tok != T_LEFT ) Expecting( T_LEFT ); tok = NextTok(); switch( tok ) { case T_net_number: tok = NextTok(); if( tok!= T_NUMBER ) Expecting( T_NUMBER ); growth->net_number = atoi( CurText() ); NeedRIGHT(); break; case T_rule: if( growth->rules ) Unexpected( tok ); growth->rules = new RULE( growth, tok ); doRULE( growth->rules ); break; case T_wire: WIRE* wire; wire = new WIRE( growth ); growth->wires.push_back( wire ); doWIRE( wire ); break; case T_via: WIRE_VIA* wire_via; wire_via = new WIRE_VIA( growth ); growth->wire_vias.push_back( wire_via ); doWIRE_VIA( wire_via ); break; case T_supply_pin: SUPPLY_PIN* supply_pin; supply_pin = new SUPPLY_PIN( growth ); growth->supply_pins.push_back( supply_pin ); doSUPPLY_PIN( supply_pin ); break; default: Unexpected( CurText() ); } } } void SPECCTRA_DB::doSUPPLY_PIN( SUPPLY_PIN* growth ) { T tok; PIN_REF empty(growth); /* ::= (supply_pin { } [(net )]) */ NeedSYMBOL(); growth->net_id = CurText(); while( ( tok = NextTok() ) != T_RIGHT ) { if( IsSymbol(tok) ) { growth->pin_refs.push_back( empty ); PIN_REF* pin_ref = &growth->pin_refs.back(); readCOMPnPIN( &pin_ref->component_id, &pin_ref->pin_id ); } else if( tok == T_LEFT ) { tok = NextTok(); if( tok != T_net ) Expecting( T_net ); growth->net_id = CurText(); NeedRIGHT(); } else Unexpected( CurText() ); } } void SPECCTRA_DB::ExportPCB( const wxString& aFilename, bool aNameChange ) { if( m_pcb ) { FILE_OUTPUTFORMATTER formatter( aFilename, wxT( "wt" ), m_quote_char[0] ); if( aNameChange ) m_pcb->pcbname = TO_UTF8( aFilename ); m_pcb->Format( &formatter, 0 ); } } void SPECCTRA_DB::ExportSESSION( const wxString& aFilename ) { if( m_session ) { FILE_OUTPUTFORMATTER formatter( aFilename, wxT( "wt" ), m_quote_char[0] ); m_session->Format( &formatter, 0 ); } } PCB* SPECCTRA_DB::MakePCB() { PCB* pcb = new PCB(); pcb->parser = new PARSER( pcb ); pcb->resolution = new UNIT_RES( pcb, T_resolution ); pcb->unit = new UNIT_RES( pcb, T_unit ); pcb->structure = new STRUCTURE( pcb ); pcb->structure->boundary = new BOUNDARY( pcb->structure ); pcb->structure->via = new VIA( pcb->structure ); pcb->structure->rules = new RULE( pcb->structure, T_rule ); pcb->placement = new PLACEMENT( pcb ); pcb->library = new LIBRARY( pcb ); pcb->network = new NETWORK( pcb ); pcb->wiring = new WIRING( pcb ); return pcb; } //-------------------------------------------------------------------- ELEM::ELEM( T aType, ELEM* aParent ) : type( aType ), parent( aParent ) { } ELEM::~ELEM() { } const char* ELEM::Name() const { return SPECCTRA_DB::TokenName( type ); } UNIT_RES* ELEM::GetUnits() const { if( parent ) return parent->GetUnits(); return &UNIT_RES::Default; } void ELEM::Format( OUTPUTFORMATTER* out, int nestLevel ) { out->Print( nestLevel, "(%s\n", Name() ); FormatContents( out, nestLevel+1 ); out->Print( nestLevel, ")\n" ); } void ELEM_HOLDER::FormatContents( OUTPUTFORMATTER* out, int nestLevel ) { for( int i = 0; i < Length(); ++i ) At(i)->Format( out, nestLevel ); } int ELEM_HOLDER::FindElem( 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; } // a reasonably small memory price to pay for improved performance STRING_FORMATTER ELEM::sf; UNIT_RES UNIT_RES::Default( nullptr, T_resolution ); int PADSTACK::Compare( PADSTACK* lhs, PADSTACK* rhs ) { if( !lhs->hash.size() ) lhs->hash = lhs->makeHash(); if( !rhs->hash.size() ) rhs->hash = rhs->makeHash(); int result = lhs->hash.compare( rhs->hash ); if( result ) return result; // Via names hold the drill diameters, so we have to include those to discern // between two vias with same copper size but with different drill sizes. result = lhs->padstack_id.compare( rhs->padstack_id ); return result; } int IMAGE::Compare( IMAGE* lhs, IMAGE* rhs ) { if( !lhs->hash.size() ) lhs->hash = lhs->makeHash(); if( !rhs->hash.size() ) rhs->hash = rhs->makeHash(); int result = lhs->hash.compare( rhs->hash ); return result; } /* int COMPONENT::Compare( COMPONENT* lhs, COMPONENT* rhs ) { if( !lhs->hash.size() ) lhs->hash = lhs->makeHash(); if( !rhs->hash.size() ) rhs->hash = rhs->makeHash(); int result = lhs->hash.compare( rhs->hash ); return result; } */ PARSER::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; generated_by_freeroute = false; host_cad = "KiCad's Pcbnew"; wxString msg = GetBuildVersion(); host_version = TO_UTF8(msg); } void PARSER::FormatContents( OUTPUTFORMATTER* out, int nestLevel ) { out->Print( nestLevel, "(string_quote %c)\n", string_quote ); out->Print( nestLevel, "(space_in_quoted_tokens %s)\n", space_in_quoted_tokens ? "on" : "off" ); out->Print( nestLevel, "(host_cad \"%s\")\n", host_cad.c_str() ); out->Print( nestLevel, "(host_version \"%s\")\n", host_version.c_str() ); for( STRINGS::iterator i = constants.begin(); i != constants.end(); ) { const std::string& s1 = *i++; const std::string& s2 = *i++; const char* q1 = out->GetQuoteChar( s1.c_str() ); const char* q2 = out->GetQuoteChar( s2.c_str() ); out->Print( nestLevel, "(constant %s%s%s %s%s%s)\n", q1, s1.c_str(), q1, q2, s2.c_str(), q2 ); } if( routes_include_testpoint || routes_include_guides || routes_include_image_conductor ) { out->Print( nestLevel, "(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, "(wires_include testpoint)\n" ); if( !via_rotate_first ) out->Print( nestLevel, "(via_rotate_first off)\n" ); if( case_sensitive ) out->Print( nestLevel, "(case_sensitive %s)\n", case_sensitive ? "on" : "off" ); } void PLACE::Format( OUTPUTFORMATTER* out, int nestLevel ) { bool useMultiLine; const char* quote = out->GetQuoteChar( component_id.c_str() ); if( place_rules || properties.size() || rules || region ) { useMultiLine = true; out->Print( nestLevel, "(%s %s%s%s\n", Name(), quote, component_id.c_str(), quote ); out->Print( nestLevel+1, "%s", "" ); } else { useMultiLine = false; out->Print( nestLevel, "(%s %s%s%s", Name(), quote, component_id.c_str(), quote ); } if( hasVertex ) { out->Print( 0, " %.6f %.6f", vertex.x, vertex.y ); out->Print( 0, " %s", GetTokenText( side ) ); out->Print( 0, " %.6f", rotation ); } const char* space = " "; // one space, as c string. if( mirror != T_NONE ) { out->Print( 0, "%s(mirror %s)", space, GetTokenText( mirror ) ); space = ""; } if( status != T_NONE ) { out->Print( 0, "%s(status %s)", space, GetTokenText( status ) ); space = ""; } if( logical_part.size() ) { quote = out->GetQuoteChar( logical_part.c_str() ); out->Print( 0, "%s(logical_part %s%s%s)", space, quote, logical_part.c_str(), quote ); space = ""; } if( useMultiLine ) { out->Print( 0, "\n" ); if( place_rules ) { place_rules->Format( out, nestLevel+1 ); } if( properties.size() ) { out->Print( nestLevel + 1, "(property \n" ); for( PROPERTIES::const_iterator i = properties.begin(); i != properties.end(); ++i ) i->Format( out, nestLevel + 2 ); out->Print( nestLevel + 1, ")\n" ); } if( lock_type != T_NONE ) out->Print( nestLevel + 1, "(lock_type %s)\n", GetTokenText( lock_type ) ); if( rules ) rules->Format( out, nestLevel+1 ); if( region ) region->Format( out, nestLevel+1 ); if( part_number.size() ) { quote = out->GetQuoteChar( part_number.c_str() ); out->Print( nestLevel + 1, "(PN %s%s%s)\n", quote, part_number.c_str(), quote ); } } else { if( lock_type != T_NONE ) { out->Print( 0, "%s(lock_type %s)", space, GetTokenText( lock_type ) ); space = ""; } if( part_number.size() ) { quote = out->GetQuoteChar( part_number.c_str() ); out->Print( 0, "%s(PN %s%s%s)", space, quote, part_number.c_str(), quote ); } } out->Print( 0, ")\n" ); } } // namespace DSN