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