-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathForeachNode.cs
More file actions
90 lines (69 loc) · 3.77 KB
/
Copy pathForeachNode.cs
File metadata and controls
90 lines (69 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using NodeDev.Core.Connections;
using NodeDev.Core.Types;
using System.Linq.Expressions;
namespace NodeDev.Core.Nodes.Flow;
public class ForeachNode : FlowNode
{
public override bool IsFlowNode => true;
public override bool FetchState => true;
public override bool ReOrderExecInputsAndOutputs => false;
public override bool AllowRemergingExecConnections => false;
public ForeachNode(Graph graph, string? id = null) : base(graph, id)
{
Name = "Foreach";
var t = new UndefinedGenericType("T");
Inputs.Add(new("Exec", this, TypeFactory.ExecType));
Inputs.Add(new("IEnumerable", this, TypeFactory.Get(typeof(IEnumerable<>), [t])));
Outputs.Add(new("ExecLoop", this, TypeFactory.ExecType));
Outputs.Add(new("Item", this, t, linkedExec: Outputs[0]));
Outputs.Add(new("ExecOut", this, TypeFactory.ExecType));
}
public override string GetExecOutputPathId(string pathId, Connection execOutput)
{
if (execOutput == Outputs[0])
{
return pathId + "-" + execOutput.Id;
}
else if (execOutput == Outputs[2])
return pathId;
throw new Exception("Unable to find execOutput");
}
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)
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.
private readonly string LabelName = $"break_{Random.Shared.Next(0, 100000)}";
internal override Expression BuildExpression(Dictionary<Connection, Graph.NodePathChunks>? subChunks, BuildExpressionInfo info)
{
ArgumentNullException.ThrowIfNull(subChunks);
var getEnumerator = Inputs[1].Type.MakeRealType().GetMethod(nameof(IEnumerable<int>.GetEnumerator), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (getEnumerator == null)
throw new Exception($"Unable to find GetEnumerator method on input parameter type: {Inputs[1].Type.FriendlyName}");
var moveNext = typeof(System.Collections.IEnumerator).GetMethod(nameof(System.Collections.IEnumerator.MoveNext), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (moveNext == null)
throw new Exception($"Unable to find MoveNext method on input parameter type: {getEnumerator.DeclaringType!.Name}");
var current = getEnumerator.ReturnType.GetProperty(nameof(IEnumerator<int>.Current), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (current == null)
throw new Exception($"Unable to find Current property on input parameter type: {getEnumerator.DeclaringType!.Name}");
var enumeratorVariable = Expression.Variable(getEnumerator.ReturnType!);
var assignEnumerator = Expression.Assign(enumeratorVariable, Expression.Call(info.LocalVariables[Inputs[1]], getEnumerator));
var assignCurrent = Expression.Assign(info.LocalVariables[Outputs[1]], Expression.Property(enumeratorVariable, current));
var loopBody = Expression.Block(
Graph.BuildExpression(subChunks[Outputs[0]], info)
.Prepend(assignCurrent)
);
var afterLoop = Expression.Block(Graph.BuildExpression(subChunks[Outputs[2]], info));
var breakLabel = Expression.Label(LabelName);
var loop = Expression.Loop(
Expression.IfThenElse(
Expression.Call(enumeratorVariable, moveNext), // if the enumerator.MoveNext() returns true
loopBody, // does the assign for enumerator.Current, as well as the loop body
Expression.Break(breakLabel) // break the loop
),
breakLabel
);
// Return a block that does :
// var enumerator = inputs[1].GetEnumerator();
// while(enumerator.MoveNext())
// after loop...
return Expression.Block([enumeratorVariable], assignEnumerator, loop, afterLoop);
}
}