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