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