KiCad PCB EDA Suite
drc_rule_parser.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 
25 #include <fctsys.h>
26 #include <drc/drc_rule_parser.h>
27 #include <drc_rules_lexer.h>
28 #include <class_board.h>
29 #include <class_board_item.h>
30 
31 using namespace DRCRULE_T;
32 
33 
34 DRC_RULES_PARSER::DRC_RULES_PARSER( BOARD* aBoard, const wxString& aSource,
35  const wxString& aSourceDescr ) :
36  DRC_RULES_LEXER( aSource.ToStdString(), aSourceDescr ),
37  m_board( aBoard ),
38  m_requiredVersion( 0 ),
39  m_tooRecent( false )
40 {
41  initLayerMap();
42 }
43 
44 
45 DRC_RULES_PARSER::DRC_RULES_PARSER( BOARD* aBoard, FILE* aFile, const wxString& aFilename ) :
46  DRC_RULES_LEXER( aFile, aFilename ),
47  m_board( aBoard ),
48  m_requiredVersion( 0 ),
49  m_tooRecent( false )
50 {
51  initLayerMap();
52 }
53 
54 
56 {
57  for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
58  {
59  std::string untranslated = TO_UTF8( wxString( LSET::Name( PCB_LAYER_ID( layer ) ) ) );
60  m_layerMap[ untranslated ] = PCB_LAYER_ID( layer );
61 
62  std::string userName = m_board->GetLayerName( PCB_LAYER_ID( layer ) ).ToStdString();
63  m_layerMap[ userName ] = PCB_LAYER_ID( layer );
64  }
65 }
66 
67 
68 void DRC_RULES_PARSER::Parse( std::vector<DRC_SELECTOR*>& aSelectors,
69  std::vector<DRC_RULE*>& aRules )
70 {
71  std::vector< std::pair<DRC_SELECTOR*, wxString> > selectorRules;
72  bool haveVersion = false;
73 
74  for( T token = NextTok(); token != T_EOF; token = NextTok() )
75  {
76  if( token != T_LEFT )
77  Expecting( T_LEFT );
78 
79  token = NextTok();
80 
81  if( !haveVersion && token != T_version )
82  Expecting( "version" );
83 
84  switch( token )
85  {
86  case T_version:
87  NeedNUMBER( "version" );
88  m_requiredVersion = (int)strtol( CurText(), NULL, 10 );
90  haveVersion = true;
91  NeedRIGHT();
92  break;
93 
94  case T_selector:
95  {
96  wxString ruleName;
97 
98  aSelectors.push_back( parseDRC_SELECTOR( &ruleName ) );
99  selectorRules.emplace_back( aSelectors.back(), ruleName );
100  }
101  break;
102 
103  case T_rule:
104  aRules.push_back( parseDRC_RULE() );
105  break;
106 
107  default:
108  Expecting( "selector or rule" );
109  }
110  }
111 
112  // Hook up the selectors to their rules
113  std::map<wxString, DRC_RULE*> ruleMap;
114 
115  for( DRC_RULE* rule : aRules )
116  ruleMap[ rule->m_Name ] = rule;
117 
118  for( const std::pair<DRC_SELECTOR*, wxString>& entry : selectorRules )
119  {
120  if( ruleMap.count( entry.second ) )
121  {
122  entry.first->m_Rule = ruleMap[ entry.second ];
123  }
124  else
125  {
126  wxString errText = wxString::Format( _( "Rule \"%s\" not found." ), entry.second );
127  THROW_PARSE_ERROR( errText, CurSource(), "", 0, 0 );
128  }
129  }
130 }
131 
132 
134 {
136  DRC_SELECTOR* selector = new DRC_SELECTOR();
137  T token;
138 
139  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
140  {
141  if( token != T_LEFT )
142  Expecting( T_LEFT );
143 
144  token = NextTok();
145 
146  switch( token )
147  {
148  case T_match_netclass:
149  {
150  NeedSYMBOL();
151  NETCLASSPTR netclass = netclasses.Find( FromUTF8() );
152 
153  if( netclass )
154  {
155  selector->m_MatchNetclasses.push_back( std::move( netclass ) );
156  }
157  else
158  {
159  // Interesting situation here: if we don't inform the user they may have a typo
160  // and can't figure out why their rules don't work.
161  // If we do tell them then it gets really noisy if they're using a single rule
162  // file for a class of board.
163  }
164 
165  NeedRIGHT();
166  }
167  break;
168 
169  case T_match_type:
170  switch( NextTok() )
171  {
172  case T_track: selector->m_MatchTypes.push_back( PCB_TRACE_T ); break;
173  case T_via: selector->m_MatchTypes.push_back( PCB_LOCATE_STDVIA_T ); break;
174  case T_micro_via: selector->m_MatchTypes.push_back( PCB_LOCATE_UVIA_T ); break;
175  case T_buried_via: selector->m_MatchTypes.push_back( PCB_LOCATE_BBVIA_T ); break;
176  case T_pad: selector->m_MatchTypes.push_back( PCB_PAD_T ); break;
177  case T_zone: selector->m_MatchTypes.push_back( PCB_ZONE_AREA_T ); break;
178  case T_text: selector->m_MatchTypes.push_back( PCB_LOCATE_TEXT_T ); break;
179  case T_graphic: selector->m_MatchTypes.push_back( PCB_LOCATE_GRAPHIC_T ); break;
180  case T_hole: selector->m_MatchTypes.push_back( PCB_LOCATE_HOLE_T ); break;
181  case T_npth: selector->m_MatchTypes.push_back( PCB_LOCATE_NPTH_T ); break;
182  case T_pth: selector->m_MatchTypes.push_back( PCB_LOCATE_PTH_T ); break;
183  case T_board_edge: selector->m_MatchTypes.push_back( PCB_LOCATE_BOARD_EDGE_T ); break;
184  default: Expecting( "track, via, micro_via, buried_via, pad, zone, text, "
185  "graphic, hole, npth, pth, or board_edge" );
186  }
187  NeedRIGHT();
188  break;
189 
190  case T_match_layer:
191  NeedSYMBOL();
192 
193  if( m_layerMap.count( curText ) )
194  {
195  selector->m_MatchLayers.push_back( m_layerMap[ curText ] );
196  }
197  else
198  {
199  wxString errText = wxString::Format( _( "Layer \"%s\" not found." ),
200  wxString( curText ) );
201  THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
202  }
203 
204  NeedRIGHT();
205  break;
206 
207  case T_match_area:
208  // TODO
209  break;
210 
211  case T_rule:
212  NeedSYMBOL();
213  *aRuleName = FromUTF8();
214  NeedRIGHT();
215  break;
216 
217  default:
218  Expecting( "match_netclass, match_type, match_layer, match_area, or rule" );
219  }
220  }
221 
222  return selector;
223 }
224 
225 
227 {
228  DRC_RULE* rule = new DRC_RULE();
229  T token = NextTok();
230 
231  if( !IsSymbol( token ) )
232  Expecting( "rule name" );
233 
234  rule->m_Name = FromUTF8();
235 
236  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
237  {
238  if( token != T_LEFT )
239  Expecting( T_LEFT );
240 
241  token = NextTok();
242 
243  switch( token )
244  {
245  case T_disallow:
246  switch( NextTok() )
247  {
248  case T_track: rule->m_DisallowFlags |= DISALLOW_TRACKS; break;
249  case T_via: rule->m_DisallowFlags |= DISALLOW_VIAS; break;
250  case T_micro_via: rule->m_DisallowFlags |= DISALLOW_MICRO_VIAS; break;
251  case T_buried_via: rule->m_DisallowFlags |= DISALLOW_BB_VIAS; break;
252  case T_pad: rule->m_DisallowFlags |= DISALLOW_PADS; break;
253  case T_zone: rule->m_DisallowFlags |= DISALLOW_ZONES; break;
254  case T_text: rule->m_DisallowFlags |= DISALLOW_TEXTS; break;
255  case T_graphic: rule->m_DisallowFlags |= DISALLOW_GRAPHICS; break;
256  case T_hole: rule->m_DisallowFlags |= DISALLOW_HOLES; break;
257  case T_footprint: rule->m_DisallowFlags |= DISALLOW_FOOTPRINTS; break;
258  default: Expecting( "track, via, micro_via, blind_via, pad, zone, text, "
259  "graphic, or hole" );
260  }
261 
263  NeedRIGHT();
264  break;
265 
266  case T_constraint:
267  parseConstraint( rule );
268  break;
269 
270  case T_condition:
271  NeedSYMBOL();
272  rule->m_Condition = FromUTF8();
273  NeedRIGHT();
274  break;
275 
276  default:
277  Expecting( "disallow, constraint or condition" );
278  }
279  }
280 
281  return rule;
282 }
283 
284 
286 {
287  T token;
288  int constraintType;
289  int value;
290 
291  switch( NextTok() )
292  {
293  case T_clearance: constraintType = CLEARANCE_CONSTRAINT; break;
294  case T_track_width: constraintType = TRACK_CONSTRAINT; break;
295  case T_annulus_width: constraintType = ANNULUS_CONSTRAINT; break;
296  case T_hole: constraintType = HOLE_CONSTRAINT; break;
297  default: Expecting( "clearance, track_width, annulus_width, or hole" ); return;
298  }
299 
300  aRule->m_ConstraintFlags |= constraintType;
301 
302  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
303  {
304  if( token != T_LEFT )
305  Expecting( T_LEFT );
306 
307  token = NextTok();
308 
309  switch( token )
310  {
311  case T_min:
312  NextTok();
313  value = parseValue( token );
314 
315  switch( constraintType )
316  {
317  case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Min = value; break;
318  case TRACK_CONSTRAINT: aRule->m_TrackConstraint.Min = value; break;
319  case ANNULUS_CONSTRAINT: aRule->m_MinAnnulusWidth = value; break;
320  case HOLE_CONSTRAINT: aRule->m_MinHole = value; break;
321  }
322 
323  NeedRIGHT();
324  break;
325 
326  case T_max:
327  NextTok();
328  value = parseValue( token );
329 
330  switch( constraintType )
331  {
332  case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Max = value; break;
333  case TRACK_CONSTRAINT: aRule->m_TrackConstraint.Max = value; break;
334  default: Expecting( "min" );
335  }
336 
337  NeedRIGHT();
338  break;
339 
340  case T_opt:
341  NextTok();
342  value = parseValue( token );
343 
344  switch( constraintType )
345  {
346  case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Opt = value; break;
347  case TRACK_CONSTRAINT: aRule->m_TrackConstraint.Opt = value; break;
348  default: Expecting( "min" );
349  }
350 
351  NeedRIGHT();
352  break;
353 
354  default:
355  Expecting( "allow or constraint" );
356  }
357  }
358 }
359 
360 
361 int DRC_RULES_PARSER::parseValue( DRCRULE_T::T aToken )
362 {
363  return (int) ValueFromString( EDA_UNITS::MILLIMETRES, CurText(), true );
364 }
DRC_RULES_PARSER(BOARD *aBoard, const wxString &aSource, const wxString &aSourceDescr)
std::vector< PCB_LAYER_ID > m_MatchLayers
Definition: drc_rule.h:99
#define DISALLOW_TRACKS
Definition: drc_rule.h:44
#define DISALLOW_TEXTS
Definition: drc_rule.h:47
NETCLASSPTR Find(const wxString &aName) const
Function Find searches this container for a NETCLASS given by aName.
Definition: netclass.cpp:142
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Function GetLayerName returns the name of a layer.
wxString m_Name
Definition: drc_rule.h:73
Classes BOARD_ITEM and BOARD_CONNECTED_ITEM.
class ZONE_CONTAINER, a zone area
Definition: typeinfo.h:102
#define TRACK_CONSTRAINT
Definition: drc_rule.h:37
int m_ConstraintFlags
Definition: drc_rule.h:74
#define CLEARANCE_CONSTRAINT
Definition: drc_rule.h:35
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:551
#define DISALLOW_MICRO_VIAS
Definition: drc_rule.h:42
class D_PAD, a pad in a footprint
Definition: typeinfo.h:90
#define ANNULUS_CONSTRAINT
Definition: drc_rule.h:36
DRC_SELECTOR * parseDRC_SELECTOR(wxString *aRuleName)
MINOPTMAX m_TrackConstraint
Definition: drc_rule.h:81
#define DISALLOW_HOLES
Definition: drc_rule.h:49
int m_DisallowFlags
Definition: drc_rule.h:75
void Parse(std::vector< DRC_SELECTOR * > &aSelectors, std::vector< DRC_RULE * > &aRules)
#define DISALLOW_PADS
Definition: drc_rule.h:45
int m_MinHole
Definition: drc_rule.h:82
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
#define DRC_RULE_FILE_VERSION
#define DISALLOW_GRAPHICS
Definition: drc_rule.h:48
PCB_LAYER_ID
A quick note on layer IDs:
NETCLASSES is a container for NETCLASS instances.
Definition: netclass.h:221
#define NULL
int m_MinAnnulusWidth
Definition: drc_rule.h:80
MINOPTMAX m_Clearance
Definition: drc_rule.h:79
static const wxChar * Name(PCB_LAYER_ID aLayerId)
Function Name returns the fixed name association with aLayerId.
Definition: lset.cpp:78
std::unordered_map< std::string, PCB_LAYER_ID > m_layerMap
#define DISALLOW_CONSTRAINT
Definition: drc_rule.h:39
DRC_RULE * parseDRC_RULE()
wxString m_Condition
Definition: drc_rule.h:84
#define DISALLOW_FOOTPRINTS
Definition: drc_rule.h:50
int parseValue(DRCRULE_T::T aToken)
NETCLASSES & GetNetClasses() const
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
#define DISALLOW_VIAS
Definition: drc_rule.h:41
std::vector< NETCLASSPTR > m_MatchNetclasses
Definition: drc_rule.h:97
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
void parseConstraint(DRC_RULE *aRule)
std::vector< KICAD_T > m_MatchTypes
Definition: drc_rule.h:98
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:180
#define _(s)
Definition: 3d_actions.cpp:33
#define HOLE_CONSTRAINT
Definition: drc_rule.h:38
#define TO_UTF8(wxstring)
#define DISALLOW_BB_VIAS
Definition: drc_rule.h:43
#define DISALLOW_ZONES
Definition: drc_rule.h:46
long long int ValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, bool aUseMils, EDA_DATA_TYPE aType)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application.
Definition: base_units.cpp:471