KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The KiCad Developers, see AUTHORS.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, see <https://www.gnu.org/licenses/>.
18 */
19
20
21#include <board.h>
22#include <zones.h>
23#include <drc/drc_rule_parser.h>
25#include <drc_rules_lexer.h>
26#include <pcbexpr_evaluator.h>
27#include <reporter.h>
29#include <properties/property.h>
31
32using namespace DRCRULE_T;
33
34
35DRC_RULES_PARSER::DRC_RULES_PARSER( const wxString& aSource, const wxString& aSourceDescr ) :
36 DRC_RULES_LEXER( aSource.ToStdString(), aSourceDescr ),
38 m_tooRecent( false ),
39 m_reporter( nullptr )
40{
41}
42
43
44void DRC_RULES_PARSER::reportError( const wxString& aMessage, int aOffset )
45{
46 reportErrorAt( aMessage, CurLineNumber(), CurOffset() + aOffset, CurLine() );
47}
48
49
50void DRC_RULES_PARSER::reportErrorAt( const wxString& aMessage, int aLine, int aOffset, const char* aSourceLine )
51{
52 wxString rest;
53 wxString first = aMessage.BeforeFirst( '|', &rest );
54
55 if( m_reporter )
56 {
57 wxString msg = wxString::Format( _( "ERROR: <a href='%d:%d'>%s</a>%s" ),
58 aLine,
59 aOffset,
60 first,
61 rest );
62
63 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
64 }
65 else
66 {
67 wxString msg = wxString::Format( _( "ERROR: %s%s" ), first, rest );
68
69 THROW_PARSE_ERROR( msg, CurSource(), aSourceLine, aLine, aOffset );
70 }
71}
72
73
74void DRC_RULES_PARSER::reportDeprecation( const wxString& oldToken, const wxString& newToken )
75{
76 if( m_reporter )
77 {
78 wxString msg = wxString::Format( _( "The '%s' keyword has been deprecated. "
79 "Please use '%s' instead." ),
80 oldToken,
81 newToken);
82
83 m_reporter->Report( msg, RPT_SEVERITY_WARNING );
84 }
85}
86
87
89{
90 for( size_t pos = curText.find( "${" ); pos != std::string::npos;
91 pos = curText.find( "${", pos + 2 ) )
92 {
93 size_t end = curText.find( '}', pos + 2 );
94
95 if( end != std::string::npos )
96 {
97 wxString token = wxString::FromUTF8( curText.substr( pos + 2, end - ( pos + 2 ) ) );
98
99 // ${Class:X} is a DRC expression directive handled by testFootprintSelector(), so it is
100 // left unexpanded on purpose and must not be reported as unresolved.
101 if( IsComponentClassSelector( token ) )
102 continue;
103 }
104
105 reportError( _( "Unresolved text variable" ), (int) pos );
106 return true;
107 }
108
109 return false;
110}
111
112
114{
115 int depth = 1;
116
117 for( T token = NextTok(); token != T_EOF; token = NextTok() )
118 {
119 if( token == T_LEFT )
120 depth++;
121
122 if( token == T_RIGHT )
123 {
124 if( --depth == 0 )
125 break;
126 }
127 }
128}
129
130
131void DRC_RULES_PARSER::expected( const wxString& expectedTokens )
132{
133 wxString msg;
134
135 if( curText.starts_with( "${" ) )
136 msg.Printf( _( "Unresolved text variable." ) );
137 else
138 msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(), expectedTokens );
139
140 reportError( msg );
141 parseUnknown();
142}
143
144
146{
147 wxString expr;
148 int depth = 1;
149
150 for( T token = NextTok(); token != T_EOF; token = NextTok() )
151 {
152 if( token == T_LEFT )
153 depth++;
154
155 if( token == T_RIGHT )
156 {
157 if( --depth == 0 )
158 break;
159 }
160
161 if( !expr.IsEmpty() )
162 expr += CurSeparator();
163
165 expr += FromUTF8();
166 }
167
168 return expr;
169}
170
171
172void DRC_RULES_PARSER::Parse( std::vector<std::shared_ptr<DRC_RULE>>& aRules, REPORTER* aReporter )
173{
174 bool haveVersion = false;
175
176 m_reporter = aReporter;
177
178 for( T token = NextTok(); token != T_EOF; token = NextTok() )
179 {
181 continue;
182
183 if( token != T_LEFT )
184 reportError( _( "Missing '('." ) );
185
186 token = NextTok();
187
188 if( !haveVersion && token != T_version )
189 {
190 reportError( _( "Missing version statement." ) );
191 haveVersion = true; // don't keep on reporting it
192 }
193
194 switch( token )
195 {
196 case T_version:
197 haveVersion = true;
198 token = NextTok();
199
200 if( (int) token == DSN_RIGHT )
201 {
202 reportError( _( "Missing version number." ) );
203 }
204 else if( (int) token == DSN_NUMBER )
205 {
206 m_requiredVersion = (int)strtol( CurText(), nullptr, 10 );
208
209 if( (int) NextTok() != DSN_RIGHT )
210 reportError( _( "Missing ')'." ) );
211 }
212 else
213 {
214 expected( _( "version number" ) ); // translate "version number"; it is not a token
215 }
216
217 break;
218
219 case T_rule:
220 aRules.emplace_back( parseDRC_RULE() );
221 break;
222
223 case T_EOF:
224 reportError( _( "Incomplete statement." ) );
225 break;
226
227 default:
228 expected( wxT( "rule or version" ) );
229 }
230 }
231
232 if( m_reporter && !m_reporter->HasMessage() )
233 m_reporter->Report( _( "No errors found." ), RPT_SEVERITY_INFO );
234
235 m_reporter = nullptr;
236}
237
238
240 std::vector<std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE>>& aRules, REPORTER* aReporter )
241{
242 bool haveVersion = false;
243
244 m_reporter = aReporter;
245
246 for( T token = NextTok(); token != T_EOF; token = NextTok() )
247 {
248 if( token != T_LEFT )
249 reportError( _( "Missing '('." ) );
250
251 token = NextTok();
252
253 if( !haveVersion && token != T_version )
254 {
255 reportError( _( "Missing version statement." ) );
256 haveVersion = true; // don't keep on reporting it
257 }
258
259 switch( token )
260 {
261 case T_version:
262 haveVersion = true;
263 token = NextTok();
264
265 if( (int) token == DSN_RIGHT )
266 {
267 reportError( _( "Missing version number." ) );
268 }
269 else if( (int) token == DSN_NUMBER )
270 {
271 m_requiredVersion = (int) strtol( CurText(), nullptr, 10 );
273
274 if( (int) NextTok() != DSN_RIGHT )
275 reportError( _( "Missing ')'." ) );
276 }
277 else
278 {
279 expected( _( "version number" ) ); // translate "version number"; it is not a token
280 }
281
282 break;
283
284 case T_assign_component_class:
285 aRules.emplace_back( parseComponentClassAssignment() );
286 break;
287
288 case T_EOF:
289 reportError( _( "Incomplete statement." ) );
290 break;
291
292 default:
293 expected( wxT( "assign_component_class or version" ) );
294 }
295 }
296
297 if( m_reporter && !m_reporter->HasMessage() )
298 m_reporter->Report( _( "No errors found." ), RPT_SEVERITY_INFO );
299
300 m_reporter = nullptr;
301}
302
303
304std::shared_ptr<DRC_RULE> DRC_RULES_PARSER::parseDRC_RULE()
305{
306 std::shared_ptr<DRC_RULE> rule = std::make_shared<DRC_RULE>();
307 int conditionLine = 0;
308 int conditionOffset = 0;
309 std::string conditionSource;
310
311 T token = NextTok();
312
313 if( !IsSymbol( token ) )
314 reportError( _( "Missing rule name." ) );
315
317 rule->m_Name = FromUTF8();
318
319 for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
320 {
322 continue;
323
324 if( token != T_LEFT )
325 reportError( _( "Missing '('." ) );
326
327 token = NextTok();
328
329 switch( token )
330 {
331 case T_constraint:
332 parseConstraint( rule.get() );
333 break;
334
335 case T_condition:
336 token = NextTok();
337
338 if( (int) token == DSN_RIGHT )
339 {
340 reportError( _( "Missing condition expression." ) );
341 }
342 else if( IsSymbol( token ) )
343 {
345 rule->m_Condition = new DRC_RULE_CONDITION( FromUTF8() );
346 conditionLine = CurLineNumber();
347 conditionOffset = CurOffset();
348 conditionSource = CurLine();
349
350 if( !rule->m_Condition->Compile( m_reporter, CurLineNumber(), CurOffset() ) )
351 reportError( wxString::Format( _( "Could not parse expression '%s'." ), FromUTF8() ) );
352
353 if( (int) NextTok() != DSN_RIGHT )
354 reportError( _( "Missing ')'." ) );
355 }
356 else
357 {
358 expected( _( "quoted expression" ) ); // translate "quoted expression"; it is not a token
359 }
360
361 break;
362
363 case T_layer:
364 if( rule->m_LayerCondition != LSET::AllLayersMask() )
365 reportError( _( "'layer' keyword already present." ) );
366
367 rule->m_LayerCondition = parseLayer( &rule->m_LayerSource );
368 break;
369
370 case T_severity:
371 rule->m_Severity = parseSeverity();
372 break;
373
374 case T_EOF:
375 reportError( _( "Incomplete statement." ) );
376 return rule;
377
378 default:
379 expected( wxT( "constraint, condition, or disallow" ) );
380 }
381 }
382
383 if( (int) CurTok() != DSN_RIGHT )
384 reportError( _( "Missing ')'." ) );
385
386 if( rule->m_Condition && rule->m_Condition->RequiresPairItems() )
387 {
388 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
389 {
390 // isCoupledDiffPair() can identify the pair from A's net for these constraints
391 if( !rule->m_Condition->ReferencesItemB()
392 && ( constraint.m_Type == LENGTH_CONSTRAINT
393 || constraint.m_Type == NET_CHAIN_LENGTH_CONSTRAINT
394 || constraint.m_Type == SKEW_CONSTRAINT ) )
395 {
396 continue;
397 }
398
399 if( constraint.IsUnary() )
400 {
401 reportErrorAt( wxString::Format(
402 _( "Item 'B' is not available for a single-item constraint in rule '%s'." ), rule->m_Name ),
403 conditionLine, conditionOffset, conditionSource.c_str() );
404 break;
405 }
406 }
407 }
408
409 return rule;
410}
411
412
413std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE> DRC_RULES_PARSER::parseComponentClassAssignment()
414{
415 std::shared_ptr<DRC_RULE_CONDITION> condition;
416
417 T token = NextTok();
418
419 if( !IsSymbol( token ) )
420 reportError( _( "Missing component class name." ) );
421
423 wxString componentClass = FromUTF8();
424
425 for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
426 {
427 if( token != T_LEFT )
428 reportError( _( "Missing '('." ) );
429
430 token = NextTok();
431
432 switch( token )
433 {
434 case T_condition:
435 token = NextTok();
436
437 if( (int) token == DSN_RIGHT )
438 {
439 reportError( _( "Missing condition expression." ) );
440 }
441 else if( IsSymbol( token ) )
442 {
444 condition = std::make_shared<DRC_RULE_CONDITION>( FromUTF8() );
445
446 if( !condition->Compile( m_reporter, CurLineNumber(), CurOffset() ) )
447 reportError( wxString::Format( _( "Could not parse expression '%s'." ), FromUTF8() ) );
448
449 if( (int) NextTok() != DSN_RIGHT )
450 reportError( _( "Missing ')'." ) );
451 }
452 else
453 {
454 expected( _( "quoted expression" ) ); // translate "quoted expression"; it is not a token
455 }
456
457 break;
458
459 case T_EOF:
460 reportError( _( "Incomplete statement." ) );
461 return nullptr;
462
463 default:
464 expected( wxT( "condition" ) );
465 }
466 }
467
468 if( (int) CurTok() != DSN_RIGHT )
469 reportError( _( "Missing ')'." ) );
470
471 return std::make_shared<COMPONENT_CLASS_ASSIGNMENT_RULE>( componentClass, std::move( condition ) );
472}
473
474
476{
478 int value;
481 wxString msg;
482 bool allowsTimeDomain = false;
483
484 auto validateAndSetValueWithUnits =
485 [this, &allowsTimeDomain, &unitsType, &c]( int aValue, const EDA_UNITS aUnits, auto aSetter )
486 {
487 const EDA_DATA_TYPE unitsTypeTmp = UNITS_PROVIDER::GetTypeFromUnits( aUnits );
488
489 if( !allowsTimeDomain && unitsTypeTmp == EDA_DATA_TYPE::TIME )
490 reportError( _( "Time based units not allowed for constraint type." ) );
491
492 if( ( c.m_Value.HasMin() || c.m_Value.HasMax() || c.m_Value.HasOpt() )
493 && unitsType != unitsTypeTmp )
494 {
495 reportError( _( "Mixed units for constraint values." ) );
496 }
497
498 unitsType = unitsTypeTmp;
499 aSetter( aValue );
500
501 if( allowsTimeDomain )
502 {
504 {
507 }
508 else
509 {
512 }
513 }
514 };
515
516 T token = NextTok();
517
519 return;
520
521 if( token == T_mechanical_clearance )
522 {
523 reportDeprecation( wxT( "mechanical_clearance" ), wxT( "physical_clearance" ) );
524 token = T_physical_clearance;
525 }
526 else if( token == T_mechanical_hole_clearance )
527 {
528 reportDeprecation( wxT( "mechanical_hole_clearance" ), wxT( "physical_hole_clearance" ) );
529 token = T_physical_hole_clearance;
530 }
531 else if( token == T_hole )
532 {
533 reportDeprecation( wxT( "hole" ), wxT( "hole_size" ) );
534 token = T_hole_size;
535 }
536 else if( (int) token == DSN_RIGHT || token == T_EOF )
537 {
538 msg.Printf( _( "Missing constraint type.| Expected %s." ),
539 wxT( "assertion, clearance, hole_clearance, edge_clearance, physical_clearance, "
540 "physical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
541 "hole_to_hole, track_width, track_angle, track_segment_length, annular_width, "
542 "disallow, zone_connection, thermal_relief_gap, thermal_spoke_width, "
543 "min_resolved_spokes, microvia_stack_depth, microvia_aspect_ratio, solder_mask_expansion, "
544 "solder_paste_abs_margin, "
545 "solder_paste_rel_margin, length, net_chain_length, skew, via_count, "
546 "via_dangling, via_diameter, diff_pair_gap or diff_pair_uncoupled" ) );
547
548 reportError( msg );
549 return;
550 }
551
552 switch( token )
553 {
554 case T_assertion: c.m_Type = ASSERTION_CONSTRAINT; break;
555 case T_clearance: c.m_Type = CLEARANCE_CONSTRAINT; break;
556 case T_creepage: c.m_Type = CREEPAGE_CONSTRAINT; break;
557 case T_hole_clearance: c.m_Type = HOLE_CLEARANCE_CONSTRAINT; break;
558 case T_edge_clearance: c.m_Type = EDGE_CLEARANCE_CONSTRAINT; break;
559 case T_hole_size: c.m_Type = HOLE_SIZE_CONSTRAINT; break;
560 case T_hole_to_hole: c.m_Type = HOLE_TO_HOLE_CONSTRAINT; break;
561 case T_courtyard_clearance: c.m_Type = COURTYARD_CLEARANCE_CONSTRAINT; break;
562 case T_silk_clearance: c.m_Type = SILK_CLEARANCE_CONSTRAINT; break;
563 case T_text_height: c.m_Type = TEXT_HEIGHT_CONSTRAINT; break;
564 case T_text_thickness: c.m_Type = TEXT_THICKNESS_CONSTRAINT; break;
565 case T_track_width: c.m_Type = TRACK_WIDTH_CONSTRAINT; break;
566 case T_track_angle: c.m_Type = TRACK_ANGLE_CONSTRAINT; break;
567 case T_track_segment_length: c.m_Type = TRACK_SEGMENT_LENGTH_CONSTRAINT; break;
568 case T_connection_width: c.m_Type = CONNECTION_WIDTH_CONSTRAINT; break;
569 case T_annular_width: c.m_Type = ANNULAR_WIDTH_CONSTRAINT; break;
570 case T_via_diameter: c.m_Type = VIA_DIAMETER_CONSTRAINT; break;
571 case T_via_dangling: c.m_Type = VIA_DANGLING_CONSTRAINT; break;
572 case T_zone_connection: c.m_Type = ZONE_CONNECTION_CONSTRAINT; break;
573 case T_thermal_relief_gap: c.m_Type = THERMAL_RELIEF_GAP_CONSTRAINT; break;
574 case T_thermal_spoke_width: c.m_Type = THERMAL_SPOKE_WIDTH_CONSTRAINT; break;
575 case T_microvia_stack_depth: c.m_Type = MICROVIA_STACK_DEPTH_CONSTRAINT; break;
576 case T_microvia_aspect_ratio: c.m_Type = MICROVIA_ASPECT_RATIO_CONSTRAINT; break;
577 case T_min_resolved_spokes: c.m_Type = MIN_RESOLVED_SPOKES_CONSTRAINT; break;
578 case T_solder_mask_expansion: c.m_Type = SOLDER_MASK_EXPANSION_CONSTRAINT; break;
579 case T_solder_mask_sliver: c.m_Type = SOLDER_MASK_SLIVER_CONSTRAINT; break;
580 case T_solder_paste_abs_margin: c.m_Type = SOLDER_PASTE_ABS_MARGIN_CONSTRAINT; break;
581 case T_solder_paste_rel_margin: c.m_Type = SOLDER_PASTE_REL_MARGIN_CONSTRAINT; break;
582 case T_disallow: c.m_Type = DISALLOW_CONSTRAINT; break;
583 case T_length: c.m_Type = LENGTH_CONSTRAINT; break;
584 case T_net_chain_length: c.m_Type = NET_CHAIN_LENGTH_CONSTRAINT; break;
585 case T_stub_length: c.m_Type = NET_CHAIN_STUB_LENGTH_CONSTRAINT; break;
586 case T_return_path: c.m_Type = NET_CHAIN_RETURN_PATH_CONSTRAINT; break;
587 case T_skew: c.m_Type = SKEW_CONSTRAINT; break;
588 case T_via_count: c.m_Type = VIA_COUNT_CONSTRAINT; break;
589 case T_diff_pair_gap: c.m_Type = DIFF_PAIR_GAP_CONSTRAINT; break;
590 case T_diff_pair_uncoupled: c.m_Type = MAX_UNCOUPLED_CONSTRAINT; break;
591 case T_physical_clearance: c.m_Type = PHYSICAL_CLEARANCE_CONSTRAINT; break;
592 case T_physical_hole_clearance: c.m_Type = PHYSICAL_HOLE_CLEARANCE_CONSTRAINT; break;
593 case T_bridged_mask: c.m_Type = BRIDGED_MASK_CONSTRAINT; break;
594 default:
595 expected( wxT( "assertion, clearance, hole_clearance, edge_clearance, physical_clearance, "
596 "physical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
597 "hole_to_hole, track_width, track_angle, track_segment_length, annular_width, "
598 "disallow, zone_connection, thermal_relief_gap, thermal_spoke_width, "
599 "min_resolved_spokes, microvia_stack_depth, microvia_aspect_ratio, solder_mask_expansion, "
600 "solder_mask_sliver, "
601 "solder_paste_abs_margin, solder_paste_rel_margin, length, net_chain_length, "
602 "skew, via_count, via_dangling, via_diameter, diff_pair_gap, "
603 "diff_pair_uncoupled or bridged_mask" ) );
604 return;
605 }
606
607 if( aRule->FindConstraint( c.m_Type ) )
608 {
609 msg.Printf( _( "Rule already has a '%s' constraint." ), FromUTF8() );
610 reportError( msg );
611 }
612
614
615 bool unitless = ratio || c.m_Type == VIA_COUNT_CONSTRAINT || c.m_Type == MIN_RESOLVED_SPOKES_CONSTRAINT
618
619 allowsTimeDomain = c.m_Type == LENGTH_CONSTRAINT || c.m_Type == NET_CHAIN_LENGTH_CONSTRAINT
621 || c.m_Type == SKEW_CONSTRAINT;
622
623 if( c.m_Type == DISALLOW_CONSTRAINT )
624 {
625 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
626 {
627 if( (int) token == DSN_STRING )
628 token = GetCurStrAsToken();
629
630 switch( token )
631 {
632 case T_track: c.m_DisallowFlags |= DRC_DISALLOW_TRACKS; break;
637 case T_through_via: c.m_DisallowFlags |= DRC_DISALLOW_THROUGH_VIAS; break;
638 case T_blind_via: c.m_DisallowFlags |= DRC_DISALLOW_BLIND_VIAS; break;
639 case T_buried_via: c.m_DisallowFlags |= DRC_DISALLOW_BURIED_VIAS; break;
640 case T_micro_via: c.m_DisallowFlags |= DRC_DISALLOW_MICRO_VIAS; break;
641 case T_pad: c.m_DisallowFlags |= DRC_DISALLOW_PADS; break;
642 case T_zone: c.m_DisallowFlags |= DRC_DISALLOW_ZONES; break;
643 case T_text: c.m_DisallowFlags |= DRC_DISALLOW_TEXTS; break;
644 case T_graphic: c.m_DisallowFlags |= DRC_DISALLOW_GRAPHICS; break;
645 case T_hole: c.m_DisallowFlags |= DRC_DISALLOW_HOLES; break;
646 case T_footprint: c.m_DisallowFlags |= DRC_DISALLOW_FOOTPRINTS; break;
647
648 case T_EOF:
649 reportError( _( "Missing ')'." ) );
650 return;
651
652 default:
653 expected( wxT( "track, via, through_via, blind_via, micro_via, buried_via, pad, zone, text, "
654 "graphic, hole, or footprint." ) );
655 return;
656 }
657 }
658
659 if( (int) CurTok() != DSN_RIGHT )
660 reportError( _( "Missing ')'." ) );
661
662 aRule->AddConstraint( c );
663 return;
664 }
666 {
667 // (constraint return_path (layer "B.Cu") (net "GND"))
668 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
669 {
670 if( token == T_LEFT )
671 token = NextTok();
672
673 switch( token )
674 {
675 case T_layer:
676 NeedSYMBOLorNUMBER();
677 c.m_ReferenceLayer = FromUTF8();
678 NeedRIGHT();
679 break;
680
681 case T_net:
682 NeedSYMBOLorNUMBER();
683 c.m_ReferenceNet = FromUTF8();
684 NeedRIGHT();
685 break;
686
687 case T_EOF:
688 reportError( _( "Missing ')'." ) );
689 return;
690
691 default:
692 // Unknown sub-option; skip its subtree.
693 while( NextTok() != T_RIGHT )
694 ;
695 break;
696 }
697 }
698
699 aRule->AddConstraint( c );
700 return;
701 }
702 else if( c.m_Type == ZONE_CONNECTION_CONSTRAINT )
703 {
704 token = NextTok();
705
706 if( (int) token == DSN_STRING )
707 token = GetCurStrAsToken();
708
709 switch( token )
710 {
711 case T_solid: c.m_ZoneConnection = ZONE_CONNECTION::FULL; break;
712 case T_thermal_reliefs: c.m_ZoneConnection = ZONE_CONNECTION::THERMAL; break;
713 case T_none: c.m_ZoneConnection = ZONE_CONNECTION::NONE; break;
714
715 case T_EOF:
716 reportError( _( "Missing ')'." ) );
717 return;
718
719 default:
720 expected( wxT( "solid, thermal_reliefs or none." ) );
721 return;
722 }
723
724 if( (int) NextTok() != DSN_RIGHT )
725 reportError( _( "Missing ')'." ) );
726
727 aRule->AddConstraint( c );
728 return;
729 }
731 {
732 // We don't use a min/max/opt structure here because it would give a strong implication
733 // that you could specify the optimal number of spokes. We don't want to open that door
734 // because the spoke generator is highly optimized around being able to "cheat" off of a
735 // cartesian coordinate system.
736
737 token = NextTok();
738
739 if( (int) token == DSN_NUMBER )
740 {
741 value = (int) strtol( CurText(), nullptr, 10 );
742 c.m_Value.SetMin( value );
743
744 if( (int) NextTok() != DSN_RIGHT )
745 reportError( _( "Missing ')'." ) );
746 }
747 else
748 {
749 expected( _( "number" ) ); // translate "number"; it is not a token
750 }
751
752 aRule->AddConstraint( c );
753 return;
754 }
755 else if( c.m_Type == ASSERTION_CONSTRAINT )
756 {
757 token = NextTok();
758
759 if( (int) token == DSN_RIGHT )
760 reportError( _( "Missing assertion expression." ) );
761
762 if( IsSymbol( token ) )
763 {
764 c.m_Test = new DRC_RULE_CONDITION( FromUTF8() );
765 c.m_Test->Compile( m_reporter, CurLineNumber(), CurOffset() );
766
767 if( c.m_Test->RequiresPairItems() )
768 reportError( _( "Item 'B' is not available in assertion expressions." ) );
769
770 if( (int) NextTok() != DSN_RIGHT )
771 reportError( _( "Missing ')'." ) );
772 }
773 else
774 {
775 expected( _( "quoted expression" ) ); // translate "quoted expression"; it is not a token
776 }
777
778 aRule->AddConstraint( c );
779 return;
780 }
781
782 for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
783 {
784 if( token != T_LEFT )
785 reportError( _( "Missing '('." ) );
786
787 token = NextTok();
788
789 switch( token )
790 {
791 case T_within_diff_pairs:
792 if( c.m_Type == SKEW_CONSTRAINT )
794 else
795 reportError( _( "within_diff_pairs option invalid for constraint type." ) );
796
797 if( (int) NextTok() != DSN_RIGHT )
798 reportError( _( "Missing ')'." ) );
799
800 break;
801
802 case T_min:
803 {
804 size_t offset = CurOffset() + GetTokenString( token ).length();
805 wxString expr = parseExpression();
806
807 if( expr.IsEmpty() )
808 {
809 reportError( _( "Missing min value." ) );
810 break;
811 }
812
813 parseValueWithUnits( (int) offset, expr, value, units, unitless, ratio );
814 validateAndSetValueWithUnits( value, units,
815 [&c]( const int aValue )
816 {
817 c.m_Value.SetMin( aValue );
818 } );
819
820 break;
821 }
822
823 case T_max:
824 {
825 size_t offset = CurOffset() + GetTokenString( token ).length();
826 wxString expr = parseExpression();
827
828 if( expr.IsEmpty() )
829 {
830 reportError( _( "Missing max value." ) );
831 break;
832 }
833
834 parseValueWithUnits( (int) offset, expr, value, units, unitless, ratio );
835 validateAndSetValueWithUnits( value, units,
836 [&c]( const int aValue )
837 {
838 c.m_Value.SetMax( aValue );
839 } );
840
841 break;
842 }
843
844 case T_opt:
845 {
846 size_t offset = CurOffset() + GetTokenString( token ).length();
847 wxString expr = parseExpression();
848
849 if( expr.IsEmpty() )
850 {
851 reportError( _( "Missing opt value." ) );
852 break;
853 }
854
855 parseValueWithUnits( (int) offset, expr, value, units, unitless, ratio );
856 validateAndSetValueWithUnits( value, units,
857 [&c]( const int aValue )
858 {
859 c.m_Value.SetOpt( aValue );
860 } );
861
862 break;
863 }
864
865 case T_EOF:
866 reportError( _( "Incomplete statement." ) );
867 return;
868
869 default:
870 expected( wxT( "min, max, opt, or within_diff_pairs" ) );
871 }
872 }
873
874 aRule->AddConstraint( c );
875}
876
877
878void DRC_RULES_PARSER::parseValueWithUnits( int aOffset, const wxString& aExpr, int& aResult, EDA_UNITS& aUnits,
879 bool aUnitless, bool aRatio )
880{
881 aResult = 0.0;
882 aUnits = EDA_UNITS::UNSCALED;
883
884 auto errorHandler =
885 [&]( const wxString& message, int offset )
886 {
887 wxString rest;
888 wxString first = message.BeforeFirst( '|', &rest );
889
890 if( m_reporter )
891 {
892 wxString msg = wxString::Format( _( "ERROR: <a href='%d:%d'>%s</a>%s" ),
893 CurLineNumber(),
894 aOffset + offset,
895 first,
896 rest );
897
898 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
899 }
900 else
901 {
902 wxString msg = wxString::Format( _( "ERROR: %s%s" ), first, rest );
903
904 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(),
905 CurOffset() + aOffset );
906 }
907 };
908
911 evaluator.SetErrorCallback( errorHandler );
912
913 if( evaluator.Evaluate( aExpr ) )
914 {
915 // A ratio is fractional, so keep three decimals rather than rounding to a whole number.
916 aResult = aRatio ? KiROUND( evaluator.ResultAsDouble() * 1000.0 ) : evaluator.Result();
917 aUnits = evaluator.Units();
918 }
919}
920
921
923{
924 LSET retVal;
925 int token = NextTok();
926
927 if( (int) token == DSN_RIGHT )
928 {
929 reportError( _( "Missing layer name or type." ) );
930 return LSET::AllCuMask();
931 }
932 else if( token == T_outer )
933 {
934 *aSource = GetTokenString( token );
935 retVal = LSET::ExternalCuMask();
936 }
937 else if( token == T_inner )
938 {
939 *aSource = GetTokenString( token );
940 retVal = LSET::InternalCuMask();
941 }
942 else
943 {
944 wxString layerName = FromUTF8();
945 wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
946
947 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
948 {
949 wxPGChoiceEntry& entry = layerMap[ii];
950
951 if( entry.GetText().Matches( layerName ) )
952 {
953 *aSource = layerName;
954 retVal.set( ToLAYER_ID( entry.GetValue() ) );
955 }
956 }
957
958 if( !retVal.any() )
959 {
961 reportError( wxString::Format( _( "Unrecognized layer '%s'." ), layerName ) );
962
963 retVal.set( Rescue );
964 }
965 }
966
967 if( (int) NextTok() != DSN_RIGHT )
968 reportError( _( "Missing ')'." ) );
969
970 return retVal;
971}
972
973
975{
977
978 T token = NextTok();
979
980 if( (int) token == DSN_RIGHT || token == T_EOF )
981 {
982 reportError( _( "Missing severity name." ) );
984 }
985
986 switch( token )
987 {
988 case T_ignore: retVal = RPT_SEVERITY_IGNORE; break;
989 case T_warning: retVal = RPT_SEVERITY_WARNING; break;
990 case T_error: retVal = RPT_SEVERITY_ERROR; break;
991 case T_exclusion: retVal = RPT_SEVERITY_EXCLUSION; break;
992
993 default:
994 expected( wxT( "ignore, warning, error, or exclusion" ) );
995 }
996
997 if( (int) NextTok() != DSN_RIGHT )
998 reportError( _( "Missing ')'." ) );
999
1000 return retVal;
1001}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
BASE_SET & set(size_t pos)
Definition base_set.h:126
DRC_RULE_CONDITION * m_Test
Definition drc_rule.h:282
int m_DisallowFlags
Definition drc_rule.h:280
void SetOption(OPTIONS option)
Definition drc_rule.h:264
ZONE_CONNECTION m_ZoneConnection
Definition drc_rule.h:281
MINOPTMAX< int > m_Value
Definition drc_rule.h:279
wxString m_ReferenceLayer
Definition drc_rule.h:288
DRC_CONSTRAINT_T m_Type
Definition drc_rule.h:278
bool IsUnary() const
Definition drc_rule.cpp:87
void ClearOption(OPTIONS option)
Definition drc_rule.h:266
wxString m_ReferenceNet
Definition drc_rule.h:293
std::shared_ptr< COMPONENT_CLASS_ASSIGNMENT_RULE > parseComponentClassAssignment()
void reportDeprecation(const wxString &oldToken, const wxString &newToken)
void Parse(std::vector< std::shared_ptr< DRC_RULE > > &aRules, REPORTER *aReporter)
REPORTER * m_reporter
void reportError(const wxString &aMessage, int aOffset=0)
void parseValueWithUnits(int aOffset, const wxString &aExpr, int &aResult, EDA_UNITS &aUnits, bool aUnitless=false, bool aRatio=false)
void ParseComponentClassAssignmentRules(std::vector< std::shared_ptr< COMPONENT_CLASS_ASSIGNMENT_RULE > > &aRules, REPORTER *aReporter)
void expected(const wxString &expectedTokens)
bool checkUnresolvedTextVariable()
std::shared_ptr< DRC_RULE > parseDRC_RULE()
LSET parseLayer(wxString *aSource)
void parseConstraint(DRC_RULE *aRule)
void reportErrorAt(const wxString &aMessage, int aLine, int aOffset, const char *aSourceLine)
DRC_RULES_PARSER(const wxString &aSource, const wxString &aSourceDescr)
bool Compile(REPORTER *aReporter, int aSourceLine=0, int aSourceOffset=0)
void AddConstraint(DRC_CONSTRAINT &aConstraint)
Definition drc_rule.cpp:68
std::optional< DRC_CONSTRAINT > FindConstraint(DRC_CONSTRAINT_T aType)
Definition drc_rule.cpp:75
static ENUM_MAP< T > & Instance()
Definition property.h:783
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
static const LSET & ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition lset.cpp:630
static const LSET & AllLayersMask()
Definition lset.cpp:637
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition lset.cpp:573
void SetMin(T v)
Definition minoptmax.h:41
bool HasMax() const
Definition minoptmax.h:38
void SetOpt(T v)
Definition minoptmax.h:43
bool HasMin() const
Definition minoptmax.h:37
void SetMax(T v)
Definition minoptmax.h:42
bool HasOpt() const
Definition minoptmax.h:39
double ResultAsDouble() const
Unrounded result, for constraints whose value is a ratio rather than a count or a length.
bool Evaluate(const wxString &aExpr)
void SetErrorCallback(std::function< void(const wxString &aMessage, int aOffset)> aCallback)
EDA_UNITS Units() const
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
static EDA_DATA_TYPE GetTypeFromUnits(const EDA_UNITS aUnits)
Gets the inferred type from the given units.
bool IsComponentClassSelector(const wxString &aToken)
${Class:X} inside a DRC rule is a component-class selector consumed by testFootprintSelector(),...
Definition drc_rule.cpp:31
@ DRC_DISALLOW_PADS
Definition drc_rule.h:102
@ DRC_DISALLOW_BURIED_VIAS
Definition drc_rule.h:100
@ DRC_DISALLOW_BLIND_VIAS
Definition drc_rule.h:99
@ DRC_DISALLOW_TEXTS
Definition drc_rule.h:104
@ DRC_DISALLOW_ZONES
Definition drc_rule.h:103
@ DRC_DISALLOW_HOLES
Definition drc_rule.h:106
@ DRC_DISALLOW_GRAPHICS
Definition drc_rule.h:105
@ DRC_DISALLOW_THROUGH_VIAS
Definition drc_rule.h:97
@ DRC_DISALLOW_FOOTPRINTS
Definition drc_rule.h:107
@ DRC_DISALLOW_TRACKS
Definition drc_rule.h:101
@ DRC_DISALLOW_MICRO_VIAS
Definition drc_rule.h:98
@ ANNULAR_WIDTH_CONSTRAINT
Definition drc_rule.h:63
@ BRIDGED_MASK_CONSTRAINT
Definition drc_rule.h:88
@ COURTYARD_CLEARANCE_CONSTRAINT
Definition drc_rule.h:57
@ VIA_DIAMETER_CONSTRAINT
Definition drc_rule.h:72
@ ZONE_CONNECTION_CONSTRAINT
Definition drc_rule.h:64
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:78
@ NET_CHAIN_LENGTH_CONSTRAINT
Definition drc_rule.h:74
@ VIA_DANGLING_CONSTRAINT
Definition drc_rule.h:87
@ SOLDER_MASK_SLIVER_CONSTRAINT
Definition drc_rule.h:89
@ NET_CHAIN_STUB_LENGTH_CONSTRAINT
Definition drc_rule.h:75
@ DISALLOW_CONSTRAINT
Definition drc_rule.h:71
@ TRACK_WIDTH_CONSTRAINT
Definition drc_rule.h:61
@ SILK_CLEARANCE_CONSTRAINT
Definition drc_rule.h:58
@ EDGE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:55
@ MIN_RESOLVED_SPOKES_CONSTRAINT
Definition drc_rule.h:67
@ TRACK_SEGMENT_LENGTH_CONSTRAINT
Definition drc_rule.h:62
@ TEXT_THICKNESS_CONSTRAINT
Definition drc_rule.h:60
@ LENGTH_CONSTRAINT
Definition drc_rule.h:73
@ VIA_COUNT_CONSTRAINT
Definition drc_rule.h:81
@ NET_CHAIN_RETURN_PATH_CONSTRAINT
Definition drc_rule.h:76
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:83
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
@ MICROVIA_ASPECT_RATIO_CONSTRAINT
Definition drc_rule.h:91
@ THERMAL_SPOKE_WIDTH_CONSTRAINT
Definition drc_rule.h:66
@ CONNECTION_WIDTH_CONSTRAINT
Definition drc_rule.h:85
@ THERMAL_RELIEF_GAP_CONSTRAINT
Definition drc_rule.h:65
@ MAX_UNCOUPLED_CONSTRAINT
Definition drc_rule.h:79
@ ASSERTION_CONSTRAINT
Definition drc_rule.h:84
@ SKEW_CONSTRAINT
Definition drc_rule.h:77
@ MICROVIA_STACK_DEPTH_CONSTRAINT
Definition drc_rule.h:90
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:53
@ SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
Definition drc_rule.h:69
@ SOLDER_MASK_EXPANSION_CONSTRAINT
Definition drc_rule.h:68
@ TRACK_ANGLE_CONSTRAINT
Definition drc_rule.h:86
@ HOLE_SIZE_CONSTRAINT
Definition drc_rule.h:56
@ TEXT_HEIGHT_CONSTRAINT
Definition drc_rule.h:59
@ CREEPAGE_CONSTRAINT
Definition drc_rule.h:52
@ PHYSICAL_CLEARANCE_CONSTRAINT
Definition drc_rule.h:82
@ SOLDER_PASTE_REL_MARGIN_CONSTRAINT
Definition drc_rule.h:70
@ HOLE_TO_HOLE_CONSTRAINT
Definition drc_rule.h:54
#define DRC_RULE_FILE_VERSION
@ DSN_RIGHT
Definition dsnlexer.h:62
@ DSN_NUMBER
Definition dsnlexer.h:61
@ DSN_STRING
Definition dsnlexer.h:64
static std::string ToStdString(const wxString &aStr)
#define _(s)
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:34
EDA_UNITS
Definition eda_units.h:44
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
std::string message
unitsType
@ Rescue
Definition layer_ids.h:117
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
@ RPT_SEVERITY_EXCLUSION
@ RPT_SEVERITY_IGNORE
@ RPT_SEVERITY_INFO
VECTOR3I expected(15, 30, 45)
VECTOR2I end
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ NONE
Pads are not covered.
Definition zones.h:45
@ FULL
pads are covered by copper
Definition zones.h:47