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