internal void ConvertNodesToCodeInternal(EngineController engineController, INamingProvider namingProvider) { var selectedNodes = DynamoSelection.Instance .Selection .OfType<NodeModel>() .Where(n => n.IsConvertible); if (!selectedNodes.Any()) return; var cliques = NodeToCodeCompiler.GetCliques(selectedNodes).Where(c => !(c.Count == 1 && c.First() is CodeBlockNodeModel)); var codeBlockNodes = new List<CodeBlockNodeModel>(); //UndoRedo Action Group---------------------------------------------- NodeToCodeUndoHelper undoHelper = new NodeToCodeUndoHelper(); // using (UndoRecorder.BeginActionGroup()) { foreach (var nodeList in cliques) { //Create two dictionarys to store the details of the external connections that have to //be recreated after the conversion var externalInputConnections = new Dictionary<ConnectorModel, string>(); var externalOutputConnections = new Dictionary<ConnectorModel, string>(); //Also collect the average X and Y co-ordinates of the different nodes int nodeCount = nodeList.Count; var nodeToCodeResult = engineController.ConvertNodesToCode(this.nodes, nodeList, namingProvider); #region Step I. Delete all nodes and their connections double totalX = 0, totalY = 0; foreach (var node in nodeList) { #region Step I.A. Delete the connections for the node foreach (var connector in node.AllConnectors.ToList()) { if (!IsInternalNodeToCodeConnection(nodeList, connector)) { //If the connector is an external connector, the save its details //for recreation later var startNode = connector.Start.Owner; int index = startNode.OutPorts.IndexOf(connector.Start); //We use the varibleName as the connection between the port of the old Node //to the port of the new node. var variableName = startNode.GetAstIdentifierForOutputIndex(index).Value; //Store the data in the corresponding dictionary if (startNode == node) { if (nodeToCodeResult.OutputMap.ContainsKey(variableName)) variableName = nodeToCodeResult.OutputMap[variableName]; externalOutputConnections.Add(connector, variableName); } else { if (nodeToCodeResult.InputMap.ContainsKey(variableName)) variableName = nodeToCodeResult.InputMap[variableName]; externalInputConnections.Add(connector, variableName); } } //Delete the connector undoHelper.RecordDeletion(connector); connector.Delete(); } #endregion #region Step I.B. Delete the node totalX += node.X; totalY += node.Y; undoHelper.RecordDeletion(node); RemoveNode(node); #endregion } #endregion #region Step II. Create the new code block node var outputVariables = externalOutputConnections.Values; var newResult = NodeToCodeCompiler.ConstantPropagationForTemp(nodeToCodeResult, outputVariables); // Rewrite the AST using the shortest unique name in case of namespace conflicts NodeToCodeCompiler.ReplaceWithShortestQualifiedName( engineController.LibraryServices.LibraryManagementCore.ClassTable, newResult.AstNodes, ElementResolver); var codegen = new ProtoCore.CodeGenDS(newResult.AstNodes); var code = codegen.GenerateCode(); var codeBlockNode = new CodeBlockNodeModel( code, System.Guid.NewGuid(), totalX / nodeCount, totalY / nodeCount, engineController.LibraryServices, ElementResolver); undoHelper.RecordCreation(codeBlockNode); AddAndRegisterNode(codeBlockNode, false); codeBlockNodes.Add(codeBlockNode); #endregion #region Step III. Recreate the necessary connections var newInputConnectors = ReConnectInputConnections(externalInputConnections, codeBlockNode); foreach (var connector in newInputConnectors) { undoHelper.RecordCreation(connector); } var newOutputConnectors = ReConnectOutputConnections(externalOutputConnections, codeBlockNode); foreach (var connector in newOutputConnectors) { undoHelper.RecordCreation(connector); } #endregion } } undoHelper.ApplyActions(UndoRecorder); DynamoSelection.Instance.ClearSelection(); DynamoSelection.Instance.Selection.AddRange(codeBlockNodes); RequestRun(); }
internal NodeToCodeResult ConvertNodesToCode(IEnumerable <NodeModel> graph, IEnumerable <NodeModel> nodes, INamingProvider namingProvider = null) { return(NodeToCodeCompiler.NodeToCode(libraryServices.LibraryManagementCore, graph, nodes, namingProvider)); }
internal static void ConvertNodesToCodeInternal(this WorkspaceModel workspace, EngineController engineController, INamingProvider namingProvider) { var selectedNodes = DynamoSelection.Instance .Selection .OfType <NodeModel>() .Where(n => n.IsConvertible); if (!selectedNodes.Any()) { return; } var cliques = NodeToCodeCompiler.GetCliques(selectedNodes).Where(c => !(c.Count == 1 && c.First() is CodeBlockNodeModel)); var codeBlockNodes = new List <CodeBlockNodeModel>(); //UndoRedo Action Group---------------------------------------------- NodeToCodeUndoHelper undoHelper = new NodeToCodeUndoHelper(); foreach (var nodeList in cliques) { //Create two dictionarys to store the details of the external connections that have to //be recreated after the conversion var externalInputConnections = new Dictionary <ConnectorModel, string>(); var externalOutputConnections = new Dictionary <ConnectorModel, string>(); //Also collect the average X and Y co-ordinates of the different nodes int nodeCount = nodeList.Count; var nodeToCodeResult = engineController.ConvertNodesToCode(workspace.Nodes, nodeList, namingProvider); #region Step I. Delete all nodes and their connections double totalX = 0, totalY = 0; foreach (var node in nodeList) { #region Step I.A. Delete the connections for the node foreach (var connector in node.AllConnectors.ToList()) { if (!IsInternalNodeToCodeConnection(nodeList, connector)) { //If the connector is an external connector, the save its details //for recreation later var startNode = connector.Start.Owner; int index = startNode.OutPorts.IndexOf(connector.Start); //We use the varibleName as the connection between the port of the old Node //to the port of the new node. var variableName = startNode.GetAstIdentifierForOutputIndex(index).Value; //Store the data in the corresponding dictionary if (startNode == node) { if (nodeToCodeResult.OutputMap.ContainsKey(variableName)) { variableName = nodeToCodeResult.OutputMap[variableName]; } externalOutputConnections.Add(connector, variableName); } else { if (nodeToCodeResult.InputMap.ContainsKey(variableName)) { variableName = nodeToCodeResult.InputMap[variableName]; } externalInputConnections.Add(connector, variableName); } } //Delete the connector undoHelper.RecordDeletion(connector); connector.Delete(); } #endregion #region Step I.B. Delete the node totalX += node.X; totalY += node.Y; undoHelper.RecordDeletion(node); workspace.RemoveAndDisposeNode(node); #endregion } #endregion #region Step II. Create the new code block node var outputVariables = externalOutputConnections.Values; var newResult = NodeToCodeCompiler.ConstantPropagationForTemp(nodeToCodeResult, outputVariables); // Rewrite the AST using the shortest unique name in case of namespace conflicts NodeToCodeCompiler.ReplaceWithShortestQualifiedName( engineController.LibraryServices.LibraryManagementCore.ClassTable, newResult.AstNodes, workspace.ElementResolver); var codegen = new ProtoCore.CodeGenDS(newResult.AstNodes); var code = codegen.GenerateCode(); var codeBlockNode = new CodeBlockNodeModel( code, System.Guid.NewGuid(), totalX / nodeCount, totalY / nodeCount, engineController.LibraryServices, workspace.ElementResolver); undoHelper.RecordCreation(codeBlockNode); workspace.AddAndRegisterNode(codeBlockNode, false); codeBlockNodes.Add(codeBlockNode); #endregion #region Step III. Recreate the necessary connections var newInputConnectors = ReConnectInputConnections(externalInputConnections, codeBlockNode, workspace); foreach (var connector in newInputConnectors) { undoHelper.RecordCreation(connector); } var newOutputConnectors = ReConnectOutputConnections(externalOutputConnections, codeBlockNode); foreach (var connector in newOutputConnectors) { undoHelper.RecordCreation(connector); } #endregion } undoHelper.ApplyActions(workspace.UndoRecorder); DynamoSelection.Instance.ClearSelection(); DynamoSelection.Instance.Selection.AddRange(codeBlockNodes); Debug.WriteLine(string.Format("Workspace has {0} nodes and {1} connectors after N2C operation.", workspace.Nodes.Count(), workspace.Connectors.Count())); workspace.RequestRun(); }
internal NodeToCodeResult ConvertNodesToCode(IEnumerable<NodeModel> graph, IEnumerable<NodeModel> nodes, INamingProvider namingProvider = null) { return NodeToCodeCompiler.NodeToCode(libraryServices.LibraryManagementCore, graph, nodes, namingProvider); }
public TypeDependentNameGenetrator(ProtoCore.Core core, INamingProvider namingProvider) { this.core = core; this.namingProvider = namingProvider; }
/// <summary> /// Compile a set of nodes to ASTs. /// /// Note: /// 1. Nodes should be a clique with regarding to convertibility and /// selection state. That is, these nodes can be safely to be /// converted into a single code block node. It shouldn't have /// unconvertible or unselected node on any path (if there is) that /// connects any two of these nodes, otherwise there will be /// circular references between unconvertible/unselected node and /// code block node. /// /// To split arbitary node set into cliques, use /// NodeToCodeUtils.GetCliques(). /// /// 2. WorkspaceNodes are all nodes in current workspace. We need the /// whole graph so that each to-be-converted node will have correct /// order in the final code block node. /// </summary> /// <param name="core">Library core</param> /// <param name="workspaceNodes">The whole workspace nodes</param> /// <param name="nodes">Selected node that can be converted to a single code block node</param> /// <param name="namingProvider"></param> /// <returns></returns> internal static NodeToCodeResult NodeToCode( ProtoCore.Core core, IEnumerable <NodeModel> workspaceNodes, IEnumerable <NodeModel> nodes, INamingProvider namingProvider) { // The basic worflow is: // 1. Compile each node to get its cde in AST format // // 2. Variable numbering to avoid confliction. For example, two // code block nodes both have assignment "y = x", we need to // rename "y" to "y1" and "y2" respectively. // // 3. Variable remapping. For example, code block node // "x = 1" connects to "a = b", where the second code block // node will have initialization "b = x_guid" where "x_guid" // is because of variable mappining in the first code block // node. We should restore it to its original name. // // Note in step 2 we may rename "x" to "xn". // // 4. Generate short name for long name variables. Typically they // are from output ports from other nodes. // // 5. Do constant progation to optimize the generated code. #region Step 1 AST compilation AstBuilder builder = new AstBuilder(null); var sortedGraph = AstBuilder.TopologicalSortForGraph(workspaceNodes); var sortedNodes = sortedGraph.Where(nodes.Contains); var allAstNodes = builder.CompileToAstNodes(sortedNodes, CompilationContext.NodeToCode, false); #endregion #region Step 2 Varialbe numbering // External inputs will be in input map // Internal non-cbn will be input map & output map // Internal cbn will be in renaming map and output map // Map from mapped variable to its original name. These variables // are from code block nodes that in the selection. Dictionary <string, string> renamingMap = null;; // Input variable to renamed input variable map Dictionary <string, string> inputMap = null; // Output variable to renamed output variable map Dictionary <string, string> outputMap = null; // Collect type ints for all variables Dictionary <string, ProtoCore.Type> typeHints = new Dictionary <string, ProtoCore.Type>(); // Collect all inputs/outputs/candidate renaming variables GetInputOutputMap(nodes, out inputMap, out outputMap, out renamingMap, out typeHints); // Variable numbering map. Value field indicates current current // numbering value of the variable. For example, there are variables // t1, t2, ... tn and the ID of variable t's NumberingState is n. var numberingMap = new Dictionary <string, NumberingState>(); // In this step, we'll renumber all variables that going to be in // the same code block node. That is, // // "x = 1; y = x;" and // "x = 2; y = x;" // // Will be renumbered to // // "x1 = 1; y1 = x1;" and // "x2 = 2; y2 = x2;" var mappedVariables = new HashSet <string>(); foreach (var t in allAstNodes) { // Reset variable numbering map foreach (var p in numberingMap) { p.Value.IsNewSession = true; } foreach (var astNode in t.Item2) { VariableNumbering(core, astNode, t.Item1, numberingMap, renamingMap, inputMap, outputMap, mappedVariables); } } renamingMap = renamingMap.Where(p => !p.Key.Contains("%")) .ToDictionary(p => p.Key, p => p.Value); #endregion #region Step 3 Variable remapping foreach (var ts in allAstNodes) { foreach (var astNode in ts.Item2) { VariableRemapping(core, astNode, renamingMap, outputMap, mappedVariables); } } #endregion #region Step 4 Generate short name var nameGenerator = new TypeDependentNameGenetrator(core, namingProvider); // temporary variables are double mapped. foreach (var key in outputMap.Keys.ToList()) { if (key.StartsWith(Constants.kTempVarForNonAssignment) && outputMap[key].StartsWith(Constants.kTempVarForNonAssignment)) { var shortName = GetNextShortName(nameGenerator, typeHints, mappedVariables, key); var tempVar = outputMap[key]; outputMap[key] = shortName; outputMap[tempVar] = shortName; mappedVariables.Add(shortName); } } foreach (var ts in allAstNodes) { foreach (var astNode in ts.Item2) { ShortNameMapping(core, astNode, inputMap, nameGenerator, mappedVariables, typeHints); foreach (var kvp in inputMap) { if (kvp.Value != String.Empty && outputMap.ContainsKey(kvp.Key)) { outputMap[kvp.Key] = kvp.Value; } } ShortNameMapping(core, astNode, outputMap, nameGenerator, mappedVariables, typeHints); } } #endregion var result = new NodeToCodeResult(allAstNodes.SelectMany(x => x.Item2), inputMap, outputMap); return(result); }