1use crate::AnalyzerError;
2use crate::conv::Context;
3use crate::ir::assign_table::{AssignContext, AssignTable};
4use crate::ir::utils::{allow_missing_reset_statement, has_cond_type};
5use crate::ir::{
6 Comptime, Expression, FfTable, FunctionCall, Op, SystemFunctionCall, Type, VarId, VarIndex,
7 VarPath, VarSelect,
8};
9use crate::value::{Value, ValueBigUint};
10use indent::indent_all_by;
11use std::borrow::Cow;
12use std::fmt;
13use veryl_parser::resource_table::StrId;
14use veryl_parser::token_range::TokenRange;
15
16#[derive(Clone, Default)]
17pub struct StatementBlock(pub Vec<Statement>);
18
19#[derive(Clone, Copy, PartialEq, Eq)]
20pub enum ControlFlow {
21 Continue,
22 Break,
23}
24
25#[derive(Clone)]
26pub enum Statement {
27 Assign(AssignStatement),
28 If(IfStatement),
29 IfReset(IfResetStatement),
30 For(ForStatement),
31 SystemFunctionCall(Box<SystemFunctionCall>),
32 FunctionCall(Box<FunctionCall>),
33 TbMethodCall(TbMethodCall),
34 Break,
35 Unsupported(TokenRange),
36 Null,
37}
38
39#[derive(Clone)]
40pub struct ForStatement {
41 pub var_id: VarId,
42 pub var_name: StrId,
43 pub var_type: Type,
44 pub range: ForRange,
45 pub body: Vec<Statement>,
46 pub token: TokenRange,
47}
48
49#[derive(Clone, Debug)]
50pub enum ForBound {
51 Const(usize),
52 Expression(Box<Expression>),
53}
54
55impl ForBound {
56 pub fn eval_value(&self, context: &mut Context) -> Option<usize> {
57 match self {
58 Self::Const(x) => Some(*x),
59 Self::Expression(exp) => {
60 let exp = exp.as_ref().clone();
61 exp.eval_value(context)?.to_usize()
62 }
63 }
64 }
65}
66
67impl fmt::Display for ForBound {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 match self {
70 ForBound::Const(x) => x.fmt(f),
71 ForBound::Expression(x) => write!(f, "{}", x.as_ref()),
72 }
73 }
74}
75
76#[derive(Clone, Debug)]
78pub enum ForRange {
79 Forward {
81 start: ForBound,
82 end: ForBound,
83 inclusive: bool,
84 step: usize,
85 },
86 Reverse {
88 start: ForBound,
89 end: ForBound,
90 inclusive: bool,
91 step: usize,
92 },
93 Stepped {
95 start: ForBound,
96 end: ForBound,
97 inclusive: bool,
98 step: usize,
99 op: Op,
100 },
101}
102
103impl ForRange {
104 pub fn eval_iter(&self, context: &mut Context) -> Option<Vec<usize>> {
105 let limit = context.config.evaluate_size_limit;
106 match self {
107 ForRange::Forward {
108 start,
109 end,
110 inclusive,
111 step,
112 } => {
113 let start = start.eval_value(context)?;
114 let end = end.eval_value(context)?;
115 let end = if *inclusive { end + 1 } else { end };
116 if end.saturating_sub(start) > limit {
117 return None;
118 }
119 if *step == 1 {
120 Some((start..end).collect())
121 } else {
122 let mut ret = vec![];
123 let mut i = start;
124 while i < end {
125 ret.push(i);
126 i += step;
127 }
128 Some(ret)
129 }
130 }
131 ForRange::Reverse {
132 start,
133 end,
134 inclusive,
135 ..
136 } => {
137 let start = start.eval_value(context)?;
138 let end = end.eval_value(context)?;
139 if end.saturating_sub(start) > limit {
140 return None;
141 }
142 if *inclusive {
143 Some((start..=end).rev().collect())
144 } else {
145 Some((start..end).rev().collect())
146 }
147 }
148 ForRange::Stepped {
149 start,
150 end,
151 inclusive,
152 step,
153 op,
154 } => {
155 let start = start.eval_value(context)?;
156 let end = end.eval_value(context)?;
157 let end = if *inclusive { end + 1 } else { end };
158 let mut ret = vec![];
159 let mut i = start;
160 while i < end {
161 if ret.len() > limit {
162 return None;
163 }
164 ret.push(i);
165 let new_i = op.eval(i, *step);
166 if new_i == i {
167 break;
168 }
169 i = new_i;
170 }
171 Some(ret)
172 }
173 }
174 }
175}
176
177#[derive(Clone)]
178pub struct TbMethodCall {
179 pub inst: StrId,
180 pub method: TbMethod,
181}
182
183#[derive(Clone)]
184pub enum TbMethod {
185 ClockNext {
186 count: Option<Expression>,
187 period: Option<Expression>,
188 },
189 ResetAssert {
190 clock: StrId,
191 duration: Option<Expression>,
192 },
193}
194
195impl Statement {
196 pub fn is_null(&self) -> bool {
197 matches!(self, Statement::Null)
198 }
199
200 pub fn eval_value(&self, context: &mut Context) -> ControlFlow {
201 match self {
202 Statement::Assign(x) => {
203 x.eval_value(context);
204 ControlFlow::Continue
205 }
206 Statement::If(x) => x.eval_value(context),
207 Statement::IfReset(_) => ControlFlow::Continue,
208 Statement::For(x) => {
209 if let Some(iter) = x.range.eval_iter(context) {
210 'outer: for i in iter {
211 if let Some(var) = context.variables.get_mut(&x.var_id)
212 && let Some(total_width) = x.var_type.total_width()
213 {
214 let val = Value::new(i as u64, total_width, x.var_type.signed);
215 var.set_value(&[], val, None);
216 }
217 for s in &x.body {
218 if s.eval_value(context) == ControlFlow::Break {
219 break 'outer;
220 }
221 }
222 }
223 }
224 ControlFlow::Continue
225 }
226 Statement::SystemFunctionCall(_) => ControlFlow::Continue,
227 Statement::FunctionCall(x) => {
228 x.eval_value(context);
229 ControlFlow::Continue
230 }
231 Statement::TbMethodCall(_) => ControlFlow::Continue,
232 Statement::Break => ControlFlow::Break,
233 Statement::Unsupported(_) => ControlFlow::Continue,
234 Statement::Null => ControlFlow::Continue,
235 }
236 }
237
238 pub fn eval_assign(
239 &self,
240 context: &mut Context,
241 assign_table: &mut AssignTable,
242 assign_context: AssignContext,
243 base_tables: &[&AssignTable],
244 ) {
245 match self {
246 Statement::Assign(x) => x.eval_assign(context, assign_table, assign_context),
247 Statement::If(x) => x.eval_assign(context, assign_table, assign_context, base_tables),
248 Statement::IfReset(x) => x.eval_assign(context, assign_table, base_tables),
249 Statement::SystemFunctionCall(x) => {
250 x.eval_assign(context, assign_table, assign_context)
251 }
252 Statement::FunctionCall(x) => x.eval_assign(context, assign_table, assign_context),
253 Statement::For(x) => {
254 for s in &x.body {
255 s.eval_assign(context, assign_table, assign_context, base_tables);
256 }
257 }
258 Statement::TbMethodCall(_) | Statement::Break => (),
259 Statement::Unsupported(_) => (),
260 Statement::Null => (),
261 }
262 }
263
264 pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
265 match self {
266 Statement::Assign(x) => x.gather_ff(context, table, decl),
267 Statement::If(x) => x.gather_ff(context, table, decl),
268 Statement::IfReset(x) => x.gather_ff(context, table, decl),
269 Statement::FunctionCall(x) => x.gather_ff(context, table, decl, None, true),
270 Statement::SystemFunctionCall(x) => x.gather_ff(context, table, decl, true),
271 Statement::For(x) => {
272 for s in &x.body {
273 s.gather_ff(context, table, decl);
274 }
275 }
276 Statement::TbMethodCall(_)
277 | Statement::Break
278 | Statement::Unsupported(_)
279 | Statement::Null => (),
280 }
281 }
282
283 pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
284 match self {
285 Statement::Assign(x) => x.gather_ff_comb_assign(context, table, decl),
286 Statement::If(x) => {
287 x.cond.gather_ff(context, table, decl, None, false);
292 for s in &x.true_side {
293 s.gather_ff_comb_assign(context, table, decl);
294 }
295 for s in &x.false_side {
296 s.gather_ff_comb_assign(context, table, decl);
297 }
298 }
299 Statement::IfReset(x) => {
300 for s in &x.true_side {
301 s.gather_ff_comb_assign(context, table, decl);
302 }
303 for s in &x.false_side {
304 s.gather_ff_comb_assign(context, table, decl);
305 }
306 }
307 Statement::FunctionCall(x) => x.gather_ff_comb_assign(context, table, decl),
308 Statement::SystemFunctionCall(x) => x.gather_ff(context, table, decl, false),
309 Statement::For(x) => {
310 for s in &x.body {
311 s.gather_ff_comb_assign(context, table, decl);
312 }
313 }
314 _ => (),
315 }
316 }
317
318 pub fn set_index(&mut self, index: &VarIndex) {
319 match self {
320 Statement::Assign(x) => x.set_index(index),
321 Statement::If(x) => x.set_index(index),
322 Statement::IfReset(x) => x.set_index(index),
323 Statement::SystemFunctionCall(_) => (),
324 Statement::FunctionCall(x) => x.set_index(index),
325 Statement::For(x) => {
326 for s in &mut x.body {
327 s.set_index(index);
328 }
329 }
330 Statement::TbMethodCall(_) | Statement::Break => (),
331 Statement::Unsupported(_) => (),
332 Statement::Null => (),
333 }
334 }
335}
336
337impl fmt::Display for Statement {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 match self {
340 Statement::Assign(x) => x.fmt(f),
341 Statement::If(x) => x.fmt(f),
342 Statement::IfReset(x) => x.fmt(f),
343 Statement::SystemFunctionCall(x) => format!("{x};").fmt(f),
344 Statement::FunctionCall(x) => format!("{x};").fmt(f),
345 Statement::TbMethodCall(x) => match &x.method {
346 TbMethod::ClockNext { count, .. } => {
347 if let Some(c) = count {
348 write!(f, "{}.next({c});", x.inst)
349 } else {
350 write!(f, "{}.next();", x.inst)
351 }
352 }
353 TbMethod::ResetAssert { clock, duration } => {
354 if let Some(d) = duration {
355 write!(f, "{}.assert({clock}, {d});", x.inst)
356 } else {
357 write!(f, "{}.assert({clock});", x.inst)
358 }
359 }
360 },
361 Statement::For(x) => {
362 let range_op = if let ForRange::Reverse { .. } = &x.range {
363 "rev "
364 } else {
365 ""
366 };
367 let (start, end, inclusive, step_info) = match &x.range {
368 ForRange::Forward {
369 start,
370 end,
371 inclusive,
372 step,
373 } => (
374 start,
375 end,
376 *inclusive,
377 if *step == 1 {
378 None
379 } else {
380 Some(format!("+= {step}"))
381 },
382 ),
383 ForRange::Reverse {
384 start,
385 end,
386 inclusive,
387 ..
388 } => (start, end, *inclusive, None),
389 ForRange::Stepped {
390 start,
391 end,
392 inclusive,
393 step,
394 op,
395 } => (start, end, *inclusive, Some(format!("{op}= {step}"))),
396 };
397 let dots = if inclusive { "..=" } else { ".." };
398 if let Some(step_str) = step_info {
399 writeln!(
400 f,
401 "for {} in {range_op}{start}{dots}{end} step {step_str} {{",
402 x.var_name
403 )?;
404 } else {
405 writeln!(f, "for {} in {range_op}{start}{dots}{end} {{", x.var_name)?;
406 }
407 for s in &x.body {
408 writeln!(f, " {s}")?;
409 }
410 write!(f, "}}")
411 }
412 Statement::Break => "break;".fmt(f),
413 Statement::Unsupported(_) => "/* unsupported */".fmt(f),
414 Statement::Null => "".fmt(f),
415 }
416 }
417}
418
419#[derive(Clone, Debug)]
420pub struct AssignDestination {
421 pub id: VarId,
422 pub path: VarPath,
423 pub index: VarIndex,
424 pub select: VarSelect,
425 pub comptime: Comptime,
426 pub token: TokenRange,
427}
428
429impl AssignDestination {
430 pub fn total_width(&self, context: &mut Context) -> Option<usize> {
431 let (beg, end) = self
432 .select
433 .eval_value(context, &self.comptime.r#type, false)?;
434 Some(beg - end + 1)
435 }
436
437 pub fn eval_assign(
438 &self,
439 context: &mut Context,
440 assign_table: &mut AssignTable,
441 assign_context: AssignContext,
442 ) {
443 if let Some(variable) = context.get_variable_info(self.id) {
444 let is_index_const = self.index.is_const();
445 let is_select_const = self.select.is_const();
446 let is_const = is_index_const & is_select_const;
447
448 let range = if !is_index_const {
449 variable.r#type.array.calc_range(&[])
450 } else {
451 let Some(index) = self.index.eval_value(context) else {
452 return;
453 };
454 variable.r#type.array.calc_range(&index)
455 };
456
457 let mask = if !is_select_const {
459 let Some(width) = variable.total_width() else {
460 return;
461 };
462 ValueBigUint::gen_mask(width)
463 } else {
464 let Some((beg, end)) = self.select.eval_value(context, &variable.r#type, false)
465 else {
466 return;
467 };
468 ValueBigUint::gen_mask_range(beg, end)
469 };
470
471 let mut errors = vec![];
472 if let Some((beg, end)) = range {
473 let skip_large_array =
477 variable.r#type.total_array().unwrap_or(0) > assign_table.array_limit;
478
479 if !skip_large_array {
480 for i in beg..=end {
481 let index = VarIndex::from_index(i, &variable.r#type.array);
482 if let Some(index) = index.eval_value(context) {
483 if assign_context.is_comb()
484 && assign_table.check_refered(&variable.id, &index, &mask)
485 {
486 let mut text = variable.path.to_string();
487 for i in &index {
488 text.push_str(&format!("[{i}]"));
489 }
490 errors.push(AnalyzerError::unassign_variable(&text, &self.token));
492 }
493
494 let maybe = !is_const | assign_context.is_system_verilog();
495 let _ = assign_table.insert_assign(
496 &variable,
497 index,
498 mask.clone(),
499 maybe,
500 self.token,
501 );
502 }
503 }
504 }
505 }
506
507 for e in errors {
508 context.insert_error(e);
509 }
510 }
511 }
512
513 pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
514 if let Some(variable) = context.get_variable_info(self.id) {
515 if variable.kind == crate::ir::VarKind::Let {
518 return;
519 }
520 if let Some(index) = self.index.eval_value(context) {
521 if let Some(index) = variable.r#type.array.calc_index(&index) {
522 table.insert_assigned(self.id, index, decl);
523 }
524 } else if let Some(total_array) = variable.r#type.total_array() {
525 for i in 0..total_array {
526 table.insert_assigned(self.id, i, decl);
527 }
528 }
529 }
530 }
531
532 pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
533 if let Some(variable) = context.get_variable_info(self.id) {
534 if let Some(index) = self.index.eval_value(context) {
535 if let Some(index) = variable.r#type.array.calc_index(&index) {
536 table.insert_assigned_comb(self.id, index, decl);
537 }
538 } else if let Some(total_array) = variable.r#type.total_array() {
539 for i in 0..total_array {
540 table.insert_assigned_comb(self.id, i, decl);
541 }
542 }
543 }
544 }
545
546 pub fn set_index(&mut self, index: &VarIndex) {
547 self.index.add_prelude(index);
548 }
549}
550
551impl fmt::Display for AssignDestination {
552 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553 let ret = format!("{}{}{}", self.id, self.index, self.select);
554 ret.fmt(f)
555 }
556}
557
558#[derive(Clone)]
559pub struct AssignStatement {
560 pub dst: Vec<AssignDestination>,
561 pub width: Option<usize>,
562 pub expr: Expression,
563 pub token: TokenRange,
564}
565
566impl AssignStatement {
567 pub fn eval_value(&self, context: &mut Context) {
568 if let Some(value) = self.expr.eval_value(context) {
569 if let Some(index) = self.dst[0].index.eval_value(context)
571 && let Some((beg, end)) =
572 self.dst[0]
573 .select
574 .eval_value(context, &self.dst[0].comptime.r#type, false)
575 && let Some(variable) = context.variables.get_mut(&self.dst[0].id)
576 {
577 variable.set_value(&index, value, Some((beg, end)));
578 }
579 }
580 }
581
582 pub fn eval_assign(
583 &self,
584 context: &mut Context,
585 assign_table: &mut AssignTable,
586 assign_context: AssignContext,
587 ) {
588 self.expr.eval_assign(context, assign_table, assign_context);
589 for dst in &self.dst {
590 dst.eval_assign(context, assign_table, assign_context);
591 }
592 }
593
594 pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
595 let assign_target = self.dst.first().map(|d| {
596 let idx = d
597 .index
598 .eval_value(context)
599 .and_then(|v| context.get_variable_info(d.id)?.r#type.array.calc_index(&v));
600 (d.id, idx)
601 });
602 self.expr
603 .gather_ff(context, table, decl, assign_target, true);
604 for dst in &self.dst {
605 dst.gather_ff(context, table, decl);
606 }
607 }
608
609 pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
610 let assign_target = self.dst.first().map(|d| {
611 let idx = d
612 .index
613 .eval_value(context)
614 .and_then(|v| context.get_variable_info(d.id)?.r#type.array.calc_index(&v));
615 (d.id, idx)
616 });
617 self.expr
618 .gather_ff(context, table, decl, assign_target, false);
619 for dst in &self.dst {
620 dst.gather_ff_comb_assign(context, table, decl);
621 }
622 }
623
624 pub fn set_index(&mut self, index: &VarIndex) {
625 for dst in &mut self.dst {
626 dst.set_index(index);
627 }
628 self.expr.set_index(index);
629 }
630}
631
632impl fmt::Display for AssignStatement {
633 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634 let mut ret = String::new();
635
636 if self.dst.len() == 1 {
637 ret.push_str(&format!("{} = {};", self.dst[0], self.expr));
638 } else {
639 ret.push_str(&format!("{{{}", self.dst[0]));
640 for d in &self.dst[1..] {
641 ret.push_str(&format!(", {}", d));
642 }
643 ret.push_str(&format!("}} = {};", self.expr));
644 }
645 ret.fmt(f)
646 }
647}
648
649#[derive(Clone)]
650pub struct IfStatement {
651 pub cond: Expression,
652 pub true_side: Vec<Statement>,
653 pub false_side: Vec<Statement>,
654 pub token: TokenRange,
655}
656
657impl IfStatement {
658 pub fn eval_value(&self, context: &mut Context) -> ControlFlow {
659 if let Some(cond) = self.cond.eval_value(context) {
660 if cond.to_usize().unwrap_or(0) != 0 {
661 for stmt in &self.true_side {
662 if stmt.eval_value(context) == ControlFlow::Break {
663 return ControlFlow::Break;
664 }
665 }
666 } else {
667 for stmt in &self.false_side {
668 if stmt.eval_value(context) == ControlFlow::Break {
669 return ControlFlow::Break;
670 }
671 }
672 }
673 }
674 ControlFlow::Continue
675 }
676
677 pub fn insert_leaf_false(&mut self, false_side: Vec<Statement>) {
678 if self.false_side.is_empty() {
679 self.false_side = false_side;
680 } else if let Statement::If(x) = &mut self.false_side[0] {
681 x.insert_leaf_false(false_side);
682 }
683 }
684
685 pub fn eval_assign(
686 &self,
687 context: &mut Context,
688 assign_table: &mut AssignTable,
689 assign_context: AssignContext,
690 base_tables: &[&AssignTable],
691 ) {
692 let mut true_table = AssignTable::new(context);
693 let mut false_table = AssignTable::new(context);
694
695 std::mem::swap(&mut true_table.refernced, &mut assign_table.refernced);
696
697 let base_tables = if assign_table.table.is_empty() {
698 Cow::Borrowed(base_tables)
699 } else {
700 let mut base_tables = base_tables.to_vec();
701 base_tables.push(assign_table);
702 Cow::Owned(base_tables)
703 };
704
705 for x in &self.true_side {
706 x.eval_assign(context, &mut true_table, assign_context, &base_tables);
707 }
708
709 std::mem::swap(&mut false_table.refernced, &mut true_table.refernced);
710 for x in &self.false_side {
711 x.eval_assign(context, &mut false_table, assign_context, &base_tables);
712 }
713
714 if assign_context.is_comb() && !has_cond_type(&self.token) {
715 true_table.check_uncoverd(context, &false_table, &base_tables);
716 }
717
718 true_table.merge_by_or(context, &mut false_table, false);
719 assign_table.merge_by_or(context, &mut true_table, false);
720 std::mem::swap(&mut assign_table.refernced, &mut false_table.refernced);
721 }
722
723 pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
724 self.cond.gather_ff(context, table, decl, None, true);
725 for x in &self.true_side {
726 x.gather_ff(context, table, decl);
727 }
728 for x in &self.false_side {
729 x.gather_ff(context, table, decl);
730 }
731 }
732
733 pub fn set_index(&mut self, index: &VarIndex) {
734 self.cond.set_index(index);
735 for x in &mut self.true_side {
736 x.set_index(index);
737 }
738 for x in &mut self.false_side {
739 x.set_index(index);
740 }
741 }
742}
743
744impl fmt::Display for IfStatement {
745 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
746 let mut ret = format!("if {} {{\n", self.cond);
747 for x in &self.true_side {
748 let text = format!("{}\n", x);
749 ret.push_str(&indent_all_by(2, text));
750 }
751 ret.push('}');
752 if !self.false_side.is_empty() {
753 ret.push_str(" else {\n");
754 for x in &self.false_side {
755 let text = format!("{}\n", x);
756 ret.push_str(&indent_all_by(2, text));
757 }
758 ret.push('}');
759 }
760 ret.fmt(f)
761 }
762}
763
764#[derive(Clone)]
765pub struct IfResetStatement {
766 pub true_side: Vec<Statement>,
767 pub false_side: Vec<Statement>,
768 pub token: TokenRange,
769}
770
771impl IfResetStatement {
772 pub fn eval_assign(
773 &self,
774 context: &mut Context,
775 assign_table: &mut AssignTable,
776 base_tables: &[&AssignTable],
777 ) {
778 let mut true_table = AssignTable::new(context);
779 let mut false_table = AssignTable::new(context);
780
781 std::mem::swap(&mut true_table.refernced, &mut assign_table.refernced);
782
783 let mut base_tables = base_tables.to_vec();
784 base_tables.push(assign_table);
785
786 for x in &self.true_side {
787 x.eval_assign(context, &mut true_table, AssignContext::Ff, &base_tables);
788 }
789
790 std::mem::swap(&mut false_table.refernced, &mut true_table.refernced);
791 for x in &self.false_side {
792 x.eval_assign(context, &mut false_table, AssignContext::Ff, &base_tables);
793 }
794
795 if !allow_missing_reset_statement(&self.token) {
796 true_table.check_missing_reset(context, &false_table);
797 }
798
799 true_table.merge_by_or(context, &mut false_table, false);
800 assign_table.merge_by_or(context, &mut true_table, false);
801 std::mem::swap(&mut assign_table.refernced, &mut false_table.refernced);
802 }
803
804 pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
805 for x in &self.true_side {
806 x.gather_ff(context, table, decl);
807 }
808 for x in &self.false_side {
809 x.gather_ff(context, table, decl);
810 }
811 }
812
813 pub fn set_index(&mut self, index: &VarIndex) {
814 for x in &mut self.true_side {
815 x.set_index(index);
816 }
817 for x in &mut self.false_side {
818 x.set_index(index);
819 }
820 }
821}
822
823impl fmt::Display for IfResetStatement {
824 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
825 let mut ret = "if_reset {\n".to_string();
826 for x in &self.true_side {
827 let text = format!("{}\n", x);
828 ret.push_str(&indent_all_by(2, text));
829 }
830 ret.push('}');
831 if !self.false_side.is_empty() {
832 ret.push_str(" else {\n");
833 for x in &self.false_side {
834 let text = format!("{}\n", x);
835 ret.push_str(&indent_all_by(2, text));
836 }
837 ret.push('}');
838 }
839 ret.fmt(f)
840 }
841}