|
1 |
| -using NodeDev.Core.Connections; |
| 1 | +using FastExpressionCompiler; |
| 2 | +using NodeDev.Core.Connections; |
2 | 3 | using NodeDev.Core.Types;
|
3 | 4 | using System.Linq.Expressions;
|
4 | 5 |
|
5 | 6 | namespace NodeDev.Core.Nodes.Flow;
|
6 | 7 |
|
7 | 8 | public class ForNode : FlowNode
|
8 | 9 | {
|
9 |
| - public override bool IsFlowNode => true; |
| 10 | + public override bool IsFlowNode => true; |
10 | 11 |
|
11 |
| - public override bool FetchState => true; |
| 12 | + public override bool FetchState => true; |
12 | 13 |
|
13 |
| - public override bool ReOrderExecInputsAndOutputs => false; |
| 14 | + public override bool ReOrderExecInputsAndOutputs => false; |
14 | 15 |
|
15 |
| - public override bool AllowRemergingExecConnections => false; |
| 16 | + public override bool AllowRemergingExecConnections => false; |
16 | 17 |
|
17 |
| - public ForNode(Graph graph, string? id = null) : base(graph, id) |
18 |
| - { |
19 |
| - Name = "For"; |
| 18 | + public ForNode(Graph graph, string? id = null) : base(graph, id) |
| 19 | + { |
| 20 | + Name = "For"; |
20 | 21 |
|
21 |
| - Inputs.Add(new("Exec", this, TypeFactory.ExecType)); |
22 |
| - Inputs.Add(new("Start", this, TypeFactory.Get<int>())); |
23 |
| - Inputs.Add(new("End (Exclude)", this, TypeFactory.Get<int>())); |
| 22 | + Inputs.Add(new("Exec", this, TypeFactory.ExecType)); |
| 23 | + Inputs.Add(new("Start", this, TypeFactory.Get<int>())); |
| 24 | + Inputs.Add(new("End (Exclude)", this, TypeFactory.Get<int>())); |
24 | 25 |
|
25 |
| - Outputs.Add(new("ExecLoop", this, TypeFactory.ExecType)); |
26 |
| - Outputs.Add(new("Index", this, TypeFactory.Get<int>(), linkedExec: Outputs[0])); |
27 |
| - Outputs.Add(new("ExecOut", this, TypeFactory.ExecType)); |
28 |
| - } |
| 26 | + Outputs.Add(new("ExecLoop", this, TypeFactory.ExecType)); |
| 27 | + Outputs.Add(new("Index", this, TypeFactory.Get<int>(), linkedExec: Outputs[0])); |
| 28 | + Outputs.Add(new("ExecOut", this, TypeFactory.ExecType)); |
| 29 | + } |
29 | 30 |
|
30 |
| - public override string GetExecOutputPathId(string pathId, Connection execOutput) |
31 |
| - { |
32 |
| - if (execOutput == Outputs[0]) |
33 |
| - return pathId + "-" + execOutput.Id; |
34 |
| - else if (execOutput == Outputs[2]) |
35 |
| - return pathId; |
36 |
| - else |
37 |
| - throw new Exception("Invalid exec output"); |
38 |
| - } |
| 31 | + public override string GetExecOutputPathId(string pathId, Connection execOutput) |
| 32 | + { |
| 33 | + if (execOutput == Outputs[0]) |
| 34 | + return pathId + "-" + execOutput.Id; |
| 35 | + else if (execOutput == Outputs[2]) |
| 36 | + return pathId; |
| 37 | + else |
| 38 | + throw new Exception("Invalid exec output"); |
| 39 | + } |
39 | 40 |
|
40 |
| - public override bool DoesOutputPathAllowDeadEnd(Connection execOutput) => execOutput == Outputs[0]; // The loop exec path must be a dead end (or a breaking node, such as return, continue, break) |
| 41 | + public override bool DoesOutputPathAllowDeadEnd(Connection execOutput) => execOutput == Outputs[0]; // The loop exec path must be a dead end (or a breaking node, such as return, continue, break) |
41 | 42 |
|
42 |
| - public override bool DoesOutputPathAllowMerge(Connection execOutput) => execOutput == Outputs[2]; // the ExecOut path allows merging, but not the loop. The loop is always a dead end. |
| 43 | + public override bool DoesOutputPathAllowMerge(Connection execOutput) => execOutput == Outputs[2]; // the ExecOut path allows merging, but not the loop. The loop is always a dead end. |
43 | 44 |
|
44 |
| - internal override Expression BuildExpression(Dictionary<Connection, Graph.NodePathChunks>? subChunks, BuildExpressionInfo info) |
45 |
| - { |
46 |
| - throw new NotImplementedException(); |
47 |
| - } |
| 45 | + internal override Expression BuildExpression(Dictionary<Connection, Graph.NodePathChunks>? subChunks, BuildExpressionInfo info) |
| 46 | + { |
| 47 | + ArgumentNullException.ThrowIfNull(subChunks); |
| 48 | + |
| 49 | + var count = info.LocalVariables[Outputs[1]]; |
| 50 | + var assignCount = Expression.Assign(count, info.LocalVariables[Inputs[1]]); // assign the start value to the count variable |
| 51 | + var incrementCount = Expression.PreIncrementAssign(count); // increment the count variable |
| 52 | + |
| 53 | + var loopBody = Expression.Block(Graph.BuildExpression(subChunks[Outputs[0]], info).Append(incrementCount)); // Build the loop body and the counter increment |
| 54 | + var afterLoop = Expression.Block(Graph.BuildExpression(subChunks[Outputs[2]], info)); // Build the after loop body |
| 55 | + |
| 56 | + var breakLabel = Expression.Label(); |
| 57 | + var loop = Expression.Loop( |
| 58 | + Expression.IfThenElse( |
| 59 | + Expression.LessThan(count, info.LocalVariables[Inputs[2]]), // i < end |
| 60 | + loopBody, // does the assign for enumerator.Current, as well as the loop body |
| 61 | + Expression.Break(breakLabel) // break the loop |
| 62 | + ), |
| 63 | + breakLabel |
| 64 | + ); |
| 65 | + |
| 66 | + return Expression.Block(assignCount, loop, afterLoop); |
| 67 | + } |
48 | 68 | }
|
0 commit comments