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