KiCad PCB EDA Suite
Loading...
Searching...
No Matches
cadstar_parts_lib_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) 2023 Roberto Fernandez Bautista <[email protected]>
5 * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <iostream>
22
25
26#include <fmt.h>
27#include <set>
28#include <string>
29
30using namespace CADSTAR_PARTS_LIB;
31
32
37{
38 std::string m_CurrentString;
39 std::string m_CurrentAttrName;
41 long m_CurrentLong = 0;
45 std::set<std::string> m_CurrentElementsParsed;
46 bool m_ReadOnly = false;
49 std::vector<CADSTAR_PART_PIN> m_CurrentPinList;
52
54};
55
56
57// Default action: Do nothing
58template <typename Rule>
59struct CADSTAR_LIB_PARSER_ACTION : tao::pegtl::nothing<Rule>
60{
61};
62
63
64long helperStringToLong( std::string aString )
65{
66 std::stringstream ss( aString );
67 long number;
68 ss >> number;
69 return number;
70};
71
72
73//
74// CONTENT TO NUMBER ACTIONS:
75// Take the current content string, convert it to a long and store it in StateVariable
76//
77#define DEFINE_CONTENT_TO_NUMBER_ACTION( Rule, StateVariable ) \
78template <> \
79struct CADSTAR_LIB_PARSER_ACTION<Rule> \
80{ \
81 template <typename ActionInput> \
82 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \
83 { \
84 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \
85 s.StateVariable = helperStringToLong( in.string() ); \
86 } \
87}
88
91DEFINE_CONTENT_TO_NUMBER_ACTION( MAX_PIN_COUNT, m_CurrentPart.m_MaxPinCount );
92DEFINE_CONTENT_TO_NUMBER_ACTION( PIN_IDENTIFIER, m_CurrentPin.m_Identifier );
95
96
97// unfortunately the one below needs to be defined separately
98template <>
100{
101 template <typename ActionInput>
102 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
103 {
104 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
105 s.m_CurrentPinEquivalenceGroup.push_back( helperStringToLong( in.string() ) );
106 }
107};
108
109
110//
111// CONTENT TO CURRENT STRING ACTIONS:
112// Take the current content string, store it in the state current string
113//
114#define DEFINE_CONTENT_TO_STRING_ACTION( Rule ) \
115template <> \
116struct CADSTAR_LIB_PARSER_ACTION<Rule> \
117{ \
118 template <typename ActionInput> \
119 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \
120 { \
121 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \
122 s.m_CurrentString = in.string(); \
123 } \
124}
125
127
128//
129// STRING REPLACEMENT ACTIONS:
130// Take the current string in the parser state and store it in StateVariable
131//
132#define DEFINE_STRING_ACTION( Rule, StateVariable ) \
133template <> \
134struct CADSTAR_LIB_PARSER_ACTION<Rule> \
135{ \
136 /* @todo : convert to use apply0 to improve performance( once fully tested ) */ \
137 template <typename ActionInput> \
138 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \
139 { \
140 assert( in.string().size() >= s.m_CurrentString.size() ); \
141 s.StateVariable = s.m_CurrentString; \
142 s.m_CurrentString = ""; \
143 } \
144} \
145
146DEFINE_STRING_ACTION( PART_NAME, m_CurrentPart.m_Name );
147DEFINE_STRING_ACTION( PART_VERSION, m_CurrentPart.m_Version );
148DEFINE_STRING_ACTION( PART_NUMBER, m_CurrentPart.m_Number );
149DEFINE_STRING_ACTION( PART_DESCRIPTION, m_CurrentPart.m_Description );
150DEFINE_STRING_ACTION( PCB_COMPONENT, m_CurrentPart.m_Pcb_component );
151DEFINE_STRING_ACTION( PCB_ALTERNATE, m_CurrentPart.m_Pcb_alternate );
152DEFINE_STRING_ACTION( VALUE, m_CurrentPart.m_Value );
153DEFINE_STRING_ACTION( DEFINITION_NAME, m_CurrentPart.m_PartDefinitionName );
154DEFINE_STRING_ACTION( STEM, m_CurrentPart.m_ComponentStem );
155DEFINE_STRING_ACTION( SYM_ELEMENT_NAME, m_CurrentSwapGroup.m_Name );
158DEFINE_STRING_ACTION( PIN_SIGNAL_NAME, m_CurrentSignalName );
159DEFINE_STRING_ACTION( ACCEPTANCE_PART_NAME, m_CurrentPart.m_AcceptancePartName );
160DEFINE_STRING_ACTION( ACCEPTANCE_TEXT, m_CurrentPart.m_AcceptanceText );
161DEFINE_STRING_ACTION( SPICE_PART_NAME, m_CurrentPart.m_SpicePartName );
162DEFINE_STRING_ACTION( SPICE_MODEL, m_CurrentPart.m_SpiceModel );
163DEFINE_STRING_ACTION( SCH_NAME, m_CurrentSymbol.m_SymbolName );
164DEFINE_STRING_ACTION( SCH_ALTERNATE, m_CurrentSymbol.m_SymbolAlternateName );
165
166
167// STRING SEGMENT action
168// Any strings we match, append to the current state string (the state string gets
169// reset after we extract the string to store somewhere else).
170// The reason we append is because in the fileformat, there can be line continuations,
171// which we don't want to have in the final string - saves post-processing.
172template <typename... EXCLUSION_RULES>
174{
175 template <typename ActionInput>
176 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
177 {
178 s.m_CurrentString += in.string();
179 }
180};
181
182
183//
184// HIERARCHY actions
185//
186template <>
188{
190 {
191 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
193 { s.m_CurrentNodeIdx, std::move( s.m_CurrentNode ) } );
195 }
196};
197
198
199template <>
201{
203 {
204 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
206 }
207};
208
209
210template <>
212{
214 {
215 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
217 }
218};
219
220
221template <>
223{
225 {
226 assert( s.m_CurrentAttrName == "" );
227 s.m_CurrentNode.m_Name = std::move( s.m_CurrentString );
228 s.m_CurrentString = "";
229 }
230};
231
232
233template <>
235{
237 {
238 assert( s.m_CurrentAttrName == "" );
239 s.m_CurrentNode.m_PartNames.push_back( std::move( s.m_CurrentString ) );
240 s.m_CurrentString = "";
241 }
242};
243
244
245// PART_ENTRY action
246// We just push the part to the vector of parts in our state
247template <>
249{
251 {
252 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
253 //Finish the entry
256 s.m_CurrentElementsParsed.clear();
257 // Todo-we could add progress reporting here?
258 }
259};
260
261
262template <>
264{
266 {
267 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" );
268 s.m_ReadOnly = true;
269 }
270};
271
272
273//
274// SINGLE RULE ACTIONS:
275// Make sure that this rule is only matched once per part and throw a parse error
276// when this is not the case.
277//
278#define DECLARE_SINGLE_MATCH_RULE( Rule, ExtraCode ) \
279template <> \
280struct CADSTAR_LIB_PARSER_ACTION<Rule> \
281{ \
282 template <typename ActionInput> \
283 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \
284 { \
285 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \
286 \
287 if( s.m_CurrentElementsParsed.count( #Rule ) ) \
288 { \
289 throw parse_error( #Rule \
290 " was already defined for this part!", \
291 in ); \
292 } \
293 \
294 s.m_CurrentElementsParsed.insert( #Rule ); \
295 ExtraCode; \
296 } \
297} \
298
301DECLARE_SINGLE_MATCH_RULE( NGS_LINE, s.m_CurrentPart.m_GateSwappingAllowed = false );
302DECLARE_SINGLE_MATCH_RULE( NPV_LINE, s.m_CurrentPart.m_PinsVisible = false );
307
308
309template <>
311{
313 {
314 assert( s.m_CurrentAttrName == "" );
315 // m_CurrentLong should have been parsed as part of the PINNUM action
316 // m_CurrentString should have been parsed as part of the PINNAME action
318 s.m_CurrentString = "";
319 }
320};
321
322
323template <>
325{
327 {
328 assert( s.m_CurrentAttrName == "" );
329 // m_CurrentLong should have been parsed as part of the PINNUM action
330 // m_CurrentString should have been parsed as part of the PINLABEL action
332 s.m_CurrentString = "";
333 }
334};
335
336//
337// PIN EQUIVALENCE GROUP ACTIONS:
338// Take the current m_CurrentPinEquivalenceGroup in the parser state and store it in StateVariable
339// then clear m_CurrentPinEquivalenceGroup.
340// Note that m_CurrentPinEquivalenceGroup should have been parsed as part of EQUIVALENT_PIN action
341//
342#define DEFINE_PIN_GROUP_ACTION( Rule, StateVariable ) \
343template <> \
344struct CADSTAR_LIB_PARSER_ACTION<Rule> \
345{ \
346 static void apply0( CADSTAR_LIB_PARSER_STATE& s ) \
347 { \
348 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \
349 s.StateVariable.push_back( s.m_CurrentPinEquivalenceGroup ); \
350 s.m_CurrentPinEquivalenceGroup.clear(); \
351 } \
352} \
353
354DEFINE_PIN_GROUP_ACTION( EQUIVALENT_PINS_GROUP, m_CurrentPart.m_PinEquivalences );
355DEFINE_PIN_GROUP_ACTION( INTERNAL_SWAP_GATE, m_CurrentSwapGroup.m_Gates );
356DEFINE_PIN_GROUP_ACTION( EXTERNAL_SWAP_GATE, m_CurrentSwapGroup.m_Gates );
357
358
359//
360// SWAP GROUP ACTIONS:
361// Take the current m_CurrentSwapGroup in the parser state and store it in StateVariable
362// then reset m_CurrentSwapGroup.
363//
364#define DEFINE_SWAP_GROUP_ACTION( Rule, StateVariable ) \
365template <> \
366struct CADSTAR_LIB_PARSER_ACTION<Rule> \
367{ \
368 static void apply0( CADSTAR_LIB_PARSER_STATE& s ) \
369 { \
370 assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \
371 s.StateVariable.push_back( s.m_CurrentSwapGroup ); \
372 s.m_CurrentSwapGroup = CADSTAR_SWAP_GROUP(); \
373 } \
374} \
375
376DEFINE_SWAP_GROUP_ACTION( INTERNAL_SWAP_GROUP, m_CurrentPart.m_InternalSwapGroup );
377DEFINE_SWAP_GROUP_ACTION( EXTERNAL_SWAP_GROUP, m_CurrentPart.m_ExternalSwapGroup );
378
379
380template <>
382{
383 template <typename ActionInput>
384 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
385 {
386 // The format allows user defined "part" attrbutes, but the ones listed here are in-built
387 // with special meaning
388 static const std::set<std::string> reservedWordsStarLines = { "VALUE", "PNM", "PLB", "EQU",
389 "SYM", "INT", "EXT", "DFN",
390 "NGS", "NPV", "STM", "MXP",
391 "SPI", "PAC" };
392
394 {
395 throw parse_error( fmt::format( "Duplicate attribute name '{}'", s.m_CurrentAttrName ),
396 in );
397 }
398
399 if( reservedWordsStarLines.count( s.m_CurrentAttrName ) )
400 {
401 throw parse_error(
402 fmt::format(
403 "Invalid use of in-built attribute name '{}'. Either the attribute "
404 "was already defined for this part or it has an unexpected syntax.",
406 in );
407 }
408
410 s.m_CurrentAttrName = "";
411 s.m_CurrentString = "";
412 }
413};
414
415#define DEFINE_ATTRIBUTE_ACTION( Rule, StateVariable ) \
416template <> \
417struct CADSTAR_LIB_PARSER_ACTION<Rule> \
418{ \
419 template <typename ActionInput> \
420 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \
421 { \
422 if( s.StateVariable.count( s.m_CurrentAttrName ) ) \
423 { \
424 throw parse_error( \
425 fmt::format( "Duplicate attribute name '{}'", s.m_CurrentAttrName ), \
426 in ); \
427 } \
428 \
429 CADSTAR_ATTRIBUTE_VALUE val; \
430 val.m_ReadOnly = s.m_ReadOnly; \
431 val.m_Value = s.m_CurrentString; \
432 \
433 s.StateVariable.insert( { s.m_CurrentAttrName, val } ); \
434 s.m_CurrentAttrName = ""; \
435 s.m_CurrentString = ""; \
436 s.m_ReadOnly = false; \
437 } \
438} \
439
440DEFINE_ATTRIBUTE_ACTION( SCM_ATTRIBUTE, m_CurrentPart.m_SchAttributes );
441DEFINE_ATTRIBUTE_ACTION( PCB_ATTRIBUTE, m_CurrentPart.m_PcbAttributes );
442DEFINE_ATTRIBUTE_ACTION( PART_ATTRIBUTE, m_CurrentPart.m_PartAttributes );
443DEFINE_ATTRIBUTE_ACTION( SCH_PCB_ATTRIBUTE, m_CurrentPart.m_SchAndPcbAttributes );
444
445
446template <>
448{
450 {
452 s.m_CurrentPart.m_Symbols.push_back( std::move( s.m_CurrentSymbol ) );
454 }
455};
456
457
458template <>
460{
462 {
463 s.m_CurrentPinList.push_back( std::move( s.m_CurrentPin ) );
465 }
466};
467
468
469template <>
471{
473 {
475 { std::move( s.m_CurrentSignalName ), std::move( s.m_CurrentPinList ) } );
476 s.m_CurrentPinList.clear();
477 }
478};
479
480
481template <>
483{
484 template <typename ActionInput>
485 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
486 {
488 }
489};
490
491
492template <>
494{
495 template <typename ActionInput>
496 static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
497 {
498 // The format allows user defined "part" attrbutes, but the ones listed here are in-built
499 // with special meaning
500 static const std::map<std::string, CADSTAR_PIN_TYPE> tokenToPinType = {
501 { "U", CADSTAR_PIN_TYPE::UNCOMMITTED },
502 { "I", CADSTAR_PIN_TYPE::INPUT },
503 { "N", CADSTAR_PIN_TYPE::OUTPUT_NOT_OR },
504 { "Y", CADSTAR_PIN_TYPE::OUTPUT_OR },
505 { "Q", CADSTAR_PIN_TYPE::OUTPUT_NOT_NORM_OR },
506 { "P", CADSTAR_PIN_TYPE::POWER },
507 { "G", CADSTAR_PIN_TYPE::GROUND },
508 { "T", CADSTAR_PIN_TYPE::TRISTATE_BIDIR },
509 { "TI", CADSTAR_PIN_TYPE::TRISTATE_INPUT },
510 { "TD", CADSTAR_PIN_TYPE::TRISTATE_DRIVER }
511 };
512
513 if( !tokenToPinType.count( in.string() ) )
514 throw parse_error( fmt::format( "Unexpected pin type '{}'", in.string() ), in );
515
516 s.m_CurrentPin.m_Type = tokenToPinType.at( in.string() );
517 }
518};
519
520
521template <typename INPUT_TYPE>
522bool checkHeaderHelper( INPUT_TYPE& aInput )
523{
524 try
525 {
526 if( !parse<VALID_HEADER>( aInput ) )
527 return false;
528 }
529 catch( const parse_error& )
530 {
531 return false;
532 }
533
534 return true;
535}
536
537
538bool CADSTAR_PARTS_LIB_PARSER::CheckContentHeader( const std::string& aSource ) const
539{
540 string_input in( aSource, "from_content" );
541 return checkHeaderHelper( in );
542}
543
544
545bool CADSTAR_PARTS_LIB_PARSER::CheckFileHeader( const std::filesystem::path& aPath ) const
546{
547 file_input in( aPath );
548 return checkHeaderHelper( in );
549}
550
551
552template<typename INPUT_TYPE>
554{
556
557 try
558 {
559 // Todo: We could reserve space for the partEntries vector
560 // to improve performance? E.g.:
561 // s.m_ParsedModel.m_PartEntries.reserve( expectedNumParts );
562
563 if( !parse<GRAMMAR, CADSTAR_LIB_PARSER_ACTION>( aInput, s ) )
564 printf( "Some error occurred!\n" );
565 }
566 catch( const parse_error& e )
567 {
568 const auto& p = e.positions().front();
569 std::cerr << "Error at line " << p.line << ", column " << p.column << std::endl
570 << aInput.line_at( p ) << std::endl
571 << std::setw( p.column ) << '^' << std::endl
572 << e.message() << std::endl;
573 }
574
575 return s.m_ParsedModel;
576}
577
578
580{
581 string_input in( aSource, "from_content" );
582 return readCadstarHelper( in );
583}
584
585
587CADSTAR_PARTS_LIB_PARSER::ReadFile( const std::filesystem::path& aPath ) const
588{
589 file_input in( aPath );
590 return readCadstarHelper( in );
591}
CADSTAR_PIN_POSITION
Positioning of pin names can be in one of four quadrants.
bool checkHeaderHelper(INPUT_TYPE &aInput)
#define DEFINE_ATTRIBUTE_ACTION(Rule, StateVariable)
#define DEFINE_PIN_GROUP_ACTION(Rule, StateVariable)
long helperStringToLong(std::string aString)
#define DEFINE_CONTENT_TO_STRING_ACTION(Rule)
#define DEFINE_SWAP_GROUP_ACTION(Rule, StateVariable)
#define DEFINE_STRING_ACTION(Rule, StateVariable)
#define DECLARE_SINGLE_MATCH_RULE(Rule, ExtraCode)
#define DEFINE_CONTENT_TO_NUMBER_ACTION(Rule, StateVariable)
CADSTAR_PARTS_LIB_MODEL readCadstarHelper(INPUT_TYPE &aInput)
bool CheckFileHeader(const std::filesystem::path &aPath) const
CADSTAR_PARTS_LIB_MODEL ReadFile(const std::filesystem::path &aPath) const
CADSTAR_PARTS_LIB_MODEL ReadContent(const std::string &aSource) const
bool CheckContentHeader(const std::string &aSource) const
static void apply(const ActionInput &in, CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply(const ActionInput &in, CADSTAR_LIB_PARSER_STATE &s)
static void apply(const ActionInput &in, CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply(const ActionInput &in, CADSTAR_LIB_PARSER_STATE &s)
static void apply0(CADSTAR_LIB_PARSER_STATE &s)
static void apply(const ActionInput &in, CADSTAR_LIB_PARSER_STATE &s)
Struture that will be populated by the PEGTL parser.
CADSTAR_PARTS_LIB_MODEL m_ParsedModel
CADSTAR_PART_SYMBOL_ENTRY m_CurrentSymbol
std::vector< long > m_CurrentPinEquivalenceGroup
std::set< std::string > m_CurrentElementsParsed
std::vector< CADSTAR_PART_PIN > m_CurrentPinList
String segment( no line continuation ), with exclusion rules.
CADSTAR Parts Library (*.lib) model - a data structure describing the contents of the file format.
std::map< long, CADSTAR_PART_NODE > m_HierarchyNodes
std::vector< CADSTAR_PART_ENTRY > m_PartEntries
std::map< std::string, std::vector< CADSTAR_PART_PIN > > m_HiddenPins
Pins with an implied electrical connection to a net, not part of any symbol (Note: we probably will n...
std::vector< CADSTAR_PART_SYMBOL_ENTRY > m_Symbols
Symbols that form this part.
std::map< std::string, std::string > m_UserAttributes
Star (*) line *<User-defined name> This line is ignored by CADSTAR.
std::map< long, std::string > m_PinNamesMap
Map of pin identifiers to alphanumeric pin names Pin names can be a maximum of 10 characters (Typical...
std::map< long, std::string > m_PinLabelsMap
Map of pin identifiers to alphanumeric pin labels.
std::optional< long > m_ParentNodeIdx
std::vector< std::string > m_PartNames
Part names belonging to this hierarchy.
CADSTAR_PIN_POSITION m_Position
std::vector< CADSTAR_PART_PIN > m_Pins