ADDED: text variable processing in DRC rules.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/11231
This commit is contained in:
Jeff Young 2025-06-22 16:51:32 -06:00
parent 38f4a21b96
commit 89fcd3134e
7 changed files with 63 additions and 25 deletions

View File

@ -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 )

View File

@ -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;
}

View File

@ -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.

View File

@ -708,7 +708,18 @@ void PANEL_SETUP_RULES::OnCompile( wxCommandEvent& event )
{
std::vector<std::shared_ptr<DRC_RULE>> dummyRules;
DRC_RULES_PARSER parser( m_textEditor->GetText(), _( "DRC rules" ) );
std::function<bool( wxString* )> 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 );
}

View File

@ -494,11 +494,24 @@ void DRC_ENGINE::loadRules( const wxFileName& aPath )
{
std::vector<std::shared_ptr<DRC_RULE>> 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<bool( wxString* )> 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 );
}

View File

@ -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: <a href='%d:%d'>%s</a>%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 ) )

View File

@ -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<std::shared_ptr<DRC_RULE>>& aRules, REPORTER* aReporter );
@ -55,7 +54,8 @@ private:
std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE> 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();