private Expression GetDataExpression(int?node, int?output, Dictionary <int, bool> visited) { if (visited.ContainsKey(node.Value) && visited[node.Value]) { throw new InvalidGraphException("Infinite data loop"); } var currentNode = GetNode(node.Value); if (FunctionsFacade.IsPureFunction(currentNode.Type)) { visited[node.Value] = true; var inputExpressions = new Expression[currentNode.Inputs.Length]; for (int i = 0; i < currentNode.Inputs.Length; i++) { var connection = currentNode.Inputs[i][0]; if (connection == null) { inputExpressions[i] = GetDefaultValueOfEmptyInput(currentNode.Type, i, currentNode.Data.Value); } else { inputExpressions[i] = GetDataExpression(connection.Node, connection.Output, visited); } } visited[node.Value] = false; if (currentNode.Type == FunctionNames.Number) { return(Expression.Constant((double?)Convert.ToDouble(currentNode.Data.Value), typeof(double?))); } else if (currentNode.Type == FunctionNames.Boolean) { return(Expression.Constant((bool?)Convert.ToBoolean(currentNode.Data.Value), typeof(bool?))); } else if (currentNode.Type == FunctionNames.NumberArray) { return(Expression.Constant(JsonConvert.DeserializeObject <double?[]>(currentNode.Data.Value), typeof(double?[]))); } if (currentNode.Type == FunctionNames.Get) { return(LocalVars[currentNode.Data.Value]); } else { try { var tree = FunctionsFacade.GetExpressionTree(currentNode.Type, inputExpressions); return(tree); } catch (NeedToConstructExpressionTreeException) { // If the function has been constructed before use that if (CachedTrees.ContainsKey(currentNode.Type)) { return(Expression.Invoke(CachedTrees[currentNode.Type], inputExpressions)); } // If this is a recursive call of a function that hasn't been constructed yet use self pointer else if (CallStackTreePointers.ContainsKey(currentNode.Type)) { return(Expression.Invoke(CallStackTreePointers[currentNode.Type], inputExpressions)); } // Otherwise construct a tree for the function else { var graph = FunctionsFacade.GetFunctionGraph(currentNode.Type); var constructor = new ExpressionTreeConstructor(currentNode.Type, graph, FunctionsFacade, CachedTrees); var tree = constructor.Construct(); return(Expression.Invoke(tree, inputExpressions)); } } } } else { switch (currentNode.Type) { case FunctionNames.Entry: return(EntryParams[output.Value - 1]); case FunctionNames.ForEach: case FunctionNames.For: return(LoopVars[Tuple.Create(currentNode.Id, output.Value)]); case FunctionNames.Set: return(LocalVars[currentNode.Data.Value]); } } throw new InvalidGraphException($"Couldn't recognize node {currentNode.Type}"); }
private Expression GetControlExpression(int node, Dictionary <int, bool> visited) { if (visited.ContainsKey(node) && visited[node]) { throw new InvalidGraphException("Infinite flow loop"); } visited[node] = true; var currentNode = GetNode(node); var inputDataExpressions = new Expression[currentNode.Inputs.Length]; var outputFlowExpressions = new Expression[currentNode.Outputs.Length]; // ASSUMPTION: first input socket is a flow socket, the rest are data sockets for (int i = 1; i < currentNode.Inputs.Length; i++) { var connection = currentNode.Inputs[i][0]; if (connection == null) { inputDataExpressions[i] = GetDefaultValueOfEmptyInput(currentNode.Type, i, currentNode.Data.Value); } else { inputDataExpressions[i] = GetDataExpression(node, i); } } for (int i = 0; i < currentNode.Outputs.Length; i++) { var connection = currentNode.Outputs[i][0]; if (connection != null && !FunctionsFacade.IsPureFunction(GetNode(connection.Node).Type)) { outputFlowExpressions[i] = GetControlExpression(connection.Node, visited); } else { outputFlowExpressions[i] = Expression.Empty(); } } visited[node] = false; switch (currentNode.Type) { case FunctionNames.If: return(Expression.IfThenElse( Expression.Convert(inputDataExpressions.ElementAtOrDefault(1), typeof(bool)), outputFlowExpressions.ElementAtOrDefault(0), outputFlowExpressions.ElementAtOrDefault(1) )); case FunctionNames.ForEach: return(Expression.Block( ExpressionUtils.ForEach( inputDataExpressions.ElementAtOrDefault(1), LoopVars[Tuple.Create(node, 1)], outputFlowExpressions.ElementAtOrDefault(0) ), outputFlowExpressions.ElementAtOrDefault(3) )); case FunctionNames.For: throw new NotImplementedException(); case FunctionNames.While: throw new NotImplementedException(); case FunctionNames.Return: return(Expression.Return(ReturnTarget, inputDataExpressions.ElementAtOrDefault(1))); case FunctionNames.Set: return(Expression.Block( Expression.Assign(LocalVars[currentNode.Data.Value], inputDataExpressions.ElementAtOrDefault(1)), outputFlowExpressions[0] )); default: throw new InvalidGraphException("Not a control function or is entry"); } }