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 wxString msg;
176
177 m_reporter = aReporter;
178
179 for( T token = NextTok(); token != T_EOF; token = NextTok() )
180 {
182 continue;
183
184 if( token != T_LEFT )
185 reportError( _( "Missing '('." ) );
186
187 token = NextTok();
188
189 if( !haveVersion && token != T_version )
190 {
191 reportError( _( "Missing version statement." ) );
192 haveVersion = true; // don't keep on reporting it
193 }
194
195 switch( token )
196 {
197 case T_version:
198 haveVersion = true;
199 token = NextTok();
200
201 if( (int) token == DSN_RIGHT )
202 {
203 reportError( _( "Missing version number." ) );
204 }
205 else if( (int) token == DSN_NUMBER )
206 {
207 m_requiredVersion = (int)strtol( CurText(), nullptr, 10 );
209
210 if( (int) NextTok() != DSN_RIGHT )
211 reportError( _( "Missing ')'." ) );
212 }
213 else
214 {
215 expected( _( "version number" ) ); // translate "version number"; it is not a token
216 }
217
218 break;
219
220 case T_rule:
221 aRules.emplace_back( parseDRC_RULE() );
222 break;
223
224 case T_EOF:
225 reportError( _( "Incomplete statement." ) );
226 break;
227
228 default:
229 expected( wxT( "rule or version" ) );
230 }
231 }
232
233 if( m_reporter && !m_reporter->HasMessage() )
234 m_reporter->Report( _( "No errors found." ), RPT_SEVERITY_INFO );
235
236 m_reporter = nullptr;
237}
238
239
241 std::vector<std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE>>& aRules, REPORTER* aReporter )
242{
243 bool haveVersion = false;
244 wxString msg;
245
246 m_reporter = aReporter;
247
248 for( T token = NextTok(); token != T_EOF; token = NextTok() )
249 {
250 if( token != T_LEFT )
251 reportError( _( "Missing '('." ) );
252
253 token = NextTok();
254
255 if( !haveVersion && token != T_version )
256 {
257 reportError( _( "Missing version statement." ) );
258 haveVersion = true; // don't keep on reporting it
259 }
260
261 switch( token )
262 {
263 case T_version:
264 haveVersion = true;
265 token = NextTok();
266
267 if( (int) token == DSN_RIGHT )
268 {
269 reportError( _( "Missing version number." ) );
270 }
271 else if( (int) token == DSN_NUMBER )
272 {
273 m_requiredVersion = (int) strtol( CurText(), nullptr, 10 );
275
276 if( (int) NextTok() != DSN_RIGHT )
277 reportError( _( "Missing ')'." ) );
278 }
279 else
280 {
281 expected( _( "version number" ) ); // translate "version number"; it is not a token
282 }
283
284 break;
285
286 case T_assign_component_class:
287 aRules.emplace_back( parseComponentClassAssignment() );
288 break;
289
290 case T_EOF:
291 reportError( _( "Incomplete statement." ) );
292 break;
293
294 default:
295 expected( wxT( "assign_component_class or version" ) );
296 }
297 }
298
299 if( m_reporter && !m_reporter->HasMessage() )
300 m_reporter->Report( _( "No errors found." ), RPT_SEVERITY_INFO );
301
302 m_reporter = nullptr;
303}
304
305
306std::shared_ptr<DRC_RULE> DRC_RULES_PARSER::parseDRC_RULE()
307{
308 std::shared_ptr<DRC_RULE> rule = std::make_shared<DRC_RULE>();
309 int conditionLine = 0;
310 int conditionOffset = 0;
311 std::string conditionSource;
312
313 T token = NextTok();
314 wxString msg;
315
316 if( !IsSymbol( token ) )
317 reportError( _( "Missing rule name." ) );
318
320 rule->m_Name = FromUTF8();
321
322 for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
323 {
325 continue;
326
327 if( token != T_LEFT )
328 reportError( _( "Missing '('." ) );
329
330 token = NextTok();
331
332 switch( token )
333 {
334 case T_constraint:
335 parseConstraint( rule.get() );
336 break;
337
338 case T_condition:
339 token = NextTok();
340
341 if( (int) token == DSN_RIGHT )
342 {
343 reportError( _( "Missing condition expression." ) );
344 }
345 else if( IsSymbol( token ) )
346 {
348 rule->m_Condition = new DRC_RULE_CONDITION( FromUTF8() );
349 conditionLine = CurLineNumber();
350 conditionOffset = CurOffset();
351 conditionSource = CurLine();
352
353 if( !rule->m_Condition->Compile( m_reporter, CurLineNumber(), CurOffset() ) )
354 reportError( wxString::Format( _( "Could not parse expression '%s'." ), FromUTF8() ) );
355
356 if( (int) NextTok() != DSN_RIGHT )
357 reportError( _( "Missing ')'." ) );
358 }
359 else
360 {
361 expected( _( "quoted expression" ) ); // translate "quoted expression"; it is not a token
362 }
363
364 break;
365
366 case T_layer:
367 if( rule->m_LayerCondition != LSET::AllLayersMask() )
368 reportError( _( "'layer' keyword already present." ) );
369
370 rule->m_LayerCondition = parseLayer( &rule->m_LayerSource );
371 break;
372
373 case T_severity:
374 rule->m_Severity = parseSeverity();
375 break;
376
377 case T_EOF:
378 reportError( _( "Incomplete statement." ) );
379 return rule;
380
381 default:
382 expected( wxT( "constraint, condition, or disallow" ) );
383 }
384 }
385
386 if( (int) CurTok() != DSN_RIGHT )
387 reportError( _( "Missing ')'." ) );
388
389 if( rule->m_Condition && rule->m_Condition->RequiresPairItems() )
390 {
391 for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
392 {
393 // isCoupledDiffPair() can identify the pair from A's net for these constraints
394 if( !rule->m_Condition->ReferencesItemB()
395 && ( constraint.m_Type == LENGTH_CONSTRAINT
396 || constraint.m_Type == NET_CHAIN_LENGTH_CONSTRAINT
397 || constraint.m_Type == SKEW_CONSTRAINT ) )
398 {
399 continue;
400 }
401
402 if( constraint.IsUnary() )
403 {
404 reportErrorAt( wxString::Format(
405 _( "Item 'B' is not available for a single-item constraint in rule '%s'." ), rule->m_Name ),
406 conditionLine, conditionOffset, conditionSource.c_str() );
407 break;
408 }
409 }
410 }
411
412 return rule;
413}
414
415
416std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE> DRC_RULES_PARSER::parseComponentClassAssignment()
417{
418 std::shared_ptr<DRC_RULE_CONDITION> condition;
419
420 T token = NextTok();
421 wxString msg;
422
423 if( !IsSymbol( token ) )
424 reportError( _( "Missing component class name." ) );
425
427 wxString componentClass = FromUTF8();
428
429 for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
430 {
431 if( token != T_LEFT )
432 reportError( _( "Missing '('." ) );
433
434 token = NextTok();
435
436 switch( token )
437 {
438 case T_condition:
439 token = NextTok();
440
441 if( (int) token == DSN_RIGHT )
442 {
443 reportError( _( "Missing condition expression." ) );
444 }
445 else if( IsSymbol( token ) )
446 {
448 condition = std::make_shared<DRC_RULE_CONDITION>( FromUTF8() );
449
450 if( !condition->Compile( m_reporter, CurLineNumber(), CurOffset() ) )
451 reportError( wxString::Format( _( "Could not parse expression '%s'." ), FromUTF8() ) );
452
453 if( (int) NextTok() != DSN_RIGHT )
454 reportError( _( "Missing ')'." ) );
455 }
456 else
457 {
458 expected( _( "quoted expression" ) ); // translate "quoted expression"; it is not a token
459 }
460
461 break;
462
463 case T_EOF:
464 reportError( _( "Incomplete statement." ) );
465 return nullptr;
466
467 default:
468 expected( wxT( "condition" ) );
469 }
470 }
471
472 if( (int) CurTok() != DSN_RIGHT )
473 reportError( _( "Missing ')'." ) );
474
475 return std::make_shared<COMPONENT_CLASS_ASSIGNMENT_RULE>( componentClass, std::move( condition ) );
476}
477
478
480{
482 int value;
485 wxString msg;
486 bool allowsTimeDomain = false;
487
488 auto validateAndSetValueWithUnits =
489 [this, &allowsTimeDomain, &unitsType, &c]( int aValue, const EDA_UNITS aUnits, auto aSetter )
490 {
491 const EDA_DATA_TYPE unitsTypeTmp = UNITS_PROVIDER::GetTypeFromUnits( aUnits );
492
493 if( !allowsTimeDomain && unitsTypeTmp == EDA_DATA_TYPE::TIME )
494 reportError( _( "Time based units not allowed for constraint type." ) );
495
496 if( ( c.m_Value.HasMin() || c.m_Value.HasMax() || c.m_Value.HasOpt() )
497 && unitsType != unitsTypeTmp )
498 {
499 reportError( _( "Mixed units for constraint values." ) );
500 }
501
502 unitsType = unitsTypeTmp;
503 aSetter( aValue );
504
505 if( allowsTimeDomain )
506 {
508 {
511 }
512 else
513 {
516 }
517 }
518 };
519
520 T token = NextTok();
521
523 return;
524
525 if( token == T_mechanical_clearance )
526 {
527 reportDeprecation( wxT( "mechanical_clearance" ), wxT( "physical_clearance" ) );
528 token = T_physical_clearance;
529 }
530 else if( token == T_mechanical_hole_clearance )
531 {
532 reportDeprecation( wxT( "mechanical_hole_clearance" ), wxT( "physical_hole_clearance" ) );
533 token = T_physical_hole_clearance;
534 }
535 else if( token == T_hole )
536 {
537 reportDeprecation( wxT( "hole" ), wxT( "hole_size" ) );
538 token = T_hole_size;
539 }
540 else if( (int) token == DSN_RIGHT || token == T_EOF )
541 {
542 msg.Printf( _( "Missing constraint type.| Expected %s." ),
543 wxT( "assertion, clearance, hole_clearance, edge_clearance, physical_clearance, "
544 "physical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
545 "hole_to_hole, track_width, track_angle, track_segment_length, annular_width, "
546 "disallow, zone_connection, thermal_relief_gap, thermal_spoke_width, "
547 "min_resolved_spokes, microvia_stack_depth, microvia_aspect_ratio, solder_mask_expansion, "
548 "solder_paste_abs_margin, "
549 "solder_paste_rel_margin, length, net_chain_length, skew, via_count, "
550 "via_dangling, via_diameter, diff_pair_gap or diff_pair_uncoupled" ) );
551
552 reportError( msg );
553 return;
554 }
555
556 switch( token )
557 {
558 case T_assertion: c.m_Type = ASSERTION_CONSTRAINT; break;
559 case T_clearance: c.m_Type = CLEARANCE_CONSTRAINT; break;
560 case T_creepage: c.m_Type = CREEPAGE_CONSTRAINT; break;
561 case T_hole_clearance: c.m_Type = HOLE_CLEARANCE_CONSTRAINT; break;
562 case T_edge_clearance: c.m_Type = EDGE_CLEARANCE_CONSTRAINT; break;
563 case T_hole_size: c.m_Type = HOLE_SIZE_CONSTRAINT; break;
564 case T_hole_to_hole: c.m_Type = HOLE_TO_HOLE_CONSTRAINT; break;
565 case T_courtyard_clearance: c.m_Type = COURTYARD_CLEARANCE_CONSTRAINT; break;
566 case T_silk_clearance: c.m_Type = SILK_CLEARANCE_CONSTRAINT; break;
567 case T_text_height: c.m_Type = TEXT_HEIGHT_CONSTRAINT; break;
568 case T_text_thickness: c.m_Type = TEXT_THICKNESS_CONSTRAINT; break;
569 case T_track_width: c.m_Type = TRACK_WIDTH_CONSTRAINT; break;
570 case T_track_angle: c.m_Type = TRACK_ANGLE_CONSTRAINT; break;
571 case T_track_segment_length: c.m_Type = TRACK_SEGMENT_LENGTH_CONSTRAINT; break;
572 case T_connection_width: c.m_Type = CONNECTION_WIDTH_CONSTRAINT; break;
573 case T_annular_width: c.m_Type = ANNULAR_WIDTH_CONSTRAINT; break;
574 case T_via_diameter: c.m_Type = VIA_DIAMETER_CONSTRAINT; break;
575 case T_via_dangling: c.m_Type = VIA_DANGLING_CONSTRAINT; break;
576 case T_zone_connection: c.m_Type = ZONE_CONNECTION_CONSTRAINT; break;
577 case T_thermal_relief_gap: c.m_Type = THERMAL_RELIEF_GAP_CONSTRAINT; break;
578 case T_thermal_spoke_width: c.m_Type = THERMAL_SPOKE_WIDTH_CONSTRAINT; break;
579 case T_microvia_stack_depth: c.m_Type = MICROVIA_STACK_DEPTH_CONSTRAINT; break;
580 case T_microvia_aspect_ratio: c.m_Type = MICROVIA_ASPECT_RATIO_CONSTRAINT; break;
581 case T_min_resolved_spokes: c.m_Type = MIN_RESOLVED_SPOKES_CONSTRAINT; break;
582 case T_solder_mask_expansion: c.m_Type = SOLDER_MASK_EXPANSION_CONSTRAINT; break;
583 case T_solder_mask_sliver: c.m_Type = SOLDER_MASK_SLIVER_CONSTRAINT; break;
584 case T_solder_paste_abs_margin: c.m_Type = SOLDER_PASTE_ABS_MARGIN_CONSTRAINT; break;
585 case T_solder_paste_rel_margin: c.m_Type = SOLDER_PASTE_REL_MARGIN_CONSTRAINT; break;
586 case T_disallow: c.m_Type = DISALLOW_CONSTRAINT; break;
587 case T_length: c.m_Type = LENGTH_CONSTRAINT; break;
588 case T_net_chain_length: c.m_Type = NET_CHAIN_LENGTH_CONSTRAINT; break;
589 case T_stub_length: c.m_Type = NET_CHAIN_STUB_LENGTH_CONSTRAINT; break;
590 case T_return_path: c.m_Type = NET_CHAIN_RETURN_PATH_CONSTRAINT; break;
591 case T_skew: c.m_Type = SKEW_CONSTRAINT; break;
592 case T_via_count: c.m_Type = VIA_COUNT_CONSTRAINT; break;
593 case T_diff_pair_gap: c.m_Type = DIFF_PAIR_GAP_CONSTRAINT; break;
594 case T_diff_pair_uncoupled: c.m_Type = MAX_UNCOUPLED_CONSTRAINT; break;
595 case T_physical_clearance: c.m_Type = PHYSICAL_CLEARANCE_CONSTRAINT; break;
596 case T_physical_hole_clearance: c.m_Type = PHYSICAL_HOLE_CLEARANCE_CONSTRAINT; break;
597 case T_bridged_mask: c.m_Type = BRIDGED_MASK_CONSTRAINT; break;
598 default:
599 expected( wxT( "assertion, clearance, hole_clearance, edge_clearance, physical_clearance, "
600 "physical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
601 "hole_to_hole, track_width, track_angle, track_segment_length, annular_width, "
602 "disallow, zone_connection, thermal_relief_gap, thermal_spoke_width, "
603 "min_resolved_spokes, microvia_stack_depth, microvia_aspect_ratio, solder_mask_expansion, "
604 "solder_mask_sliver, "
605 "solder_paste_abs_margin, solder_paste_rel_margin, length, net_chain_length, "
606 "skew, via_count, via_dangling, via_diameter, diff_pair_gap, "
607 "diff_pair_uncoupled or bridged_mask" ) );
608 return;
609 }
610
611 if( aRule->FindConstraint( c.m_Type ) )
612 {
613 msg.Printf( _( "Rule already has a '%s' constraint." ), FromUTF8() );
614 reportError( msg );
615 }
616
618
619 bool unitless = ratio || c.m_Type == VIA_COUNT_CONSTRAINT || c.m_Type == MIN_RESOLVED_SPOKES_CONSTRAINT
622
623 allowsTimeDomain = c.m_Type == LENGTH_CONSTRAINT || c.m_Type == NET_CHAIN_LENGTH_CONSTRAINT
625 || c.m_Type == SKEW_CONSTRAINT;
626
627 if( c.m_Type == DISALLOW_CONSTRAINT )
628 {
629 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
630 {
631 if( (int) token == DSN_STRING )
632 token = GetCurStrAsToken();
633
634 switch( token )
635 {
636 case T_track: c.m_DisallowFlags |= DRC_DISALLOW_TRACKS; break;
641 case T_through_via: c.m_DisallowFlags |= DRC_DISALLOW_THROUGH_VIAS; break;
642 case T_blind_via: c.m_DisallowFlags |= DRC_DISALLOW_BLIND_VIAS; break;
643 case T_buried_via: c.m_DisallowFlags |= DRC_DISALLOW_BURIED_VIAS; break;
644 case T_micro_via: c.m_DisallowFlags |= DRC_DISALLOW_MICRO_VIAS; break;
645 case T_pad: c.m_DisallowFlags |= DRC_DISALLOW_PADS; break;
646 case T_zone: c.m_DisallowFlags |= DRC_DISALLOW_ZONES; break;
647 case T_text: c.m_DisallowFlags |= DRC_DISALLOW_TEXTS; break;
648 case T_graphic: c.m_DisallowFlags |= DRC_DISALLOW_GRAPHICS; break;
649 case T_hole: c.m_DisallowFlags |= DRC_DISALLOW_HOLES; break;
650 case T_footprint: c.m_DisallowFlags |= DRC_DISALLOW_FOOTPRINTS; break;
651
652 case T_EOF:
653 reportError( _( "Missing ')'." ) );
654 return;
655
656 default:
657 expected( wxT( "track, via, through_via, blind_via, micro_via, buried_via, pad, zone, text, "
658 "graphic, hole, or footprint." ) );
659 return;
660 }
661 }
662
663 if( (int) CurTok() != DSN_RIGHT )
664 reportError( _( "Missing ')'." ) );
665
666 aRule->AddConstraint( c );
667 return;
668 }
670 {
671 // (constraint return_path (layer "B.Cu") (net "GND"))
672 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
673 {
674 if( token == T_LEFT )
675 token = NextTok();
676
677 switch( token )
678 {
679 case T_layer:
680 NeedSYMBOLorNUMBER();
681 c.m_ReferenceLayer = FromUTF8();
682 NeedRIGHT();
683 break;
684
685 case T_net:
686 NeedSYMBOLorNUMBER();
687 c.m_ReferenceNet = FromUTF8();
688 NeedRIGHT();
689 break;
690
691 case T_EOF:
692 reportError( _( "Missing ')'." ) );
693 return;
694
695 default:
696 // Unknown sub-option; skip its subtree.
697 while( NextTok() != T_RIGHT )
698 ;
699 break;
700 }
701 }
702
703 aRule->AddConstraint( c );
704 return;
705 }
706 else if( c.m_Type == ZONE_CONNECTION_CONSTRAINT )
707 {
708 token = NextTok();
709
710 if( (int) token == DSN_STRING )
711 token = GetCurStrAsToken();
712
713 switch( token )
714 {
715 case T_solid: c.m_ZoneConnection = ZONE_CONNECTION::FULL; break;
716 case T_thermal_reliefs: c.m_ZoneConnection = ZONE_CONNECTION::THERMAL; break;
717 case T_none: c.m_ZoneConnection = ZONE_CONNECTION::NONE; break;
718
719 case T_EOF:
720 reportError( _( "Missing ')'." ) );
721 return;
722
723 default:
724 expected( wxT( "solid, thermal_reliefs or none." ) );
725 return;
726 }
727
728 if( (int) NextTok() != DSN_RIGHT )
729 reportError( _( "Missing ')'." ) );
730
731 aRule->AddConstraint( c );
732 return;
733 }
735 {
736 // We don't use a min/max/opt structure here because it would give a strong implication
737 // that you could specify the optimal number of spokes. We don't want to open that door
738 // because the spoke generator is highly optimized around being able to "cheat" off of a
739 // cartesian coordinate system.
740
741 token = NextTok();
742
743 if( (int) token == DSN_NUMBER )
744 {
745 value = (int) strtol( CurText(), nullptr, 10 );
746 c.m_Value.SetMin( value );
747
748 if( (int) NextTok() != DSN_RIGHT )
749 reportError( _( "Missing ')'." ) );
750 }
751 else
752 {
753 expected( _( "number" ) ); // translate "number"; it is not a token
754 }
755
756 aRule->AddConstraint( c );
757 return;
758 }
759 else if( c.m_Type == ASSERTION_CONSTRAINT )
760 {
761 token = NextTok();
762
763 if( (int) token == DSN_RIGHT )
764 reportError( _( "Missing assertion expression." ) );
765
766 if( IsSymbol( token ) )
767 {
768 c.m_Test = new DRC_RULE_CONDITION( FromUTF8() );
769 c.m_Test->Compile( m_reporter, CurLineNumber(), CurOffset() );
770
771 if( c.m_Test->RequiresPairItems() )
772 reportError( _( "Item 'B' is not available in assertion expressions." ) );
773
774 if( (int) NextTok() != DSN_RIGHT )
775 reportError( _( "Missing ')'." ) );
776 }
777 else
778 {
779 expected( _( "quoted expression" ) ); // translate "quoted expression"; it is not a token
780 }
781
782 aRule->AddConstraint( c );
783 return;
784 }
785
786 for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
787 {
788 if( token != T_LEFT )
789 reportError( _( "Missing '('." ) );
790
791 token = NextTok();
792
793 switch( token )
794 {
795 case T_within_diff_pairs:
796 if( c.m_Type == SKEW_CONSTRAINT )
798 else
799 reportError( _( "within_diff_pairs option invalid for constraint type." ) );
800
801 if( (int) NextTok() != DSN_RIGHT )
802 reportError( _( "Missing ')'." ) );
803
804 break;
805
806 case T_min:
807 {
808 size_t offset = CurOffset() + GetTokenString( token ).length();
809 wxString expr = parseExpression();
810
811 if( expr.IsEmpty() )
812 {
813 reportError( _( "Missing min value." ) );
814 break;
815 }
816
817 parseValueWithUnits( (int) offset, expr, value, units, unitless, ratio );
818 validateAndSetValueWithUnits( value, units,
819 [&c]( const int aValue )
820 {
821 c.m_Value.SetMin( aValue );
822 } );
823
824 break;
825 }
826
827 case T_max:
828 {
829 size_t offset = CurOffset() + GetTokenString( token ).length();
830 wxString expr = parseExpression();
831
832 if( expr.IsEmpty() )
833 {
834 reportError( _( "Missing max value." ) );
835 break;
836 }
837
838 parseValueWithUnits( (int) offset, expr, value, units, unitless, ratio );
839 validateAndSetValueWithUnits( value, units,
840 [&c]( const int aValue )
841 {
842 c.m_Value.SetMax( aValue );
843 } );
844
845 break;
846 }
847
848 case T_opt:
849 {
850 size_t offset = CurOffset() + GetTokenString( token ).length();
851 wxString expr = parseExpression();
852
853 if( expr.IsEmpty() )
854 {
855 reportError( _( "Missing opt value." ) );
856 break;
857 }
858
859 parseValueWithUnits( (int) offset, expr, value, units, unitless, ratio );
860 validateAndSetValueWithUnits( value, units,
861 [&c]( const int aValue )
862 {
863 c.m_Value.SetOpt( aValue );
864 } );
865
866 break;
867 }
868
869 case T_EOF:
870 reportError( _( "Incomplete statement." ) );
871 return;
872
873 default:
874 expected( wxT( "min, max, opt, or within_diff_pairs" ) );
875 }
876 }
877
878 aRule->AddConstraint( c );
879}
880
881
882void DRC_RULES_PARSER::parseValueWithUnits( int aOffset, const wxString& aExpr, int& aResult, EDA_UNITS& aUnits,
883 bool aUnitless, bool aRatio )
884{
885 aResult = 0.0;
886 aUnits = EDA_UNITS::UNSCALED;
887
888 auto errorHandler =
889 [&]( const wxString& message, int offset )
890 {
891 wxString rest;
892 wxString first = message.BeforeFirst( '|', &rest );
893
894 if( m_reporter )
895 {
896 wxString msg = wxString::Format( _( "ERROR: <a href='%d:%d'>%s</a>%s" ),
897 CurLineNumber(),
898 aOffset + offset,
899 first,
900 rest );
901
902 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
903 }
904 else
905 {
906 wxString msg = wxString::Format( _( "ERROR: %s%s" ), first, rest );
907
908 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(),
909 CurOffset() + aOffset );
910 }
911 };
912
915 evaluator.SetErrorCallback( errorHandler );
916
917 if( evaluator.Evaluate( aExpr ) )
918 {
919 // A ratio is fractional, so keep three decimals rather than rounding to a whole number.
920 aResult = aRatio ? KiROUND( evaluator.ResultAsDouble() * 1000.0 ) : evaluator.Result();
921 aUnits = evaluator.Units();
922 }
923}
924
925
927{
928 LSET retVal;
929 int token = NextTok();
930
931 if( (int) token == DSN_RIGHT )
932 {
933 reportError( _( "Missing layer name or type." ) );
934 return LSET::AllCuMask();
935 }
936 else if( token == T_outer )
937 {
938 *aSource = GetTokenString( token );
939 retVal = LSET::ExternalCuMask();
940 }
941 else if( token == T_inner )
942 {
943 *aSource = GetTokenString( token );
944 retVal = LSET::InternalCuMask();
945 }
946 else
947 {
948 wxString layerName = FromUTF8();
949 wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
950
951 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
952 {
953 wxPGChoiceEntry& entry = layerMap[ii];
954
955 if( entry.GetText().Matches( layerName ) )
956 {
957 *aSource = layerName;
958 retVal.set( ToLAYER_ID( entry.GetValue() ) );
959 }
960 }
961
962 if( !retVal.any() )
963 {
965 reportError( wxString::Format( _( "Unrecognized layer '%s'." ), layerName ) );
966
967 retVal.set( Rescue );
968 }
969 }
970
971 if( (int) NextTok() != DSN_RIGHT )
972 reportError( _( "Missing ')'." ) );
973
974 return retVal;
975}
976
977
979{
981 wxString msg;
982
983 T token = NextTok();
984
985 if( (int) token == DSN_RIGHT || token == T_EOF )
986 {
987 reportError( _( "Missing severity name." ) );
989 }
990
991 switch( token )
992 {
993 case T_ignore: retVal = RPT_SEVERITY_IGNORE; break;
994 case T_warning: retVal = RPT_SEVERITY_WARNING; break;
995 case T_error: retVal = RPT_SEVERITY_ERROR; break;
996 case T_exclusion: retVal = RPT_SEVERITY_EXCLUSION; break;
997
998 default:
999 expected( wxT( "ignore, warning, error, or exclusion" ) );
1000 }
1001
1002 if( (int) NextTok() != DSN_RIGHT )
1003 reportError( _( "Missing ')'." ) );
1004
1005 return retVal;
1006}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
BASE_SET & set(size_t pos)
Definition base_set.h:126
DRC_RULE_CONDITION * m_Test
Definition drc_rule.h:247
int m_DisallowFlags
Definition drc_rule.h:245
void SetOption(OPTIONS option)
Definition drc_rule.h:229
ZONE_CONNECTION m_ZoneConnection
Definition drc_rule.h:246
MINOPTMAX< int > m_Value
Definition drc_rule.h:244
wxString m_ReferenceLayer
Definition drc_rule.h:253
DRC_CONSTRAINT_T m_Type
Definition drc_rule.h:243
bool IsUnary() const
Definition drc_rule.cpp:87
void ClearOption(OPTIONS option)
Definition drc_rule.h:231
wxString m_ReferenceNet
Definition drc_rule.h:258
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:770
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:38
bool HasMax() const
Definition minoptmax.h:35
void SetOpt(T v)
Definition minoptmax.h:40
bool HasMin() const
Definition minoptmax.h:34
void SetMax(T v)
Definition minoptmax.h:39
bool HasOpt() const
Definition minoptmax.h:36
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
unitsType
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
@ 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