Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions src/NodeDev.Blazor/Components/SourceViewer.razor
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,11 @@ else

CodeCs += $"{System.Environment.NewLine}*/";
}
catch (Exception ex)
{
CodeCs = $"/* Error during code generation: {System.Environment.NewLine}{ex}{System.Environment.NewLine}*/";
CodeMsil = "";
}
}
catch (Exception)
catch (Exception ex)
{
Creator = null;
return;
CodeCs = $"/* Error during code generation: {System.Environment.NewLine}{ex}{System.Environment.NewLine}*/";
CodeMsil = "";
}
}
}
2 changes: 1 addition & 1 deletion src/NodeDev.Core/Class/NodeClassTypeCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Dis2Msil;
using System.Text;
using System.Runtime.Loader;
using System.Linq.Expressions;

namespace NodeDev.Core.Class;

Expand Down Expand Up @@ -47,7 +48,6 @@ public void CreateProjectClassesAndAssembly()
var persisted = Activator.CreateInstance(TemporaryReflectionAssembly.ExportedTypes.First(), new AssemblyName("NodeProject_" + this.Project.Id.ToString().Replace('-', '_')), typeof(object).Assembly, null)!;
Assembly = (AssemblyBuilder)persisted;


// https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.assemblybuilder?view=net-7.0
//var persisted = new PersistedAssemblyBuilder(new AssemblyName("NodeProject_" + this.Project.Id.ToString().Replace('-', '_')), typeof(object).Assembly);
//Assembly = persisted;
Expand Down
34 changes: 31 additions & 3 deletions src/NodeDev.Core/Graph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ internal record class NodePathChunks(Connection OutputStartPoint, List<NodePathC
{
internal bool ContainOutput(Connection output)
{
if (OutputStartPoint == output)
return true;

foreach (var part in Chunks)
{
if (part.ContainOutput(output))
Expand Down Expand Up @@ -107,8 +110,33 @@ internal NodePathChunks GetChunks(Connection execOutput, bool allowDeadEnd)
{
if (currentInput.Parent.Outputs.Count(x => x.Type.IsExec) <= 1) // we can keep adding to the straight path. It's either a dead end or the path keeps going
{
// find the next output node to follow
var output = currentInput.Parent.Outputs.FirstOrDefault(x => x.Type.IsExec && x != execOutput);
if (currentInput.Connections.Count != 1)
{
// We reached a merging point. We need to validate if this merging point was already validated by the last chunk. It is possible that we are in a junction like so :
// if(...)
// {
// if(...)
// {
// ...
// }
// else
// {
// ...
// }
// }
// else
// {
// ...
// }
// ... <- we are here
// If we are indeed in this scenario, everything from the first "if" should be in the "chunks"
// We can check any merge point of the last chunk. They should all either be the same of null
if(chunks.LastOrDefault()?.SubChunk?.Values?.FirstOrDefault(x => x.InputMergePoint != null)?.InputMergePoint != currentInput)
return new NodePathChunks(execOutput, chunks, currentInput, null); // we reached a merging point
}

// find the next output node to follow
var output = currentInput.Parent.Outputs.FirstOrDefault(x => x.Type.IsExec && x != execOutput);
var nextInput = output?.Connections.FirstOrDefault(); // get the next input, there's either 1 or none

if (nextInput == null)
Expand Down Expand Up @@ -280,7 +308,7 @@ public LambdaExpression BuildExpression(BuildExpressionOptions options)
.Distinct() // lots of inputs use the same variable as another node's output, make sure we only declare them once
.Except(info.MethodParametersExpression.Values); // Remove the method parameters as they are declared later and not here

var expressionBlock = Expression.Block(localVariables, expressions.Append(Expression.Label(returnLabelTarget, Expression.Default(returnLabelTarget.Type))));
var expressionBlock = Expression.Block(localVariables, expressions.Append(returnLabel));

var parameters = SelfMethod.IsStatic ? info.MethodParametersExpression.Values : info.MethodParametersExpression.Values.Prepend(info.ThisExpression!);
var lambdaExpression = Expression.Lambda(expressionBlock, parameters);
Expand Down
29 changes: 29 additions & 0 deletions src/NodeDev.Core/Nodes/DeclareVariableNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using NodeDev.Core.Connections;
using NodeDev.Core.Types;
using System.Linq.Expressions;

namespace NodeDev.Core.Nodes;

public class DeclareVariableNode : NormalFlowNode
{
public DeclareVariableNode(Graph graph, string? id = null) : base(graph, id)
{
Name = "Declare Variable";

var t = new UndefinedGenericType("T");
Outputs.Add(new Connection("Variable", this, t));
Inputs.Add(new Connection("InitialValue", this, t));
}

public override string TitleColor => "blue";

internal override Expression BuildExpression(Dictionary<Connection, Graph.NodePathChunks>? subChunks, BuildExpressionInfo info)
{
return Expression.Assign(info.LocalVariables[Outputs[1]], info.LocalVariables[Inputs[1]]);
}

internal override void BuildInlineExpression(BuildExpressionInfo info)
{
throw new NotImplementedException();
}
}
11 changes: 8 additions & 3 deletions src/NodeDev.Core/Nodes/Flow/Branch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@ internal override Expression BuildExpression(Dictionary<Connection, Graph.NodePa
var ifTrue = Graph.BuildExpression(subChunks[Outputs[0]], info);
var ifFalse = Graph.BuildExpression(subChunks[Outputs[1]], info);

var ifThenElse = Expression.IfThenElse(info.LocalVariables[Inputs[1]], Expression.Block(ifTrue), Expression.Block(ifFalse));

return ifThenElse;
if(ifTrue.Length == 0 && ifFalse.Length == 0)
throw new InvalidOperationException("Branch node must have at least a 'IfTrue' of 'IfFalse' statement.");
else if(ifTrue.Length == 0) // NOT the operation, instead of "if(condition){} else { ...}" we'll have if(!condition) { ... }
return Expression.IfThen(Expression.Not(info.LocalVariables[Inputs[1]]), Expression.Block(ifFalse));
else if(ifFalse.Length == 0)
return Expression.IfThen(info.LocalVariables[Inputs[1]], Expression.Block(ifTrue));
else
return Expression.IfThenElse(info.LocalVariables[Inputs[1]], Expression.Block(ifTrue), Expression.Block(ifFalse));
}
}
2 changes: 2 additions & 0 deletions src/NodeDev.Core/Nodes/Flow/ReturnNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public ReturnNode(Graph graph, string? id = null) : base(graph, id)
Name = "Return";

Inputs.Add(new("Exec", this, TypeFactory.ExecType));

Refresh();
}

private bool HasReturnValue => Graph.SelfMethod.ReturnType != TypeFactory.Void;
Expand Down
55 changes: 55 additions & 0 deletions src/NodeDev.Core/Nodes/Flow/TryCatchNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using NodeDev.Core.Connections;
using NodeDev.Core.Types;
using System.Linq.Expressions;

namespace NodeDev.Core.Nodes.Flow;

public class TryCatchNode : FlowNode
{
public override bool IsFlowNode => true;

public TryCatchNode(Graph graph, string? id = null) : base(graph, id)
{
Name = "TryCatch";

Inputs.Add(new("Exec", this, TypeFactory.ExecType));

Outputs.Add(new("Try", this, TypeFactory.ExecType));
Outputs.Add(new("Catch", this, TypeFactory.ExecType));
Outputs.Add(new("Finally", this, TypeFactory.ExecType));
Outputs.Add(new("Exception", this, new UndefinedGenericType("T"), linkedExec: Outputs[1]));
}

public override string GetExecOutputPathId(string pathId, Connection execOutput)
{
return pathId + "-" + execOutput.Id;
}

// We're allowed to have 'nothing' in the blocks, ex an empty "finally" or an empty "catch"
public override bool DoesOutputPathAllowDeadEnd(Connection execOutput)
{
return execOutput.Connections.Count == 0;
}

/// <summary>
/// We allow merging back together at the end. Technically all paths could continue, such as :
/// Try { } catch( Exception ex) {} finally { } ...
/// </summary>
public override bool DoesOutputPathAllowMerge(Connection execOutput) => true;

internal override Expression BuildExpression(Dictionary<Connection, Graph.NodePathChunks>? subChunks, BuildExpressionInfo info)
{
ArgumentNullException.ThrowIfNull(subChunks);

var tryBlock = Expression.Block(Graph.BuildExpression(subChunks[Outputs[0]], info));
var catchBlock = Expression.Block(Graph.BuildExpression(subChunks[Outputs[1]], info));
var finallyBlock = Expression.Block(Graph.BuildExpression(subChunks[Outputs[2]], info));

//var exceptionVariable = Expression.Variable(Outputs[3].Type.MakeRealType(), "ex");
//info.LocalVariables[Outputs[3]] = exceptionVariable; // Make sure other pieces of code use the right variable for that exception

var catchClause = Expression.Catch(Outputs[3].Type.MakeRealType(), catchBlock);

return Expression.Block(Expression.TryCatchFinally(tryBlock, Outputs[2].Connections.Count == 0 ? null : finallyBlock, catchClause));
}
}
22 changes: 22 additions & 0 deletions src/NodeDev.Core/Nodes/SetVariableValueNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using NodeDev.Core.Connections;
using NodeDev.Core.Types;
using System.Linq.Expressions;

namespace NodeDev.Core.Nodes;

public class SetVariableValueNode : NormalFlowNode
{
public SetVariableValueNode(Graph graph, string? id = null) : base(graph, id)
{
Name = "Set Variable";

var type = new UndefinedGenericType("T");
Inputs.Add(new Connection("Variable", this, type));
Inputs.Add(new Connection("Value", this, type));
}

internal override Expression BuildExpression(Dictionary<Connection, Graph.NodePathChunks>? subChunks, BuildExpressionInfo info)
{
return Expression.Assign(info.LocalVariables[Inputs[1]], info.LocalVariables[Inputs[2]]);
}
}
6 changes: 3 additions & 3 deletions src/NodeDev.Core/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ public static Project CreateNewDefaultProject(out NodeClassMethod main)

var programClass = new NodeClass("Program", "NewProject", project);

main = new NodeClassMethod(programClass, "Main", project.TypeFactory.Get(typeof(void), null), new Graph());
main.IsStatic = true;
main = new NodeClassMethod(programClass, "Main", project.TypeFactory.Get<int>(), new Graph(), true);

var entry = new EntryNode(main.Graph);
var returnNode = new ReturnNode(main.Graph);
returnNode.Inputs[1].UpdateTextboxText("0");
main.Graph.AddNode(entry, false);
main.Graph.AddNode(returnNode, false);
programClass.Methods.Add(main);
Expand Down Expand Up @@ -165,7 +165,7 @@ private static string GetNetCoreVersion()
return "";
}

private AssemblyBuilder BuildAndGetAssembly(BuildOptions buildOptions)
public AssemblyBuilder BuildAndGetAssembly(BuildOptions buildOptions)
{
CreateNodeClassTypeCreator(buildOptions);

Expand Down
Loading
Loading