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