1+ using System . Collections . Generic ;
2+
3+ namespace KitX . Core . Contract . Workflow ;
4+
5+ /// <summary>
6+ /// Shared control-flow arms model embedded by both <c>CFGStatement</c> (CFG layer) and
7+ /// <c>FlowControlStatement</c> (BlockScript layer). Eliminates the ~80 lines of
8+ /// character-identical <c>Arms</c>/<c>TrueBlockName</c>/<c>FalseBlockName</c>/
9+ /// <c>LoopbackTarget</c>/<c>SetArm</c> duplication that had drifted between the two layers
10+ /// (including a hidden null-vs-string.Empty inconsistency in the convenience accessors).
11+ ///
12+ /// <para>Both layers expose this via their own delegating properties (<c>Arms</c>,
13+ /// <c>TrueBlockName</c>, <c>FalseBlockName</c>, <c>LoopbackTarget</c>) so existing call
14+ /// sites (<c>stmt.Arms</c>, <c>flow.TrueBlockName</c>, ...) are unchanged.</para>
15+ ///
16+ /// <list type="bullet">
17+ /// <item>Branch: [<c>"True"</c>, <c>"False"</c>]</item>
18+ /// <item>Loop: [<c>"LoopBody"</c>, <c>"LoopEnd"</c>]</item>
19+ /// <item>ToLoopCond: [<c>"Exec"</c> with <see cref="BranchArm.IsLoopback"/>=true]</item>
20+ /// <item>Switch: [<c>"Default"</c>, <c>"0"</c>, <c>"1"</c>, ... , <c>"N-1"</c>]</item>
21+ /// </list>
22+ /// </summary>
23+ public class ControlFlowArms
24+ {
25+ /// <summary>
26+ /// Outgoing arms of this control-flow statement. Generalised model replacing the former
27+ /// fixed <c>TrueBlockName</c>/<c>FalseBlockName</c>/<c>ToLoopCondReturnTo</c> triple.
28+ /// See <see cref="BranchArm"/> for the per-arm layout of each control-flow kind.
29+ /// </summary>
30+ public List < BranchArm > Arms { get ; set ; } = [ ] ;
31+
32+ /// <summary>Convenience accessor: the true-branch / loop-body target (Arms[0]).</summary>
33+ public string ? TrueBlockName
34+ {
35+ get => Arms . Count > 0 ? Arms [ 0 ] . TargetBlockName : null ;
36+ set => SetArm ( 0 , "True" , value ) ;
37+ }
38+
39+ /// <summary>Convenience accessor: the false-branch / loop-exit target (Arms[1]).</summary>
40+ public string ? FalseBlockName
41+ {
42+ get => Arms . Count > 1 ? Arms [ 1 ] . TargetBlockName : null ;
43+ set => SetArm ( 1 , "False" , value ) ;
44+ }
45+
46+ /// <summary>
47+ /// The ToLoopCond loopback target — the loop condition block this statement returns to.
48+ /// Unified into <see cref="Arms"/>[0] (PinName="Exec", IsLoopback=true) so ToLoopCond is
49+ /// treated uniformly with Branch/Loop/Switch: all control-flow targets live in Arms.
50+ /// The former standalone <c>ToLoopCondReturnTo</c> field was a patch over an Arms[0]
51+ /// collision that no longer exists; Loop statements no longer carry this metadata at all
52+ /// (it was dead — never read for control flow, only copied and debug-printed).
53+ /// </summary>
54+ public string ? LoopbackTarget
55+ {
56+ get => Arms . Count > 0 ? Arms [ 0 ] . TargetBlockName : null ;
57+ set => SetArm ( 0 , "Exec" , value , isLoopback : true ) ;
58+ }
59+
60+ /// <summary>
61+ /// Sets the arm at <paramref name="index"/>, growing <see cref="Arms"/> with blank arms
62+ /// as needed. Normalises a null <paramref name="value"/> to <c>string.Empty</c> so the
63+ /// convenience accessors never surface null into SourceCode concatenation.
64+ /// </summary>
65+ public void SetArm ( int index , string pinName , string ? value , bool isLoopback = false )
66+ {
67+ while ( Arms . Count <= index )
68+ Arms . Add ( new BranchArm ( ) ) ;
69+ Arms [ index ] . PinName = pinName ;
70+ Arms [ index ] . TargetBlockName = value ?? string . Empty ;
71+ Arms [ index ] . IsLoopback = isLoopback ;
72+ }
73+ }
0 commit comments