These three control structures must work together with the context. It looks like a tag. Therefore, their implementation is quite simple:
public class Break : ControlFlow { public override void Print(int tabs = 0) { Debug.WriteLine(new string('\t', tabs) + "break;"); } }
public class Continue : ControlFlow { public override void Print(int tabs = 0) { Debug.WriteLine(new string('\t', tabs) + "continue;"); } }
Return has an extra return value expression:
public class Return : ControlFlow { public Expression.ExpressionNode Expression; public override void Print(int tabs = 0) { if (Expression == null) Debug.WriteLine(new string('\t', tabs) + "return ;"); else { Debug.WriteLine(new string('\t', tabs) + "return " + Expression.ToString()); } } }
Everything needs to be traced back to the Block. During the Block operation, these three control structures will be detected:
public override void Run(Context ctx) { if (IsFirstRunning) { Block parentBlock = this.ParentBlock; if (parentBlock != null) { this.OnReturn += parentBlock.OnReturn; this.OnBreak += parentBlock.OnBreak; this.OnContinue += parentBlock.OnContinue; } IsFirstRunning = false; } foreach (Context stx in Children) { if (stx is ControlFlow.ControlFlow) { if (!ExecuteControlFlow(stx)) break;
Let's look at ExecuteControlFlow ():
private bool ExecuteControlFlow(Context stx) { if (stx is ControlFlow.Return) { if (OnReturn != null) OnReturn(this, stx as ControlFlow.Return); return false; } else { if (stx is ControlFlow.Break) { if (OnBreak != null) OnBreak(this); return false; } else if (stx is ControlFlow.Continue) { if (OnContinue != null) OnContinue(this); return false; } else stx.Run(this); } return true; }
Returns false to immediately stop the running cycle of the upper Block.