From 80c8cfbbda795c7045e165f8bcf359de2f72b232 Mon Sep 17 00:00:00 2001 From: Roberto Fernandez Bautista Date: Fri, 27 May 2022 14:47:40 +0100 Subject: [PATCH] Add PEGTL playground tool --- CMakeLists.txt | 4 + qa/tools/CMakeLists.txt | 4 + qa/tools/pegtl/CMakeLists.txt | 37 ++++++ qa/tools/pegtl/main.cpp | 216 ++++++++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 qa/tools/pegtl/CMakeLists.txt create mode 100644 qa/tools/pegtl/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f5bce6c777..02798d9f53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,10 @@ option( KICAD_DRC_PROTO "Build the DRC prototype QA tool" OFF ) +option( KICAD_BUILD_PEGTL_DEBUG_TOOL + "Build the PEGTL parser debugging/playground QA tool" + OFF ) + option( KICAD_BUILD_PNS_DEBUG_TOOL "Build the P&S debugging/playground QA tool" OFF ) diff --git a/qa/tools/CMakeLists.txt b/qa/tools/CMakeLists.txt index 2d5ce372a3..e5698f2752 100644 --- a/qa/tools/CMakeLists.txt +++ b/qa/tools/CMakeLists.txt @@ -30,6 +30,10 @@ endif() add_subdirectory( common_tools ) add_subdirectory( pcbnew_tools ) +if( KICAD_BUILD_PEGTL_DEBUG_TOOL ) + add_subdirectory( pegtl ) +endif() + if( KICAD_BUILD_PNS_DEBUG_TOOL ) add_subdirectory( pns ) endif() diff --git a/qa/tools/pegtl/CMakeLists.txt b/qa/tools/pegtl/CMakeLists.txt new file mode 100644 index 0000000000..c7f2f10df3 --- /dev/null +++ b/qa/tools/pegtl/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# This program source code file is part of KiCad, a free EDA CAD application. +# +# Copyright (C) 2022 Roberto Fernandez Bautista +# 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 2 +# 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, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +add_executable( test_pegtl + main.cpp + ) + +add_dependencies( test_pegtl pegtl ) + +include_directories( BEFORE ${INC_BEFORE} ) +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/thirdparty/pegtl/ + ${INC_AFTER} +) + diff --git a/qa/tools/pegtl/main.cpp b/qa/tools/pegtl/main.cpp new file mode 100644 index 0000000000..9788cbb500 --- /dev/null +++ b/qa/tools/pegtl/main.cpp @@ -0,0 +1,216 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Roberto Fernandez Bautista + * 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 . + */ + + +/** + * A program to test a PEGTL grammar + */ + +#include +#include +#include +#include +#include + +#include +#include + +using namespace tao::pegtl; + +//-------------------- Grammar definition ---------------------------------------------------- + +struct LINE_CONTINUATION : seq, eol>{}; ///< Any text can span multiple lines using '&' + +/** + * String segment( no line continuation ), with exclusion rules + */ +template +struct STR_SEGMENT_EXCLUDING : plus>, any>{}; + +/** + * String with optional line continuation and exclusion rules + */ +template +struct STRING_EXCLUDING : plus, opt> {}; + +/** + * Control character with or without preceding whitespace + */ +template +struct spaced_ch : seq, one>{}; + +// Definition of "Format" +// # Format +struct CURRENT_FORMAT_NUMBER : plus {}; +struct FORMAT : seq, CURRENT_FORMAT_NUMBER>{}; + + +// Definition of part +//._[()][:][;] + // string filters: +struct PART_NAME_FILTER : sor, spaced_ch<':'>, spaced_ch<';'>>{}; +struct PART_NUMBER_FILTER : one<')'>{}; +struct PART_VERSION_FILTER : spaced_ch<';'>{}; + + // part elements: +struct PART_NAME : STRING_EXCLUDING {}; +struct PART_NUMBER : STRING_EXCLUDING {}; +struct PART_VERSION : STRING_EXCLUDING {}; +struct PART_DESCRIPTION : STRING_EXCLUDING<> {}; + + // the part itself +struct PART : seq + < + bol, + one<'.'>, + PART_NAME, + opt, PART_NUMBER, one<')'>>>, + opt, PART_VERSION>>, + opt, PART_DESCRIPTION>> + > +{}; + + +struct UNMATCHED_CONTENT : STRING_EXCLUDING {}; //@todo remove once parser is complete + + +struct GRAMMAR : plus + < + sor, + opt + > +{}; + +//--------------------------Simple selector definition ----------------------------- +// disable all parse nodes by default +template< typename PEGTL_RULE > struct SIMPLE_SELECTOR : std::false_type {}; +// enable only nodes that match certain rules: +template<> struct SIMPLE_SELECTOR< STR_SEGMENT_EXCLUDING< PART_NAME_FILTER > > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< STR_SEGMENT_EXCLUDING< PART_NUMBER_FILTER > > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< STR_SEGMENT_EXCLUDING< PART_VERSION_FILTER > > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< STR_SEGMENT_EXCLUDING<> > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< CURRENT_FORMAT_NUMBER > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< FORMAT > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< PART > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< PART_NAME > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< PART_NUMBER > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< PART_VERSION > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< PART_DESCRIPTION > : std::true_type {}; +template<> struct SIMPLE_SELECTOR< UNMATCHED_CONTENT > : std::true_type {}; //@todo remove + + +//--------------------------Complex selector definition ----------------------------- +// Helper function to clean up the tree +struct FOLD_CONTENT : parse_tree::apply +{ + template + static void transform( Node& n ) + { + if( n->children.size() == 1 ) + { + n->children.pop_back(); + } + else if( n->children.size() != 0 ) + { + n->remove_content(); + } + } +}; + + +template +using COMPLEX_SELECTOR = parse_tree::selector< + PEGTL_RULE, + parse_tree::store_content::on< + STR_SEGMENT_EXCLUDING< PART_NAME_FILTER >, + STR_SEGMENT_EXCLUDING< PART_NUMBER_FILTER >, + STR_SEGMENT_EXCLUDING< PART_VERSION_FILTER >, + STR_SEGMENT_EXCLUDING<>, + CURRENT_FORMAT_NUMBER, + UNMATCHED_CONTENT //@todo remove when parser complete. For debugging only + >, + parse_tree::remove_content::on< + FORMAT, + PART + >, + parse_tree::apply< FOLD_CONTENT >::on< + PART_NAME, + PART_NUMBER, + PART_VERSION, + PART_DESCRIPTION + > + >; + + +int main( int argc, char** argv ) +{ + // .\test_pegtl.exe complex my_file.lib | dot -Tsvg > output.svg; .\output.svg + + if( argc != 3 ) + { + printf( "usage: %s ", argv[0] ); + return -1; + } + + std::string chosen_selector = argv[1]; + std::filesystem::path filepath( argv[2] ); + + file_input in( filepath ); + + const std::size_t issues = tao::pegtl::analyze(); + + if( issues > 0 ) + { + std::cout << "\n***ERROR***: " << issues << " issues found in the grammar!\n"; + return -1; + } + + std::unique_ptr root; + + if( chosen_selector == "complex" ) + { + root = parse_tree::parse( in ); + } + else if( chosen_selector == "simple" ) + { + root = parse_tree::parse( in ); + } + else + { + printf( "Invalid selector '%s' requested. Valid values are: complex or simple. \n", + argv[1] ); + printf( "usage: %s \n", argv[0] ); + } + + + if( root ) + { + parse_tree::print_dot( std::cout, *root ); + } + else + { + std::cout << "\n***ERROR***: No root tree node!\n"; + + // lets print out the trace for debugging + standard_trace( in ); + } + + return 0; +} \ No newline at end of file