KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbnew/netlist_reader/kicad_netlist_reader.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) 1992-2011 Jean-Pierre Charras.
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <wx/log.h>
22
23#include <netlist_lexer.h> // netlist_lexer is common to Eeschema and Pcbnew
24#include <string_utils.h>
25#include <json_common.h>
26
27#include "pcb_netlist.h"
28#include "netlist_reader.h"
30
31using namespace NL_T;
32
33
35{
36 KICAD_NETLIST_PARSER parser( m_lineReader, m_netlist );
37
38 parser.Parse();
39
41 {
43
44 // Sort the component pins so they are in the same order as the legacy format. This
45 // is useful for comparing legacy and s-expression netlist dumps.
46 for( unsigned i = 0; i < m_netlist->GetCount(); i++ )
47 m_netlist->GetComponent( i )->SortPins();
48 }
49}
50
51
52// KICAD_NETLIST_PARSER
54 NETLIST_LEXER( aReader )
55{
56 m_lineReader = aReader;
57 m_netlist = aNetlist;
58 token = T_NONE;
59}
60
61
63{
64 int curr_level = 0;
65
66 while( ( token = NextTok() ) != T_EOF )
67 {
68 if( token == T_LEFT )
69 curr_level--;
70
71 if( token == T_RIGHT )
72 {
73 curr_level++;
74
75 if( curr_level > 0 )
76 return;
77 }
78 }
79}
80
81
83{
84 int plevel = 0; // the count of ')' to read at end of file after parsing all sections
85
86 while( ( token = NextTok() ) != T_EOF )
87 {
88 if( token == T_LEFT )
89 token = NextTok();
90
91 switch( token )
92 {
93 case T_export: // The netlist starts here.
94 // nothing to do here, just increment the count of ')' to read at end of file
95 plevel++;
96 break;
97
98 case T_version: // The netlist starts here.
99 // version id not yet used: read it but does not use it
100 NextTok();
101 NeedRIGHT();
102 break;
103
104 case T_components: // The section comp starts here.
105 wxLogTrace( "CVPCB_PINCOUNT", wxT( "Parse: entering components section" ) );
106
107 while( ( token = NextTok() ) != T_EOF )
108 {
109 if( token == T_RIGHT )
110 break;
111 else if( token == T_LEFT )
112 token = NextTok();
113
114 if( token == T_comp ) // A component section found. Read it
116 }
117
118 break;
119
120 case T_groups: // The section groups starts here.
121 while( ( token = NextTok() ) != T_EOF )
122 {
123 if( token == T_RIGHT )
124 break;
125 else if( token == T_LEFT )
126 token = NextTok();
127
128 if( token == T_group ) // A group section found. Read it
129 parseGroup();
130 }
131
132 break;
133
134 case T_variants: // The section variants starts here.
135 while( ( token = NextTok() ) != T_EOF )
136 {
137 if( token == T_RIGHT )
138 break;
139 else if( token == T_LEFT )
140 token = NextTok();
141
142 if( token == T_variant ) // A variant section found. Read it
143 parseVariant();
144 }
145
146 break;
147
148 case T_nets: // The section nets starts here.
149 wxLogTrace( "CVPCB_PINCOUNT", wxT( "Parse: entering nets section" ) );
150
151 while( ( token = NextTok() ) != T_EOF )
152 {
153 if( token == T_RIGHT )
154 break;
155 else if( token == T_LEFT )
156 token = NextTok();
157
158 if( token == T_net ) // A net section if found. Read it
159 parseNet();
160 }
161
162 break;
163
164 case T_net_chains:
165 while( ( token = NextTok() ) != T_EOF )
166 {
167 if( token == T_RIGHT )
168 break;
169 else if( token == T_LEFT )
170 token = NextTok();
171
172 if( token == T_net_chain )
174 }
175
176 break;
177
178 case T_libparts: // The section libparts starts here.
179 wxLogTrace( "CVPCB_PINCOUNT", wxT( "Parse: entering libparts section" ) );
180
181 while( ( token = NextTok() ) != T_EOF )
182 {
183 if( token == T_RIGHT )
184 break;
185 else if( token == T_LEFT )
186 token = NextTok();
187
188 if( token == T_libpart ) // A libpart section if found. Read it
190 }
191
192 break;
193
194 case T_libraries: // The section libraries starts here.
195 // List of libraries in use.
196 // Not used here, just skip it
197 skipCurrent();
198 break;
199
200 case T_design: // The section design starts here.
201 // Not used (mainly they are comments), just skip it
202 skipCurrent();
203 break;
204
205 case T_RIGHT: // The closing parenthesis of the file.
206 plevel--;
207 break;
208
209 default:
210 skipCurrent();
211 break;
212 }
213 }
214
215 // Go back and apply group information to the components
216 m_netlist->ApplyGroupMembership();
217
218 if( plevel != 0 )
219 {
220 wxFAIL_MSG( wxString::Format( wxT( "KICAD_NETLIST_PARSER::Parse(): bad parenthesis "
221 "count (count = %d" ),
222 plevel ) );
223 }
224}
225
226
228{
229 /* Parses a section like
230 * (net (code 20) (name /PC-A0)
231 * (node (ref "BUS1") (pin "62)")
232 * (node (ref "U3") ("pin 3") (pin_function "clock"))
233 * (node (ref "U9") (pin "M6") (pin_function "reset")))
234 */
235
236 wxString code;
237 wxString name;
238 wxString reference;
239 wxString pin_number;
240 wxString pin_function;
241 wxString pin_type;
242
243 // The token net was read, so the next data is (code <number>)
244 while( (token = NextTok() ) != T_EOF )
245 {
246 if( token == T_RIGHT )
247 break;
248 else if( token == T_LEFT )
249 token = NextTok();
250
251 switch( token )
252 {
253 case T_code:
254 NeedSYMBOLorNUMBER();
255 code = From_UTF8( CurText() );
256 NeedRIGHT();
257 break;
258
259 case T_name:
260 NeedSYMBOLorNUMBER();
261 name = From_UTF8( CurText() );
262 NeedRIGHT();
263 break;
264
265 case T_node:
266 // By default: no pin function or type.
267 pin_function.Clear();
268 pin_type.Clear();
269
270 while( (token = NextTok() ) != T_EOF )
271 {
272 if( token == T_RIGHT )
273 break;
274 else if( token == T_LEFT )
275 token = NextTok();
276
277 switch( token )
278 {
279 case T_ref:
280 NeedSYMBOLorNUMBER();
281 reference = From_UTF8( CurText() );
282 NeedRIGHT();
283 break;
284
285 case T_pin:
286 NeedSYMBOLorNUMBER();
287 pin_number = From_UTF8( CurText() );
288 NeedRIGHT();
289 break;
290
291 case T_pinfunction:
292 NeedSYMBOLorNUMBER();
293 pin_function = From_UTF8( CurText() );
294 NeedRIGHT();
295 break;
296
297 case T_pintype:
298 NeedSYMBOLorNUMBER();
299 pin_type = From_UTF8( CurText() );
300 NeedRIGHT();
301 break;
302
303 default:
304 skipCurrent();
305 break;
306 }
307 }
308
309 // Don't assume component will be found; it might be "DNP" or "Exclude from board".
310 if( COMPONENT* component = m_netlist->GetComponentByReference( reference ) )
311 {
312 if( strtol( code.c_str(), nullptr, 10 ) >= 1 )
313 {
314 if( name.IsEmpty() ) // Give a dummy net name like N-000009
315 name = wxT("N-00000") + code;
316
317 component->AddNet( pin_number, name, pin_function, pin_type );
318 }
319 }
320
321 break;
322
323 default:
324 skipCurrent();
325 break;
326 }
327 }
328}
329
330
332{
333 /* Parses a section like
334 * (comp (ref P1)
335 * (value DB25FEMALE)
336 * (footprint DB25FC)
337 * (libsource (lib conn) (part DB25))
338 * (property (name PINCOUNT) (value 25))
339 * (sheetpath (names /) (tstamps /))
340 * (component_classes (class (name "CLASS")))
341 * (tstamp 68183921-93a5-49ac-91b0-49d05a0e1647))
342 *
343 * other fields (unused) are skipped
344 * A component need a reference, value, footprint name and a full time stamp
345 * The full time stamp is the sheetpath time stamp + the component time stamp
346 */
347 LIB_ID fpid;
348 wxString footprint;
349 wxString ref;
350 wxString value;
351 wxString library;
352 wxString name;
353 wxString humanSheetPath;
354 KIID_PATH path;
355
356 std::vector<KIID> uuids;
357 std::map<wxString, wxString> properties;
358 nlohmann::ordered_map<wxString, wxString> fields;
359 std::unordered_set<wxString> componentClasses;
360
361 bool duplicatePinsAreJumpers = false;
362 JUMPER_GROUP_SET jumperPinGroups;
363
364 std::vector<COMPONENT::UNIT_INFO> parsedUnits;
365 std::vector<COMPONENT_VARIANT> parsedVariants;
366
367 // The token comp was read, so the next data is (ref P1)
368 while( (token = NextTok() ) != T_RIGHT )
369 {
370 if( token == T_LEFT )
371 token = NextTok();
372
373 switch( token )
374 {
375 case T_ref:
376 NeedSYMBOLorNUMBER();
377 ref = From_UTF8( CurText() );
378 NeedRIGHT();
379 break;
380
381 case T_value:
382 NeedSYMBOLorNUMBER();
383 value = From_UTF8( CurText() );
384 NeedRIGHT();
385 break;
386
387 case T_footprint:
388 NeedSYMBOLorNUMBER();
389 footprint = FromUTF8();
390 NeedRIGHT();
391 break;
392
393 case T_libsource:
394 // Read libsource
395 while( ( token = NextTok() ) != T_RIGHT )
396 {
397 if( token == T_LEFT )
398 token = NextTok();
399
400 if( token == T_lib )
401 {
402 NeedSYMBOLorNUMBER();
403 library = From_UTF8( CurText() );
404 NeedRIGHT();
405 }
406 else if( token == T_part )
407 {
408 NeedSYMBOLorNUMBER();
409 name = From_UTF8( CurText() );
410 NeedRIGHT();
411 }
412 else if( token == T_description )
413 {
414 NeedSYMBOLorNUMBER();
415 NeedRIGHT();
416 }
417 else
418 {
419 Expecting( "part, lib or description" );
420 }
421 }
422 wxLogTrace( "CVPCB_PINCOUNT", wxT( "parseComponent: ref='%s' libsource='%s:%s'" ),
423 ref, library, name );
424 break;
425
426 case T_property:
427 {
428 wxString propName;
429 wxString propValue;
430
431 while( (token = NextTok() ) != T_RIGHT )
432 {
433 if( token == T_LEFT )
434 token = NextTok();
435
436 if( token == T_name )
437 {
438 NeedSYMBOLorNUMBER();
439 propName = From_UTF8( CurText() );
440 NeedRIGHT();
441 }
442 else if( token == T_value )
443 {
444 NeedSYMBOLorNUMBER();
445 propValue = From_UTF8( CurText() );
446 NeedRIGHT();
447 }
448 else
449 {
450 Expecting( "name or value" );
451 }
452 }
453
454 if( !propName.IsEmpty() )
455 properties[propName] = std::move( propValue );
456 break;
457 }
458
459 case T_fields:
460 while( ( token = NextTok() ) != T_RIGHT )
461 {
462 if( token == T_LEFT )
463 token = NextTok();
464
465 if( token == T_field )
466 {
467 wxString fieldName;
468 wxString fieldValue;
469
470 while( ( token = NextTok() ) != T_RIGHT )
471 {
472 if( token == T_LEFT )
473 token = NextTok();
474
475 if( token == T_name )
476 {
477 NeedSYMBOLorNUMBER();
478 fieldName = From_UTF8( CurText() );
479 NeedRIGHT();
480 }
481 else if( token == T_STRING )
482 {
483 fieldValue = From_UTF8( CurText() );
484 }
485 }
486
487 if( !fieldName.IsEmpty() )
488 fields[fieldName] = std::move( fieldValue );
489 }
490 else
491 {
492 Expecting( "field" );
493 }
494 }
495 break;
496
497 case T_sheetpath:
498 while( ( token = NextTok() ) != T_EOF )
499 {
500 if( token == T_names )
501 {
502 NeedSYMBOLorNUMBER();
503 humanSheetPath = From_UTF8( CurText() );
504 NeedRIGHT();
505 }
506
507 if( token == T_tstamps )
508 {
509 NeedSYMBOLorNUMBER();
510 path = KIID_PATH( From_UTF8( CurText() ) );
511 NeedRIGHT();
512 break;
513 }
514 }
515
516 NeedRIGHT();
517
518 break;
519
520 case T_tstamps:
521 while( ( token = NextTok() ) != T_EOF )
522 {
523 if( token == T_RIGHT )
524 break;
525
526 uuids.emplace_back( From_UTF8( CurText() ) );
527 }
528
529 break;
530
531 case T_units:
532 {
533 // Parse a section like:
534 // (units (unit (ref "U1A") (name "A") (pins (pin "1") (pin "2"))))
535 while( ( token = NextTok() ) != T_RIGHT )
536 {
537 if( token == T_LEFT )
538 token = NextTok();
539
540 if( token == T_unit )
541 {
542 COMPONENT::UNIT_INFO info;
543
544 while( ( token = NextTok() ) != T_RIGHT )
545 {
546 if( token == T_LEFT )
547 token = NextTok();
548
549 switch( token )
550 {
551 case T_name:
552 NeedSYMBOLorNUMBER();
553 info.m_unitName = From_UTF8( CurText() );
554 NeedRIGHT();
555 break;
556
557 case T_pins:
558 while( ( token = NextTok() ) != T_RIGHT )
559 {
560 if( token == T_LEFT )
561 token = NextTok();
562
563 if( token == T_pin )
564 {
565 wxString pinNum;
566
567 // Parse pins in attribute style: (pin (num "1"))
568 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
569 {
570 if( token == T_LEFT )
571 token = NextTok();
572
573 if( token == T_num )
574 {
575 NeedSYMBOLorNUMBER();
576 pinNum = From_UTF8( CurText() );
577 NeedRIGHT();
578 }
579 else
580 {
581 // ignore other subfields of pin
582 // leave bare tokens untouched; they are not supported in this context
583 }
584 }
585
586 if( !pinNum.IsEmpty() )
587 info.m_pins.emplace_back( pinNum );
588 }
589 else
590 {
591 skipCurrent();
592 }
593 }
594 break;
595
596 default:
597 skipCurrent();
598 break;
599 }
600 }
601
602 parsedUnits.push_back( info );
603 }
604 else
605 {
606 skipCurrent();
607 }
608 }
609
610 break;
611 }
612
613 case T_component_classes:
614 while( ( token = NextTok() ) != T_RIGHT )
615 {
616 if( token != T_LEFT )
617 Expecting( T_LEFT );
618
619 if( ( token = NextTok() ) != T_class )
620 Expecting( T_class );
621
622 NeedSYMBOLorNUMBER();
623 componentClasses.insert( From_UTF8( CurText() ) );
624 NeedRIGHT();
625 }
626
627 break;
628
629 case T_duplicate_pin_numbers_are_jumpers:
630 {
631 NeedSYMBOLorNUMBER();
632 duplicatePinsAreJumpers = From_UTF8( CurText() ) == wxT( "1" );
633 NeedRIGHT();
634 break;
635 }
636
637 case T_jumper_pin_groups:
638 {
639 std::set<wxString> currentNames;
640 bool inGroup = false;
641
642 for( token = NextTok(); inGroup || token != T_RIGHT; token = NextTok() )
643 {
644 if( token == T_LEFT )
645 token = NextTok();
646
647 switch( token )
648 {
649 case T_group:
650 inGroup = true;
651 break;
652
653 case T_pin:
654 {
655 NeedSYMBOLorNUMBER();
656 wxString padName = From_UTF8( CurText() );
657 NeedRIGHT();
658 wxCHECK2( inGroup, continue );
659 currentNames.insert( padName );
660 break;
661 }
662
663 case T_RIGHT:
664 if( std::optional<JUMPER_GROUP> group = JUMPER_GROUP::Make( std::move( currentNames ) ) )
665 jumperPinGroups.Add( std::move( *group ) );
666
667 currentNames.clear();
668 inGroup = false;
669 break;
670
671 default:
672 Expecting( "group or pin" );
673 }
674 }
675
676 break;
677 }
678
679 case T_variants:
680 {
681 while( ( token = NextTok() ) != T_RIGHT )
682 {
683 if( token == T_LEFT )
684 token = NextTok();
685
686 if( token != T_variant )
687 {
688 skipCurrent();
689 continue;
690 }
691
692 COMPONENT_VARIANT variant;
693
694 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
695 {
696 if( token == T_LEFT )
697 token = NextTok();
698
699 switch( token )
700 {
701 case T_name:
702 NeedSYMBOLorNUMBER();
703 variant.m_name = From_UTF8( CurText() );
704 NeedRIGHT();
705 break;
706
707 case T_property:
708 {
709 wxString propName;
710 wxString propValue;
711 bool hasValue = false;
712
713 while( ( token = NextTok() ) != T_RIGHT )
714 {
715 if( token == T_LEFT )
716 token = NextTok();
717
718 if( token == T_name )
719 {
720 NeedSYMBOLorNUMBER();
721 propName = From_UTF8( CurText() );
722 NeedRIGHT();
723 }
724 else if( token == T_value )
725 {
726 NeedSYMBOLorNUMBER();
727 propValue = From_UTF8( CurText() );
728 hasValue = true;
729 NeedRIGHT();
730 }
731 else
732 {
733 Expecting( "name or value" );
734 }
735 }
736
737 if( propName.IsEmpty() )
738 break;
739
740 bool propBool = true;
741
742 if( hasValue )
743 {
744 wxString normalized = propValue;
745 normalized.MakeLower();
746
747 if( normalized == wxT( "0" ) || normalized == wxT( "false" ) )
748 propBool = false;
749 else if( normalized == wxT( "1" ) || normalized == wxT( "true" ) )
750 propBool = true;
751 else
752 propBool = !propValue.IsEmpty();
753 }
754
755 if( propName.CmpNoCase( wxT( "dnp" ) ) == 0 )
756 {
757 variant.m_dnp = propBool;
758 variant.m_hasDnp = true;
759 }
760 else if( propName.CmpNoCase( wxT( "exclude_from_bom" ) ) == 0 )
761 {
762 variant.m_excludedFromBOM = propBool;
763 variant.m_hasExcludedFromBOM = true;
764 }
765 else if( propName.CmpNoCase( wxT( "exclude_from_sim" ) ) == 0 )
766 {
767 variant.m_excludedFromSim = propBool;
768 variant.m_hasExcludedFromSim = true;
769 }
770 else if( propName.CmpNoCase( wxT( "exclude_from_pos_files" ) ) == 0 )
771 {
772 variant.m_excludedFromPosFiles = propBool;
773 variant.m_hasExcludedFromPosFiles = true;
774 }
775
776 break;
777 }
778
779 case T_fields:
780 while( ( token = NextTok() ) != T_RIGHT )
781 {
782 if( token == T_LEFT )
783 token = NextTok();
784
785 if( token == T_field )
786 {
787 wxString fieldName;
788 wxString fieldValue;
789
790 while( ( token = NextTok() ) != T_RIGHT )
791 {
792 if( token == T_LEFT )
793 token = NextTok();
794
795 if( token == T_name )
796 {
797 NeedSYMBOLorNUMBER();
798 fieldName = From_UTF8( CurText() );
799 NeedRIGHT();
800 }
801 else if( token == T_value )
802 {
803 NeedSYMBOLorNUMBER();
804 fieldValue = From_UTF8( CurText() );
805 NeedRIGHT();
806 }
807 else if( token == T_STRING )
808 {
809 fieldValue = From_UTF8( CurText() );
810 }
811 }
812
813 if( !fieldName.IsEmpty() )
814 variant.m_fields[fieldName] = std::move( fieldValue );
815 }
816 else
817 {
818 Expecting( "field" );
819 }
820 }
821 break;
822
823 default:
824 skipCurrent();
825 break;
826 }
827 }
828
829 if( !variant.m_name.IsEmpty() )
830 parsedVariants.push_back( std::move( variant ) );
831 }
832
833 break;
834 }
835
836 default:
837 // Skip not used data (i.e all other tokens)
838 skipCurrent();
839 break;
840 }
841 }
842
843 if( !footprint.IsEmpty() && fpid.Parse( footprint, true ) >= 0 )
844 {
845 THROW_IO_ERRORF( _( "Invalid footprint ID in\nfile: '%s'\nline: %d\noffset: %d" ),
846 CurSource(), CurLineNumber(), CurOffset() );
847 }
848
849 COMPONENT* component = new COMPONENT( fpid, ref, value, path, uuids );
850 component->SetName( name );
851 component->SetLibrary( library );
852 component->SetProperties( properties );
853 component->SetFields( fields );
854 component->SetHumanReadablePath( humanSheetPath );
855 component->SetComponentClassNames( componentClasses );
856 component->SetDuplicatePadNumbersAreJumpers( duplicatePinsAreJumpers );
857 component->JumperPadGroups() = std::move( jumperPinGroups );
858 component->SetUnitInfo( parsedUnits );
859
860 for( const COMPONENT_VARIANT& variant : parsedVariants )
861 component->AddVariant( variant );
862
863 m_netlist->AddComponent( component );
864}
865
866
868{
869 /* Parses a section like
870 * (groups
871 * (group (name "") (lib_id "DesignBlock:Block") (uuid "/f1b49c40-.../7b1488be-...")
872 * (members
873 * (member (uuid "/f1b49c40-.../dfef752d-..."))
874 *
875 * Group and member uuids are full instance paths (sheet path + item uuid), so a group on
876 * a shared sheet resolves to one group per sheet instance. Bare uuids are also accepted.
877 */
878
879 wxString name;
880 KIID uuid;
881 wxString libId; // Design block library link
882 std::vector<KIID_PATH> members;
883
884 // The token net was read, so the next data is (code <number>)
885 while( (token = NextTok() ) != T_EOF )
886 {
887 if( token == T_RIGHT )
888 break;
889 else if( token == T_LEFT )
890 token = NextTok();
891
892 switch( token )
893 {
894 case T_name:
895 NeedSYMBOLorNUMBER();
896 name = From_UTF8( CurText() );
897 NeedRIGHT();
898 break;
899
900 case T_uuid:
901 {
902 NeedSYMBOLorNUMBER();
903 wxString uuidStr = From_UTF8( CurText() );
904
905 // An instance path collapses to a deterministic uuid so re-reading the same
906 // netlist finds the same board group.
907 if( uuidStr.StartsWith( wxT( "/" ) ) )
908 uuid = KIID::FromName( std::string( KIID_PATH( uuidStr ).AsString().ToUTF8() ) );
909 else
910 uuid = uuidStr;
911
912 NeedRIGHT();
913 break;
914 }
915
916 case T_lib_id:
917 NeedSYMBOLorNUMBER();
918 libId = FromUTF8();
919 NeedRIGHT();
920 break;
921
922 case T_members:
923 while( ( token = NextTok() ) != T_RIGHT )
924 {
925 if( token == T_LEFT )
926 token = NextTok();
927
928 if( token == T_member )
929 {
930 wxString memberUuid;
931
932 while( ( token = NextTok() ) != T_RIGHT )
933 {
934 if( token == T_LEFT )
935 token = NextTok();
936
937 if( token == T_uuid )
938 {
939 NeedSYMBOLorNUMBER();
940 memberUuid = From_UTF8( CurText() );
941 NeedRIGHT();
942 }
943 else
944 {
945 Expecting( "uuid" );
946 }
947 }
948
949 members.emplace_back( memberUuid );
950 }
951 else
952 {
953 Expecting( "member" );
954 }
955
956 }
957 break;
958
959 default:
960 skipCurrent();
961 break;
962 }
963 }
964
965 LIB_ID groupLibId;
966
967 if( !libId.IsEmpty() && groupLibId.Parse( libId, true ) >= 0 )
968 {
969 THROW_IO_ERRORF( _( "Invalid lib_id ID in\nfile: '%s'\nline: %d\noffset: %d" ),
970 CurSource(), CurLineNumber(), CurOffset() );
971 }
972
973 NETLIST_GROUP* group = new NETLIST_GROUP{ std::move( name ), std::move( uuid ), std::move( groupLibId ),
974 std::move( members ) };
975 m_netlist->AddGroup( group );
976}
977
978
980{
981 // Parses a variant section like:
982 // (variant (name "Variant1") (description "First variant"))
983
984 wxString name;
985 wxString description;
986
987 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
988 {
989 if( token == T_LEFT )
990 token = NextTok();
991
992 switch( token )
993 {
994 case T_name:
995 NeedSYMBOLorNUMBER();
996 name = From_UTF8( CurText() );
997 NeedRIGHT();
998 break;
999
1000 case T_description:
1001 NeedSYMBOLorNUMBER();
1002 description = From_UTF8( CurText() );
1003 NeedRIGHT();
1004 break;
1005
1006 default:
1007 skipCurrent();
1008 break;
1009 }
1010 }
1011
1012 if( !name.IsEmpty() )
1013 m_netlist->AddVariant( name, description );
1014}
1015
1016
1018{
1019 wxString name;
1020 wxString netClass;
1021 wxString chainClass;
1022 wxString colorStr;
1023 std::vector<wxString> members;
1024 std::vector<std::pair<wxString, wxString>> terminals;
1025
1026 while( ( token = NextTok() ) != T_EOF )
1027 {
1028 if( token == T_RIGHT )
1029 break;
1030 else if( token == T_LEFT )
1031 token = NextTok();
1032
1033 switch( token )
1034 {
1035 case T_name:
1036 NeedSYMBOLorNUMBER();
1037 name = From_UTF8( CurText() );
1038 NeedRIGHT();
1039 break;
1040
1041 case T_net_class:
1042 NeedSYMBOLorNUMBER();
1043 netClass = From_UTF8( CurText() );
1044 NeedRIGHT();
1045 break;
1046
1047 case T_net_chain_class:
1048 NeedSYMBOLorNUMBER();
1049 chainClass = From_UTF8( CurText() );
1050 NeedRIGHT();
1051 break;
1052
1053 case T_color:
1054 NeedSYMBOLorNUMBER();
1055 colorStr = From_UTF8( CurText() );
1056 NeedRIGHT();
1057 break;
1058
1059 case T_members:
1060 while( ( token = NextTok() ) != T_RIGHT )
1061 {
1062 if( token == T_LEFT )
1063 token = NextTok();
1064
1065 if( token == T_member )
1066 {
1067 while( ( token = NextTok() ) != T_RIGHT )
1068 {
1069 if( token == T_LEFT )
1070 token = NextTok();
1071
1072 if( token == T_net )
1073 {
1074 NeedSYMBOLorNUMBER();
1075 members.emplace_back( From_UTF8( CurText() ) );
1076 NeedRIGHT();
1077 }
1078 else
1079 skipCurrent();
1080 }
1081 }
1082 else
1083 skipCurrent();
1084 }
1085 break;
1086
1087 case T_terminal_pins:
1088 while( ( token = NextTok() ) != T_RIGHT )
1089 {
1090 if( token == T_LEFT )
1091 token = NextTok();
1092
1093 if( token == T_terminal_pin )
1094 {
1095 wxString ref;
1096 wxString pin;
1097
1098 while( ( token = NextTok() ) != T_RIGHT )
1099 {
1100 if( token == T_LEFT )
1101 token = NextTok();
1102
1103 if( token == T_ref )
1104 {
1105 NeedSYMBOLorNUMBER();
1106 ref = From_UTF8( CurText() );
1107 NeedRIGHT();
1108 }
1109 else if( token == T_pin )
1110 {
1111 NeedSYMBOLorNUMBER();
1112 pin = From_UTF8( CurText() );
1113 NeedRIGHT();
1114 }
1115 else
1116 skipCurrent();
1117 }
1118
1119 if( !ref.IsEmpty() && !pin.IsEmpty() )
1120 terminals.emplace_back( ref, pin );
1121 }
1122 else
1123 skipCurrent();
1124 }
1125 break;
1126
1127 default:
1128 skipCurrent();
1129 break;
1130 }
1131 }
1132
1133 for( const wxString& netName : members )
1134 m_netlist->SetNetChainFor( netName, name );
1135
1136 for( const auto& term : terminals )
1137 m_netlist->AddSignalTerminalPin( name, term.first, term.second );
1138
1139 if( !netClass.IsEmpty() )
1140 m_netlist->SetSignalNetClass( name, netClass );
1141
1142 if( !colorStr.IsEmpty() )
1143 m_netlist->SetSignalColor( name, colorStr );
1144
1145 if( !chainClass.IsEmpty() )
1146 m_netlist->SetSignalChainClass( name, chainClass );
1147}
1148
1149
1151{
1152 /* Parses a section like
1153 * (libpart (lib device) (part C)
1154 * (aliases
1155 * (alias Cxx)
1156 * (alias Cyy))
1157 * (description "Condensateur non polarise")
1158 * (footprints
1159 * (fp SM*)
1160 * (fp C?)
1161 * (fp C1-1))
1162 * (fields
1163 * (field (name Reference) C)
1164 * (field (name Value) C))
1165 * (pins
1166 * (pin (num 1) (name ~) (type passive))
1167 * (pin (num 2) (name ~) (type passive))))
1168 *
1169 * Currently footprints section/fp are read and data stored
1170 * other fields (unused) are skipped
1171 */
1172 COMPONENT* component = NULL;
1173 wxString libName;
1174 wxString libPartName;
1175 wxArrayString footprintFilters;
1176 wxArrayString aliases;
1177 int pinCount = 0;
1178
1179 // The last token read was libpart, so read the next token
1180 wxLogTrace( "CVPCB_PINCOUNT", wxT( "parseLibPartList: begin libpart" ) );
1181 while( (token = NextTok() ) != T_RIGHT )
1182 {
1183 if( token == T_LEFT )
1184 token = NextTok();
1185
1186 switch( token )
1187 {
1188 case T_lib:
1189 NeedSYMBOLorNUMBER();
1190 libName = From_UTF8( CurText() );
1191 NeedRIGHT();
1192 break;
1193
1194 case T_part:
1195 NeedSYMBOLorNUMBER();
1196 libPartName = From_UTF8( CurText() );
1197 NeedRIGHT();
1198 break;
1199
1200 case T_footprints:
1201 // Read all fp elements (footprint filter item)
1202 while( (token = NextTok() ) != T_RIGHT )
1203 {
1204 if( token == T_LEFT )
1205 token = NextTok();
1206
1207 if( token != T_fp )
1208 Expecting( T_fp );
1209
1210 token = NextTok();
1211
1212 // Accept an empty (fp) sexpr. We do write them out.
1213 if( token == T_RIGHT )
1214 continue;
1215
1216 if( !IsSymbol( token ) && !IsNumber( token ) )
1217 Expecting( "footprint ID" );
1218
1219 footprintFilters.Add( From_UTF8( CurText() ) );
1220 NeedRIGHT();
1221 }
1222 break;
1223
1224 case T_aliases:
1225 while( (token = NextTok() ) != T_RIGHT )
1226 {
1227 if( token == T_LEFT )
1228 token = NextTok();
1229
1230 if( token != T_alias )
1231 Expecting( T_alias );
1232
1233 NeedSYMBOLorNUMBER();
1234 aliases.Add( From_UTF8( CurText() ) );
1235 NeedRIGHT();
1236 }
1237 break;
1238
1239 case T_pins:
1240 wxLogTrace( "CVPCB_PINCOUNT", wxT( "parseLibPartList: entering pins for '%s:%s'" ),
1241 libName, libPartName );
1242 while( (token = NextTok() ) != T_RIGHT )
1243 {
1244 if( token == T_LEFT )
1245 token = NextTok();
1246
1247 if( token != T_pin )
1248 Expecting( T_pin );
1249
1250 pinCount++;
1251 wxLogTrace( "CVPCB_PINCOUNT", wxT( "parseLibPartList: pin #%d for '%s:%s'" ),
1252 pinCount, libName, libPartName );
1253
1254 skipCurrent();
1255 }
1256 wxLogTrace( "CVPCB_PINCOUNT", wxT( "Parsed libpart '%s:%s' pins => pinCount=%d" ),
1257 libName, libPartName, pinCount );
1258 break;
1259
1260 default:
1261 // Skip not used data (i.e all other tokens)
1262 skipCurrent();
1263 break;
1264 }
1265 }
1266
1267 // Find all of the components that reference this component library part definition.
1268 wxLogTrace( "CVPCB_PINCOUNT", wxT( "parseLibPartList: assigning pinCount=%d for libpart '%s:%s'" ),
1269 pinCount, libName, libPartName );
1270 for( unsigned i = 0; i < m_netlist->GetCount(); i++ )
1271 {
1272 component = m_netlist->GetComponent( i );
1273
1274 if( component->IsLibSource( libName, libPartName ) )
1275 {
1276 component->SetFootprintFilters( footprintFilters );
1277 component->SetPinCount( pinCount );
1278 wxLogTrace( "CVPCB_PINCOUNT", wxT( "Assign pinCount=%d to component ref='%s' part='%s:%s'" ),
1279 pinCount, component->GetReference(), libName, libPartName );
1280 }
1281
1282 for( unsigned jj = 0; jj < aliases.GetCount(); jj++ )
1283 {
1284 if( component->IsLibSource( libName, aliases[jj] ) )
1285 {
1286 component->SetFootprintFilters( footprintFilters );
1287 component->SetPinCount( pinCount );
1288 wxLogTrace( "CVPCB_PINCOUNT",
1289 wxT( "Assign pinCount=%d to component ref='%s' via alias='%s:%s'" ),
1290 pinCount, component->GetReference(), libName, aliases[jj] );
1291 }
1292 }
1293
1294 }
1295}
const char * name
static std::optional< VECTOR2D > normalized(const VECTOR2D &aVec)
Store all of the related component information found in a netlist.
void SetLibrary(const wxString &aLibrary)
const wxString & GetReference() const
void AddVariant(const COMPONENT_VARIANT &aVariant)
void SetProperties(std::map< wxString, wxString > aProps)
void SetPinCount(int aPinCount)
void SetUnitInfo(const std::vector< UNIT_INFO > &aUnits)
bool IsLibSource(const wxString &aLibrary, const wxString &aName) const
void SetFootprintFilters(const wxArrayString &aFilters)
void SetFields(nlohmann::ordered_map< wxString, wxString > aFields)
void SetHumanReadablePath(const wxString &aPath)
void SetDuplicatePadNumbersAreJumpers(bool aEnabled)
JUMPER_GROUP_SET & JumperPadGroups()
void SetComponentClassNames(const std::unordered_set< wxString > &aClassNames)
void SetName(const wxString &aName)
void Add(JUMPER_GROUP aGroup)
Add a JUMPER_GROUP to the set.
static std::optional< JUMPER_GROUP > Make(std::set< wxString > aNames)
Factory method to create a JUMPER_GROUP from a set of names.
KICAD_NETLIST_PARSER(LINE_READER *aReader, NETLIST *aNetlist)
void parseComponent()
Parse a component description: (comp (ref P1) (value DB25FEMELLE) (footprint DB25FC) (libsource (lib ...
void Parse()
Function Parse parse the full netlist.
void parseVariant()
Parse a variant section (variant (name "VariantName") (description "Description"))
void parseNet()
Parse a net section (net (code 20) (name /PC-A0) (node (ref BUS1) (pin 62)) (node (ref U3) (pin 3)) (...
void parseLibPartList()
Read the section "libparts" in the netlist: (libparts (libpart (lib device) (part C) (description "Co...
void parseGroup()
Parse a group section (group (name "GroupName") (member (uuid "..."))))
void skipCurrent()
Skip the current token level, i.e search for the RIGHT parenthesis which closes the current descripti...
NETLIST * m_netlist
The netlist to parse into. Not owned.
virtual void LoadNetlist() override
Load the contents of the netlist file into aNetlist.
static KIID FromName(const std::string &aName)
Return a KIID derived from a name, the same name always gives the same KIID.
Definition kiid.cpp:237
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:65
CMP_READER * m_footprintReader
The reader used to load the footprint links. If NULL, footprint links are not read.
LINE_READER * m_lineReader
The line reader of the netlist.
NETLIST * m_netlist
The net list to read the file(s) into.
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
#define _(s)
#define THROW_IO_ERRORF(msg,...)
static wxString From_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition idf_helpers.h:44
_OUT_STRING AsString(const std::string &aString)
Definition sexpr.h:131
static bool IsNumber(char x)
bool m_hasExcludedFromPosFiles
nlohmann::ordered_map< wxString, wxString > m_fields
std::string path
KIBIS_PIN * pin