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