예제 #1
0
        /// <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);
        }
예제 #2
0
        /// <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));
        }