kicad/common/io/cadstar/cadstar_parts_lib_grammar.h

622 lines
18 KiB
C++

/*
* 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-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 <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>> {};
/**
* Control character with or without preceding whitespace
*/
template <char... CHAR_TO_FIND>
struct spaced_ch : seq<star<WHITESPACE>, one<CHAR_TO_FIND...>>{};
/**
* String inside quotation marks
*/
struct QUOTED_STRING : seq<one<'"'>, STRING_EXCLUDING<one<'"'>>, one<'"'>> {};
/**
* String inside single quotation marks
*/
struct SINGLE_QUOTED_STRING : seq<one<'\''>, STRING_EXCLUDING<one<'\''>>, one<'\''>> {};
/**
* String inside brackets with preceding spaces
*/
struct STRING_IN_BRACKETS :
seq
<
spaced_ch<'('>,
sor<
QUOTED_STRING,
STRING_EXCLUDING<one<')'>>
>,
one<')'>
>
{};
/**
* String inside brackets with preceding spaces, ending with EOL or EOF
*/
struct STRING_IN_BRACKETS_EOLF :
seq
<
spaced_ch<'('>,
sor<
QUOTED_STRING,
STRING_EXCLUDING<seq<one<')'>, eolf>>
>,
seq<one<')'>, eolf>
>
{};
// **************
// * 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>
>
{};
// Newer Parts files have possibility of specifying a tree-like structure to show hierarchy
//
// Example:
//+N0 'root' &
//'part1' 'part2'
//+N1 N0 'subnode1' 'part3' 'part4'
struct HIERARCHY_NODE_INDEX : plus<digit>{};
struct HIERARCHY_CURRENT_NODE : seq<one<'N'>, HIERARCHY_NODE_INDEX>{};
struct HIERARCHY_PARENT_NODE : seq<one<'N'>, HIERARCHY_NODE_INDEX>{}; // Different action
struct HIERARCHY_NODE_NAME : SINGLE_QUOTED_STRING {};
struct HIERARCHY_PART_NAME : SINGLE_QUOTED_STRING {};
struct HIERARCHY_NODE_ENTRY :
seq
<
bol,
one<'+'>,
HIERARCHY_CURRENT_NODE, // N1
plus<WHITESPACE_OR_CONTINUATION>,
opt<HIERARCHY_PARENT_NODE>, // N0
star<WHITESPACE_OR_CONTINUATION>,
HIERARCHY_NODE_NAME, // 'subnode1'
star<WHITESPACE_OR_CONTINUATION>,
star<HIERARCHY_PART_NAME, star<WHITESPACE_OR_CONTINUATION>>, // 'part1' 'part2'
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_IN_BRACKETS {};
struct PART_VERSION : STRING_EXCLUDING<PART_VERSION_FILTER> {};
struct PART_DESCRIPTION : STRING_EXCLUDING<> {};
struct PART_HEADER :
seq
<
bol,
one<'.'>,
must<PART_NAME>,
opt<PART_NUMBER>,
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_IN_BRACKETS {};
struct PART_PCB_COMPONENT :
seq
<
bol,
PCB_COMPONENT,
opt<PCB_ALTERNATE>,
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<'='>, star<WHITESPACE_OR_CONTINUATION>, 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_IN_BRACKETS {};
struct SPICE_MODEL : sor<QUOTED_STRING, STRING_EXCLUDING<>> {};
struct SPI_LINE :
seq
<
bol,
TAO_PEGTL_ISTRING( "*SPI"),
plus<WHITESPACE>,
opt<SPICE_PART_NAME>,
star<WHITESPACE>,
SPICE_MODEL,
opt<eol>
>
{};
//[*PAC_(<Part name>)_<Acceptance Text>]
struct ACCEPTANCE_PART_NAME : STRING_IN_BRACKETS {};
struct ACCEPTANCE_TEXT : STRING_EXCLUDING<> {};
struct PAC_LINE :
seq
<
bol,
TAO_PEGTL_ISTRING( "*PAC"),
plus<WHITESPACE>,
opt<ACCEPTANCE_PART_NAME>,
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 : STRING_IN_BRACKETS {};
struct ATTRIBUTE_VALUE_EOLF : STRING_IN_BRACKETS_EOLF {};
template<char START_TOKEN>
struct GENERIC_ATTRIBUTE :
seq
<
bol,
one<START_TOKEN>,
opt<READONLY>,
ATTRIBUTE_NAME,
ATTRIBUTE_VALUE_EOLF
>
{};
//[$[!]<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<'@'>{};
//[<SCM Symbol Refname>][_(<SCM Alternate Refname>)]
struct SCH_NAME : sor<QUOTED_STRING, STRING_EXCLUDING<spaced_ch<'('>>> {};
struct SCH_ALTERNATE : STRING_IN_BRACKETS {};
struct SCH_SYMBOL_LINE : seq<SCH_NAME, opt<SCH_ALTERNATE>, opt<eol>>{};
//[<PinIdentifier>[.<Position>] [!<Pintype>] [:<Loading>]]
struct PIN_IDENTIFIER : plus<digit>{};
struct PIN_POSITION : range<'0', '3'>{};
struct PIN_TYPE : star<alpha>{};
struct PIN_LOADING : plus<digit>{};
struct PIN_ENTRY :
seq
<
PIN_IDENTIFIER,
one<'.'>,
PIN_POSITION,
opt< one<'!'>, PIN_TYPE>,
opt< one<':'>, PIN_LOADING>
>
{};
struct PIN_LIST : plus<PIN_ENTRY, star<WHITESPACE>, opt<LINE_CONTINUATION>> {};
struct SYMBOL_ENTRY : seq<SCH_SYMBOL_LINE, PIN_LIST, opt<eol>>{};
// /<Signame>_<PinIdentifier>[.<Position>][!<Pintype>][:<Loading>]
struct PIN_SIGNAL_NAME : seq<one<'/'>, STRING_EXCLUDING<WHITESPACE>> {};
struct HIDDEN_PIN_ENTRY : seq<PIN_SIGNAL_NAME, plus<WHITESPACE>, PIN_LIST, opt<eol>>{};
//******************
// 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>)]
>>,
star<SYMBOL_ENTRY>, //[<SCM Symbol Refname>][_(<SCM Alternate Refname>)]
//[Pin entry] [Pin entry] ...
star<HIDDEN_PIN_ENTRY> //[/<Signame>_<Pin entry>]
>
{};
/**
* Grammar for CADSTAR Parts Library file format (*.lib)
*/
struct GRAMMAR :
must<
opt<FORMAT>,
star<star<EMPTY_LINE>, HIERARCHY_NODE_ENTRY>,
plus
<
sor
<
PART_ENTRY,
EMPTY_LINE // optional empty line
>,
opt<eol>
>,
opt<TAO_PEGTL_ISTRING( ".END"), opt<eol>>,
star<EMPTY_LINE>,
tao::pegtl::eof // just putting "eof" results in ambiguous symbol
>
{};
/**
* 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