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