From 68f3d09ac14fe9a1001ab60433cf29e4b53592c6 Mon Sep 17 00:00:00 2001 From: Thomas Pointhuber Date: Sun, 21 Mar 2021 16:37:02 +0100 Subject: [PATCH] altium: introduce tokenizer for query language Based on this tokenizer, a LL1 parser will be built in the future. --- pcbnew/plugins/altium/CMakeLists.txt | 3 +- .../altium/altium_rule_transformer.cpp | 221 ++++++++++ .../plugins/altium/altium_rule_transformer.h | 143 +++++++ qa/pcbnew/CMakeLists.txt | 2 + .../altium/test_altium_rule_transformer.cpp | 388 ++++++++++++++++++ 5 files changed, 756 insertions(+), 1 deletion(-) create mode 100644 pcbnew/plugins/altium/altium_rule_transformer.cpp create mode 100644 pcbnew/plugins/altium/altium_rule_transformer.h create mode 100644 qa/pcbnew/plugins/altium/test_altium_rule_transformer.cpp diff --git a/pcbnew/plugins/altium/CMakeLists.txt b/pcbnew/plugins/altium/CMakeLists.txt index c7645f7404..8181d9d4e0 100644 --- a/pcbnew/plugins/altium/CMakeLists.txt +++ b/pcbnew/plugins/altium/CMakeLists.txt @@ -5,8 +5,9 @@ set( ALTIUM2PCBNEW_SRCS altium_circuit_maker_plugin.cpp altium_circuit_studio_plugin.cpp altium_designer_plugin.cpp - altium_pcb.cpp altium_parser_pcb.cpp + altium_pcb.cpp + altium_rule_transformer.cpp ) add_library( altium2pcbnew STATIC ${ALTIUM2PCBNEW_SRCS} ) diff --git a/pcbnew/plugins/altium/altium_rule_transformer.cpp b/pcbnew/plugins/altium/altium_rule_transformer.cpp new file mode 100644 index 0000000000..b7cc7bd6ed --- /dev/null +++ b/pcbnew/plugins/altium/altium_rule_transformer.cpp @@ -0,0 +1,221 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 Thomas Pointhuber + * Copyright (C) 2021 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 + */ + +#include "altium_rule_transformer.h" + +#include + +const ALTIUM_RULE_TOKEN& ALTIUM_RULE_TOKENIZER::Next() +{ + m_currentToken = m_nextToken; + + // skip whitespaces + for( ; m_it != m_expr.end() && wxIsspace( curChar() ); nextChar() ) + ; + + // check for end of string + if( m_it == m_expr.end() ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, m_pos }; + return m_currentToken; + } + + const size_t startPos = m_pos; + const wxUniChar curCh = curChar(); + wxUniChar nextCh = nextChar(); + + if( curCh == '(' ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::LPAR, startPos }; + return m_currentToken; + } + else if( curCh == ')' ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::RPAR, startPos }; + return m_currentToken; + } + else if( curCh == '*' ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::MUL, startPos }; + return m_currentToken; + } + else if( curCh == '/' ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::DIV, startPos }; + return m_currentToken; + } + else if( curCh == '=' ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::EQUAL, startPos }; + return m_currentToken; + } + else if( curCh == '<' ) + { + if( nextCh == '=' ) + { + nextChar(); + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::LESS_EQUAL, startPos }; + } + else if( nextCh == '>' ) + { + nextChar(); + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::NOT_EQUAL, startPos }; + } + else + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::LESS, startPos }; + } + return m_currentToken; + } + else if( curCh == '>' ) + { + if( nextCh == '=' ) + { + nextChar(); + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::GREATER_EQUAL, startPos }; + } + else + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::GREATER, startPos }; + } + return m_currentToken; + } + else if( curCh == '&' && nextCh == '&' ) + { + nextChar(); + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::LOW_AND, startPos }; + return m_currentToken; + } + else if( curCh == '|' && nextCh == '|' ) + { + nextChar(); + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::LOW_OR, startPos }; + return m_currentToken; + } + else if( curCh == '\'' ) + { + std::cout << "start const string" << std::endl; + wxString constString; + while( m_it != m_expr.end() && nextCh != '\'' ) + { + constString += nextCh; // TODO: escaping? + nextCh = nextChar(); + } + std::cout << "end const string: " << constString << std::endl; + + if( m_it != m_expr.end() ) + { + nextChar(); // TODO: exception if EOF reached? + } + + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::CONST_STRING, startPos, constString }; + return m_currentToken; + } + else if( curCh == '+' && !wxIsdigit( nextCh ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::ADD, startPos }; + return m_currentToken; + } + else if( curCh == '-' && !wxIsdigit( nextCh ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::SUB, startPos }; + return m_currentToken; + } + else if( curCh == '+' || curCh == '-' || wxIsdigit( curCh ) ) + { + wxString digitString = curCh; + while( wxIsdigit( nextCh ) ) + { + digitString += nextCh; + nextCh = nextChar(); + } + + long value; + digitString.ToLong( &value ); // TODO: check return value + + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::CONST_INT, startPos, value }; + return m_currentToken; + } + else + { + wxString identString = curCh; + while( wxIsalnum( nextCh ) ) + { + identString += nextCh; + nextCh = nextChar(); + } + + if( identString.IsSameAs( "True", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::CONST_TRUE, startPos }; + } + else if( identString.IsSameAs( "False", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::CONST_FALSE, startPos }; + } + else if( identString.IsSameAs( "Div", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::INTEGRAL_DIV, startPos }; + } + else if( identString.IsSameAs( "Mod", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::MOD, startPos }; + } + else if( identString.IsSameAs( "And", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::AND, startPos }; + } + else if( identString.IsSameAs( "Or", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::OR, startPos }; + } + else if( identString.IsSameAs( "Xor", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::XOR, startPos }; + } + else if( identString.IsSameAs( "Not", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::NOT, startPos }; + } + else if( identString.IsSameAs( "Between", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::BETWEEN, startPos }; + } + else if( identString.IsSameAs( "Like", false ) ) + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::LIKE, startPos }; + } + else + { + m_nextToken = { ALTIUM_RULE_TOKEN_KIND::IDENT, startPos, identString }; + } + + return m_currentToken; + } +} + +const ALTIUM_RULE_TOKEN& ALTIUM_RULE_TOKENIZER::Peek() const +{ + return m_nextToken; +} \ No newline at end of file diff --git a/pcbnew/plugins/altium/altium_rule_transformer.h b/pcbnew/plugins/altium/altium_rule_transformer.h new file mode 100644 index 0000000000..585b82d787 --- /dev/null +++ b/pcbnew/plugins/altium/altium_rule_transformer.h @@ -0,0 +1,143 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 Thomas Pointhuber + * Copyright (C) 2021 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 + */ + +#ifndef ALTIUM_RULE_TRANSFORMER_H +#define ALTIUM_RULE_TRANSFORMER_H + +#include "wx/string.h" + +// See: https://www.altium.com/documentation/altium-designer/query-operators-ad +enum class ALTIUM_RULE_TOKEN_KIND +{ + UNKNOWN, + + IDENT, + CONST_INT, + CONST_FLOAT, + CONST_STRING, + + CONST_TRUE, + CONST_FALSE, + + LPAR, + RPAR, + + ADD, + SUB, + MUL, + DIV, + INTEGRAL_DIV, + MOD, + + AND, + LOW_AND, + OR, + LOW_OR, + XOR, + NOT, + + LESS, + LESS_EQUAL, + GREATER_EQUAL, + GREATER, + NOT_EQUAL, + EQUAL, + + BETWEEN, + LIKE, + + END_OF_EXPR +}; + +struct ALTIUM_RULE_TOKEN +{ + ALTIUM_RULE_TOKEN_KIND kind; + size_t pos; + + long iValue; + double fValue; + wxString sValue; + + ALTIUM_RULE_TOKEN() : + kind( ALTIUM_RULE_TOKEN_KIND::UNKNOWN ), pos( 0 ), iValue( 0 ), fValue( 0. ), + sValue( "" ) + { + } + + ALTIUM_RULE_TOKEN( ALTIUM_RULE_TOKEN_KIND aKind, size_t aPos ) : + kind( aKind ), pos( aPos ), iValue( 0 ), fValue( 0. ), sValue( "" ) + { + } + + ALTIUM_RULE_TOKEN( ALTIUM_RULE_TOKEN_KIND aKind, size_t aPos, long aValue ) : + kind( aKind ), pos( aPos ), iValue( aValue ), fValue( 0. ), sValue( "" ) + { + } + + ALTIUM_RULE_TOKEN( ALTIUM_RULE_TOKEN_KIND aKind, size_t aPos, float aValue ) : + kind( aKind ), pos( aPos ), iValue( 0 ), fValue( aValue ), sValue( "" ) + { + } + + ALTIUM_RULE_TOKEN( ALTIUM_RULE_TOKEN_KIND aKind, size_t aPos, wxString aValue ) : + kind( aKind ), pos( aPos ), iValue( 0 ), fValue( 0. ), sValue( aValue ) + { + } +}; + +class ALTIUM_RULE_TOKENIZER +{ +public: + ALTIUM_RULE_TOKENIZER( const wxString& aExpr ) : m_pos( 0 ), m_expr( aExpr ) + { + m_it = m_expr.begin(); + Next(); + } + + const ALTIUM_RULE_TOKEN& Next(); + + const ALTIUM_RULE_TOKEN& Peek() const; + +private: + wxUniChar curChar() { return *m_it; } + + wxUniChar nextChar() + { + if( m_it != m_expr.end() ) + { + m_pos++; + } + return *( ++m_it ); + } + + size_t m_pos; + const wxString m_expr; + wxString::const_iterator m_it; + + ALTIUM_RULE_TOKEN m_currentToken; + ALTIUM_RULE_TOKEN m_nextToken; +}; + + +#endif //ALTIUM_RULE_TRANSFORMER_H \ No newline at end of file diff --git a/qa/pcbnew/CMakeLists.txt b/qa/pcbnew/CMakeLists.txt index 913aab77ca..8b69740d42 100644 --- a/qa/pcbnew/CMakeLists.txt +++ b/qa/pcbnew/CMakeLists.txt @@ -41,6 +41,8 @@ set( QA_PCBNEW_SRCS drc/test_drc_courtyard_invalid.cpp drc/test_drc_courtyard_overlap.cpp + plugins/altium/test_altium_rule_transformer.cpp + group_saveload.cpp ) diff --git a/qa/pcbnew/plugins/altium/test_altium_rule_transformer.cpp b/qa/pcbnew/plugins/altium/test_altium_rule_transformer.cpp new file mode 100644 index 0000000000..eeee53c8f6 --- /dev/null +++ b/qa/pcbnew/plugins/altium/test_altium_rule_transformer.cpp @@ -0,0 +1,388 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file test_altium_rule_transformer.cpp + * Test suite for #ALTIUM_RULE_TOKENIZER + */ + +#include + +#include + +struct ALTIUM_RULE_TRANSFORMER_FIXTURE +{ + ALTIUM_RULE_TRANSFORMER_FIXTURE() {} +}; + + +/** + * Declares the struct as the Boost test fixture. + */ +BOOST_FIXTURE_TEST_SUITE( AltiumRuleTransformer, ALTIUM_RULE_TRANSFORMER_FIXTURE ) + + +BOOST_AUTO_TEST_CASE( AltiumRuleTokenizerEmptyInput ) +{ + ALTIUM_RULE_TOKENIZER tokenizer( "" ); + + const ALTIUM_RULE_TOKEN& peek = tokenizer.Peek(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR == peek.kind ) ); + BOOST_CHECK_EQUAL( 0, peek.pos ); + + const ALTIUM_RULE_TOKEN& next = tokenizer.Next(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR == next.kind ) ); + BOOST_CHECK_EQUAL( 0, next.pos ); + + const ALTIUM_RULE_TOKEN& peek2 = tokenizer.Peek(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR == peek2.kind ) ); + BOOST_CHECK_EQUAL( 0, peek2.pos ); +} + +BOOST_AUTO_TEST_CASE( AltiumRuleTokenizerOnlySpaces ) +{ + ALTIUM_RULE_TOKENIZER tokenizer( " " ); + + const ALTIUM_RULE_TOKEN& peek = tokenizer.Peek(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR == peek.kind ) ); + BOOST_CHECK_EQUAL( 3, peek.pos ); + + const ALTIUM_RULE_TOKEN& next = tokenizer.Next(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR == next.kind ) ); + BOOST_CHECK_EQUAL( 3, next.pos ); + + const ALTIUM_RULE_TOKEN& peek2 = tokenizer.Peek(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR == peek2.kind ) ); + BOOST_CHECK_EQUAL( 3, peek2.pos ); +} + +BOOST_AUTO_TEST_CASE( AltiumRuleTokenizerSingleCharIdentifier ) +{ + ALTIUM_RULE_TOKENIZER tokenizer( "a" ); + + const ALTIUM_RULE_TOKEN& next = tokenizer.Next(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::IDENT == next.kind ) ); + BOOST_CHECK_EQUAL( 0, next.pos ); + BOOST_CHECK_EQUAL( "a", next.sValue ); + + const ALTIUM_RULE_TOKEN& peek = tokenizer.Peek(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR == peek.kind ) ); + BOOST_CHECK_EQUAL( 1, peek.pos ); + + const ALTIUM_RULE_TOKEN& next2 = tokenizer.Next(); + BOOST_CHECK( ( ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR == next2.kind ) ); + BOOST_CHECK_EQUAL( 1, next2.pos ); +} + + +struct ALTIUM_RULE_TOKENIZER_INPUT_OUTPUT +{ + wxString input; + std::vector exp_token; +}; + +/** + * A list of valid test strings and the expected results + */ +static const std::vector altium_rule_tokens_property = { + // Empty string + { "", + { + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 0 } + } + }, + // Single Token + { "All", + { + { ALTIUM_RULE_TOKEN_KIND::IDENT, 0, "All" }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "1234", + { + { ALTIUM_RULE_TOKEN_KIND::CONST_INT, 0, 1234L }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 4 } + } + }, + { "+1234", + { + { ALTIUM_RULE_TOKEN_KIND::CONST_INT, 0, 1234L }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 5 } + } + }, + { "-1234", + { + { ALTIUM_RULE_TOKEN_KIND::CONST_INT, 0, -1234L }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 5 } + } + }, + { "'1234'", + { + { ALTIUM_RULE_TOKEN_KIND::CONST_STRING, 0, "1234" }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 6 } + } + }, + { "True", + { + { ALTIUM_RULE_TOKEN_KIND::CONST_TRUE, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 4 } + } + }, + { "true", + { + { ALTIUM_RULE_TOKEN_KIND::CONST_TRUE, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 4 } + } + }, + { "False", + { + { ALTIUM_RULE_TOKEN_KIND::CONST_FALSE, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 5 } + } + }, + { "false", + { + { ALTIUM_RULE_TOKEN_KIND::CONST_FALSE, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 5 } + } + }, + { "+", + { + { ALTIUM_RULE_TOKEN_KIND::ADD, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 1 } + } + }, + { "-", + { + { ALTIUM_RULE_TOKEN_KIND::SUB, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 1 } + } + }, + { "*", + { + { ALTIUM_RULE_TOKEN_KIND::MUL, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 1 } + } + }, + { "/", + { + { ALTIUM_RULE_TOKEN_KIND::DIV, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 1 } + } + }, + { "Div", + { + { ALTIUM_RULE_TOKEN_KIND::INTEGRAL_DIV, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "div", + { + { ALTIUM_RULE_TOKEN_KIND::INTEGRAL_DIV, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "Mod", + { + { ALTIUM_RULE_TOKEN_KIND::MOD, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "mod", + { + { ALTIUM_RULE_TOKEN_KIND::MOD, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "And", + { + { ALTIUM_RULE_TOKEN_KIND::AND, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "and", + { + { ALTIUM_RULE_TOKEN_KIND::AND, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "&&", + { + { ALTIUM_RULE_TOKEN_KIND::LOW_AND, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 2 } + } + }, + { "Or", + { + { ALTIUM_RULE_TOKEN_KIND::OR, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 2 } + } + }, + { "or", + { + { ALTIUM_RULE_TOKEN_KIND::OR, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 2 } + } + }, + { "||", + { + { ALTIUM_RULE_TOKEN_KIND::LOW_OR, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 2 } + } + }, + { "Xor", + { + { ALTIUM_RULE_TOKEN_KIND::XOR, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "xor", + { + { ALTIUM_RULE_TOKEN_KIND::XOR, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "Not", + { + { ALTIUM_RULE_TOKEN_KIND::NOT, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "not", + { + { ALTIUM_RULE_TOKEN_KIND::NOT, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 3 } + } + }, + { "<", + { + { ALTIUM_RULE_TOKEN_KIND::LESS, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 1 } + } + }, + { "<=", + { + { ALTIUM_RULE_TOKEN_KIND::LESS_EQUAL, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 2 } + } + }, + { ">", + { + { ALTIUM_RULE_TOKEN_KIND::GREATER, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 1 } + } + }, + { ">=", + { + { ALTIUM_RULE_TOKEN_KIND::GREATER_EQUAL, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 2 } + } + }, + { "<>", + { + { ALTIUM_RULE_TOKEN_KIND::NOT_EQUAL, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 2 } + } + }, + { "=", + { + { ALTIUM_RULE_TOKEN_KIND::EQUAL, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 1 } + } + }, + { "Between", + { + { ALTIUM_RULE_TOKEN_KIND::BETWEEN, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 7 } + } + }, + { "between", + { + { ALTIUM_RULE_TOKEN_KIND::BETWEEN, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 7 } + } + }, + { "Like", + { + { ALTIUM_RULE_TOKEN_KIND::LIKE, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 4 } + } + }, + { "like", + { + { ALTIUM_RULE_TOKEN_KIND::LIKE, 0 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 4 } + } + }, + // Multiple tokens + { "ab cd ef", + { + { ALTIUM_RULE_TOKEN_KIND::IDENT, 0, "ab" }, + { ALTIUM_RULE_TOKEN_KIND::IDENT, 3, "cd" }, + { ALTIUM_RULE_TOKEN_KIND::IDENT, 6, "ef" }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 8 } + } + }, + // Complex tests + { "InComponent('LEDS1') or InComponent('LEDS2')", + { + { ALTIUM_RULE_TOKEN_KIND::IDENT, 0, "InComponent" }, + { ALTIUM_RULE_TOKEN_KIND::LPAR, 11 }, + { ALTIUM_RULE_TOKEN_KIND::CONST_STRING, 12, "LEDS1" }, + { ALTIUM_RULE_TOKEN_KIND::RPAR, 19 }, + { ALTIUM_RULE_TOKEN_KIND::OR, 21 }, + { ALTIUM_RULE_TOKEN_KIND::IDENT, 24, "InComponent" }, + { ALTIUM_RULE_TOKEN_KIND::LPAR, 35 }, + { ALTIUM_RULE_TOKEN_KIND::CONST_STRING, 36, "LEDS2" }, + { ALTIUM_RULE_TOKEN_KIND::RPAR, 43 }, + { ALTIUM_RULE_TOKEN_KIND::END_OF_EXPR, 44 } + } + } +}; + +/** + * Test conversation from wxString to Altium tokens + */ +BOOST_AUTO_TEST_CASE( AltiumRuleTokenizerParameterizedTest ) +{ + for( const auto& c : altium_rule_tokens_property ) + { + BOOST_TEST_CONTEXT( wxString::Format( wxT( "'%s'" ), c.input ) ) + { + ALTIUM_RULE_TOKENIZER tokenizer( c.input ); + + for( const auto& expected : c.exp_token ) + { + const ALTIUM_RULE_TOKEN& token = tokenizer.Next(); + BOOST_CHECK( ( expected.kind == token.kind ) ); + BOOST_CHECK_EQUAL( expected.pos, token.pos ); + BOOST_CHECK_EQUAL( expected.iValue, token.iValue ); + BOOST_CHECK_EQUAL( expected.fValue, token.fValue ); + BOOST_CHECK_EQUAL( expected.sValue, token.sValue ); + } + } + } +} + +BOOST_AUTO_TEST_SUITE_END()