/// <summary> /// Map variable to shorter name. /// </summary> /// <param name="core"></param> /// <param name="astNode"></param> /// <param name="shortNameMap"></param> /// <param name="nameGenerator"></param> /// <param name="mappedVariables"></param> /// <param name="typeHints"></param> private static void ShortNameMapping( ProtoCore.Core core, AssociativeNode astNode, Dictionary <string, string> shortNameMap, TypeDependentNameGenetrator nameGenerator, HashSet <string> mappedVariables, Dictionary <string, ProtoCore.Type> typeHints) { Action <IdentifierNode> func = n => { string shortName; if (shortNameMap.TryGetValue(n.Value, out shortName)) { if (string.IsNullOrEmpty(shortName)) { shortName = GetNextShortName(nameGenerator, typeHints, mappedVariables, n.Value); shortNameMap[n.Value] = shortName; } n.Value = n.Name = shortName; } }; IdentifierVisitor visitor = new IdentifierVisitor(func, core); astNode.Accept(visitor); }
/// <summary> /// Returns type-dependent short name based on the type hint of input variable /// </summary> /// <param name="generator"></param> /// <param name="typeHints"></param> /// <param name="mappedVariables"></param> /// <param name="varName"></param> /// <returns></returns> private static string GetNextShortName( TypeDependentNameGenetrator generator, Dictionary <string, ProtoCore.Type> typeHints, HashSet <string> mappedVariables, string varName) { ProtoCore.Type?type = null; ProtoCore.Type t; if (typeHints.TryGetValue(varName, out t)) { type = t; } var shortName = generator.GetName(type); while (mappedVariables.Contains(shortName)) { shortName = generator.GetName(type); } return(shortName); }
/// <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); }