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