KiCad PCB EDA Suite
sch_sexpr_parser.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 CERN
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Wayne Stambaugh <[email protected]>
8  *
9  * This program is free software: you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation, either version 3 of the License, or (at your
12  * option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
28 // For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
29 // base64 code.
30 #define wxUSE_BASE64 1
31 #include <wx/base64.h>
32 #include <wx/mstream.h>
33 #include <wx/tokenzr.h>
34 
35 #include <lib_id.h>
36 #include <lib_shape.h>
37 #include <lib_pin.h>
38 #include <lib_text.h>
39 #include <math/util.h> // KiROUND, Clamp
40 #include <string_utils.h>
41 #include <sch_bitmap.h>
42 #include <sch_bus_entry.h>
43 #include <sch_symbol.h>
44 #include <sch_edit_frame.h> // SYM_ORIENT_XXX
45 #include <sch_field.h>
46 #include <sch_line.h>
47 #include <sch_junction.h>
48 #include <sch_no_connect.h>
49 #include <sch_screen.h>
50 #include <sch_sheet_pin.h>
52 #include <template_fieldnames.h>
53 #include <trigo.h>
54 #include <progress_reporter.h>
55 
56 
57 using namespace TSCHEMATIC_T;
58 
59 
61  unsigned aLineCount ) :
62  SCHEMATIC_LEXER( aLineReader ),
63  m_requiredVersion( 0 ),
64  m_fieldId( 0 ),
65  m_unit( 1 ),
66  m_convert( 1 ),
67  m_progressReporter( aProgressReporter ),
68  m_lineReader( aLineReader ),
69  m_lastProgressLine( 0 ),
70  m_lineCount( aLineCount )
71 {
72 }
73 
74 
76 {
77  const unsigned PROGRESS_DELTA = 250;
78 
79  if( m_progressReporter )
80  {
81  unsigned curLine = m_lineReader->LineNumber();
82 
83  if( curLine > m_lastProgressLine + PROGRESS_DELTA )
84  {
85  m_progressReporter->SetCurrentProgress( ( (double) curLine )
86  / std::max( 1U, m_lineCount ) );
87 
89  THROW_IO_ERROR( ( "Open cancelled by user." ) );
90 
91  m_lastProgressLine = curLine;
92  }
93  }
94 }
95 
96 
98 {
99  KIID id( FromUTF8() );
100 
101  while( m_uuids.count( id ) )
102  id.Increment();
103 
104  m_uuids.insert( id );
105 
106  return id;
107 }
108 
109 
111 {
112  T token = NextTok();
113 
114  if( token == T_yes )
115  return true;
116  else if( token == T_no )
117  return false;
118  else
119  Expecting( "yes or no" );
120 
121  return false;
122 }
123 
124 
126 {
127  T token;
128 
129  NeedLEFT();
130  NextTok();
131  parseHeader( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION );
132 
133  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
134  {
135  if( token != T_LEFT )
136  Expecting( T_LEFT );
137 
138  token = NextTok();
139 
140  if( token == T_symbol )
141  {
142  m_unit = 1;
143  m_convert = 1;
144  LIB_SYMBOL* symbol = ParseSymbol( aSymbolLibMap, m_requiredVersion );
145  aSymbolLibMap[symbol->GetName()] = symbol;
146  }
147  else
148  {
149  Expecting( "symbol" );
150  }
151  }
152 }
153 
154 
155 LIB_SYMBOL* SCH_SEXPR_PARSER::ParseSymbol( LIB_SYMBOL_MAP& aSymbolLibMap, int aFileVersion )
156 {
157  wxCHECK_MSG( CurTok() == T_symbol, nullptr,
158  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
159 
160  T token;
161  long tmp;
162  wxString name;
163  wxString error;
164  LIB_ITEM* item;
165  std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
166 
167  m_requiredVersion = aFileVersion;
168  symbol->SetUnitCount( 1 );
169 
171  m_fieldIDsRead.clear();
172 
173  token = NextTok();
174 
175  if( !IsSymbol( token ) )
176  {
177  THROW_PARSE_ERROR( _( "Invalid symbol name" ), CurSource(), CurLine(), CurLineNumber(),
178  CurOffset() );
179  }
180 
181  name = FromUTF8();
182 
183  LIB_ID id;
184 
185  if( id.Parse( name ) >= 0 )
186  {
187  THROW_PARSE_ERROR( _( "Invalid library identifier" ), CurSource(), CurLine(),
188  CurLineNumber(), CurOffset() );
189  }
190 
191  m_symbolName = id.GetLibItemName().wx_str();
192  symbol->SetName( m_symbolName );
193  symbol->SetLibId( id );
194 
195  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
196  {
197  if( token != T_LEFT )
198  Expecting( T_LEFT );
199 
200  token = NextTok();
201 
202  switch( token )
203  {
204  case T_power:
205  symbol->SetPower();
206  NeedRIGHT();
207  break;
208 
209  case T_pin_names:
210  parsePinNames( symbol );
211  break;
212 
213  case T_pin_numbers:
214  token = NextTok();
215 
216  if( token != T_hide )
217  Expecting( "hide" );
218 
219  symbol->SetShowPinNumbers( false );
220  NeedRIGHT();
221  break;
222 
223  case T_in_bom:
224  symbol->SetIncludeInBom( parseBool() );
225  NeedRIGHT();
226  break;
227 
228  case T_on_board:
229  symbol->SetIncludeOnBoard( parseBool() );
230  NeedRIGHT();
231  break;
232 
233  case T_property:
234  parseProperty( symbol );
235  break;
236 
237  case T_extends:
238  {
239  token = NextTok();
240 
241  if( !IsSymbol( token ) )
242  {
243  THROW_PARSE_ERROR( _( "Invalid parent symbol name" ), CurSource(), CurLine(),
244  CurLineNumber(), CurOffset() );
245  }
246 
247  name = FromUTF8();
248  auto it = aSymbolLibMap.find( name );
249 
250  if( it == aSymbolLibMap.end() )
251  {
252  error.Printf( _( "No parent for extended symbol %s" ), name.c_str() );
253  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
254  }
255 
256  symbol->SetParent( it->second );
257  NeedRIGHT();
258  break;
259  }
260 
261  case T_symbol:
262  {
263  token = NextTok();
264 
265  if( !IsSymbol( token ) )
266  {
267  THROW_PARSE_ERROR( _( "Invalid symbol unit name" ), CurSource(), CurLine(),
268  CurLineNumber(), CurOffset() );
269  }
270 
271  name = FromUTF8();
272 
273  if( !name.StartsWith( m_symbolName ) )
274  {
275  error.Printf( _( "Invalid symbol unit name prefix %s" ), name.c_str() );
276  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
277  }
278 
279  name = name.Right( name.Length() - m_symbolName.Length() - 1 );
280 
281  wxStringTokenizer tokenizer( name, "_" );
282 
283  if( tokenizer.CountTokens() != 2 )
284  {
285  error.Printf( _( "Invalid symbol unit name suffix %s" ), name.c_str() );
286  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
287  }
288 
289  if( !tokenizer.GetNextToken().ToLong( &tmp ) )
290  {
291  error.Printf( _( "Invalid symbol unit number %s" ), name.c_str() );
292  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
293  }
294 
295  m_unit = static_cast<int>( tmp );
296 
297  if( !tokenizer.GetNextToken().ToLong( &tmp ) )
298  {
299  error.Printf( _( "Invalid symbol convert number %s" ), name.c_str() );
300  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
301  }
302 
303  m_convert = static_cast<int>( tmp );
304 
305  if( m_convert > 1 )
306  symbol->SetConversion( true, false );
307 
308  if( m_unit > symbol->GetUnitCount() )
309  symbol->SetUnitCount( m_unit, false );
310 
311  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
312  {
313  if( token != T_LEFT )
314  Expecting( T_LEFT );
315 
316  token = NextTok();
317 
318  switch( token )
319  {
320  case T_arc:
321  case T_bezier:
322  case T_circle:
323  case T_pin:
324  case T_polyline:
325  case T_rectangle:
326  case T_text:
327  item = ParseDrawItem();
328 
329  wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
330 
331  item->SetParent( symbol.get() );
332  symbol->AddDrawItem( item, false );
333  break;
334 
335  default:
336  Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
337  };
338  }
339 
340  m_unit = 1;
341  m_convert = 1;
342  break;
343  }
344 
345  case T_arc:
346  case T_bezier:
347  case T_circle:
348  case T_pin:
349  case T_polyline:
350  case T_rectangle:
351  case T_text:
352  item = ParseDrawItem();
353 
354  wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
355 
356  item->SetParent( symbol.get() );
357  symbol->AddDrawItem( item, false );
358  break;
359 
360  default:
361  Expecting( "pin_names, pin_numbers, arc, bezier, circle, pin, polyline, "
362  "rectangle, or text" );
363  }
364  }
365 
366  symbol->GetDrawItems().sort();
367  m_symbolName.clear();
368 
369  return symbol.release();
370 }
371 
372 
374 {
375  switch( CurTok() )
376  {
377  case T_arc:
378  return static_cast<LIB_ITEM*>( parseArc() );
379  break;
380 
381  case T_bezier:
382  return static_cast<LIB_ITEM*>( parseBezier() );
383  break;
384 
385  case T_circle:
386  return static_cast<LIB_ITEM*>( parseCircle() );
387  break;
388 
389  case T_pin:
390  return static_cast<LIB_ITEM*>( parsePin() );
391  break;
392 
393  case T_polyline:
394  return static_cast<LIB_ITEM*>( parsePolyLine() );
395  break;
396 
397  case T_rectangle:
398  return static_cast<LIB_ITEM*>( parseRectangle() );
399  break;
400 
401  case T_text:
402  return static_cast<LIB_TEXT*>( parseText() );
403  break;
404 
405  default:
406  Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
407  }
408 
409  return nullptr;
410 }
411 
412 
414 {
415  char* tmp;
416 
417  // In case the file got saved with the wrong locale.
418  if( strchr( CurText(), ',' ) != nullptr )
419  {
420  THROW_PARSE_ERROR( _( "Floating point number with incorrect locale" ), CurSource(),
421  CurLine(), CurLineNumber(), CurOffset() );
422  }
423 
424  errno = 0;
425 
426  double fval = strtod( CurText(), &tmp );
427 
428  if( errno )
429  {
430  THROW_PARSE_ERROR( _( "Invalid floating point number" ), CurSource(), CurLine(),
431  CurLineNumber(), CurOffset() );
432  }
433 
434  if( CurText() == tmp )
435  {
436  THROW_PARSE_ERROR( _( "Missing floating point number" ), CurSource(), CurLine(),
437  CurLineNumber(), CurOffset() );
438  }
439 
440  return fval;
441 }
442 
443 
445 {
446  auto retval = parseDouble() * IU_PER_MM;
447 
448  // Schematic internal units are represented as integers. Any values that are
449  // larger or smaller than the schematic units represent undefined behavior for
450  // the system. Limit values to the largest that can be displayed on the screen.
451  constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071; // 0.7071 = roughly 1/sqrt(2)
452 
453  return KiROUND( Clamp<double>( -int_limit, retval, int_limit ) );
454 }
455 
456 
457 int SCH_SEXPR_PARSER::parseInternalUnits( const char* aExpected )
458 {
459  auto retval = parseDouble( aExpected ) * IU_PER_MM;
460 
461  constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071;
462 
463  return KiROUND( Clamp<double>( -int_limit, retval, int_limit ) );
464 }
465 
466 
468 {
469  wxCHECK_RET( CurTok() == T_stroke,
470  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a stroke." ) );
471 
472  aStroke.SetWidth( Mils2iu( DEFAULT_LINE_WIDTH_MILS ) );
474  aStroke.SetColor( COLOR4D::UNSPECIFIED );
475 
476  T token;
477 
478  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
479  {
480  if( token != T_LEFT )
481  Expecting( T_LEFT );
482 
483  token = NextTok();
484 
485  switch( token )
486  {
487  case T_width:
488  aStroke.SetWidth( parseInternalUnits( "stroke width" ) );
489  NeedRIGHT();
490  break;
491 
492  case T_type:
493  {
494  token = NextTok();
495 
496  switch( token )
497  {
498  case T_dash: aStroke.SetPlotStyle( PLOT_DASH_TYPE::DASH ); break;
499  case T_dot: aStroke.SetPlotStyle( PLOT_DASH_TYPE::DOT ); break;
500  case T_dash_dot: aStroke.SetPlotStyle( PLOT_DASH_TYPE::DASHDOT ); break;
501  case T_solid: aStroke.SetPlotStyle( PLOT_DASH_TYPE::SOLID ); break;
502  case T_default: aStroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT ); break;
503  default:
504  Expecting( "solid, dash, dash_dot, dot or default" );
505  }
506 
507  NeedRIGHT();
508  break;
509  }
510 
511  case T_color:
512  {
513  COLOR4D color;
514 
515  color.r = parseInt( "red" ) / 255.0;
516  color.g = parseInt( "green" ) / 255.0;
517  color.b = parseInt( "blue" ) / 255.0;
518  color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
519 
520  aStroke.SetColor( color );
521  NeedRIGHT();
522  break;
523  }
524 
525  default:
526  Expecting( "width, type, or color" );
527  }
528 
529  }
530 }
531 
532 
534 {
535  wxCHECK_RET( CurTok() == T_fill, "Cannot parse " + GetTokenString( CurTok() ) + " as a fill." );
536 
537  aFill.m_FillType = FILL_T::NO_FILL;
538  aFill.m_Color = COLOR4D::UNSPECIFIED;
539 
540  T token;
541 
542  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
543  {
544  if( token != T_LEFT )
545  Expecting( T_LEFT );
546 
547  token = NextTok();
548 
549  switch( token )
550  {
551  case T_type:
552  {
553  token = NextTok();
554 
555  switch( token )
556  {
557  case T_none: aFill.m_FillType = FILL_T::NO_FILL; break;
558  case T_outline: aFill.m_FillType = FILL_T::FILLED_SHAPE; break;
559  case T_background: aFill.m_FillType = FILL_T::FILLED_WITH_BG_BODYCOLOR; break;
560  default: Expecting( "none, outline, or background" );
561  }
562 
563  NeedRIGHT();
564  break;
565  }
566 
567  case T_color:
568  {
569  COLOR4D color;
570 
571  color.r = parseInt( "red" ) / 255.0;
572  color.g = parseInt( "green" ) / 255.0;
573  color.b = parseInt( "blue" ) / 255.0;
574  color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
575  aFill.m_Color = color;
576  NeedRIGHT();
577  break;
578  }
579 
580  default:
581  Expecting( "type or color" );
582  }
583  }
584 }
585 
586 
587 void SCH_SEXPR_PARSER::parseEDA_TEXT( EDA_TEXT* aText, bool aConvertOverbarSyntax )
588 {
589  wxCHECK_RET( aText && CurTok() == T_effects,
590  "Cannot parse " + GetTokenString( CurTok() ) + " as an EDA_TEXT." );
591 
592  // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
593  // We need to convert the old syntax to the new one.
594  if( aConvertOverbarSyntax && m_requiredVersion < 20210606 )
595  aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
596 
597  T token;
598 
599  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
600  {
601  if( token == T_LEFT )
602  token = NextTok();
603 
604  switch( token )
605  {
606  case T_font:
607  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
608  {
609  if( token == T_LEFT )
610  token = NextTok();
611 
612  switch( token )
613  {
614  case T_size:
615  {
616  wxSize sz;
617  sz.SetHeight( parseInternalUnits( "text height" ) );
618  sz.SetWidth( parseInternalUnits( "text width" ) );
619  aText->SetTextSize( sz );
620  NeedRIGHT();
621  break;
622  }
623 
624  case T_thickness:
625  aText->SetTextThickness( parseInternalUnits( "text thickness" ) );
626  NeedRIGHT();
627  break;
628 
629  case T_bold:
630  aText->SetBold( true );
631  break;
632 
633  case T_italic:
634  aText->SetItalic( true );
635  break;
636 
637  default:
638  Expecting( "size, bold, or italic" );
639  }
640  }
641 
642  break;
643 
644  case T_justify:
645  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
646  {
647  switch( token )
648  {
649  case T_left: aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break;
650  case T_right: aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); break;
651  case T_top: aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break;
652  case T_bottom: aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break;
653  case T_mirror: aText->SetMirrored( true ); break;
654  default: Expecting( "left, right, top, bottom, or mirror" );
655  }
656  }
657 
658  break;
659 
660  case T_hide:
661  aText->SetVisible( false );
662  break;
663 
664  default:
665  Expecting( "font, justify, or hide" );
666  }
667  }
668 }
669 
670 
671 void SCH_SEXPR_PARSER::parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersion )
672 {
673  wxCHECK_RET( CurTok() == aHeaderType,
674  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
675 
676  NeedLEFT();
677 
678  T tok = NextTok();
679 
680  if( tok == T_version )
681  {
682  m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
683 
684  if( m_requiredVersion > aFileVersion )
685  throw FUTURE_FORMAT_ERROR( FromUTF8() );
686 
687  NeedRIGHT();
688 
689  // Skip the host name and host build version information.
690  NeedLEFT();
691  NeedSYMBOL();
692  NeedSYMBOL();
693 
694  if( m_requiredVersion < 20200827 )
695  NeedSYMBOL();
696 
697  NeedRIGHT();
698  }
699  else
700  {
701  m_requiredVersion = aFileVersion;
702 
703  // Skip the host name and host build version information.
704  NeedSYMBOL();
705  NeedSYMBOL();
706  NeedRIGHT();
707  }
708 }
709 
710 
711 void SCH_SEXPR_PARSER::parsePinNames( std::unique_ptr<LIB_SYMBOL>& aSymbol )
712 {
713  wxCHECK_RET( CurTok() == T_pin_names,
714  "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_name token." );
715 
716  wxString error;
717 
718  T token = NextTok();
719 
720  if( token == T_LEFT )
721  {
722  token = NextTok();
723 
724  if( token != T_offset )
725  Expecting( "offset" );
726 
727  aSymbol->SetPinNameOffset( parseInternalUnits( "pin name offset" ) );
728  NeedRIGHT();
729  token = NextTok(); // Either ) or hide
730  }
731 
732  if( token == T_hide )
733  {
734  aSymbol->SetShowPinNames( false );
735  NeedRIGHT();
736  }
737  else if( token != T_RIGHT )
738  {
739  THROW_PARSE_ERROR( _( "Invalid pin names definition" ), CurSource(), CurLine(),
740  CurLineNumber(), CurOffset() );
741  }
742 }
743 
744 
745 LIB_FIELD* SCH_SEXPR_PARSER::parseProperty( std::unique_ptr<LIB_SYMBOL>& aSymbol )
746 {
747  wxCHECK_MSG( CurTok() == T_property, nullptr,
748  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a property." ) );
749  wxCHECK( aSymbol, nullptr );
750 
751  wxString error;
752  wxString name;
753  wxString value;
754  std::unique_ptr<LIB_FIELD> field = std::make_unique<LIB_FIELD>( aSymbol.get(),
756 
757  T token = NextTok();
758 
759  if( !IsSymbol( token ) )
760  {
761  THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
762  CurOffset() );
763  }
764 
765  name = FromUTF8();
766 
767  if( name.IsEmpty() )
768  {
769  THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
770  CurOffset() );
771  }
772 
773  field->SetName( name );
774  token = NextTok();
775 
776  if( !IsSymbol( token ) )
777  {
778  THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
779  CurOffset() );
780  }
781 
782  // Empty property values are valid.
783  value = FromUTF8();
784 
785  field->SetText( value );
786 
787  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
788  {
789  if( token != T_LEFT )
790  Expecting( T_LEFT );
791 
792  token = NextTok();
793 
794  switch( token )
795  {
796  case T_id:
797  field->SetId( parseInt( "field ID" ) );
798  NeedRIGHT();
799  break;
800 
801  case T_at:
802  field->SetPosition( parseXY() );
803  field->SetTextAngle( static_cast<int>( parseDouble( "text angle" ) * 10.0 ) );
804  NeedRIGHT();
805  break;
806 
807  case T_effects:
808  parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ), field->GetId() == VALUE_FIELD );
809  break;
810 
811  default:
812  Expecting( "id, at or effects" );
813  }
814  }
815 
816  // Due to an bug when in #LIB_SYMBOL::Flatten, duplicate ids slipped through
817  // when writing files. This section replaces duplicate #LIB_FIELD indices on
818  // load.
819  if( m_fieldIDsRead.count( field->GetId() ) )
820  {
821  int nextAvailableId = field->GetId() + 1;
822 
823  while( m_fieldIDsRead.count( nextAvailableId ) )
824  nextAvailableId += 1;
825 
826  field->SetId( nextAvailableId );
827  }
828 
829  LIB_FIELD* existingField;
830 
831  if( field->GetId() < MANDATORY_FIELDS )
832  {
833  existingField = aSymbol->GetFieldById( field->GetId() );
834 
835  *existingField = *field;
836  m_fieldIDsRead.insert( field->GetId() );
837  return existingField;
838  }
839  else if( name == "ki_keywords" )
840  {
841  // Not a LIB_FIELD object yet.
842  aSymbol->SetKeyWords( value );
843  return nullptr;
844  }
845  else if( name == "ki_description" )
846  {
847  // Not a LIB_FIELD object yet.
848  aSymbol->SetDescription( value );
849  return nullptr;
850  }
851  else if( name == "ki_fp_filters" )
852  {
853  // Not a LIB_FIELD object yet.
854  wxArrayString filters;
855  wxStringTokenizer tokenizer( value );
856 
857  while( tokenizer.HasMoreTokens() )
858  {
859  wxString curr_token = UnescapeString( tokenizer.GetNextToken() );
860  filters.Add( curr_token );
861  }
862 
863  aSymbol->SetFPFilters( filters );
864  return nullptr;
865  }
866  else if( name == "ki_locked" )
867  {
868  // This is a temporary LIB_FIELD object until interchangeable units are determined on
869  // the fly.
870  aSymbol->LockUnits( true );
871  return nullptr;
872  }
873  else
874  {
875  // At this point, a user field is read.
876  existingField = aSymbol->FindField( field->GetCanonicalName() );
877 
878 #if 1 // Enable it to modify the name of the field to add if already existing
879  // Disable it to skip the field having the same name as previous field
880  if( existingField )
881  {
882  // We cannot handle 2 fields with the same name, so because the field name
883  // is already in use, try to build a new name (oldname_x)
884  wxString base_name = field->GetCanonicalName();
885 
886  // Arbitrary limit 10 attempts to find a new name
887  for( int ii = 1; ii < 10 && existingField ; ii++ )
888  {
889  wxString newname = base_name;
890  newname << '_' << ii;
891 
892  existingField = aSymbol->FindField( newname );
893 
894  if( !existingField ) // the modified name is not found, use it
895  field->SetName( newname );
896  }
897  }
898 #endif
899  if( !existingField )
900  {
901  aSymbol->AddDrawItem( field.get(), false );
902  m_fieldIDsRead.insert( field->GetId() );
903  return field.release();
904  }
905  else
906  {
907  // We cannot handle 2 fields with the same name, so skip this one
908  return nullptr;
909  }
910  }
911 }
912 
913 
915 {
916  wxCHECK_MSG( CurTok() == T_arc, nullptr,
917  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
918 
919  T token;
920  wxPoint startPoint( 1, 0 ); // Initialize to a non-degenerate arc just for safety
921  wxPoint midPoint( 1, 1 );
922  wxPoint endPoint( 0, 1 );
923  bool hasMidPoint = false;
925  FILL_PARAMS fill;
926 
927  // Parameters for legacy format
928  wxPoint center( 0, 0 );
929  int startAngle = 0;
930  int endAngle = 900;
931  bool hasAngles = false;
932 
933  std::unique_ptr<LIB_SHAPE> arc = std::make_unique<LIB_SHAPE>( nullptr, SHAPE_T::ARC );
934 
935  arc->SetUnit( m_unit );
936  arc->SetConvert( m_convert );
937 
938  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
939  {
940  if( token != T_LEFT )
941  Expecting( T_LEFT );
942 
943  token = NextTok();
944 
945  switch( token )
946  {
947  case T_start:
948  startPoint = parseXY();
949  NeedRIGHT();
950  break;
951 
952  case T_mid:
953  midPoint = parseXY();
954  NeedRIGHT();
955  hasMidPoint = true;
956  break;
957 
958  case T_end:
959  endPoint = parseXY();
960  NeedRIGHT();
961  break;
962 
963  case T_radius:
964  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
965  {
966  if( token != T_LEFT )
967  Expecting( T_LEFT );
968 
969  token = NextTok();
970 
971  switch( token )
972  {
973  case T_at:
974  center = parseXY();
975  NeedRIGHT();
976  break;
977 
978  case T_length:
979  parseInternalUnits( "radius length" );
980  NeedRIGHT();
981  break;
982 
983  case T_angles:
984  {
985  startAngle = KiROUND( parseDouble( "start radius angle" ) * 10.0 );
986  endAngle = KiROUND( parseDouble( "end radius angle" ) * 10.0 );
987  NORMALIZE_ANGLE_POS( startAngle );
988  NORMALIZE_ANGLE_POS( endAngle );
989  NeedRIGHT();
990  hasAngles = true;
991  break;
992  }
993 
994  default:
995  Expecting( "at, length, or angles" );
996  }
997  }
998 
999  break;
1000 
1001  case T_stroke:
1002  parseStroke( stroke );
1003  arc->SetWidth( stroke.GetWidth() );
1004  break;
1005 
1006  case T_fill:
1007  parseFill( fill );
1008  arc->SetFillMode( fill.m_FillType );
1009  break;
1010 
1011  default:
1012  Expecting( "start, mid, end, radius, stroke, or fill" );
1013  }
1014  }
1015 
1016  if( hasMidPoint )
1017  {
1018  arc->SetArcGeometry( startPoint, midPoint, endPoint);
1019  }
1020  else if( hasAngles )
1021  {
1022  arc->SetStart( startPoint );
1023  arc->SetEnd( endPoint );
1024 
1032  if( !TRANSFORM().MapAngles( &startAngle, &endAngle ) )
1033  {
1034  wxPoint temp = arc->GetStart();
1035  arc->SetStart( arc->GetEnd() );
1036  arc->SetEnd( temp );
1037  }
1038 
1039  arc->SetCenter( center );
1040  }
1041  else
1042  {
1043  wxFAIL_MSG( "Setting arc without either midpoint or angles not implemented." );
1044  }
1045 
1046  return arc.release();
1047 }
1048 
1049 
1051 {
1052  wxCHECK_MSG( CurTok() == T_bezier, nullptr,
1053  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
1054 
1055  T token;
1057  FILL_PARAMS fill;
1058 
1059  std::unique_ptr<LIB_SHAPE> bezier = std::make_unique<LIB_SHAPE>( nullptr, SHAPE_T::BEZIER );
1060 
1061  bezier->SetUnit( m_unit );
1062  bezier->SetConvert( m_convert );
1063 
1064  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1065  {
1066  if( token != T_LEFT )
1067  Expecting( T_LEFT );
1068 
1069  token = NextTok();
1070 
1071  switch( token )
1072  {
1073  case T_pts:
1074  {
1075  int ii = 0;
1076 
1077  for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
1078  {
1079  if( token != T_LEFT )
1080  Expecting( T_LEFT );
1081 
1082  token = NextTok();
1083 
1084  if( token != T_xy )
1085  Expecting( "xy" );
1086 
1087  switch( ii )
1088  {
1089  case 0: bezier->SetStart( parseXY() ); break;
1090  case 1: bezier->SetBezierC1( parseXY() ); break;
1091  case 2: bezier->SetBezierC2( parseXY() ); break;
1092  case 3: bezier->SetEnd( parseXY() ); break;
1093  default: Unexpected( "control point" ); break;
1094  }
1095 
1096  NeedRIGHT();
1097  }
1098  }
1099  break;
1100 
1101  case T_stroke:
1102  parseStroke( stroke );
1103  bezier->SetWidth( stroke.GetWidth() );
1104  break;
1105 
1106  case T_fill:
1107  parseFill( fill );
1108  bezier->SetFillMode( fill.m_FillType );
1109  break;
1110 
1111  default:
1112  Expecting( "pts, stroke, or fill" );
1113  }
1114  }
1115 
1116  bezier->RebuildBezierToSegmentsPointsList( bezier->GetWidth() );
1117 
1118  return bezier.release();
1119 }
1120 
1121 
1123 {
1124  wxCHECK_MSG( CurTok() == T_circle, nullptr,
1125  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
1126 
1127  T token;
1128  wxPoint center( 0, 0 );
1129  int radius = 1; // defaulting to 0 could result in troublesome math....
1131  FILL_PARAMS fill;
1132 
1133  std::unique_ptr<LIB_SHAPE> circle = std::make_unique<LIB_SHAPE>( nullptr, SHAPE_T::CIRCLE );
1134 
1135  circle->SetUnit( m_unit );
1136  circle->SetConvert( m_convert );
1137 
1138  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1139  {
1140  if( token != T_LEFT )
1141  Expecting( T_LEFT );
1142 
1143  token = NextTok();
1144 
1145  switch( token )
1146  {
1147  case T_center:
1148  center = parseXY();
1149  NeedRIGHT();
1150  break;
1151 
1152  case T_radius:
1153  radius = parseInternalUnits( "radius length" );
1154  NeedRIGHT();
1155  break;
1156 
1157  case T_stroke:
1158  parseStroke( stroke );
1159  circle->SetWidth( stroke.GetWidth() );
1160  break;
1161 
1162  case T_fill:
1163  parseFill( fill );
1164  circle->SetFillMode( fill.m_FillType );
1165  break;
1166 
1167  default:
1168  Expecting( "center, radius, stroke, or fill" );
1169  }
1170  }
1171 
1172  circle->SetCenter( center );
1173  circle->SetEnd( wxPoint( center.x + radius, center.y ) );
1174 
1175  return circle.release();
1176 }
1177 
1178 
1180 {
1181  auto parseType = [&]( T token ) -> ELECTRICAL_PINTYPE
1182  {
1183  switch( token )
1184  {
1185  case T_input: return ELECTRICAL_PINTYPE::PT_INPUT;
1186  case T_output: return ELECTRICAL_PINTYPE::PT_OUTPUT;
1187  case T_bidirectional: return ELECTRICAL_PINTYPE::PT_BIDI;
1188  case T_tri_state: return ELECTRICAL_PINTYPE::PT_TRISTATE;
1189  case T_passive: return ELECTRICAL_PINTYPE::PT_PASSIVE;
1190  case T_unspecified: return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
1191  case T_power_in: return ELECTRICAL_PINTYPE::PT_POWER_IN;
1192  case T_power_out: return ELECTRICAL_PINTYPE::PT_POWER_OUT;
1193  case T_open_collector: return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
1194  case T_open_emitter: return ELECTRICAL_PINTYPE::PT_OPENEMITTER;
1195  case T_unconnected:
1196  case T_no_connect: return ELECTRICAL_PINTYPE::PT_NC;
1197  case T_free: return ELECTRICAL_PINTYPE::PT_NIC;
1198 
1199  default:
1200  Expecting( "input, output, bidirectional, tri_state, passive, "
1201  "unspecified, power_in, power_out, open_collector, "
1202  "open_emitter, free or no_connect" );
1204  }
1205  };
1206 
1207  auto parseShape = [&]( T token ) -> GRAPHIC_PINSHAPE
1208  {
1209  switch( token )
1210  {
1211  case T_line: return GRAPHIC_PINSHAPE::LINE;
1212  case T_inverted: return GRAPHIC_PINSHAPE::INVERTED;
1213  case T_clock: return GRAPHIC_PINSHAPE::CLOCK;
1214  case T_inverted_clock: return GRAPHIC_PINSHAPE::INVERTED_CLOCK;
1215  case T_input_low: return GRAPHIC_PINSHAPE::INPUT_LOW;
1216  case T_clock_low: return GRAPHIC_PINSHAPE::CLOCK_LOW;
1217  case T_output_low: return GRAPHIC_PINSHAPE::OUTPUT_LOW;
1218  case T_edge_clock_high: return GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
1219  case T_non_logic: return GRAPHIC_PINSHAPE::NONLOGIC;
1220 
1221  default:
1222  Expecting( "line, inverted, clock, inverted_clock, input_low, "
1223  "clock_low, output_low, edge_clock_high, non_logic" );
1224  return GRAPHIC_PINSHAPE::LINE;
1225  }
1226  };
1227 
1228  wxCHECK_MSG( CurTok() == T_pin, nullptr,
1229  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin token." ) );
1230 
1231  T token;
1232  wxString tmp;
1233  wxString error;
1234  std::unique_ptr<LIB_PIN> pin = std::make_unique<LIB_PIN>( nullptr );
1235 
1236  pin->SetUnit( m_unit );
1237  pin->SetConvert( m_convert );
1238 
1239  // Pin electrical type.
1240  token = NextTok();
1241  pin->SetType( parseType( token ) );
1242 
1243  // Pin shape.
1244  token = NextTok();
1245  pin->SetShape( parseShape( token ) );
1246 
1247  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1248  {
1249  if( token == T_hide )
1250  {
1251  pin->SetVisible( false );
1252  continue;
1253  }
1254 
1255  if( token != T_LEFT )
1256  Expecting( T_LEFT );
1257 
1258  token = NextTok();
1259 
1260  switch( token )
1261  {
1262  case T_at:
1263  pin->SetPosition( parseXY() );
1264 
1265  switch( parseInt( "pin orientation" ) )
1266  {
1267  case 0: pin->SetOrientation( PIN_RIGHT ); break;
1268  case 90: pin->SetOrientation( PIN_UP ); break;
1269  case 180: pin->SetOrientation( PIN_LEFT ); break;
1270  case 270: pin->SetOrientation( PIN_DOWN ); break;
1271  default: Expecting( "0, 90, 180, or 270" );
1272  }
1273 
1274  NeedRIGHT();
1275  break;
1276 
1277  case T_length:
1278  pin->SetLength( parseInternalUnits( "pin length" ) );
1279  NeedRIGHT();
1280  break;
1281 
1282  case T_name:
1283  token = NextTok();
1284 
1285  if( !IsSymbol( token ) )
1286  {
1287  THROW_PARSE_ERROR( _( "Invalid pin name" ), CurSource(), CurLine(), CurLineNumber(),
1288  CurOffset() );
1289  }
1290 
1291  if( m_requiredVersion < 20210606 )
1292  pin->SetName( ConvertToNewOverbarNotation( FromUTF8() ) );
1293  else
1294  pin->SetName( FromUTF8() );
1295 
1296  token = NextTok();
1297 
1298  if( token != T_RIGHT )
1299  {
1300  token = NextTok();
1301 
1302  if( token == T_effects )
1303  {
1304  // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1305  // so duplicate parsing is not required.
1306  EDA_TEXT text;
1307 
1308  parseEDA_TEXT( &text, true );
1309  pin->SetNameTextSize( text.GetTextHeight() );
1310  NeedRIGHT();
1311  }
1312  else
1313  {
1314  Expecting( "effects" );
1315  }
1316  }
1317 
1318  break;
1319 
1320  case T_number:
1321  token = NextTok();
1322 
1323  if( !IsSymbol( token ) )
1324  {
1325  THROW_PARSE_ERROR( _( "Invalid pin number" ), CurSource(), CurLine(),
1326  CurLineNumber(), CurOffset() );
1327  }
1328 
1329  pin->SetNumber( FromUTF8() );
1330  token = NextTok();
1331 
1332  if( token != T_RIGHT )
1333  {
1334  token = NextTok();
1335 
1336  if( token == T_effects )
1337  {
1338  // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1339  // so duplicate parsing is not required.
1340  EDA_TEXT text;
1341 
1342  parseEDA_TEXT( &text, false );
1343  pin->SetNumberTextSize( text.GetTextHeight() );
1344  NeedRIGHT();
1345  }
1346  else
1347  {
1348  Expecting( "effects" );
1349  }
1350  }
1351 
1352  break;
1353 
1354  case T_alternate:
1355  {
1356  LIB_PIN::ALT alt;
1357 
1358  token = NextTok();
1359 
1360  if( !IsSymbol( token ) )
1361  {
1362  THROW_PARSE_ERROR( _( "Invalid alternate pin name" ), CurSource(), CurLine(),
1363  CurLineNumber(), CurOffset() );
1364  }
1365 
1366  alt.m_Name = FromUTF8();
1367 
1368  token = NextTok();
1369  alt.m_Type = parseType( token );
1370 
1371  token = NextTok();
1372  alt.m_Shape = parseShape( token );
1373 
1374  pin->GetAlternates()[ alt.m_Name ] = alt;
1375 
1376  NeedRIGHT();
1377  }
1378  break;
1379 
1380  default:
1381  Expecting( "at, name, number, length, or alternate" );
1382  }
1383  }
1384 
1385  return pin.release();
1386 }
1387 
1388 
1390 {
1391  wxCHECK_MSG( CurTok() == T_polyline, nullptr,
1392  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a poly." ) );
1393 
1394  T token;
1396  FILL_PARAMS fill;
1397  std::unique_ptr<LIB_SHAPE> poly = std::make_unique<LIB_SHAPE>( nullptr, SHAPE_T::POLY );
1398 
1399  poly->SetUnit( m_unit );
1400  poly->SetConvert( m_convert );
1401 
1402  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1403  {
1404  if( token != T_LEFT )
1405  Expecting( T_LEFT );
1406 
1407  token = NextTok();
1408 
1409  switch( token )
1410  {
1411  case T_pts:
1412  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1413  {
1414  if( token != T_LEFT )
1415  Expecting( T_LEFT );
1416 
1417  token = NextTok();
1418 
1419  if( token != T_xy )
1420  Expecting( "xy" );
1421 
1422  poly->AddPoint( parseXY() );
1423 
1424  NeedRIGHT();
1425  }
1426 
1427  break;
1428 
1429  case T_stroke:
1430  parseStroke( stroke );
1431  poly->SetWidth( stroke.GetWidth() );
1432  break;
1433 
1434  case T_fill:
1435  parseFill( fill );
1436  poly->SetFillMode( fill.m_FillType );
1437  break;
1438 
1439  default:
1440  Expecting( "pts, stroke, or fill" );
1441  }
1442  }
1443 
1444  return poly.release();
1445 }
1446 
1447 
1449 {
1450  wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
1451  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
1452 
1453  T token;
1455  FILL_PARAMS fill;
1456  std::unique_ptr<LIB_SHAPE> rectangle = std::make_unique<LIB_SHAPE>( nullptr, SHAPE_T::RECT );
1457 
1458  rectangle->SetUnit( m_unit );
1459  rectangle->SetConvert( m_convert );
1460 
1461  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1462  {
1463  if( token != T_LEFT )
1464  Expecting( T_LEFT );
1465 
1466  token = NextTok();
1467 
1468  switch( token )
1469  {
1470  case T_start:
1471  rectangle->SetPosition( parseXY() );
1472  NeedRIGHT();
1473  break;
1474 
1475  case T_end:
1476  rectangle->SetEnd( parseXY() );
1477  NeedRIGHT();
1478  break;
1479 
1480  case T_stroke:
1481  parseStroke( stroke );
1482  rectangle->SetWidth( stroke.GetWidth() );
1483  break;
1484 
1485  case T_fill:
1486  parseFill( fill );
1487  rectangle->SetFillMode( fill.m_FillType );
1488  break;
1489 
1490  default:
1491  Expecting( "start, end, stroke, or fill" );
1492  }
1493  }
1494 
1495  return rectangle.release();
1496 }
1497 
1498 
1500 {
1501  wxCHECK_MSG( CurTok() == T_text, nullptr,
1502  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text token." ) );
1503 
1504  T token;
1505  wxString tmp;
1506  std::unique_ptr<LIB_TEXT> text = std::make_unique<LIB_TEXT>( nullptr );
1507 
1508  text->SetUnit( m_unit );
1509  text->SetConvert( m_convert );
1510  token = NextTok();
1511 
1512  if( !IsSymbol( token ) )
1513  {
1514  THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
1515  CurOffset() );
1516  }
1517 
1518  text->SetText( FromUTF8() );
1519 
1520  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1521  {
1522  if( token != T_LEFT )
1523  Expecting( T_LEFT );
1524 
1525  token = NextTok();
1526 
1527  switch( token )
1528  {
1529  case T_at:
1530  text->SetPosition( parseXY() );
1531  text->SetTextAngle( parseDouble( "text angle" ) );
1532  NeedRIGHT();
1533  break;
1534 
1535  case T_effects:
1536  parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
1537  break;
1538 
1539  default:
1540  Expecting( "at or effects" );
1541  }
1542  }
1543 
1544  return text.release();
1545 }
1546 
1547 
1549 {
1550  wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200506 ) || CurTok() == T_paper,
1551  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
1552 
1553  T token;
1554 
1555  NeedSYMBOL();
1556 
1557  wxString pageType = FromUTF8();
1558 
1559  if( !aPageInfo.SetType( pageType ) )
1560  {
1561  THROW_PARSE_ERROR( _( "Invalid page type" ), CurSource(), CurLine(), CurLineNumber(),
1562  CurOffset() );
1563  }
1564 
1565  if( pageType == PAGE_INFO::Custom )
1566  {
1567  int width = Mm2mils( parseDouble( "width" ) ); // width stored in mm so we convert to mils
1568 
1569  // Perform some controls to avoid crashes if the size is edited by hands
1570  if( width < MIN_PAGE_SIZE_MILS )
1571  width = MIN_PAGE_SIZE_MILS;
1572  else if( width > MAX_PAGE_SIZE_EESCHEMA_MILS )
1574 
1575  int height = Mm2mils( parseDouble( "height" ) ); // height stored in mm so we convert to mils
1576 
1577  if( height < MIN_PAGE_SIZE_MILS )
1578  height = MIN_PAGE_SIZE_MILS;
1579  else if( height > MAX_PAGE_SIZE_EESCHEMA_MILS )
1580  height = MAX_PAGE_SIZE_EESCHEMA_MILS;
1581 
1582  aPageInfo.SetWidthMils( width );
1583  aPageInfo.SetHeightMils( height );
1584  }
1585 
1586  token = NextTok();
1587 
1588  if( token == T_portrait )
1589  {
1590  aPageInfo.SetPortrait( true );
1591  NeedRIGHT();
1592  }
1593  else if( token != T_RIGHT )
1594  {
1595  Expecting( "portrait" );
1596  }
1597 }
1598 
1599 
1601 {
1602  wxCHECK_RET( CurTok() == T_title_block,
1603  "Cannot parse " + GetTokenString( CurTok() ) + " as a TITLE_BLOCK." );
1604 
1605  T token;
1606 
1607  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1608  {
1609  if( token != T_LEFT )
1610  Expecting( T_LEFT );
1611 
1612  token = NextTok();
1613 
1614  switch( token )
1615  {
1616  case T_title:
1617  NextTok();
1618  aTitleBlock.SetTitle( FromUTF8() );
1619  break;
1620 
1621  case T_date:
1622  NextTok();
1623  aTitleBlock.SetDate( FromUTF8() );
1624  break;
1625 
1626  case T_rev:
1627  NextTok();
1628  aTitleBlock.SetRevision( FromUTF8() );
1629  break;
1630 
1631  case T_company:
1632  NextTok();
1633  aTitleBlock.SetCompany( FromUTF8() );
1634  break;
1635 
1636  case T_comment:
1637  {
1638  int commentNumber = parseInt( "comment" );
1639 
1640  switch( commentNumber )
1641  {
1642  case 1:
1643  NextTok();
1644  aTitleBlock.SetComment( 0, FromUTF8() );
1645  break;
1646 
1647  case 2:
1648  NextTok();
1649  aTitleBlock.SetComment( 1, FromUTF8() );
1650  break;
1651 
1652  case 3:
1653  NextTok();
1654  aTitleBlock.SetComment( 2, FromUTF8() );
1655  break;
1656 
1657  case 4:
1658  NextTok();
1659  aTitleBlock.SetComment( 3, FromUTF8() );
1660  break;
1661 
1662  case 5:
1663  NextTok();
1664  aTitleBlock.SetComment( 4, FromUTF8() );
1665  break;
1666 
1667  case 6:
1668  NextTok();
1669  aTitleBlock.SetComment( 5, FromUTF8() );
1670  break;
1671 
1672  case 7:
1673  NextTok();
1674  aTitleBlock.SetComment( 6, FromUTF8() );
1675  break;
1676 
1677  case 8:
1678  NextTok();
1679  aTitleBlock.SetComment( 7, FromUTF8() );
1680  break;
1681 
1682  case 9:
1683  NextTok();
1684  aTitleBlock.SetComment( 8, FromUTF8() );
1685  break;
1686 
1687  default:
1688  THROW_PARSE_ERROR( _( "Invalid title block comment number" ), CurSource(),
1689  CurLine(), CurLineNumber(), CurOffset() );
1690  }
1691 
1692  break;
1693  }
1694 
1695  default:
1696  Expecting( "title, date, rev, company, or comment" );
1697  }
1698 
1699  NeedRIGHT();
1700  }
1701 }
1702 
1703 
1705 {
1706  wxCHECK_MSG( CurTok() == T_property, nullptr,
1707  "Cannot parse " + GetTokenString( CurTok() ) + " as a property token." );
1708 
1709  T token = NextTok();
1710 
1711  if( !IsSymbol( token ) )
1712  {
1713  THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
1714  CurOffset() );
1715  }
1716 
1717  wxString name = FromUTF8();
1718 
1719  if( name.IsEmpty() )
1720  {
1721  THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
1722  CurOffset() );
1723  }
1724 
1725  token = NextTok();
1726 
1727  if( !IsSymbol( token ) )
1728  {
1729  THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
1730  CurOffset() );
1731  }
1732 
1733  // Empty property values are valid.
1734  wxString value = FromUTF8();
1735 
1736  std::unique_ptr<SCH_FIELD> field = std::make_unique<SCH_FIELD>( wxDefaultPosition, -1,
1737  aParent, name );
1738 
1739  field->SetText( value );
1740  field->SetVisible( true );
1741 
1742  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1743  {
1744  if( token != T_LEFT )
1745  Expecting( T_LEFT );
1746 
1747  token = NextTok();
1748 
1749  switch( token )
1750  {
1751  case T_id:
1752  field->SetId( parseInt( "field ID" ) );
1753  NeedRIGHT();
1754  break;
1755 
1756  case T_at:
1757  field->SetPosition( parseXY() );
1758  field->SetTextAngle( static_cast<int>( parseDouble( "text angle" ) * 10.0 ) );
1759  NeedRIGHT();
1760  break;
1761 
1762  case T_effects:
1763  parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ), field->GetId() == VALUE_FIELD );
1764  break;
1765 
1766  default:
1767  Expecting( "at or effects" );
1768  }
1769  }
1770 
1771  return field.release();
1772 }
1773 
1774 
1776 {
1777  wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
1778  wxCHECK_MSG( CurTok() == T_pin, nullptr,
1779  "Cannot parse " + GetTokenString( CurTok() ) + " as a sheet pin token." );
1780 
1781  T token = NextTok();
1782 
1783  if( !IsSymbol( token ) )
1784  {
1785  THROW_PARSE_ERROR( _( "Invalid sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
1786  CurOffset() );
1787  }
1788 
1789  wxString name = FromUTF8();
1790 
1791  if( name.IsEmpty() )
1792  {
1793  THROW_PARSE_ERROR( _( "Empty sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
1794  CurOffset() );
1795  }
1796 
1797  auto sheetPin = std::make_unique<SCH_SHEET_PIN>( aSheet, wxPoint( 0, 0 ), name );
1798 
1799  token = NextTok();
1800 
1801  switch( token )
1802  {
1803  case T_input: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT ); break;
1804  case T_output: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT ); break;
1805  case T_bidirectional: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI ); break;
1806  case T_tri_state: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_TRISTATE ); break;
1807  case T_passive: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED ); break;
1808  default:
1809  Expecting( "input, output, bidirectional, tri_state, or passive" );
1810  }
1811 
1812  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1813  {
1814  if( token != T_LEFT )
1815  Expecting( T_LEFT );
1816 
1817  token = NextTok();
1818 
1819  switch( token )
1820  {
1821  case T_at:
1822  {
1823  sheetPin->SetPosition( parseXY() );
1824 
1825  double angle = parseDouble( "sheet pin angle (side)" );
1826 
1827  if( angle == 0.0 )
1828  sheetPin->SetSide( SHEET_SIDE::RIGHT );
1829  else if( angle == 90.0 )
1830  sheetPin->SetSide( SHEET_SIDE::TOP );
1831  else if( angle == 180.0 )
1832  sheetPin->SetSide( SHEET_SIDE::LEFT );
1833  else if( angle == 270.0 )
1834  sheetPin->SetSide( SHEET_SIDE::BOTTOM );
1835  else
1836  Expecting( "0, 90, 180, or 270" );
1837 
1838  NeedRIGHT();
1839  break;
1840  }
1841 
1842  case T_effects:
1843  parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ), true );
1844  break;
1845 
1846  case T_uuid:
1847  NeedSYMBOL();
1848  const_cast<KIID&>( sheetPin->m_Uuid ) = parseKIID();
1849  NeedRIGHT();
1850  break;
1851 
1852  default:
1853  Expecting( "at, uuid or effects" );
1854  }
1855  }
1856 
1857  return sheetPin.release();
1858 }
1859 
1860 
1862 {
1863  wxCHECK_RET( CurTok() == T_sheet_instances,
1864  "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
1865  wxCHECK( aScreen, /* void */ );
1866 
1867  T token;
1868 
1869  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1870  {
1871  if( token != T_LEFT )
1872  Expecting( T_LEFT );
1873 
1874  token = NextTok();
1875 
1876  switch( token )
1877  {
1878  case T_path:
1879  {
1880  NeedSYMBOL();
1881 
1882  SCH_SHEET_INSTANCE instance;
1883 
1884  instance.m_Path = KIID_PATH( FromUTF8() );
1885 
1886  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1887  {
1888  if( token != T_LEFT )
1889  Expecting( T_LEFT );
1890 
1891  token = NextTok();
1892 
1893  std::vector<wxString> whitespaces = { wxT( "\r" ), wxT( "\n" ), wxT( "\t" ),
1894  wxT( " " ) };
1895 
1896  size_t numReplacements = 0;
1897 
1898  switch( token )
1899  {
1900  case T_page:
1901  NeedSYMBOL();
1902  instance.m_PageNumber = FromUTF8();
1903 
1904  // Whitespaces are not permitted
1905  for( wxString ch : whitespaces )
1906  numReplacements += instance.m_PageNumber.Replace( ch, wxEmptyString );
1907 
1908 
1909  // Empty page numbers are not permitted
1910  if( instance.m_PageNumber.IsEmpty() )
1911  {
1912  // Use hash character instead
1913  instance.m_PageNumber = wxT( "#" );
1914  numReplacements++;
1915  }
1916 
1917  // Set the file as modified so the user can be warned.
1918  if( numReplacements > 0 )
1919  aScreen->SetContentModified();
1920 
1921  NeedRIGHT();
1922  break;
1923 
1924  default:
1925  Expecting( "path or page" );
1926  }
1927  }
1928 
1929  aScreen->m_sheetInstances.emplace_back( instance );
1930  break;
1931  }
1932 
1933  default:
1934  Expecting( "path" );
1935  }
1936  }
1937 }
1938 
1939 
1941 {
1942  wxCHECK_RET( CurTok() == T_symbol_instances,
1943  "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
1944  wxCHECK( aScreen, /* void */ );
1945 
1946  T token;
1947 
1948  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1949  {
1950  if( token != T_LEFT )
1951  Expecting( T_LEFT );
1952 
1953  token = NextTok();
1954 
1955  switch( token )
1956  {
1957  case T_path:
1958  {
1959  NeedSYMBOL();
1960 
1961  SYMBOL_INSTANCE_REFERENCE instance;
1962 
1963  instance.m_Path = KIID_PATH( FromUTF8() );
1964 
1965  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1966  {
1967  if( token != T_LEFT )
1968  Expecting( T_LEFT );
1969 
1970  token = NextTok();
1971 
1972  switch( token )
1973  {
1974  case T_reference:
1975  NeedSYMBOL();
1976  instance.m_Reference = FromUTF8();
1977  NeedRIGHT();
1978  break;
1979 
1980  case T_unit:
1981  instance.m_Unit = parseInt( "symbol unit" );
1982  NeedRIGHT();
1983  break;
1984 
1985  case T_value:
1986  NeedSYMBOL();
1987  instance.m_Value = FromUTF8();
1988  NeedRIGHT();
1989  break;
1990 
1991  case T_footprint:
1992  NeedSYMBOL();
1993  instance.m_Footprint = FromUTF8();
1994  NeedRIGHT();
1995  break;
1996 
1997  default:
1998  Expecting( "path, unit, value or footprint" );
1999  }
2000  }
2001 
2002  aScreen->m_symbolInstances.emplace_back( instance );
2003  break;
2004  }
2005 
2006  default:
2007  Expecting( "path" );
2008  }
2009  }
2010 }
2011 
2012 
2013 void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly, int aFileVersion )
2014 {
2015  wxCHECK( aSheet != nullptr, /* void */ );
2016 
2017  SCH_SCREEN* screen = aSheet->GetScreen();
2018 
2019  wxCHECK( screen != nullptr, /* void */ );
2020 
2021  if( aIsCopyableOnly )
2022  m_requiredVersion = aFileVersion;
2023 
2024  T token;
2025 
2026  if( !aIsCopyableOnly )
2027  {
2028  NeedLEFT();
2029  NextTok();
2030 
2031  if( CurTok() != T_kicad_sch )
2032  Expecting( "kicad_sch" );
2033 
2034  parseHeader( T_kicad_sch, SEXPR_SCHEMATIC_FILE_VERSION );
2035  }
2036 
2038 
2039  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2040  {
2041  if( aIsCopyableOnly && token == T_EOF )
2042  break;
2043 
2044  if( token != T_LEFT )
2045  Expecting( T_LEFT );
2046 
2047  token = NextTok();
2048 
2049  checkpoint();
2050 
2051  if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
2052  token = T_paper;
2053 
2054  switch( token )
2055  {
2056  case T_uuid:
2057  NeedSYMBOL();
2058  screen->m_uuid = parseKIID();
2059  NeedRIGHT();
2060  break;
2061 
2062  case T_paper:
2063  {
2064  if( aIsCopyableOnly )
2065  Unexpected( T_paper );
2066 
2067  PAGE_INFO pageInfo;
2068  parsePAGE_INFO( pageInfo );
2069  screen->SetPageSettings( pageInfo );
2070  break;
2071  }
2072 
2073  case T_page:
2074  {
2075  if( aIsCopyableOnly )
2076  Unexpected( T_page );
2077 
2078  // Only saved for top-level sniffing in Kicad Manager frame and other external
2079  // tool usage with flat hierarchies
2080  NeedSYMBOLorNUMBER();
2081  NeedSYMBOLorNUMBER();
2082  NeedRIGHT();
2083  break;
2084  }
2085 
2086  case T_title_block:
2087  {
2088  if( aIsCopyableOnly )
2089  Unexpected( T_title_block );
2090 
2091  TITLE_BLOCK tb;
2092  parseTITLE_BLOCK( tb );
2093  screen->SetTitleBlock( tb );
2094  break;
2095  }
2096 
2097  case T_lib_symbols:
2098  {
2099  // Dummy map. No derived symbols are allowed in the library cache.
2100  LIB_SYMBOL_MAP symbolLibMap;
2101  LIB_SYMBOL* symbol;
2102 
2103  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2104  {
2105  if( token != T_LEFT )
2106  Expecting( T_LEFT );
2107 
2108  token = NextTok();
2109 
2110  switch( token )
2111  {
2112  case T_symbol:
2113  symbol = ParseSymbol( symbolLibMap, m_requiredVersion );
2114  symbol->UpdateFieldOrdinals();
2115  screen->AddLibSymbol( symbol );
2116  break;
2117 
2118  default:
2119  Expecting( "symbol" );
2120  }
2121  }
2122 
2123  break;
2124  }
2125 
2126  case T_symbol:
2127  screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
2128  break;
2129 
2130  case T_image:
2131  screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
2132  break;
2133 
2134  case T_sheet:
2135  {
2136  SCH_SHEET* sheet = parseSheet();
2137 
2138  // Set the parent to aSheet. This effectively creates a method to find
2139  // the root sheet from any sheet so a pointer to the root sheet does not
2140  // need to be stored globally. Note: this is not the same as a hierarchy.
2141  // Complex hierarchies can have multiple copies of a sheet. This only
2142  // provides a simple tree to find the root sheet.
2143  sheet->SetParent( aSheet );
2144  screen->Append( static_cast<SCH_ITEM*>( sheet ) );
2145  break;
2146  }
2147 
2148  case T_junction:
2149  screen->Append( static_cast<SCH_ITEM*>( parseJunction() ) );
2150  break;
2151 
2152  case T_no_connect:
2153  screen->Append( static_cast<SCH_ITEM*>( parseNoConnect() ) );
2154  break;
2155 
2156  case T_bus_entry:
2157  screen->Append( static_cast<SCH_ITEM*>( parseBusEntry() ) );
2158  break;
2159 
2160  case T_polyline:
2161  case T_bus:
2162  case T_wire:
2163  screen->Append( static_cast<SCH_ITEM*>( parseLine() ) );
2164  break;
2165 
2166  case T_text:
2167  case T_label:
2168  case T_global_label:
2169  case T_hierarchical_label:
2170  screen->Append( static_cast<SCH_ITEM*>( parseSchText() ) );
2171  break;
2172 
2173  case T_sheet_instances:
2174  parseSchSheetInstances( aSheet, screen );
2175  break;
2176 
2177  case T_symbol_instances:
2178  parseSchSymbolInstances( screen );
2179  break;
2180 
2181  case T_bus_alias:
2182  if( aIsCopyableOnly )
2183  Unexpected( T_bus_alias );
2184 
2185  parseBusAlias( screen );
2186  break;
2187 
2188  default:
2189  Expecting( "symbol, paper, page, title_block, bitmap, sheet, junction, no_connect, "
2190  "bus_entry, line, bus, text, label, global_label, hierarchical_label, "
2191  "symbol_instances, or bus_alias" );
2192  }
2193  }
2194 
2195  screen->UpdateLocalLibSymbolLinks();
2196 }
2197 
2198 
2200 {
2201  wxCHECK_MSG( CurTok() == T_symbol, nullptr,
2202  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
2203 
2204  T token;
2205  wxString tmp;
2206  wxString libName;
2207  SCH_FIELD* field;
2208  std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
2209  TRANSFORM transform;
2210  std::set<int> fieldIDsRead;
2211 
2212  // We'll reset this if we find a fields_autoplaced token
2213  symbol->ClearFieldsAutoplaced();
2214 
2216 
2217  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2218  {
2219  if( token != T_LEFT )
2220  Expecting( T_LEFT );
2221 
2222  token = NextTok();
2223 
2224  switch( token )
2225  {
2226  case T_lib_name:
2227  {
2228  LIB_ID libId;
2229 
2230  token = NextTok();
2231 
2232  if( !IsSymbol( token ) )
2233  {
2234  THROW_PARSE_ERROR( _( "Invalid symbol library name" ), CurSource(), CurLine(),
2235  CurLineNumber(), CurOffset() );
2236  }
2237 
2238  libName = FromUTF8();
2239  NeedRIGHT();
2240  break;
2241  }
2242 
2243  case T_lib_id:
2244  {
2245  token = NextTok();
2246 
2247  if( !IsSymbol( token ) && token != T_NUMBER )
2248  Expecting( "symbol|number" );
2249 
2250  LIB_ID libId;
2251 
2252  if( libId.Parse( FromUTF8() ) >= 0 )
2253  {
2254  THROW_PARSE_ERROR( _( "Invalid symbol library ID" ), CurSource(), CurLine(),
2255  CurLineNumber(), CurOffset() );
2256  }
2257 
2258  symbol->SetLibId( libId );
2259  NeedRIGHT();
2260  break;
2261  }
2262 
2263  case T_at:
2264  symbol->SetPosition( parseXY() );
2265 
2266  switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
2267  {
2268  case 0: transform = TRANSFORM(); break;
2269  case 90: transform = TRANSFORM( 0, -1, -1, 0 ); break;
2270  case 180: transform = TRANSFORM( -1, 0, 0, 1 ); break;
2271  case 270: transform = TRANSFORM( 0, 1, 1, 0 ); break;
2272  default: Expecting( "0, 90, 180, or 270" );
2273  }
2274 
2275  symbol->SetTransform( transform );
2276  NeedRIGHT();
2277  break;
2278 
2279  case T_mirror:
2280  token = NextTok();
2281 
2282  if( token == T_x )
2283  symbol->SetOrientation( SYM_MIRROR_X );
2284  else if( token == T_y )
2285  symbol->SetOrientation( SYM_MIRROR_Y );
2286  else
2287  Expecting( "x or y" );
2288 
2289  NeedRIGHT();
2290  break;
2291 
2292  case T_unit:
2293  symbol->SetUnit( parseInt( "symbol unit" ) );
2294  NeedRIGHT();
2295  break;
2296 
2297  case T_convert:
2298  symbol->SetConvert( parseInt( "symbol convert" ) );
2299  NeedRIGHT();
2300  break;
2301 
2302  case T_in_bom:
2303  symbol->SetIncludeInBom( parseBool() );
2304  NeedRIGHT();
2305  break;
2306 
2307  case T_on_board:
2308  symbol->SetIncludeOnBoard( parseBool() );
2309  NeedRIGHT();
2310  break;
2311 
2312  case T_fields_autoplaced:
2313  symbol->SetFieldsAutoplaced();
2314  NeedRIGHT();
2315  break;
2316 
2317  case T_uuid:
2318  NeedSYMBOL();
2319  const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
2320  NeedRIGHT();
2321  break;
2322 
2323  case T_property:
2324  // The field parent symbol must be set and its orientation must be set before
2325  // the field positions are set.
2326  field = parseSchField( symbol.get() );
2327 
2328  // It would appear that at some point we allowed duplicate ids to slip through
2329  // when writing files. The easiest (and most complete) solution is to disallow
2330  // multiple instances of the same id (for all files since the source of the error
2331  // *might* in fact be hand-edited files).
2332  //
2333  // While no longer used, -1 is still a valid id for user field. It gets converted
2334  // to the next unused number on save.
2335  if( fieldIDsRead.count( field->GetId() ) )
2336  field->SetId( -1 );
2337  else
2338  fieldIDsRead.insert( field->GetId() );
2339 
2340  // Set the default symbol reference prefix.
2341  if( field->GetId() == REFERENCE_FIELD )
2342  {
2343  wxString refDesignator = field->GetText();
2344 
2345  refDesignator.Replace( "~", " " );
2346 
2347  wxString prefix = refDesignator;
2348 
2349  while( prefix.Length() )
2350  {
2351  if( ( prefix.Last() < '0' || prefix.Last() > '9') && prefix.Last() != '?' )
2352  break;
2353 
2354  prefix.RemoveLast();
2355  }
2356 
2357  // Avoid a prefix containing trailing/leading spaces
2358  prefix.Trim( true );
2359  prefix.Trim( false );
2360 
2361  if( prefix.IsEmpty() )
2362  symbol->SetPrefix( wxString( "U" ) );
2363  else
2364  symbol->SetPrefix( prefix );
2365  }
2366 
2367  if( symbol->GetFieldById( field->GetId() ) )
2368  *symbol->GetFieldById( field->GetId() ) = *field;
2369  else
2370  symbol->AddField( *field );
2371 
2372  delete field;
2373  break;
2374 
2375  case T_pin:
2376  {
2377  // Read an alternate pin designation
2378  wxString number;
2379  KIID uuid;
2380  wxString alt;
2381 
2382  NeedSYMBOL();
2383  number = FromUTF8();
2384 
2385  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2386  {
2387  if( token != T_LEFT )
2388  Expecting( T_LEFT );
2389 
2390  token = NextTok();
2391 
2392  switch( token )
2393  {
2394  case T_alternate:
2395  NeedSYMBOL();
2396  alt = FromUTF8();
2397  NeedRIGHT();
2398  break;
2399 
2400  case T_uuid:
2401  NeedSYMBOL();
2402 
2403  // First version to write out pin uuids accidentally wrote out the symbol's
2404  // uuid for each pin, so ignore uuids coming from that version.
2405  if( m_requiredVersion >= 20210126 )
2406  uuid = parseKIID();
2407 
2408  NeedRIGHT();
2409  break;
2410 
2411  default:
2412  Expecting( "alternate or uuid" );
2413  }
2414  }
2415 
2416  symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(),
2417  number, alt ) );
2418 
2419  const_cast<KIID&>( symbol->GetRawPins().back()->m_Uuid ) = uuid;
2420  }
2421  break;
2422 
2423  default:
2424  Expecting( "lib_id, lib_name, at, mirror, uuid, property, pin, or instances" );
2425  }
2426  }
2427 
2428  if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
2429  symbol->SetSchSymbolLibraryName( libName );
2430 
2431  // Ensure edit/status flags are cleared after these initializations:
2432  symbol->ClearFlags();
2433 
2434  return symbol.release();
2435 }
2436 
2437 
2439 {
2440  wxCHECK_MSG( CurTok() == T_image, nullptr,
2441  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
2442 
2443  T token;
2444  std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
2445 
2446  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2447  {
2448  if( token != T_LEFT )
2449  Expecting( T_LEFT );
2450 
2451  token = NextTok();
2452 
2453  switch( token )
2454  {
2455  case T_at:
2456  bitmap->SetPosition( parseXY() );
2457  NeedRIGHT();
2458  break;
2459 
2460  case T_scale:
2461  bitmap->GetImage()->SetScale( parseDouble( "image scale factor" ) );
2462 
2463  if( !std::isnormal( bitmap->GetImage()->GetScale() ) )
2464  bitmap->GetImage()->SetScale( 1.0 );
2465 
2466  NeedRIGHT();
2467  break;
2468 
2469  case T_uuid:
2470  NeedSYMBOL();
2471  const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
2472  NeedRIGHT();
2473  break;
2474 
2475  case T_data:
2476  {
2477  token = NextTok();
2478 
2479  wxString data;
2480 
2481  // Reserve 128K because most image files are going to be larger than the default
2482  // 1K that wxString reserves.
2483  data.reserve( 1 << 17 );
2484 
2485  while( token != T_RIGHT )
2486  {
2487  if( !IsSymbol( token ) )
2488  Expecting( "base64 image data" );
2489 
2490  data += FromUTF8();
2491  token = NextTok();
2492  }
2493 
2494  wxMemoryBuffer buffer = wxBase64Decode( data );
2495  wxMemoryOutputStream stream( buffer.GetData(), buffer.GetBufSize() );
2496  wxImage* image = new wxImage();
2497  wxMemoryInputStream istream( stream );
2498  image->LoadFile( istream, wxBITMAP_TYPE_PNG );
2499  bitmap->GetImage()->SetImage( image );
2500  bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
2501  break;
2502  }
2503 
2504  default:
2505  Expecting( "at, scale, uuid or data" );
2506  }
2507  }
2508 
2509  return bitmap.release();
2510 }
2511 
2512 
2514 {
2515  wxCHECK_MSG( CurTok() == T_sheet, nullptr,
2516  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
2517 
2518  T token;
2520  FILL_PARAMS fill;
2521  SCH_FIELD* field;
2522  std::vector<SCH_FIELD> fields;
2523  std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
2524  std::set<int> fieldIDsRead;
2525 
2526  // We'll reset this if we find a fields_autoplaced token
2527  sheet->ClearFieldsAutoplaced();
2528 
2529  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2530  {
2531  if( token != T_LEFT )
2532  Expecting( T_LEFT );
2533 
2534  token = NextTok();
2535 
2536  switch( token )
2537  {
2538  case T_at:
2539  sheet->SetPosition( parseXY() );
2540  NeedRIGHT();
2541  break;
2542 
2543  case T_size:
2544  {
2545  wxSize size;
2546  size.SetWidth( parseInternalUnits( "sheet width" ) );
2547  size.SetHeight( parseInternalUnits( "sheet height" ) );
2548  sheet->SetSize( size );
2549  NeedRIGHT();
2550  break;
2551  }
2552 
2553  case T_fields_autoplaced:
2554  sheet->SetFieldsAutoplaced();
2555  NeedRIGHT();
2556  break;
2557 
2558  case T_stroke:
2559  parseStroke( stroke );
2560  sheet->SetBorderWidth( stroke.GetWidth() );
2561  sheet->SetBorderColor( stroke.GetColor() );
2562  break;
2563 
2564  case T_fill:
2565  parseFill( fill );
2566  sheet->SetBackgroundColor( fill.m_Color );
2567  break;
2568 
2569  case T_uuid:
2570  NeedSYMBOL();
2571  const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
2572  NeedRIGHT();
2573  break;
2574 
2575  case T_property:
2576  field = parseSchField( sheet.get() );
2577 
2578  if( m_requiredVersion <= 20200310 )
2579  {
2580  // Earlier versions had the wrong ids (and names) saved for sheet fields.
2581  // Fortunately they only saved the sheetname and sheetfilepath (and always
2582  // in that order), so we can hack in a recovery.
2583  if( fields.empty() )
2584  field->SetId( SHEETNAME );
2585  else
2586  field->SetId( SHEETFILENAME );
2587  }
2588 
2589  // It would appear the problem persists past 20200310, but this time with the
2590  // earlier ids being re-used for later (user) fields. The easiest (and most
2591  // complete) solution is to disallow multiple instances of the same id (for all
2592  // files since the source of the error *might* in fact be hand-edited files).
2593  //
2594  // While no longer used, -1 is still a valid id for user field. It gets converted
2595  // to the next unused number on save.
2596  if( fieldIDsRead.count( field->GetId() ) )
2597  field->SetId( -1 );
2598  else
2599  fieldIDsRead.insert( field->GetId() );
2600 
2601  fields.emplace_back( *field );
2602  delete field;
2603  break;
2604 
2605  case T_pin:
2606  sheet->AddPin( parseSchSheetPin( sheet.get() ) );
2607  break;
2608 
2609  default:
2610  Expecting( "at, size, stroke, background, uuid, property, or pin" );
2611  }
2612  }
2613 
2614  sheet->SetFields( fields );
2615 
2616  return sheet.release();
2617 }
2618 
2619 
2621 {
2622  wxCHECK_MSG( CurTok() == T_junction, nullptr,
2623  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
2624 
2625  T token;
2626  std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
2627 
2628  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2629  {
2630  if( token != T_LEFT )
2631  Expecting( T_LEFT );
2632 
2633  token = NextTok();
2634 
2635  switch( token )
2636  {
2637  case T_at:
2638  junction->SetPosition( parseXY() );
2639  NeedRIGHT();
2640  break;
2641 
2642  case T_diameter:
2643  junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
2644  NeedRIGHT();
2645  break;
2646 
2647  case T_color:
2648  {
2649  COLOR4D color;
2650 
2651  color.r = parseInt( "red" ) / 255.0;
2652  color.g = parseInt( "green" ) / 255.0;
2653  color.b = parseInt( "blue" ) / 255.0;
2654  color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
2655 
2656  junction->SetColor( color );
2657  NeedRIGHT();
2658  break;
2659  }
2660 
2661  case T_uuid:
2662  NeedSYMBOL();
2663  const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
2664  NeedRIGHT();
2665  break;
2666 
2667  default:
2668  Expecting( "at, diameter, color or uuid" );
2669  }
2670  }
2671 
2672  return junction.release();
2673 }
2674 
2675 
2677 {
2678  wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
2679  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
2680 
2681  T token;
2682  std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
2683 
2684  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2685  {
2686  if( token != T_LEFT )
2687  Expecting( T_LEFT );
2688 
2689  token = NextTok();
2690 
2691  switch( token )
2692  {
2693  case T_at:
2694  no_connect->SetPosition( parseXY() );
2695  NeedRIGHT();
2696  break;
2697 
2698  case T_uuid:
2699  NeedSYMBOL();
2700  const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
2701  NeedRIGHT();
2702  break;
2703 
2704  default:
2705  Expecting( "at or uuid" );
2706  }
2707  }
2708 
2709  return no_connect.release();
2710 }
2711 
2712 
2714 {
2715  wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
2716  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
2717 
2718  T token;
2720  std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
2721 
2722  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2723  {
2724  if( token != T_LEFT )
2725  Expecting( T_LEFT );
2726 
2727  token = NextTok();
2728 
2729  switch( token )
2730  {
2731  case T_at:
2732  busEntry->SetPosition( parseXY() );
2733  NeedRIGHT();
2734  break;
2735 
2736  case T_size:
2737  {
2738  wxSize size;
2739 
2740  size.SetWidth( parseInternalUnits( "bus entry height" ) );
2741  size.SetHeight( parseInternalUnits( "bus entry width" ) );
2742  busEntry->SetSize( size );
2743  NeedRIGHT();
2744  break;
2745  }
2746 
2747  case T_stroke:
2748  parseStroke( stroke );
2749  busEntry->SetStroke( stroke );
2750  break;
2751 
2752  case T_uuid:
2753  NeedSYMBOL();
2754  const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
2755  NeedRIGHT();
2756  break;
2757 
2758  default:
2759  Expecting( "at, size, uuid or stroke" );
2760  }
2761  }
2762 
2763  return busEntry.release();
2764 }
2765 
2766 
2768 {
2769  T token;
2771  int layer;
2772 
2773  switch( CurTok() )
2774  {
2775  case T_polyline: layer = LAYER_NOTES; break;
2776  case T_wire: layer = LAYER_WIRE; break;
2777  case T_bus: layer = LAYER_BUS; break;
2778  default:
2779  wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
2780  }
2781 
2782  std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( wxPoint(), layer );
2783 
2784  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2785  {
2786  if( token != T_LEFT )
2787  Expecting( T_LEFT );
2788 
2789  token = NextTok();
2790 
2791  switch( token )
2792  {
2793  case T_pts:
2794  NeedLEFT();
2795  token = NextTok();
2796 
2797  if( token != T_xy )
2798  Expecting( "xy" );
2799 
2800  line->SetStartPoint( parseXY() );
2801  NeedRIGHT();
2802  NeedLEFT();
2803  token = NextTok();
2804 
2805  if( token != T_xy )
2806  Expecting( "xy" );
2807 
2808  line->SetEndPoint( parseXY() );
2809  NeedRIGHT();
2810  NeedRIGHT();
2811  break;
2812 
2813  case T_stroke:
2814  parseStroke( stroke );
2815  line->SetStroke( stroke );
2816  break;
2817 
2818  case T_uuid:
2819  NeedSYMBOL();
2820  const_cast<KIID&>( line->m_Uuid ) = parseKIID();
2821  NeedRIGHT();
2822  break;
2823 
2824  default:
2825  Expecting( "at, uuid or stroke" );
2826  }
2827  }
2828 
2829  return line.release();
2830 }
2831 
2832 
2834 {
2835  T token;
2836  std::unique_ptr<SCH_TEXT> text;
2837 
2838  switch( CurTok() )
2839  {
2840  case T_text: text = std::make_unique<SCH_TEXT>(); break;
2841  case T_label: text = std::make_unique<SCH_LABEL>(); break;
2842  case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
2843  case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
2844  default:
2845  wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
2846  }
2847 
2848  // We'll reset this if we find a fields_autoplaced token
2849  text->ClearFieldsAutoplaced();
2850 
2851  NeedSYMBOL();
2852 
2853  text->SetText( FromUTF8() );
2854 
2855  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2856  {
2857  if( token != T_LEFT )
2858  Expecting( T_LEFT );
2859 
2860  token = NextTok();
2861 
2862  switch( token )
2863  {
2864  case T_at:
2865  text->SetPosition( parseXY() );
2866 
2867  switch( static_cast<int>( parseDouble( "text angle" ) ) )
2868  {
2869  case 0: text->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT ); break;
2870  case 90: text->SetLabelSpinStyle( LABEL_SPIN_STYLE::UP ); break;
2871  case 180: text->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT ); break;
2872  case 270: text->SetLabelSpinStyle( LABEL_SPIN_STYLE::BOTTOM ); break;
2873  default:
2874  wxFAIL;
2875  text->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );
2876  break;
2877  }
2878 
2879  NeedRIGHT();
2880  break;
2881 
2882  case T_shape:
2883  if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
2884  Unexpected( T_shape );
2885 
2886  token = NextTok();
2887 
2888  switch( token )
2889  {
2890  case T_input: text->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT ); break;
2891  case T_output: text->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT ); break;
2892  case T_bidirectional: text->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI ); break;
2893  case T_tri_state: text->SetShape( PINSHEETLABEL_SHAPE::PS_TRISTATE ); break;
2894  case T_passive: text->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED ); break;
2895  default:
2896  Expecting( "input, output, bidirectional, tri_state, or passive" );
2897  }
2898 
2899  NeedRIGHT();
2900  break;
2901 
2902  case T_fields_autoplaced:
2903  text->SetFieldsAutoplaced();
2904  NeedRIGHT();
2905  break;
2906 
2907  case T_effects:
2908  parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
2909 
2910  // Spin style is defined differently for graphical text (#SCH_TEXT) objects.
2911  if( text->Type() == SCH_TEXT_T )
2912  {
2913  if( text->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT
2914  && text->GetTextAngle() == TEXT_ANGLE_VERT )
2915  {
2916  // The vertically aligned text angle is always 90 (labels use 270 for the
2917  // down direction) combined with the text justification flags.
2918  text->SetLabelSpinStyle( LABEL_SPIN_STYLE::BOTTOM );
2919  }
2920  else if( text->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT
2921  && text->GetTextAngle() == TEXT_ANGLE_HORIZ )
2922  {
2923  // The horizontally aligned text angle is always 0 (labels use 180 for the
2924  // left direction) combined with the text justification flags.
2925  text->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
2926  }
2927  }
2928 
2929  break;
2930 
2931  case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
2932  if( text->Type() == SCH_GLOBAL_LABEL_T )
2933  {
2934  SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
2935  SCH_FIELD* field = label->GetIntersheetRefs();
2936 
2937  field->SetTextPos( parseXY() );
2938  NeedRIGHT();
2939 
2940  field->SetVisible( true );
2941  }
2942  break;
2943 
2944  case T_uuid:
2945  NeedSYMBOL();
2946  const_cast<KIID&>( text->m_Uuid ) = parseKIID();
2947  NeedRIGHT();
2948  break;
2949 
2950  case T_property:
2951  if( text->Type() == SCH_GLOBAL_LABEL_T )
2952  {
2953  SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
2954  SCH_FIELD* field = parseSchField( label );
2955 
2956  field->SetLayer( LAYER_GLOBLABEL );
2957  label->SetIntersheetRefs( *field );
2958 
2959  delete field;
2960  }
2961  break;
2962 
2963  default:
2964  Expecting( "at, shape, iref, uuid or effects" );
2965  }
2966  }
2967 
2968  return text.release();
2969 }
2970 
2971 
2973 {
2974  wxCHECK_RET( CurTok() == T_bus_alias,
2975  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
2976  wxCHECK( aScreen, /* void */ );
2977 
2978  T token;
2979  std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>( aScreen );
2980  wxString alias;
2981  wxString member;
2982 
2983  NeedSYMBOL();
2984 
2985  alias = FromUTF8();
2986 
2987  if( m_requiredVersion < 20210621 )
2988  alias = ConvertToNewOverbarNotation( alias );
2989 
2990  busAlias->SetName( alias );
2991 
2992  NeedLEFT();
2993  token = NextTok();
2994 
2995  if( token != T_members )
2996  Expecting( "members" );
2997 
2998  token = NextTok();
2999 
3000  while( token != T_RIGHT )
3001  {
3002  if( !IsSymbol( token ) )
3003  Expecting( "quoted string" );
3004 
3005  member = FromUTF8();
3006 
3007  if( m_requiredVersion < 20210621 )
3008  member = ConvertToNewOverbarNotation( member );
3009 
3010  busAlias->AddMember( member );
3011 
3012  token = NextTok();
3013  }
3014 
3015  NeedRIGHT();
3016 
3017  aScreen->AddBusAlias( busAlias );
3018 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:209
Field Reference of part, i.e. "IC21".
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
int m_fieldId
The current field ID.
power input (GND, VCC for ICs). Must be connected to a power output.
#define TEXT_ANGLE_HORIZ
Frequent text rotations, used with {Set,Get}TextAngle(), in 0.1 degrees for now, hoping to migrate to...
Definition: eda_text.h:71
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
LIB_FIELD * parseProperty(std::unique_ptr< LIB_SYMBOL > &aSymbol)
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
Symbol map used by symbol library object.
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:80
void SetWidth(int aWidth)
Definition: sch_item.h:153
Schematic and symbol library s-expression file format parser definitions.
std::vector< SYMBOL_INSTANCE_REFERENCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition: sch_screen.h:571
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
void SetRevision(const wxString &aRevision)
Definition: title_block.h:81
wxString GetName() const override
Definition: lib_symbol.h:133
Define a symbol library graphical text item.
Definition: lib_text.h:39
SCH_SHEET_PIN * parseSchSheetPin(SCH_SHEET *aSheet)
static constexpr double IU_PER_MM
Mock up a conversion function.
A progress reporter interface for use in multi-threaded environments.
void parseBusAlias(SCH_SCREEN *aScreen)
void parseEDA_TEXT(EDA_TEXT *aText, bool aConvertOverbarSyntax)
void SetItalic(bool isItalic)
Definition: eda_text.h:200
int color
Definition: DXF_plotter.cpp:57
Field object used in symbol libraries.
Definition: lib_field.h:59
unsigned m_lastProgressLine
pin for passive symbols: must be connected, and can be connected to any pin
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:267
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:119
LIB_SYMBOL * ParseSymbol(LIB_SYMBOL_MAP &aSymbolLibMap, int aFileVersion=SEXPR_SYMBOL_LIB_FILE_VERSION)
int GetId() const
Definition: sch_field.h:113
int m_unit
The current unit being parsed.
void parseTITLE_BLOCK(TITLE_BLOCK &aTitleBlock)
unknown electrical properties: creates always a warning when connected
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition: title_block.h:71
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: sch_screen.h:133
void SetPlotStyle(PLOT_DASH_TYPE aPlotStyle)
Definition: sch_item.h:156
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:77
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:258
Definition: lib_pin.h:48
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:290
SCH_SYMBOL * parseSchematicSymbol()
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
GRAPHIC_PINSHAPE
Definition: pin_type.h:55
#define MIN_PAGE_SIZE_MILS
Min and max page sizes for clamping, in mils.
Definition: page_info.h:39
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:102
Define a library symbol object.
Definition: lib_symbol.h:96
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:40
ELECTRICAL_PINTYPE m_Type
Definition: lib_pin.h:60
void parseSchSheetInstances(SCH_SHEET *aRootSheet, SCH_SCREEN *aScreen)
void parseSchSymbolInstances(SCH_SCREEN *aScreen)
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:115
SCH_BUS_WIRE_ENTRY * parseBusEntry()
The base class for drawable items used by schematic library symbols.
Definition: lib_item.h:61
int Mm2mils(double x)
Convert mm to mils.
Definition: base_units.cpp:56
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:140
LIB_SHAPE * parsePolyLine()
not internally connected (may be connected to anything)
virtual void SetVisible(bool aVisible)
Definition: eda_text.h:206
Definition: kiid.h:44
Simple container to manage fill parameters.
#define MAX_PAGE_SIZE_EESCHEMA_MILS
Definition: page_info.h:41
void UpdateLocalLibSymbolLinks()
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in this schematic with the local projec...
Definition: sch_screen.cpp:764
for transforming drawing coordinates for a wxDC device context.
Definition: transform.h:45
void parseHeader(TSCHEMATIC_T::T aHeaderType, int aFileVersion)
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
void ParseLib(LIB_SYMBOL_MAP &aSymbolLibMap)
Field Value of part, i.e. "3.3K".
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:124
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:101
wxString m_symbolName
The current symbol name.
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition: richio.h:135
SCH_JUNCTION * parseJunction()
int m_convert
The current body style being parsed.
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition: sch_screen.h:572
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:53
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:223
LIB_SHAPE * parseCircle()
int UpdateFieldOrdinals()
Order optional field indices.
SCH_NO_CONNECT * parseNoConnect()
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
Definition: lib_field.cpp:402
void SetIntersheetRefs(const SCH_FIELD &aField)
Definition: sch_text.h:416
void SetCompany(const wxString &aCompany)
Definition: title_block.h:91
wxString m_Name
Definition: lib_pin.h:58
A simple container for schematic symbol instance information.
#define _(s)
LIB_SHAPE * parseArc()
const LINE_READER * m_lineReader
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:65
int GetWidth() const
Definition: sch_item.h:152
void SetFileFormatVersionAtLoad(int aVersion)
Definition: sch_screen.h:129
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: sch_screen.h:158
void SetTitle(const wxString &aTitle)
Definition: title_block.h:58
std::set< KIID > m_uuids
Object to handle a bitmap image that can be inserted in a schematic.
Definition: sch_bitmap.h:40
void SetHeightMils(int aHeightInMils)
Definition: page_info.cpp:257
wxString UnescapeString(const wxString &aSource)
SCH_SEXPR_PARSER(LINE_READER *aLineReader=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr, unsigned aLineCount=0)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:54
void SetContentModified(bool aModified=true)
Definition: base_screen.h:59
The first 4 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
SCH_FIELD * GetIntersheetRefs()
Definition: sch_text.h:415
void AddLibSymbol(LIB_SYMBOL *aLibSymbol)
Add aLibSymbol to the library symbol map.
LIB_ITEM * ParseDrawItem()
COLOR4D GetColor() const
Definition: sch_item.h:158
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:49
A simple container for sheet instance information.
void parsePAGE_INFO(PAGE_INFO &aPageInfo)
std::set< int > m_fieldIDsRead
Field IDs that have been read so far for the current symbol.
Schematic symbol object.
Definition: sch_symbol.h:78
int m_requiredVersion
Set to the symbol library file version required.
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
Add a bus alias definition (and transfers ownership of the pointer).
const char * name
Definition: DXF_plotter.cpp:56
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition: pin_type.h:35
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:146
SCH_BITMAP * parseImage()
void parsePinNames(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:222
Simple container to manage line stroke parameters.
Definition: sch_item.h:140
usual pin input: must be connected
KIID m_uuid
A unique identifier for each schematic file.
Definition: sch_screen.h:580
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void SetWidthMils(int aWidthInMils)
Definition: page_info.cpp:243
SCH_FIELD * parseSchField(SCH_ITEM *aParent)
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
const T & Clamp(const T &lower, const T &value, const T &upper)
Limit value within the range lower <= value <= upper.
Definition: util.h:52
void parseFill(FILL_PARAMS &aFill)
void ParseSchematic(SCH_SHEET *aSheet, bool aIsCopyablyOnly=false, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
Parse the internal LINE_READER object into aSheet.
Class for a wire to bus entry.
SCH_TEXT * parseSchText()
void SetColor(const COLOR4D &aColor)
Definition: sch_item.h:159
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
input or output (like port for a microprocessor)
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:180
double parseDouble()
Parse the current token as an ASCII numeric string with possible leading whitespace into a double pre...
#define TEXT_ANGLE_VERT
Definition: eda_text.h:72
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:174
void SetId(int aId)
Definition: sch_field.cpp:80
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
not connected (must be left open)
LIB_SHAPE * parseRectangle()
void SetBold(bool aBold)
Definition: eda_text.h:203
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:186
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:182
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
output of a regulator: intended to be connected to power input pins
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:154
GRAPHIC_PINSHAPE m_Shape
Definition: lib_pin.h:59
void parseStroke(STROKE_PARAMS &aStroke)
Parse stroke definition aStroke.
LIB_SHAPE * parseBezier()
PROGRESS_REPORTER * m_progressReporter
SCH_SHEET * parseSheet()
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).