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