24 changed files with 3662 additions and 84 deletions
-
1common/CMakeLists.txt
-
521common/plugins/cadstar/cadstar_parts_lib_grammar.h
-
176common/plugins/cadstar/cadstar_parts_lib_model.h
-
462common/plugins/cadstar/cadstar_parts_lib_parser.cpp
-
56common/plugins/cadstar/cadstar_parts_lib_parser.h
-
1933qa/data/eeschema/plugins/cadstar/cadstarDummy.lib
-
50qa/data/eeschema/plugins/cadstar/writeCadstarFile.py
-
0qa/data/eeschema/plugins/eagle/eagle-import-testfile.brd
-
0qa/data/eeschema/plugins/eagle/eagle-import-testfile.sch
-
4qa/qa_utils/CMakeLists.txt
-
28qa/qa_utils/include/qa_utils/wx_utils/unit_test_utils.h
-
31qa/qa_utils/wx_utils/unit_test_utils.cpp
-
5qa/schematic_utils/CMakeLists.txt
-
30qa/schematic_utils/schematic_file_util.cpp
-
1qa/unittests/common/CMakeLists.txt
-
390qa/unittests/common/plugins/cadstar/test_cadstar_parts_parser.cpp
-
5qa/unittests/eeschema/CMakeLists.txt
-
30qa/unittests/eeschema/eeschema_test_utils.cpp
-
10qa/unittests/eeschema/eeschema_test_utils.h
-
2qa/unittests/eeschema/sim/test_library_spice.cpp
-
2qa/unittests/eeschema/sim/test_sim_regressions.cpp
-
5qa/unittests/eeschema/test_eagle_plugin.cpp
-
2qa/unittests/eeschema/test_netlist_exporter_spice.h
-
2qa/unittests/eeschema/test_sch_sheet_list.cpp
@ -0,0 +1,521 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2022 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com> |
|||
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation, either version 3 of the License, or (at your |
|||
* option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but |
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#include <pegtl.hpp> |
|||
|
|||
namespace CADSTAR_PARTS_LIB |
|||
{ |
|||
using namespace tao::pegtl; |
|||
|
|||
//-------------------- Grammar definition ---------------------------------------------------- |
|||
|
|||
/** |
|||
* Needed, because PEGTL "space" includes newline characters |
|||
*/ |
|||
struct WHITESPACE : one<' ', '\t'>{}; |
|||
|
|||
/** |
|||
* Empty line with whitespaces |
|||
*/ |
|||
struct EMPTY_LINE : seq< bol, star<WHITESPACE>, eol>{}; |
|||
|
|||
/** |
|||
* Any text in the format can span multiple lines using '&' |
|||
*/ |
|||
struct LINE_CONTINUATION : seq<one<'&'>, eol>{}; |
|||
|
|||
|
|||
struct WHITESPACE_OR_CONTINUATION : sor<WHITESPACE, LINE_CONTINUATION> {}; |
|||
|
|||
/** |
|||
* String segment( no line continuation ), with exclusion rules |
|||
*/ |
|||
template <typename... EXCLUSION_RULES> |
|||
struct STR_SEGMENT_EXCLUDING : plus<not_at<sor<eolf, LINE_CONTINUATION, EXCLUSION_RULES...>>, any>{}; |
|||
|
|||
/** |
|||
* String with optional line continuation and exclusion rules |
|||
*/ |
|||
template <typename... EXCLUSION_RULES> |
|||
struct STRING_EXCLUDING : plus<STR_SEGMENT_EXCLUDING<EXCLUSION_RULES...>, opt<LINE_CONTINUATION>> {}; |
|||
|
|||
struct QUOTED_STRING : seq<one<'"'>, STRING_EXCLUDING<one<'"'>>, one<'"'>> {}; |
|||
|
|||
/** |
|||
* Control character with or without preceding whitespace |
|||
*/ |
|||
template <char... CHAR_TO_FIND> |
|||
struct spaced_ch : seq<star<WHITESPACE>, one<CHAR_TO_FIND...>>{}; |
|||
|
|||
|
|||
// ************** |
|||
// * FORMAT * |
|||
// ************** |
|||
|
|||
// Definition of "Format" |
|||
// # FORMAT <current format number> |
|||
struct CURRENT_FORMAT_NUMBER : plus<digit> {}; |
|||
struct FORMAT : seq |
|||
< |
|||
bol, |
|||
one<'#'>, |
|||
star<WHITESPACE>, |
|||
TAO_PEGTL_ISTRING( "FORMAT" ), |
|||
star<WHITESPACE>, |
|||
CURRENT_FORMAT_NUMBER, |
|||
opt<eol> |
|||
> {}; |
|||
|
|||
|
|||
// ************** |
|||
// * PART ENTRY * |
|||
// ************** |
|||
|
|||
// Part Header |
|||
// ----------- |
|||
//.<Part name>_[(<Part number>)][:<Part version>][;<Description>] |
|||
|
|||
// string filters: |
|||
struct PART_HEADER_START : one <'.'>{}; |
|||
struct PART_NAME_FILTER : sor<spaced_ch<'('>, spaced_ch<':'>, spaced_ch<';'>>{}; |
|||
struct PART_NUMBER_FILTER : one<')'>{}; |
|||
struct PART_VERSION_FILTER : spaced_ch<';'>{}; |
|||
|
|||
// part header elements: |
|||
struct PART_NAME : STRING_EXCLUDING<PART_NAME_FILTER> {}; |
|||
struct PART_NUMBER : STRING_EXCLUDING<PART_NUMBER_FILTER> {}; |
|||
struct PART_VERSION : STRING_EXCLUDING<PART_VERSION_FILTER> {}; |
|||
struct PART_DESCRIPTION : STRING_EXCLUDING<> {}; |
|||
|
|||
struct PART_HEADER : |
|||
seq |
|||
< |
|||
bol, |
|||
one<'.'>, |
|||
must<PART_NAME>, |
|||
opt<seq<spaced_ch<'('>, PART_NUMBER, one<')'>>>, |
|||
opt<seq<spaced_ch<':'>, PART_VERSION>>, |
|||
opt<seq<spaced_ch<';'>, PART_DESCRIPTION>>, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
// Part - PCB Component |
|||
// -------------------- |
|||
//<PCB Component Refname>[_(<PCB Alternate Refname>)] |
|||
|
|||
// string filters: |
|||
struct PCB_COMPONENT_FILTER : spaced_ch<'('>{}; |
|||
struct PCB_ALTERNATE_FILTER : one<')'>{}; |
|||
|
|||
// pcb component elements |
|||
struct PCB_COMPONENT : STRING_EXCLUDING<PCB_COMPONENT_FILTER> {}; |
|||
struct PCB_ALTERNATE : STRING_EXCLUDING<PCB_ALTERNATE_FILTER> {}; |
|||
|
|||
struct PART_PCB_COMPONENT : |
|||
seq |
|||
< |
|||
bol, |
|||
PCB_COMPONENT, |
|||
opt<seq<spaced_ch<'('>, PCB_ALTERNATE, one<')'>>>, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
// Part Value |
|||
// ----------- |
|||
//[*VALUE_<Value>] |
|||
struct VALUE : STRING_EXCLUDING<> {}; |
|||
struct PART_VALUE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*VALUE"), |
|||
plus<WHITESPACE>, |
|||
VALUE, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
struct PINNUM : plus<digit> {}; |
|||
|
|||
// Pin Names |
|||
//[*PNM_<Pinnum>=<Pinname>[_<Pinnum>=<Pinname>]_etc] |
|||
// Maximum 10 characters allowed for Pinname according to documentation |
|||
struct PINNAME : rep_min_max<1,10,alnum> {}; |
|||
struct PINNAME_ENTRY : |
|||
seq |
|||
< |
|||
plus<WHITESPACE_OR_CONTINUATION>, |
|||
PINNUM, |
|||
one<'='>, |
|||
PINNAME |
|||
> |
|||
{}; |
|||
|
|||
struct PIN_NAMES_LIST : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*PNM"), |
|||
plus<PINNAME_ENTRY>, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
// Pin Labels |
|||
//[*PLB_<Pinnum>=<Pinlabel>[_<Pinnum>=<Pinlabel>]_etc] |
|||
struct PINLABEL : sor<QUOTED_STRING, STRING_EXCLUDING<WHITESPACE>> {}; |
|||
struct PINLABEL_ENTRY : |
|||
seq |
|||
< |
|||
plus<WHITESPACE_OR_CONTINUATION>, |
|||
PINNUM, |
|||
one<'='>, |
|||
PINLABEL |
|||
> |
|||
{}; |
|||
|
|||
struct PIN_LABELS_LIST : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*PLB"), |
|||
plus<PINLABEL_ENTRY>, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
// Pin Equivalences |
|||
//[*EQU_<PinIdentifier>=<PinIdentifier>[=<PinIdentifier>=<PinIdentifier>_etc ...]] |
|||
struct EQUIVALENT_PIN : PINNUM {}; // same grammar but different action to be applied |
|||
struct EQUIVALENT_PINS_GROUP : |
|||
seq |
|||
< |
|||
plus<WHITESPACE_OR_CONTINUATION>, |
|||
EQUIVALENT_PIN, |
|||
plus<one<'='>, EQUIVALENT_PIN> |
|||
> |
|||
{}; |
|||
|
|||
struct PIN_EQUIVALENCES : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*EQU"), |
|||
EQUIVALENT_PINS_GROUP, |
|||
star<one<','>, EQUIVALENT_PINS_GROUP>, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
|
|||
// INTERNAL AND EXTERNAL PIN SWAPPING |
|||
// *SYM_[<Element name>] |
|||
struct SYM_ELEMENT_NAME : STRING_EXCLUDING<> {}; |
|||
struct SYM_LINE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*SYM"), |
|||
star<WHITESPACE>, |
|||
opt<SYM_ELEMENT_NAME>, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
struct GATE_PINS_LIST : |
|||
seq |
|||
< |
|||
plus<WHITESPACE>, |
|||
EQUIVALENT_PIN, |
|||
star |
|||
< |
|||
plus<WHITESPACE_OR_CONTINUATION>, |
|||
EQUIVALENT_PIN |
|||
> |
|||
> |
|||
{}; |
|||
|
|||
//[*INT_<Pinnum>_[<Pinnum>_ etc ...]] |
|||
struct INTERNAL_SWAP_GATE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*INT"), |
|||
GATE_PINS_LIST, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
//[*EXT_<Pinnum>_[<Pinnum>_ etc ...]] |
|||
struct EXTERNAL_SWAP_GATE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*EXT"), |
|||
GATE_PINS_LIST, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
// Internal swapping group E.g.: |
|||
//*SYM SYM1 |
|||
//*INT 2 3 |
|||
//*INT 4 5 |
|||
struct INTERNAL_SWAP_GROUP : |
|||
seq |
|||
< |
|||
SYM_LINE, |
|||
plus<INTERNAL_SWAP_GATE> |
|||
> |
|||
{}; |
|||
|
|||
// External swapping group E.g.: |
|||
//*SYM SYM1 |
|||
//*EXT 2 3 |
|||
//*EXT 4 5 |
|||
struct EXTERNAL_SWAP_GROUP : |
|||
seq |
|||
< |
|||
SYM_LINE, |
|||
plus<EXTERNAL_SWAP_GATE> |
|||
> |
|||
{}; |
|||
|
|||
// Part Definition |
|||
// ----------- |
|||
//[*DFN_<Definition name>] |
|||
struct DEFINITION_NAME : STRING_EXCLUDING<> {}; |
|||
struct DFN_LINE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*DFN"), |
|||
plus<WHITESPACE>, |
|||
DEFINITION_NAME, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
//[*NGS] |
|||
struct NGS_LINE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*NGS"), |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
//[*NPV] |
|||
struct NPV_LINE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*NPV"), |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
//[*STM_<Component name stem>] |
|||
struct STEM : STRING_EXCLUDING<> {}; |
|||
struct STM_LINE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*STM"), |
|||
plus<WHITESPACE>, |
|||
STEM, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
//[*MXP <Maximum number of connector pins>] |
|||
struct MAX_PIN_COUNT : plus<digit> {}; |
|||
struct MXP_LINE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*MXP"), |
|||
plus<WHITESPACE>, |
|||
MAX_PIN_COUNT, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
//[*SPI_[(<Part name>)]_[<Model>]_<Component Value>] |
|||
struct SPICE_PART_NAME : STRING_EXCLUDING< one<')'> > {}; |
|||
struct SPICE_FIRST : sor<QUOTED_STRING, STRING_EXCLUDING<WHITESPACE>> {}; |
|||
struct SPICE_SECOND : sor<QUOTED_STRING, STRING_EXCLUDING<WHITESPACE>> {}; |
|||
struct SPI_LINE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*SPI"), |
|||
plus<WHITESPACE>, |
|||
opt<seq<spaced_ch<'('>, SPICE_PART_NAME, one<')'>>>, |
|||
plus<WHITESPACE>, |
|||
SPICE_FIRST, // Spice Value or Model |
|||
opt<plus<WHITESPACE>, SPICE_SECOND>, // Spice Value |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
|
|||
//[*PAC_(<Part name>)_<Acceptance Text>] |
|||
struct ACCEPTANCE_PART_NAME : STRING_EXCLUDING< one<')'> > {}; |
|||
struct ACCEPTANCE_TEXT : STRING_EXCLUDING<> {}; |
|||
struct PAC_LINE : |
|||
seq |
|||
< |
|||
bol, |
|||
TAO_PEGTL_ISTRING( "*PAC"), |
|||
plus<WHITESPACE>, |
|||
opt<seq<spaced_ch<'('>, ACCEPTANCE_PART_NAME, one<')'>>>, |
|||
plus<WHITESPACE>, |
|||
ACCEPTANCE_TEXT, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
// User defined part attributes |
|||
// ----------- |
|||
//[*<User-defined name>_<Value>] |
|||
struct USER_PART_ATTRIBUTE_NAME : sor<QUOTED_STRING, STRING_EXCLUDING<WHITESPACE>> {}; |
|||
struct USER_PART_ATTRIBUTE_VALUE : STRING_EXCLUDING<> {}; |
|||
struct USER_PART_ATTRIBUTE : |
|||
seq |
|||
< |
|||
bol, |
|||
one<'*'>, |
|||
USER_PART_ATTRIBUTE_NAME, |
|||
plus<WHITESPACE>, |
|||
USER_PART_ATTRIBUTE_VALUE, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
//---------------------------------------------------- |
|||
// In-built attributes: schematic, PCB, both (sch+pcb) and parts |
|||
//---------------------------------------------------- |
|||
struct READONLY : one <'!'>{}; |
|||
struct ATTRIBUTE_NAME : sor<QUOTED_STRING, STRING_EXCLUDING< spaced_ch<'('>>> {}; |
|||
struct ATTRIBUTE_VALUE : sor<QUOTED_STRING, STRING_EXCLUDING< one<')'>>> {}; |
|||
|
|||
template<char START_TOKEN> |
|||
struct GENERIC_ATTRIBUTE : |
|||
seq |
|||
< |
|||
bol, |
|||
one<START_TOKEN>, |
|||
opt<READONLY>, |
|||
ATTRIBUTE_NAME, |
|||
spaced_ch<'('>, |
|||
ATTRIBUTE_VALUE, |
|||
one<')'>, |
|||
opt<eol> |
|||
> |
|||
{}; |
|||
|
|||
//[$[!]<SCM Attribute name>(<Attribute value>)] |
|||
struct SCM_ATTRIBUTE : GENERIC_ATTRIBUTE<'$'>{}; |
|||
|
|||
|
|||
//[%[!]<PCB Attribute name>(<Attribute value>)] |
|||
struct PCB_ATTRIBUTE : GENERIC_ATTRIBUTE<'%'>{}; |
|||
|
|||
|
|||
//[~[!]<Parts Library Attribute Name>(<Attribute Value>)] |
|||
struct PART_ATTRIBUTE : GENERIC_ATTRIBUTE<'~'>{}; |
|||
|
|||
|
|||
//[@[!]<SCM/PCB Attribute name>(<Attribute value>)] |
|||
struct SCH_PCB_ATTRIBUTE : GENERIC_ATTRIBUTE<'@'>{}; |
|||
|
|||
|
|||
//****************** |
|||
// Join all together |
|||
|
|||
struct PART_ENTRY : |
|||
seq |
|||
< |
|||
PART_HEADER, //.<Part name>[ (1234): 1 ;<Description>] |
|||
PART_PCB_COMPONENT, //<PCB Component Refname> [(Alternate)] |
|||
|
|||
// In any order: |
|||
star<sor< |
|||
PART_VALUE, //[*VALUE <Value>] |
|||
PIN_NAMES_LIST, //[*PNM <ID><Name>[ <ID><Name>] ...] |
|||
PIN_LABELS_LIST, //[*PLB <ID><Label>[ <ID><Label>] ...] |
|||
PIN_EQUIVALENCES, //[*EQU_<ID>=<ID>[=<ID>=<ID>_etc ...]] |
|||
INTERNAL_SWAP_GROUP, //[*SYM SYM1 |*INT 2 3 |*INT 4 5] |
|||
EXTERNAL_SWAP_GROUP, //[*SYM SYM1 |*EXT 2 3 |*EXT 4 5] |
|||
DFN_LINE, //[*DFN_<Definition name>] |
|||
NGS_LINE, //[*NGS] |
|||
NPV_LINE, //[*NPV] |
|||
STM_LINE, //[*STM_<Component name stem>] |
|||
MXP_LINE, //[*MXP <Maximum number of connector pins>] |
|||
SPI_LINE, //[*SPI_[(<Part name>)]_[<Model>]_<Component Value>] |
|||
PAC_LINE, //[*PAC_(<Part name>)_<Acceptance Text>] |
|||
USER_PART_ATTRIBUTE, //[*<User-defined name>_<Value>] |
|||
SCM_ATTRIBUTE, //[$[!]<SCM Attribute name>(<Attribute value>)] |
|||
PCB_ATTRIBUTE, //[%[!]<PCB Attribute name>(<Attribute value>)] |
|||
PART_ATTRIBUTE, //[~[!]<Parts Library Attribute Name>(<Attribute Value>)] |
|||
SCH_PCB_ATTRIBUTE //[@[!]<SCM/PCB Attribute name>(<Attribute value>)] |
|||
>> |
|||
> |
|||
{}; |
|||
|
|||
struct UNMATCHED_CONTENT : STRING_EXCLUDING<FORMAT, PART_ENTRY> {}; //@todo remove once parser is complete |
|||
|
|||
/** |
|||
* Grammar for CADSTAR Parts Library file format (*.lib) |
|||
*/ |
|||
struct GRAMMAR : |
|||
must<seq< |
|||
opt<FORMAT>, |
|||
plus |
|||
< |
|||
sor |
|||
< |
|||
PART_ENTRY, |
|||
UNMATCHED_CONTENT, //@todo remove once parser is complete |
|||
EMPTY_LINE // optional empty line |
|||
>, |
|||
opt<eol> |
|||
>, |
|||
opt<TAO_PEGTL_ISTRING( ".END"), opt<eol>>, |
|||
star<EMPTY_LINE>, |
|||
must<eolf> |
|||
>> |
|||
{}; |
|||
|
|||
|
|||
/** |
|||
* Grammar to parse the file header only. |
|||
* In general a valid file should have `#FORMAT 32` in the first line but there appear to be some |
|||
* files that ommit the format specifier and start straight away with the part definitions. Just |
|||
* in case, we will also allow the first part to be up to 5 lines into the file (arbitrary number |
|||
* just to limit the time spent in reading a file header to determine whether it is valid). |
|||
*/ |
|||
struct VALID_HEADER : sor<FORMAT, seq< rep_max<5,EMPTY_LINE>, PART_HEADER>>{}; |
|||
|
|||
} // namespace CADSTAR_PART_LIB |
|||
@ -0,0 +1,176 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2023 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com> |
|||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation, either version 3 of the License, or (at your |
|||
* option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but |
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef CADSTAR_PARTS_LIB_MODEL_H |
|||
#define CADSTAR_PARTS_LIB_MODEL_H |
|||
|
|||
#include <map> |
|||
#include <optional> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
struct CADSTAR_PART_ENTRY; |
|||
struct CADSTAR_SWAP_GROUP; |
|||
struct CADSTAR_ATTRIBUTE_VALUE; |
|||
|
|||
/** |
|||
* CADSTAR Parts Library (*.lib) model - a data structure describing the contents of the |
|||
* file format |
|||
*/ |
|||
struct CADSTAR_PARTS_LIB_MODEL |
|||
{ |
|||
std::optional<long> m_FormatNumber; |
|||
std::vector<CADSTAR_PART_ENTRY> m_PartEntries; |
|||
}; |
|||
|
|||
|
|||
struct CADSTAR_PART_ENTRY |
|||
{ |
|||
std::string m_Name; |
|||
std::optional<std::string> m_Number; |
|||
std::optional<std::string> m_Version; |
|||
std::optional<std::string> m_Description; |
|||
std::string m_Pcb_component; |
|||
std::optional<std::string> m_Pcb_alternate; |
|||
std::optional<std::string> m_Value; // *VALUE Note: ? character = the start of a new line |
|||
std::optional<std::string> m_PartDefinitionName; // *DFN |
|||
std::string m_ComponentStem = ""; |
|||
std::optional<long> m_MaxPinCount; |
|||
|
|||
std::optional<std::string> m_SpicePartName; |
|||
std::optional<std::string> m_SpiceModel; |
|||
std::optional<std::string> m_SpiceValue; |
|||
|
|||
std::optional<std::string> m_AcceptancePartName; |
|||
std::optional<std::string> m_AcceptanceText; |
|||
|
|||
bool m_GateSwappingAllowed = true; |
|||
bool m_PinsVisible = true; |
|||
|
|||
/** |
|||
* Map of pin numbers to alphanumeric pin names |
|||
* Pin names can be a maximum of 10 characters |
|||
* (Typically used for naming of BGA pads) |
|||
* |
|||
* E.g: *PNM 1=A1 2=A2 3=A3 4=B1 5=B2 6=B3 |
|||
*/ |
|||
std::map<long, std::string> m_PinNamesMap; |
|||
|
|||
/** |
|||
* Map of pin numbers to alphanumeric pin labels |
|||
* |
|||
* E.g: *PLB 1=STROBE 2=OFFSET 3=OFFSET 5=+ 6=+v |
|||
*/ |
|||
std::map<long, std::string> m_PinLabelsMap; |
|||
|
|||
/** |
|||
* Groups of pins that are interchangeable with each other |
|||
* |
|||
* E.g: *EQU 2=1, 6=5, 8=9=10, 12=13 |
|||
*/ |
|||
std::vector<std::vector<long>> m_PinEquivalences; |
|||
|
|||
/** |
|||
* Groups of INTERNAL gates that are interchangeable with each other |
|||
* |
|||
* E.g: *SYM SYM1 |
|||
* *INT 1 3 |
|||
* *INT 2 5 |
|||
* |
|||
* The gate described by pins 1 and 3 above, can be swapped internally with the gate decribed |
|||
* by pins 2 and 5 but they CANNOT be swapped with gates in another part |
|||
*/ |
|||
std::vector<CADSTAR_SWAP_GROUP> m_InternalSwapGroup; |
|||
|
|||
/** |
|||
* Groups of EXTERNAL gates that are interchangeable with each other |
|||
* |
|||
* E.g: *SYM SYM2 |
|||
* *EXT 1 3 |
|||
* *EXT 2 5 |
|||
* |
|||
* The gate described by pins 1 and 3 above, can be swapped internally with the gate decribed |
|||
* by pins 2 and 5 AND they can be swapped with same gates in another part |
|||
*/ |
|||
std::vector<CADSTAR_SWAP_GROUP> m_ExternalSwapGroup; |
|||
|
|||
/** |
|||
* Star (*) line |
|||
* *<User-defined name> <Value> |
|||
* This line is ignored by CADSTAR. Usually they are used by third party tools. |
|||
* These lines are treated as attributes of the Parts library (i.e. Attribute Type = Parts Library). |
|||
*/ |
|||
std::map<std::string, std::string> m_UserAttributes; |
|||
|
|||
/** |
|||
* Dollar sign ($) line |
|||
* $[!]<SCM Attribute name>(<Attribute value>) |
|||
* Attributes related to the schematic symbol. |
|||
* Is set to read-only if exclamation mark (!) is present |
|||
*/ |
|||
std::map<std::string, CADSTAR_ATTRIBUTE_VALUE> m_SchAttributes; |
|||
|
|||
/** |
|||
* Percentage sign (%) line |
|||
* %[!]<PCB Attribute name>(<Attribute value>) |
|||
* Attributes related to the PCB component / footprint. |
|||
* Is set to read-only if exclamation mark (!) is present |
|||
*/ |
|||
std::map<std::string, CADSTAR_ATTRIBUTE_VALUE> m_PcbAttributes; |
|||
|
|||
/** |
|||
* At symbol (@) line |
|||
* [@[!]<SCM/PCB Attribute name>(<Attribute value>)] |
|||
* Attributes related to the PCB component AND the schematic symbol. |
|||
* Is set to read-only if exclamation mark (!) is present |
|||
*/ |
|||
std::map<std::string, CADSTAR_ATTRIBUTE_VALUE> m_SchAndPcbAttributes; |
|||
|
|||
/** |
|||
* Tilde (~) line |
|||
* ~[!]<Parts Library Attribute Name>(<Attribute Value>) |
|||
* Attributes related to the Part itself. It cannot be displayed |
|||
* on the PCB or schematic but it is used in CADSTAR to search for |
|||
* parts in the library browser |
|||
* Is set to read-only if exclamation mark (!) is present |
|||
*/ |
|||
std::map<std::string, CADSTAR_ATTRIBUTE_VALUE> m_PartAttributes; |
|||
}; |
|||
|
|||
|
|||
struct CADSTAR_ATTRIBUTE_VALUE |
|||
{ |
|||
bool m_ReadOnly = false; |
|||
std::string m_Value; |
|||
}; |
|||
|
|||
|
|||
struct CADSTAR_SWAP_GROUP |
|||
{ |
|||
std::optional<std::string> m_Name; |
|||
|
|||
/** |
|||
* Each gate is a list of pin numbers. The order of the pins is important |
|||
* as it defines the equivalence between gates |
|||
*/ |
|||
std::vector<std::vector<long>> m_Gates; |
|||
}; |
|||
|
|||
#endif //CADSTAR_PARTS_LIB_MODEL_H |
|||
@ -0,0 +1,462 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2023 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com> |
|||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation, either version 3 of the License, or (at your |
|||
* option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but |
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include <iostream>
|
|||
|
|||
#include "cadstar_parts_lib_parser.h"
|
|||
#include "cadstar_parts_lib_grammar.h"
|
|||
|
|||
#include <fmt.h>
|
|||
#include <set>
|
|||
#include <string>
|
|||
|
|||
using namespace CADSTAR_PARTS_LIB; |
|||
|
|||
|
|||
/**
|
|||
* Struture that will be populated by the PEGTL parser |
|||
*/ |
|||
struct CADSTAR_LIB_PARSER_STATE |
|||
{ |
|||
std::string m_CurrentString; |
|||
std::string m_CurrentAttrName; |
|||
long m_CurrentLong = 0; |
|||
std::vector<long> m_CurrentPinEquivalenceGroup; |
|||
std::set<std::string> m_CurrentElementsParsed; |
|||
bool m_ReadOnly = false; |
|||
CADSTAR_SWAP_GROUP m_CurrentSwapGroup; |
|||
CADSTAR_PART_ENTRY m_CurrentPart; |
|||
CADSTAR_PARTS_LIB_MODEL m_ParsedModel; |
|||
}; |
|||
|
|||
|
|||
// Default action: Do nothing
|
|||
template <typename Rule> |
|||
struct CADSTAR_LIB_PARSER_ACTION : tao::pegtl::nothing<Rule> |
|||
{ |
|||
}; |
|||
|
|||
|
|||
long helperStringToLong( std::string aString ) |
|||
{ |
|||
std::stringstream ss( aString ); |
|||
long number; |
|||
ss >> number; |
|||
return number; |
|||
}; |
|||
|
|||
|
|||
//
|
|||
// CONTENT TO NUMBER ACTIONS:
|
|||
// Take the current content string, convert it to a long and store it in StateVariable
|
|||
//
|
|||
#define DEFINE_CONTENT_TO_NUMBER_ACTION( Rule, StateVariable ) \
|
|||
template <> \ |
|||
struct CADSTAR_LIB_PARSER_ACTION<Rule> \ |
|||
{ \ |
|||
template <typename ActionInput> \ |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \ |
|||
{ \ |
|||
assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \ |
|||
s.StateVariable = helperStringToLong( in.string() ); \ |
|||
} \ |
|||
} |
|||
|
|||
DEFINE_CONTENT_TO_NUMBER_ACTION( CURRENT_FORMAT_NUMBER, m_ParsedModel.m_FormatNumber ); |
|||
DEFINE_CONTENT_TO_NUMBER_ACTION( PINNUM, m_CurrentLong ); |
|||
DEFINE_CONTENT_TO_NUMBER_ACTION( MAX_PIN_COUNT, m_CurrentPart.m_MaxPinCount ); |
|||
|
|||
// unfortunately the one below needs to be defined separately
|
|||
template <> |
|||
struct CADSTAR_LIB_PARSER_ACTION<EQUIVALENT_PIN> |
|||
{ |
|||
template <typename ActionInput> |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) |
|||
{ |
|||
assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); |
|||
s.m_CurrentPinEquivalenceGroup.push_back( helperStringToLong( in.string() ) ); |
|||
} |
|||
}; |
|||
|
|||
|
|||
//
|
|||
// CONTENT TO CURRENT STRING ACTIONS:
|
|||
// Take the current content string, store it in the state current string
|
|||
//
|
|||
#define DEFINE_CONTENT_TO_STRING_ACTION( Rule ) \
|
|||
template <> \ |
|||
struct CADSTAR_LIB_PARSER_ACTION<Rule> \ |
|||
{ \ |
|||
template <typename ActionInput> \ |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \ |
|||
{ \ |
|||
assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \ |
|||
s.m_CurrentString = in.string(); \ |
|||
} \ |
|||
} |
|||
|
|||
DEFINE_CONTENT_TO_STRING_ACTION( PINNAME ); |
|||
|
|||
//
|
|||
// STRING REPLACEMENT ACTIONS:
|
|||
// Take the current string in the parser state and store it in StateVariable
|
|||
//
|
|||
#define DEFINE_STRING_ACTION( Rule, StateVariable ) \
|
|||
template <> \ |
|||
struct CADSTAR_LIB_PARSER_ACTION<Rule> \ |
|||
{ \ |
|||
/* @todo : convert to use apply0 to improve performance( once fully tested ) */ \ |
|||
template <typename ActionInput> \ |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \ |
|||
{ \ |
|||
assert( in.string().size() >= s.m_CurrentString.size() ); \ |
|||
s.StateVariable = s.m_CurrentString; \ |
|||
s.m_CurrentString = ""; \ |
|||
} \ |
|||
} \ |
|||
|
|||
DEFINE_STRING_ACTION( PART_NAME, m_CurrentPart.m_Name ); |
|||
DEFINE_STRING_ACTION( PART_VERSION, m_CurrentPart.m_Version ); |
|||
DEFINE_STRING_ACTION( PART_NUMBER, m_CurrentPart.m_Number ); |
|||
DEFINE_STRING_ACTION( PART_DESCRIPTION, m_CurrentPart.m_Description ); |
|||
DEFINE_STRING_ACTION( PCB_COMPONENT, m_CurrentPart.m_Pcb_component ); |
|||
DEFINE_STRING_ACTION( PCB_ALTERNATE, m_CurrentPart.m_Pcb_alternate ); |
|||
DEFINE_STRING_ACTION( VALUE, m_CurrentPart.m_Value ); |
|||
DEFINE_STRING_ACTION( DEFINITION_NAME, m_CurrentPart.m_PartDefinitionName ); |
|||
DEFINE_STRING_ACTION( STEM, m_CurrentPart.m_ComponentStem ); |
|||
DEFINE_STRING_ACTION( SYM_ELEMENT_NAME, m_CurrentSwapGroup.m_Name ); |
|||
DEFINE_STRING_ACTION( USER_PART_ATTRIBUTE_NAME, m_CurrentAttrName ); |
|||
DEFINE_STRING_ACTION( ATTRIBUTE_NAME, m_CurrentAttrName ); |
|||
DEFINE_STRING_ACTION( ACCEPTANCE_PART_NAME, m_CurrentPart.m_AcceptancePartName ); |
|||
DEFINE_STRING_ACTION( ACCEPTANCE_TEXT, m_CurrentPart.m_AcceptanceText ); |
|||
DEFINE_STRING_ACTION( SPICE_PART_NAME, m_CurrentPart.m_SpicePartName ); |
|||
|
|||
// Might become m_SpiceModel if SPICE_SECOND is found
|
|||
DEFINE_STRING_ACTION( SPICE_FIRST, m_CurrentPart.m_SpiceValue ); |
|||
|
|||
template <> |
|||
struct CADSTAR_LIB_PARSER_ACTION<SPICE_SECOND> |
|||
{ |
|||
/* @todo : convert to use apply0 to improve performance( once fully tested ) */ |
|||
template <typename ActionInput> |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) |
|||
{ |
|||
assert( in.string().size() >= s.m_CurrentString.size() ); |
|||
s.m_CurrentPart.m_SpiceModel = s.m_CurrentPart.m_SpiceValue; // Parsed by SPICE_FIRST
|
|||
s.m_CurrentPart.m_SpiceValue = s.m_CurrentString; |
|||
s.m_CurrentString = ""; |
|||
} |
|||
}; |
|||
|
|||
// STRING SEGMENT action
|
|||
// Any strings we match, append to the current state string (the state string gets
|
|||
// reset after we extract the string to store somewhere else).
|
|||
// The reason we append is because in the fileformat, there can be line continuations,
|
|||
// which we don't want to have in the final string - saves post-processing.
|
|||
template <typename... EXCLUSION_RULES> |
|||
struct CADSTAR_LIB_PARSER_ACTION<STR_SEGMENT_EXCLUDING<EXCLUSION_RULES...>> |
|||
{ |
|||
template <typename ActionInput> |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) |
|||
{ |
|||
s.m_CurrentString += in.string(); |
|||
} |
|||
}; |
|||
|
|||
// PART_ENTRY action
|
|||
// We just push the part to the vector of parts in our state
|
|||
template <> |
|||
struct CADSTAR_LIB_PARSER_ACTION<PART_ENTRY> |
|||
{ |
|||
static void apply0( CADSTAR_LIB_PARSER_STATE& s ) |
|||
{ |
|||
assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); |
|||
//Finish the entry
|
|||
s.m_ParsedModel.m_PartEntries.push_back( s.m_CurrentPart ); |
|||
s.m_CurrentPart = CADSTAR_PART_ENTRY(); |
|||
s.m_CurrentElementsParsed.clear(); |
|||
// Todo-we could add progress reporting here?
|
|||
} |
|||
}; |
|||
|
|||
|
|||
template <> |
|||
struct CADSTAR_LIB_PARSER_ACTION<READONLY> |
|||
{ |
|||
static void apply0( CADSTAR_LIB_PARSER_STATE& s ) |
|||
{ |
|||
assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); |
|||
s.m_ReadOnly = true; |
|||
} |
|||
}; |
|||
|
|||
|
|||
//
|
|||
// SINGLE RULE ACTIONS:
|
|||
// Make sure that this rule is only matched once per part and throw a parse error
|
|||
// when this is not the case.
|
|||
//
|
|||
#define DECLARE_SINGLE_MATCH_RULE( Rule, ExtraCode ) \
|
|||
template <> \ |
|||
struct CADSTAR_LIB_PARSER_ACTION<Rule> \ |
|||
{ \ |
|||
template <typename ActionInput> \ |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \ |
|||
{ \ |
|||
assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \ |
|||
\ |
|||
if( s.m_CurrentElementsParsed.count( #Rule ) ) \ |
|||
{ \ |
|||
throw parse_error( #Rule \ |
|||
" was already defined for this part!", \ |
|||
in ); \ |
|||
} \ |
|||
\ |
|||
s.m_CurrentElementsParsed.insert( #Rule ); \ |
|||
ExtraCode; \ |
|||
} \ |
|||
} \ |
|||
|
|||
DECLARE_SINGLE_MATCH_RULE( PART_VALUE, ); |
|||
DECLARE_SINGLE_MATCH_RULE( DFN_LINE, ); |
|||
DECLARE_SINGLE_MATCH_RULE( NGS_LINE, s.m_CurrentPart.m_GateSwappingAllowed = false ); |
|||
DECLARE_SINGLE_MATCH_RULE( NPV_LINE, s.m_CurrentPart.m_PinsVisible = false ); |
|||
DECLARE_SINGLE_MATCH_RULE( STM_LINE, ); |
|||
DECLARE_SINGLE_MATCH_RULE( MXP_LINE, ); |
|||
DECLARE_SINGLE_MATCH_RULE( SPI_LINE, ); |
|||
DECLARE_SINGLE_MATCH_RULE( PAC_LINE, ); |
|||
|
|||
|
|||
//@todo remove once complete
|
|||
template <> |
|||
struct CADSTAR_LIB_PARSER_ACTION<UNMATCHED_CONTENT> |
|||
{ |
|||
static void apply0( CADSTAR_LIB_PARSER_STATE& s ) { s.m_CurrentString = ""; } |
|||
}; |
|||
|
|||
|
|||
template <> |
|||
struct CADSTAR_LIB_PARSER_ACTION<PINNAME_ENTRY> |
|||
{ |
|||
static void apply0( CADSTAR_LIB_PARSER_STATE& s ) |
|||
{ |
|||
assert( s.m_CurrentAttrName == "" ); |
|||
// m_CurrentLong should have been parsed as part of the PINNUM action
|
|||
// m_CurrentString should have been parsed as part of the PINNAME action
|
|||
s.m_CurrentPart.m_PinNamesMap.insert( { s.m_CurrentLong, s.m_CurrentString } ); |
|||
s.m_CurrentString = ""; |
|||
} |
|||
}; |
|||
|
|||
|
|||
template <> |
|||
struct CADSTAR_LIB_PARSER_ACTION<PINLABEL_ENTRY> |
|||
{ |
|||
static void apply0( CADSTAR_LIB_PARSER_STATE& s ) |
|||
{ |
|||
assert( s.m_CurrentAttrName == "" ); |
|||
// m_CurrentLong should have been parsed as part of the PINNUM action
|
|||
// m_CurrentString should have been parsed as part of the PINLABEL action
|
|||
s.m_CurrentPart.m_PinLabelsMap.insert( { s.m_CurrentLong, s.m_CurrentString } ); |
|||
s.m_CurrentString = ""; |
|||
} |
|||
}; |
|||
|
|||
//
|
|||
// PIN EQUIVALENCE GROUP ACTIONS:
|
|||
// Take the current m_CurrentPinEquivalenceGroup in the parser state and store it in StateVariable
|
|||
// then clear m_CurrentPinEquivalenceGroup.
|
|||
// Note that m_CurrentPinEquivalenceGroup should have been parsed as part of EQUIVALENT_PIN action
|
|||
//
|
|||
#define DEFINE_PIN_GROUP_ACTION( Rule, StateVariable ) \
|
|||
template <> \ |
|||
struct CADSTAR_LIB_PARSER_ACTION<Rule> \ |
|||
{ \ |
|||
static void apply0( CADSTAR_LIB_PARSER_STATE& s ) \ |
|||
{ \ |
|||
assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \ |
|||
s.StateVariable.push_back( s.m_CurrentPinEquivalenceGroup ); \ |
|||
s.m_CurrentPinEquivalenceGroup.clear(); \ |
|||
} \ |
|||
} \ |
|||
|
|||
DEFINE_PIN_GROUP_ACTION( EQUIVALENT_PINS_GROUP, m_CurrentPart.m_PinEquivalences ); |
|||
DEFINE_PIN_GROUP_ACTION( INTERNAL_SWAP_GATE, m_CurrentSwapGroup.m_Gates ); |
|||
DEFINE_PIN_GROUP_ACTION( EXTERNAL_SWAP_GATE, m_CurrentSwapGroup.m_Gates ); |
|||
|
|||
|
|||
//
|
|||
// SWAP GROUP ACTIONS:
|
|||
// Take the current m_CurrentSwapGroup in the parser state and store it in StateVariable
|
|||
// then reset m_CurrentSwapGroup.
|
|||
//
|
|||
#define DEFINE_SWAP_GROUP_ACTION( Rule, StateVariable ) \
|
|||
template <> \ |
|||
struct CADSTAR_LIB_PARSER_ACTION<Rule> \ |
|||
{ \ |
|||
static void apply0( CADSTAR_LIB_PARSER_STATE& s ) \ |
|||
{ \ |
|||
assert( s.m_CurrentString == "" && s.m_CurrentAttrName == "" ); \ |
|||
s.StateVariable.push_back( s.m_CurrentSwapGroup ); \ |
|||
s.m_CurrentSwapGroup = CADSTAR_SWAP_GROUP(); \ |
|||
} \ |
|||
} \ |
|||
|
|||
DEFINE_SWAP_GROUP_ACTION( INTERNAL_SWAP_GROUP, m_CurrentPart.m_InternalSwapGroup ); |
|||
DEFINE_SWAP_GROUP_ACTION( EXTERNAL_SWAP_GROUP, m_CurrentPart.m_ExternalSwapGroup ); |
|||
|
|||
|
|||
/**
|
|||
* The format allows user defined "part" attrbutes, but the ones listed here are in-built with |
|||
* special meaning |
|||
*/ |
|||
static const std::set<std::string> ReservedWordsStarLines = { "VALUE", "PNM", "PLB", "EQU", "SYM", |
|||
"INT", "EXT", "DFN", "NGS", "NPV", |
|||
"STM", "MXP", "SPI", "PAC" }; |
|||
|
|||
template <> |
|||
struct CADSTAR_LIB_PARSER_ACTION<USER_PART_ATTRIBUTE> |
|||
{ |
|||
template <typename ActionInput> |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) |
|||
{ |
|||
if( s.m_CurrentPart.m_UserAttributes.count( s.m_CurrentAttrName ) ) |
|||
{ |
|||
throw parse_error( fmt::format( "Duplicate attribute name '{}'", s.m_CurrentAttrName ), |
|||
in ); |
|||
} |
|||
|
|||
if( ReservedWordsStarLines.count( s.m_CurrentAttrName ) ) |
|||
{ |
|||
throw parse_error( |
|||
fmt::format( |
|||
"Invalid use of in-built attribute name '{}'. Either the attribute " |
|||
"was already defined for this part or it has an unexpected syntax.", |
|||
s.m_CurrentAttrName ), |
|||
in ); |
|||
} |
|||
|
|||
s.m_CurrentPart.m_UserAttributes.insert( { s.m_CurrentAttrName, s.m_CurrentString } ); |
|||
s.m_CurrentAttrName = ""; |
|||
s.m_CurrentString = ""; |
|||
} |
|||
}; |
|||
|
|||
#define DEFINE_ATTRIBUTE_ACTION( Rule, StateVariable ) \
|
|||
template <> \ |
|||
struct CADSTAR_LIB_PARSER_ACTION<Rule> \ |
|||
{ \ |
|||
template <typename ActionInput> \ |
|||
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s ) \ |
|||
{ \ |
|||
if( s.StateVariable.count( s.m_CurrentAttrName ) ) \ |
|||
{ \ |
|||
throw parse_error( \ |
|||
fmt::format( "Duplicate attribute name '{}'", s.m_CurrentAttrName ), \ |
|||
in ); \ |
|||
} \ |
|||
\ |
|||
CADSTAR_ATTRIBUTE_VALUE val; \ |
|||
val.m_ReadOnly = s.m_ReadOnly; \ |
|||
val.m_Value = s.m_CurrentString; \ |
|||
\ |
|||
s.StateVariable.insert( { s.m_CurrentAttrName, val } ); \ |
|||
s.m_CurrentAttrName = ""; \ |
|||
s.m_CurrentString = ""; \ |
|||
s.m_ReadOnly = false; \ |
|||
} \ |
|||
} \ |
|||
|
|||
DEFINE_ATTRIBUTE_ACTION( SCM_ATTRIBUTE, m_CurrentPart.m_SchAttributes ); |
|||
DEFINE_ATTRIBUTE_ACTION( PCB_ATTRIBUTE, m_CurrentPart.m_PcbAttributes ); |
|||
DEFINE_ATTRIBUTE_ACTION( PART_ATTRIBUTE, m_CurrentPart.m_PartAttributes ); |
|||
DEFINE_ATTRIBUTE_ACTION( SCH_PCB_ATTRIBUTE, m_CurrentPart.m_SchAndPcbAttributes ); |
|||
|
|||
template <typename INPUT_TYPE> |
|||
bool checkHeaderHelper( INPUT_TYPE& aInput ) |
|||
{ |
|||
try |
|||
{ |
|||
if( !parse<VALID_HEADER>( aInput ) ) |
|||
return false; |
|||
} |
|||
catch( const parse_error& e ) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool CADSTAR_PARTS_LIB_PARSER::CheckContentHeader( const std::string& aSource ) const |
|||
{ |
|||
string_input in( aSource, "from_content" ); |
|||
return checkHeaderHelper( in ); |
|||
} |
|||
|
|||
|
|||
bool CADSTAR_PARTS_LIB_PARSER::CheckFileHeader( const std::filesystem::path& aPath ) const |
|||
{ |
|||
file_input in( aPath ); |
|||
return checkHeaderHelper( in ); |
|||
} |
|||
|
|||
|
|||
template<typename INPUT_TYPE> |
|||
CADSTAR_PARTS_LIB_MODEL readCadstarHelper( INPUT_TYPE& aInput ) |
|||
{ |
|||
CADSTAR_LIB_PARSER_STATE s; |
|||
|
|||
try |
|||
{ |
|||
// Todo: We could reserve space for the partEntries vector
|
|||
// to improve performance? E.g.:
|
|||
// s.m_ParsedModel.m_PartEntries.reserve( expectedNumParts );
|
|||
|
|||
if( !parse<GRAMMAR, CADSTAR_LIB_PARSER_ACTION>( aInput, s ) ) |
|||
printf( "Some error occurred!\n" ); |
|||
} |
|||
catch( const parse_error& e ) |
|||
{ |
|||
const auto p = e.positions().front(); |
|||
std::cerr << "Error at line " << p.line << ", column " << p.column << std::endl |
|||
<< aInput.line_at( p ) << std::endl |
|||
<< std::setw( p.column ) << '^' << std::endl |
|||
<< e.message() << std::endl; |
|||
} |
|||
|
|||
return s.m_ParsedModel; |
|||
} |
|||
|
|||
|
|||
CADSTAR_PARTS_LIB_MODEL CADSTAR_PARTS_LIB_PARSER::ReadContent( const std::string& aSource ) const |
|||
{ |
|||
string_input in( aSource, "from_content" ); |
|||
return readCadstarHelper( in ); |
|||
} |
|||
|
|||
|
|||
CADSTAR_PARTS_LIB_MODEL |
|||
CADSTAR_PARTS_LIB_PARSER::ReadFile( const std::filesystem::path& aPath ) const |
|||
{ |
|||
file_input in( aPath ); |
|||
return readCadstarHelper( in ); |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2023 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com> |
|||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation, either version 3 of the License, or (at your |
|||
* option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but |
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef CADSTAR_PARTS_LIB_PARSER_H |
|||
#define CADSTAR_PARTS_LIB_PARSER_H |
|||
|
|||
#include <filesystem> |
|||
#include <string> |
|||
|
|||
#include "cadstar_parts_lib_model.h" |
|||
|
|||
class CADSTAR_PARTS_LIB_PARSER |
|||
{ |
|||
public: |
|||
CADSTAR_PARTS_LIB_PARSER(){}; |
|||
~CADSTAR_PARTS_LIB_PARSER(){}; |
|||
|
|||
bool CheckContentHeader( const std::string& aSource ) const; |
|||
|
|||
bool CheckFileHeader( const std::filesystem::path& aPath ) const; |
|||
|
|||
bool CheckFileHeader( const std::string& aPath ) const |
|||
{ |
|||
return CheckFileHeader( std::filesystem::path( aPath ) ); |
|||
}; |
|||
|
|||
|
|||
CADSTAR_PARTS_LIB_MODEL ReadContent( const std::string& aSource ) const; |
|||
|
|||
CADSTAR_PARTS_LIB_MODEL ReadFile( const std::filesystem::path& aPath ) const; |
|||
|
|||
CADSTAR_PARTS_LIB_MODEL ReadFile( const std::string& aPath ) const |
|||
{ |
|||
return ReadFile( std::filesystem::path( aPath ) ); |
|||
}; |
|||
}; |
|||
|
|||
#endif //CADSTAR_PARTS_LIB_PARSER_H |
|||
|
|||
1933
qa/data/eeschema/plugins/cadstar/cadstarDummy.lib
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,50 @@ |
|||
|
|||
from math import factorial |
|||
from itertools import permutations,islice |
|||
|
|||
with open('cadstarDummy.lib', 'w', newline='\r\n') as f: |
|||
f.write('# FORMAT 32\n') |
|||
|
|||
for i in range(100): |
|||
f.write('\n') |
|||
f.write(f'.PartName{i} ({i*5}) :2 ;Part {i} Description\n') |
|||
f.write(f'FOOTPRINT{i} (variant{i*5})\n') |
|||
|
|||
currentPartData=[] |
|||
|
|||
currentPartData.append(f'*VALUE {i} uH\n') |
|||
currentPartData.append(f'*DFN PartName{i}\n') |
|||
|
|||
if i%10 == 1: |
|||
currentPartData.append(f'*NGS\n') |
|||
|
|||
if i%5 == 1: |
|||
currentPartData.append(f'*NPV\n') |
|||
|
|||
|
|||
currentPartData.append('*STM L\n') |
|||
currentPartData.append(f'*MXP {i+10}\n') |
|||
currentPartData.append(f'*SPI (PartName{i}) {i}uH\n') |
|||
currentPartData.append(f'*PAC (PartName{i}) Acceptance{i}\n') |
|||
currentPartData.append(f'*UserFieldpartNo {i*5}\n') |
|||
currentPartData.append(f'*"UserFieldpartNoCreated by" Person{i}\n') |
|||
currentPartData.append(f'$"SCH val1" (val{i})\n') |
|||
currentPartData.append(f'$!SCH val2 (readOnly{i})\n') |
|||
currentPartData.append(f'%"PCB val1" (val{i})\n') |
|||
currentPartData.append(f'%!PCB val2 (readOnly{i})\n') |
|||
currentPartData.append(f'~"Part val1" (val{i})\n') |
|||
currentPartData.append(f'~!Part val2 (readOnly{i})\n') |
|||
currentPartData.append(f'@SCH and PCB val1 (val{i})\n') |
|||
currentPartData.append(f'@!SCH and PCB val2 (readOnly{i})\n') |
|||
#currentPartData.append(f'Symbol{i}\n') |
|||
#currentPartData.append('1.0 2.0\n') |
|||
|
|||
# Change ordering to test parser works |
|||
nth=(i*101)%factorial(len(currentPartData)) # make a permutation that exists |
|||
permutatedData=next(islice(permutations(currentPartData), nth, None)) |
|||
|
|||
for data in permutatedData: |
|||
f.write(data) |
|||
|
|||
|
|||
f.write('\n.END\n') |
|||
@ -0,0 +1,390 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2022-2023 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com> |
|||
* Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation, either version 3 of the License, or (at your |
|||
* option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, but |
|||
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include <iostream>
|
|||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
|||
|
|||
#include <pegtl/contrib/analyze.hpp>
|
|||
#include <pegtl/contrib/trace.hpp>
|
|||
|
|||
// Modules under test:
|
|||
#include <common/plugins/cadstar/cadstar_parts_lib_grammar.h>
|
|||
#include <common/plugins/cadstar/cadstar_parts_lib_parser.h>
|
|||
|
|||
|
|||
BOOST_AUTO_TEST_SUITE( CadstarPartParser ); |
|||
|
|||
|
|||
static std::string getCadstarTestFile( const std::string& aFile ) |
|||
{ |
|||
return KI_TEST::GetEeschemaTestDataDir() + "/plugins/cadstar/" + aFile; |
|||
} |
|||
|
|||
|
|||
BOOST_AUTO_TEST_CASE( AnalyzeGrammar ) |
|||
{ |
|||
// Verify the grammar has no loops without progress and other issues
|
|||
// See: https://github.com/taocpp/PEGTL/blob/3.2.7/doc/Grammar-Analysis.md
|
|||
const std::size_t grammarIssues = tao::pegtl::analyze<CADSTAR_PARTS_LIB::GRAMMAR>(); |
|||
BOOST_CHECK_EQUAL( grammarIssues, 0 ); |
|||
|
|||
const std::size_t headerIssues = tao::pegtl::analyze<CADSTAR_PARTS_LIB::VALID_HEADER>(); |
|||
BOOST_CHECK_EQUAL( headerIssues, 0 ); |
|||
} |
|||
|
|||
|
|||
struct CHECK_HEADER_CASE |
|||
{ |
|||
std::string m_CaseName; |
|||
std::string m_Content; |
|||
bool m_ExpectedResult; |
|||
}; |
|||
|
|||
|
|||
static const std::vector<CHECK_HEADER_CASE> check_header_cases = |
|||
{ |
|||
{ "1: Normal header", "# Format 32\r\n", true }, |
|||
{ "2: Normal header, extra content", "# Format 32\r\nExtraUnrelatedContent", true }, |
|||
{ "3: Normal header extra spaces (1)", "# Format 32\r\n", true }, |
|||
{ "4: Normal header extra spaces (2)", "# FORMAT 32\r\n", true }, |
|||
{ "5: Normal header on 2nd line", "\r\n# Format 32\r\n", false }, |
|||
{ "6: Normal header prepended", "+# Format 32\r\n", false }, |
|||
{ "7: Normal header prepended spaces", " # Format 32\r\n", false }, |
|||
|
|||
// There appear to be some files on the internet that just don't have a header and
|
|||
// start straight away with the part definitions.
|
|||
{ "8: No header", ".PART-NAME :1 ;Part Descr\r\n", true }, |
|||
{ "9: No header, extra content", ".PART-NAME :1 ;Part Descr\r\nExtra", true }, |
|||
{ "10: No header, on 2nd line", "\r\n.PART-NAME :1 ;Part Descr\r\n", true }, |
|||
{ "11: No header, on 3rd line", "\r\n\r\n.PART-NAME :1 ;Part Descr\r\n", true }, |
|||
{ "12: No header, on 4th line", "\r\n\r\n.PART-NAME :1 ;Part Descr\r\n", true }, |
|||
{ "13: No header, on 4th line", "\r\n\r\n\r\n\r\n.PART-NAME :1 ;P Descr\r\n", true }, |
|||
{ "14: No header, on 5th line", "\r\n\r\n\r\n\r\n\r\n.P-NAME :1 ;PDescr\r\n", true }, |
|||
{ "15: No header, on 6th line", "\r\n\r\n\r\n\r\n\r\n\r\n.P-NAM :1 ;PDes\r\n", false }, |
|||
{ "16: No header, space prepend", " .PART-NAME :1 ;Part Descr\r\n", false }, |
|||
{ "17: No header, spaces & 2nd line", " \r\n.PART-NAME :1 ;Part Descr\r\n", true }, |
|||
{ "18: No header, 2nd line & space", " \r\n .PART-NAME :1 ;Part Descr\r\n", false }, |
|||
}; |
|||
|
|||
|
|||
BOOST_AUTO_TEST_CASE( CheckHeader ) |
|||
{ |
|||
for( const auto& c : check_header_cases ) |
|||
{ |
|||
BOOST_TEST_INFO_SCOPE( c.m_CaseName ); |
|||
CADSTAR_PARTS_LIB_PARSER p; |
|||
|
|||
BOOST_CHECK_EQUAL( p.CheckContentHeader( c.m_Content ), c.m_ExpectedResult ); |
|||
} |
|||
} |
|||
|
|||
|
|||
BOOST_AUTO_TEST_CASE( ReadFile ) |
|||
{ |
|||
CADSTAR_PARTS_LIB_PARSER p; |
|||
|
|||
// Test a programatically generated file (see writeCadstarFile.py)
|
|||
auto ret = p.ReadFile( getCadstarTestFile( "cadstarDummy.lib" ) ); |
|||
|
|||
BOOST_CHECK_EQUAL( ret.m_FormatNumber, 32 ); |
|||
BOOST_CHECK_EQUAL( ret.m_PartEntries.size(), 100 ); |
|||
|
|||
int i = 0; |
|||
|
|||
for( CADSTAR_PART_ENTRY& partEntry : ret.m_PartEntries ) |
|||
{ |
|||
// Part header
|
|||
BOOST_CHECK_EQUAL( partEntry.m_Name, "PartName" + std::to_string( i ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_Number, std::to_string( i * 5 ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_Version, std::to_string( 2 ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_Description, |
|||
"Part " + std::to_string( i ) + " Description" ); |
|||
|
|||
BOOST_CHECK_EQUAL( partEntry.m_Pcb_component, "FOOTPRINT" + std::to_string( i ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_Pcb_alternate, "variant" + std::to_string( i * 5 ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_Value, std::to_string( i ) + " uH" ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_ComponentStem, "L" ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_MaxPinCount, i + 10 ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_GateSwappingAllowed, i % 10 != 1 ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_PinsVisible, i % 5 != 1 ); |
|||
|
|||
BOOST_CHECK_EQUAL( partEntry.m_SpicePartName, "PartName" + std::to_string( i ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_SpiceModel, std::optional<std::string>() ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_SpiceValue, std::to_string( i ) + "uH" ); |
|||
|
|||
BOOST_CHECK_EQUAL( partEntry.m_AcceptancePartName, "PartName" + std::to_string( i ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_AcceptanceText, "Acceptance" + std::to_string( i ) ); |
|||
|
|||
// User part attributes (* lines)
|
|||
BOOST_CHECK_EQUAL( partEntry.m_UserAttributes["UserFieldpartNo"], |
|||
std::to_string( i * 5 ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_UserAttributes["UserFieldpartNoCreated by"], |
|||
"Person" + std::to_string( i ) ); |
|||
|
|||
// SCH attributes ($ lines)
|
|||
BOOST_CHECK_EQUAL( partEntry.m_SchAttributes["SCH val1"].m_ReadOnly, false ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_SchAttributes["SCH val1"].m_Value, |
|||
"val" + std::to_string( i ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_SchAttributes["SCH val2"].m_ReadOnly, true ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_SchAttributes["SCH val2"].m_Value, |
|||
"readOnly" + std::to_string( i ) ); |
|||
|
|||
// PCB attributes (% lines)
|
|||
BOOST_CHECK_EQUAL( partEntry.m_PcbAttributes["PCB val1"].m_ReadOnly, false ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_PcbAttributes["PCB val1"].m_Value, |
|||
"val" + std::to_string( i ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_PcbAttributes["PCB val2"].m_ReadOnly, true ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_PcbAttributes["PCB val2"].m_Value, |
|||
"readOnly" + std::to_string( i ) ); |
|||
|
|||
// Parts attributes (~ lines)
|
|||
BOOST_CHECK_EQUAL( partEntry.m_PartAttributes["Part val1"].m_ReadOnly, false ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_PartAttributes["Part val1"].m_Value, |
|||
"val" + std::to_string( i ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_PartAttributes["Part val2"].m_ReadOnly, true ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_PartAttributes["Part val2"].m_Value, |
|||
"readOnly" + std::to_string( i ) ); |
|||
|
|||
// PCB and SCH attributes (@ lines)
|
|||
BOOST_CHECK_EQUAL( partEntry.m_SchAndPcbAttributes["SCH and PCB val1"].m_ReadOnly, false ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_SchAndPcbAttributes["SCH and PCB val1"].m_Value, |
|||
"val" + std::to_string( i ) ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_SchAndPcbAttributes["SCH and PCB val2"].m_ReadOnly, true ); |
|||
BOOST_CHECK_EQUAL( partEntry.m_SchAndPcbAttributes["SCH and PCB val2"].m_Value, |
|||
"readOnly" + std::to_string( i ) ); |
|||
|
|||
i++; |
|||
} |
|||
} |
|||
|
|||
|
|||
BOOST_AUTO_TEST_CASE( ReadContent ) |
|||
{ |
|||
std::string test = |
|||
"# Format 32\r\n" |
|||
"\r\n" |
|||
"\r\n" |
|||
" \r\n" |
|||
"\r\n" |
|||
"\r\n" |
|||
"\r\n" |
|||
".<Part name> (<Part number>):<Part version>;<Description>\r\n" |
|||
"<PCB Component Refname> (<PCB Alternate Refname>)\r\n" |
|||
"*VALUE <Value>\r\n" |
|||
"*PNM 1=A1 2=A2 3=B1 4=B2 5=C1 6=C2\r\n" // <PinId>=<Pinname> <PinId>=<Pinname> etc
|
|||
"*PLB 1=\"VCC\" 2=\"GND\" 3=\"'EN\" 4=\"OUT\" 5=\"OUT\" 6=\"IN\"\r\n" // <id>=<label>
|
|||
"*EQU 4=5, 6=7=8, 9=10=11\r\n" // <PinId>=<PinId>=<PinId> <PinId>=<PinId> etc ...
|
|||
"*SYM Group1\r\n" |
|||
"*INT 4 5\r\n" |
|||
"*INT 6 7\r\n" |
|||
"*SYM Group2\r\n" |
|||
"*EXT 1 2\r\n" |
|||
"*EXT 4 7\r\n" |
|||
"*DFN <Definition name>\r\n" |
|||
"*NGS\r\n" |
|||
"*NPV\r\n" |
|||
"*STM <Component name stem>\r\n" |
|||
"*MXP 32\r\n" //<Maximum number of connector pins>
|
|||
"*SPI (<Part name>) <Model> <Value>\r\n" |
|||
"*PAC (<Part name>) <Acceptance Text>\r\n" |
|||
"*userAttribute userAttributeVal\r\n" |
|||
"*\"User spaced name\" userSpacedAttributeVal\r\n" |
|||
"$<SCM Attribute name1>(<Attribute value for name1>)\r\n" |
|||
"$!<SCM Attribute name2>(\"<Attribute value for name2>\")\r\n" |
|||
"%<PCB Attribute name1>(\"<Attribute value1>\")\r\n" |
|||
"%!\"<PCB Attribute name2>\"(<Attribute value2>)\r\n" |
|||
"~<Parts Attribute name1>(<Attribute value1>)\r\n" |
|||
"~!<Parts Attribute name2>(<Attribute value2>)\r\n" |
|||
"@<SCM/PCB Attribute name1>(<Attribute value1>)\r\n" |
|||
"@!<SCM/PCB Attribute name2>(<Attribute value2>)\r\n"; |
|||
//"etc ...\r\n"
|
|||
//"<SCM Symbol Refname> (<SCM Alternate Refname>)\r\n"
|
|||
//"<PinIdentifier>.<Position> !<Pintype> :<Loading> etc\r\n"
|
|||
//"<PinIdentifier>.<Position> !<Pintype> :<Loading> etc ...\r\n"
|
|||
//"etc ...\r\n"
|
|||
//"/<Signame> <PinIdentifier>.<Position>!<Pintype>:<Loading>\r\n"
|
|||
//"/<Signame> <PinIdentifier>.<Position>!<Pintype>:<Loading>\r\n";
|
|||
|
|||
CADSTAR_PARTS_LIB_PARSER csParser; |
|||
CADSTAR_PARTS_LIB_MODEL result = csParser.ReadContent( test ); |
|||
|
|||
std::optional<std::string> nullOptString; |
|||
|
|||
BOOST_CHECK_EQUAL( result.m_FormatNumber, 32 ); |
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries.size(), 1 ); |
|||
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_Name, "<Part name>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_Number, "<Part number>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_Version, "<Part version>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_Description, "<Description>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_Pcb_component, "<PCB Component Refname>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_Pcb_alternate, "<PCB Alternate Refname>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_Value, "<Value>" ); |
|||
|
|||
// Check pin names (*PNM)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_PinNamesMap.size(), 6 ); |
|||
|
|||
std::map<long, std::string>& pinNames = result.m_PartEntries[0].m_PinNamesMap; |
|||
BOOST_CHECK_EQUAL( pinNames[1], "A1" ); |
|||
BOOST_CHECK_EQUAL( pinNames[2], "A2" ); |
|||
BOOST_CHECK_EQUAL( pinNames[3], "B1" ); |
|||
BOOST_CHECK_EQUAL( pinNames[4], "B2" ); |
|||
BOOST_CHECK_EQUAL( pinNames[5], "C1" ); |
|||
BOOST_CHECK_EQUAL( pinNames[6], "C2" ); |
|||
|
|||
// Check pin labels (*PLB)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_PinLabelsMap.size(), 6 ); |
|||
|
|||
std::map<long, std::string>& pinlabels = result.m_PartEntries[0].m_PinLabelsMap; |
|||
BOOST_CHECK_EQUAL( pinlabels[1], "VCC" ); |
|||
BOOST_CHECK_EQUAL( pinlabels[2], "GND" ); |
|||
BOOST_CHECK_EQUAL( pinlabels[3], "'EN" ); |
|||
BOOST_CHECK_EQUAL( pinlabels[4], "OUT" ); |
|||
BOOST_CHECK_EQUAL( pinlabels[5], "OUT" ); |
|||
BOOST_CHECK_EQUAL( pinlabels[6], "IN" ); |
|||
|
|||
// Check pin equivalences (*EQU)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_PinEquivalences.size(), 3 ); |
|||
|
|||
std::vector<std::vector<long>>& pinEqus = result.m_PartEntries[0].m_PinEquivalences; |
|||
BOOST_REQUIRE_EQUAL( pinEqus[0].size(), 2 ); |
|||
BOOST_REQUIRE_EQUAL( pinEqus[1].size(), 3 ); |
|||
BOOST_REQUIRE_EQUAL( pinEqus[2].size(), 3 ); |
|||
|
|||
BOOST_CHECK_EQUAL( pinEqus[0][0], 4 ); |
|||
BOOST_CHECK_EQUAL( pinEqus[0][1], 5 ); |
|||
BOOST_CHECK_EQUAL( pinEqus[1][0], 6 ); |
|||
BOOST_CHECK_EQUAL( pinEqus[1][1], 7 ); |
|||
BOOST_CHECK_EQUAL( pinEqus[1][2], 8 ); |
|||
BOOST_CHECK_EQUAL( pinEqus[2][0], 9 ); |
|||
BOOST_CHECK_EQUAL( pinEqus[2][1], 10 ); |
|||
BOOST_CHECK_EQUAL( pinEqus[2][2], 11 ); |
|||
|
|||
// Check internal swap groups equivalences (*INT)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_InternalSwapGroup.size(), 1 ); |
|||
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_InternalSwapGroup[0].m_Name, "Group1" ); |
|||
|
|||
std::vector<std::vector<long>>& intgates = |
|||
result.m_PartEntries[0].m_InternalSwapGroup[0].m_Gates; |
|||
|
|||
BOOST_REQUIRE_EQUAL( intgates[0].size(), 2 ); |
|||
BOOST_REQUIRE_EQUAL( intgates[1].size(), 2 ); |
|||
|
|||
BOOST_CHECK_EQUAL( intgates[0][0], 4 ); |
|||
BOOST_CHECK_EQUAL( intgates[0][1], 5 ); |
|||
BOOST_CHECK_EQUAL( intgates[1][0], 6 ); |
|||
BOOST_CHECK_EQUAL( intgates[1][1], 7 ); |
|||
|
|||
// Check external swap groups equivalences (*EXT)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_ExternalSwapGroup.size(), 1 ); |
|||
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_ExternalSwapGroup[0].m_Name, "Group2" ); |
|||
|
|||
std::vector<std::vector<long>>& extgates = |
|||
result.m_PartEntries[0].m_ExternalSwapGroup[0].m_Gates; |
|||
|
|||
BOOST_REQUIRE_EQUAL( extgates[0].size(), 2 ); |
|||
BOOST_REQUIRE_EQUAL( extgates[1].size(), 2 ); |
|||
|
|||
BOOST_CHECK_EQUAL( extgates[0][0], 1 ); |
|||
BOOST_CHECK_EQUAL( extgates[0][1], 2 ); |
|||
BOOST_CHECK_EQUAL( extgates[1][0], 4 ); |
|||
BOOST_CHECK_EQUAL( extgates[1][1], 7 ); |
|||
|
|||
// Check part Definition
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_PartDefinitionName, "<Definition name>" ); |
|||
|
|||
// Check *NGS
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_GateSwappingAllowed, false ); |
|||
|
|||
// Check *NPV
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_PinsVisible, false ); |
|||
|
|||
// Check *STM
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_ComponentStem, "<Component name stem>" ); |
|||
|
|||
// Check *MXP
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_MaxPinCount, 32 ); |
|||
|
|||
// Check *SPI
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_SpicePartName, "<Part name>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_SpiceModel, "<Model>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_SpiceValue, "<Value>" ); |
|||
|
|||
// Check *PAC
|
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_AcceptancePartName, "<Part name>" ); |
|||
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_AcceptanceText, "<Acceptance Text>" ); |
|||
|
|||
// Check user attributes (* lines)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_UserAttributes.size(), 2 ); |
|||
|
|||
std::map<std::string, std::string>& userAtts = result.m_PartEntries[0].m_UserAttributes; |
|||
BOOST_CHECK_EQUAL( userAtts["userAttribute"], "userAttributeVal" ); |
|||
BOOST_CHECK_EQUAL( userAtts["User spaced name"], "userSpacedAttributeVal" ); |
|||
|
|||
// Check SCH attributes ($ lines)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_SchAttributes.size(), 2 ); |
|||
|
|||
std::map<std::string, CADSTAR_ATTRIBUTE_VALUE>& schAtts = |
|||
result.m_PartEntries[0].m_SchAttributes; |
|||
|
|||
BOOST_CHECK_EQUAL( schAtts["<SCM Attribute name1>"].m_ReadOnly, false ); |
|||
BOOST_CHECK_EQUAL( schAtts["<SCM Attribute name1>"].m_Value, "<Attribute value for name1>" ); |
|||
|
|||
BOOST_CHECK_EQUAL( schAtts["<SCM Attribute name2>"].m_ReadOnly, true ); |
|||
BOOST_CHECK_EQUAL( schAtts["<SCM Attribute name2>"].m_Value, "<Attribute value for name2>" ); |
|||
|
|||
// Check PCB attributes (% lines)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_PcbAttributes.size(), 2 ); |
|||
|
|||
std::map<std::string, CADSTAR_ATTRIBUTE_VALUE>& pcbAtts = |
|||
result.m_PartEntries[0].m_PcbAttributes; |
|||
|
|||
BOOST_CHECK_EQUAL( pcbAtts["<PCB Attribute name1>"].m_ReadOnly, false ); |
|||
BOOST_CHECK_EQUAL( pcbAtts["<PCB Attribute name1>"].m_Value, "<Attribute value1>" ); |
|||
|
|||
BOOST_CHECK_EQUAL( pcbAtts["<PCB Attribute name2>"].m_ReadOnly, true ); |
|||
BOOST_CHECK_EQUAL( pcbAtts["<PCB Attribute name2>"].m_Value, "<Attribute value2>" ); |
|||
|
|||
// Check Part attributes (~ lines)
|
|||
BOOST_REQUIRE_EQUAL( result.m_PartEntries[0].m_PartAttributes.size(), 2 ); |
|||
|
|||
std::map<std::string, CADSTAR_ATTRIBUTE_VALUE>& partAtts = |
|||
result.m_PartEntries[0].m_PartAttributes; |
|||
|
|||
BOOST_CHECK_EQUAL( partAtts["<Parts Attribute name1>"].m_ReadOnly, false ); |
|||
BOOST_CHECK_EQUAL( partAtts["<Parts Attribute name1>"].m_Value, "<Attribute value1>" ); |
|||
|
|||
BOOST_CHECK_EQUAL( partAtts["<Parts Attribute name2>"].m_ReadOnly, true ); |
|||
BOOST_CHECK_EQUAL( partAtts["<Parts Attribute name2>"].m_Value, "<Attribute value2>" ); |
|||
|
|||
// Check Compbined Sch/PCB attributes (@ lines)
|
|||
std::map<std::string, CADSTAR_ATTRIBUTE_VALUE>schAndPcbAtts = |
|||
result.m_PartEntries[0].m_SchAndPcbAttributes; |
|||
|
|||
BOOST_CHECK_EQUAL( schAndPcbAtts["<SCM/PCB Attribute name1>"].m_ReadOnly, false ); |
|||
BOOST_CHECK_EQUAL( schAndPcbAtts["<SCM/PCB Attribute name1>"].m_Value, "<Attribute value1>" ); |
|||
|
|||
BOOST_CHECK_EQUAL( schAndPcbAtts["<SCM/PCB Attribute name2>"].m_ReadOnly, true ); |
|||
BOOST_CHECK_EQUAL( schAndPcbAtts["<SCM/PCB Attribute name2>"].m_Value, "<Attribute value2>" ); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue