/// <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="astBuilder">Ast builder</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> /// <returns></returns> public static NodeToCodeResult NodeToCode( ProtoCore.Core core, AstBuilder astBuilder, IEnumerable <NodeModel> workspaceNodes, IEnumerable <NodeModel> nodes) { // 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 var sortedGraph = AstBuilder.TopologicalSortForGraph(workspaceNodes); var sortedNodes = sortedGraph.Where(nodes.Contains); var allAstNodes = astBuilder.CompileToAstNodes(sortedNodes, AstBuilder.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 all inputs/outputs/candidate renaming variables GetInputOutputMap(nodes, out inputMap, out outputMap, out renamingMap); // 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 ShortNameGenerator(); // temporary variables are double mapped. foreach (var key in outputMap.Keys.ToList()) { if (key.StartsWith(Constants.kTempVarForNonAssignment) && outputMap[key].StartsWith(Constants.kTempVarForNonAssignment)) { string shortName = nameGenerator.GetNextName(); while (mappedVariables.Contains(shortName)) { shortName = nameGenerator.GetNextName(); } 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); 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); } } #endregion var result = new NodeToCodeResult(allAstNodes.SelectMany(x => x.Item2), inputMap, outputMap); return(result); }
/// <summary> /// Remove temporary assignment from the result. That is, removing /// assignment "t1 = x" where t1 is a temporary variable. This kind /// of assginment can be safely removed, but now all nodes that /// connect to "t1" should re-connect to "x". For example, "a = t1" /// now should be updated to "a = x". /// </summary> /// <param name="result"></param> /// <returns></returns> public static NodeToCodeResult ConstantPropagationForTemp(NodeToCodeResult result, IEnumerable <string> outputVariables) { var tempVars = new HashSet <string>( result.OutputMap.Where(p => p.Key.StartsWith(Constants.kTempVarForNonAssignment)) .Select(p => p.Value)); var nonTempAsts = new List <AssociativeNode>(); var tempAssignments = new List <Tuple <IdentifierNode, AssociativeNode> >(); foreach (var ast in result.AstNodes) { var expr = ast as BinaryExpressionNode; if (expr == null || expr.Optr != Operator.assign) { nonTempAsts.Add(ast); continue; } var lhs = expr.LeftNode as IdentifierNode; var rhs = expr.RightNode; if (lhs == null || !tempVars.Contains(lhs.Value) || outputVariables.Contains(lhs.Value) || !(rhs.IsLiteral || rhs is IdentifierNode)) { nonTempAsts.Add(ast); continue; } IdentifierFinder finder = new IdentifierFinder(lhs.Value, false); bool isReferenced = result.AstNodes.Any(n => n.Accept(finder)); bool isRhsDefined = false; if (rhs is IdentifierNode) { var defFinder = new IdentifierFinder((rhs as IdentifierNode).Value, true); isRhsDefined = result.AstNodes.Any(n => n.Accept(defFinder)); } if (isReferenced || isRhsDefined) { tempAssignments.Add(Tuple.Create(lhs, rhs)); } else { nonTempAsts.Add(ast); } } foreach (var pair in tempAssignments) { // Update the map. var keys = result.OutputMap.Keys.ToList(); for (int i = 0; i < keys.Count; ++i) { var value = result.OutputMap[keys[i]]; if (value.Equals(pair.Item1.Value)) { var rhs = pair.Item2 as IdentifierNode; if (rhs != null) { result.OutputMap[keys[i]] = rhs.Value; } } } // Update ASTs. IdentifierReplacer replacer = new IdentifierReplacer(pair.Item1.Value, pair.Item2); foreach (var ast in nonTempAsts) { ast.Accept(replacer); } } return(new NodeToCodeResult(nonTempAsts, result.InputMap, result.OutputMap)); }