From 89fcd3134eb036f69e37288c00d14e39665e2107 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sun, 22 Jun 2025 16:51:32 -0600 Subject: [PATCH] ADDED: text variable processing in DRC rules. Fixes https://gitlab.com/kicad/code/kicad/-/issues/11231 --- common/dsnlexer.cpp | 7 +++++ common/libeval_compiler/libeval_compiler.cpp | 5 ++- include/dsnlexer.h | 6 ++++ pcbnew/dialogs/panel_setup_rules.cpp | 13 +++++++- pcbnew/drc/drc_engine.cpp | 21 ++++++++++--- pcbnew/drc/drc_rule_parser.cpp | 32 +++++++++----------- pcbnew/drc/drc_rule_parser.h | 4 +-- 7 files changed, 63 insertions(+), 25 deletions(-) diff --git a/common/dsnlexer.cpp b/common/dsnlexer.cpp index 0d4c1e2c99..1875d0cecb 100644 --- a/common/dsnlexer.cpp +++ b/common/dsnlexer.cpp @@ -543,6 +543,7 @@ int DSNLEXER::NextTok() const char* head = cur; prevTok = curTok; + curSeparator.clear(); if( curTok == DSN_EOF ) goto exit; @@ -565,7 +566,10 @@ L_read: // skip leading whitespace while( cur < limit && isSpace( *cur ) ) + { + curSeparator += *cur; ++cur; + } // If the first non-blank character is #, this line is a comment. // Comments cannot follow any other token on the same line. @@ -597,7 +601,10 @@ L_read: { // skip leading whitespace while( cur < limit && isSpace( *cur ) ) + { + curSeparator += *cur; ++cur; + } } if( cur >= limit ) diff --git a/common/libeval_compiler/libeval_compiler.cpp b/common/libeval_compiler/libeval_compiler.cpp index 9ef259e565..107301ec6c 100644 --- a/common/libeval_compiler/libeval_compiler.cpp +++ b/common/libeval_compiler/libeval_compiler.cpp @@ -586,7 +586,10 @@ bool COMPILER::lexDefault( T_TOKEN& aToken ) case ',': retval.token = G_COMMA; break; default: - reportError( CST_PARSE, wxString::Format( _( "Unrecognized character '%c'" ), (char) ch ) ); + if( m_tokenizer.MatchAhead( "${", []( int c ) -> bool { return c != '{'; } ) ) + reportError( CST_PARSE, _( "Unresolved text variable reference" ), m_sourcePos + 2 ); + else + reportError( CST_PARSE, wxString::Format( _( "Unrecognized character '%c'" ), (char) ch ) ); break; } diff --git a/include/dsnlexer.h b/include/dsnlexer.h index 033a95453d..6a019264fd 100644 --- a/include/dsnlexer.h +++ b/include/dsnlexer.h @@ -427,6 +427,11 @@ public: return curText; } + const std::string& CurSeparator() const + { + return curSeparator; + } + /** * Return the current token text as a wxString, assuming that the input byte stream * is UTF8 encoded. @@ -572,6 +577,7 @@ protected: int curTok; ///< The current token obtained on last NextTok(). std::string curText; ///< The text of the current token. + std::string curSeparator; ///< The text of the separator preceeding the current text. const KEYWORD* keywords; ///< Table sorted by CMake for bsearch(). unsigned keywordCount; ///< Count of keywords table. diff --git a/pcbnew/dialogs/panel_setup_rules.cpp b/pcbnew/dialogs/panel_setup_rules.cpp index 876abdbc0c..e196ba599c 100644 --- a/pcbnew/dialogs/panel_setup_rules.cpp +++ b/pcbnew/dialogs/panel_setup_rules.cpp @@ -708,7 +708,18 @@ void PANEL_SETUP_RULES::OnCompile( wxCommandEvent& event ) { std::vector> dummyRules; - DRC_RULES_PARSER parser( m_textEditor->GetText(), _( "DRC rules" ) ); + std::function resolver = + [&]( wxString* token ) -> bool + { + if( m_frame->Prj().TextVarResolver( token ) ) + return true; + + return false; + }; + + wxString rulesText = ExpandTextVars( m_textEditor->GetText(), &resolver ); + + DRC_RULES_PARSER parser( rulesText, _( "DRC rules" ) ); parser.Parse( dummyRules, m_errorsReport ); } diff --git a/pcbnew/drc/drc_engine.cpp b/pcbnew/drc/drc_engine.cpp index edc2e1c24a..5a504ccc6c 100644 --- a/pcbnew/drc/drc_engine.cpp +++ b/pcbnew/drc/drc_engine.cpp @@ -494,11 +494,24 @@ void DRC_ENGINE::loadRules( const wxFileName& aPath ) { std::vector> rules; - FILE* fp = wxFopen( aPath.GetFullPath(), wxT( "rt" ) ); - - if( fp ) + if( FILE* fp = wxFopen( aPath.GetFullPath(), wxT( "rt" ) ) ) { - DRC_RULES_PARSER parser( fp, aPath.GetFullPath() ); + FILE_LINE_READER lineReader( fp, aPath.GetFullPath() ); // Will close rules file + wxString rulesText; + + std::function resolver = + [&]( wxString* token ) -> bool + { + if( m_board && m_board->GetProject() ) + return m_board->GetProject()->TextVarResolver( token ); + + return false; + }; + + while( char* line = lineReader.ReadLine() ) + rulesText << ExpandTextVars( line, &resolver ) << '\n'; + + DRC_RULES_PARSER parser( rulesText, aPath.GetFullPath() ); parser.Parse( rules, m_logReporter ); } diff --git a/pcbnew/drc/drc_rule_parser.cpp b/pcbnew/drc/drc_rule_parser.cpp index a1c8c590bc..0d13b685a3 100644 --- a/pcbnew/drc/drc_rule_parser.cpp +++ b/pcbnew/drc/drc_rule_parser.cpp @@ -43,15 +43,6 @@ DRC_RULES_PARSER::DRC_RULES_PARSER( const wxString& aSource, const wxString& aSo } -DRC_RULES_PARSER::DRC_RULES_PARSER( FILE* aFile, const wxString& aFilename ) : - DRC_RULES_LEXER( aFile, aFilename ), - m_requiredVersion( 0 ), - m_tooRecent( false ), - m_reporter( nullptr ) -{ -} - - void DRC_RULES_PARSER::reportError( const wxString& aMessage ) { wxString rest; @@ -121,6 +112,9 @@ wxString DRC_RULES_PARSER::parseExpression() break; } + if( !expr.IsEmpty() ) + expr += CurSeparator(); + expr += FromUTF8(); } @@ -720,6 +714,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule ) case T_min: { + size_t offset = CurOffset() + GetTokenString( token ).length(); wxString expr = parseExpression(); if( expr.IsEmpty() ) @@ -728,7 +723,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule ) break; } - parseValueWithUnits( expr, value, units, unitless ); + parseValueWithUnits( offset, expr, value, units, unitless ); validateAndSetValueWithUnits( value, units, [&c]( const int aValue ) { @@ -740,6 +735,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule ) case T_max: { + size_t offset = CurOffset() + GetTokenString( token ).length(); wxString expr = parseExpression(); if( expr.IsEmpty() ) @@ -748,7 +744,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule ) break; } - parseValueWithUnits( expr, value, units, unitless ); + parseValueWithUnits( offset, expr, value, units, unitless ); validateAndSetValueWithUnits( value, units, [&c]( const int aValue ) { @@ -760,6 +756,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule ) case T_opt: { + size_t offset = CurOffset() + GetTokenString( token ).length(); wxString expr = parseExpression(); if( expr.IsEmpty() ) @@ -768,7 +765,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule ) break; } - parseValueWithUnits( expr, value, units, unitless ); + parseValueWithUnits( offset, expr, value, units, unitless ); validateAndSetValueWithUnits( value, units, [&c]( const int aValue ) { @@ -798,21 +795,22 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule ) } -void DRC_RULES_PARSER::parseValueWithUnits( const wxString& aExpr, int& aResult, EDA_UNITS& aUnits, bool aUnitless ) +void DRC_RULES_PARSER::parseValueWithUnits( int aOffset, const wxString& aExpr, int& aResult, + EDA_UNITS& aUnits, bool aUnitless ) { aResult = 0.0; aUnits = EDA_UNITS::UNSCALED; auto errorHandler = - [&]( const wxString& aMessage, int aOffset ) + [&]( const wxString& message, int offset ) { wxString rest; - wxString first = aMessage.BeforeFirst( '|', &rest ); + wxString first = message.BeforeFirst( '|', &rest ); if( m_reporter ) { wxString msg = wxString::Format( _( "ERROR: %s%s" ), - CurLineNumber(), CurOffset() + aOffset, first, rest ); + CurLineNumber(), aOffset + offset, first, rest ); m_reporter->Report( msg, RPT_SEVERITY_ERROR ); } @@ -826,7 +824,7 @@ void DRC_RULES_PARSER::parseValueWithUnits( const wxString& aExpr, int& aResult, }; PCBEXPR_EVALUATOR evaluator( aUnitless ? (LIBEVAL::UNIT_RESOLVER*) new PCBEXPR_UNITLESS_RESOLVER() - : (LIBEVAL::UNIT_RESOLVER*) new PCBEXPR_UNIT_RESOLVER() ); + : (LIBEVAL::UNIT_RESOLVER*) new PCBEXPR_UNIT_RESOLVER() ); evaluator.SetErrorCallback( errorHandler ); if( evaluator.Evaluate( aExpr ) ) diff --git a/pcbnew/drc/drc_rule_parser.h b/pcbnew/drc/drc_rule_parser.h index 49c57ce7f7..6770733f95 100644 --- a/pcbnew/drc/drc_rule_parser.h +++ b/pcbnew/drc/drc_rule_parser.h @@ -41,7 +41,6 @@ class DRC_RULES_PARSER : public DRC_RULES_LEXER { public: DRC_RULES_PARSER( const wxString& aSource, const wxString& aSourceDescr ); - DRC_RULES_PARSER( FILE* aFile, const wxString& aFilename ); void Parse( std::vector>& aRules, REPORTER* aReporter ); @@ -55,7 +54,8 @@ private: std::shared_ptr parseComponentClassAssignment(); void parseConstraint( DRC_RULE* aRule ); - void parseValueWithUnits( const wxString& aExpr, int& aResult, EDA_UNITS& aUnits, bool aUnitless = false ); + void parseValueWithUnits( int aOffset, const wxString& aExpr, int& aResult, EDA_UNITS& aUnits, + bool aUnitless = false ); LSET parseLayer( wxString* aSource ); SEVERITY parseSeverity(); void parseUnknown();