private Node ArrayCompTransformHelper(ArrayComprehension node, string arrayName) { decompiler.AddToken(Token.LB); int lineno = node.GetLineno(); Node expr = Transform(node.GetResult()); IList<ArrayComprehensionLoop> loops = node.GetLoops(); int numLoops = loops.Count; // Walk through loops, collecting and defining their iterator symbols. Node[] iterators = new Node[numLoops]; Node[] iteratedObjs = new Node[numLoops]; for (int i = 0; i < numLoops; i++) { ArrayComprehensionLoop acl = loops[i]; decompiler.AddName(" "); decompiler.AddToken(Token.FOR); if (acl.IsForEach()) { decompiler.AddName("each "); } decompiler.AddToken(Token.LP); AstNode iter = acl.GetIterator(); string name = null; if (iter.GetType() == Token.NAME) { name = iter.GetString(); decompiler.AddName(name); } else { // destructuring assignment Decompile(iter); name = currentScriptOrFn.GetNextTempName(); DefineSymbol(Token.LP, name, false); expr = CreateBinary(Token.COMMA, CreateAssignment(Token.ASSIGN, iter, CreateName(name)), expr); } Node init = CreateName(name); // Define as a let since we want the scope of the variable to // be restricted to the array comprehension DefineSymbol(Token.LET, name, false); iterators[i] = init; decompiler.AddToken(Token.IN); iteratedObjs[i] = Transform(acl.GetIteratedObject()); decompiler.AddToken(Token.RP); } // generate code for tmpArray.push(body) Node call = CreateCallOrNew(Token.CALL, CreatePropertyGet(CreateName(arrayName), null, "push", 0)); Node body = new Node(Token.EXPR_VOID, call, lineno); if (node.GetFilter() != null) { decompiler.AddName(" "); decompiler.AddToken(Token.IF); decompiler.AddToken(Token.LP); body = CreateIf(Transform(node.GetFilter()), body, null, lineno); decompiler.AddToken(Token.RP); } // Now walk loops in reverse to build up the body statement. int pushed = 0; try { for (int i_1 = numLoops - 1; i_1 >= 0; i_1--) { ArrayComprehensionLoop acl = loops[i_1]; Scope loop = CreateLoopNode(null, acl.GetLineno()); // no label PushScope(loop); pushed++; body = CreateForIn(Token.LET, loop, iterators[i_1], iteratedObjs[i_1], body, acl.IsForEach()); } } finally { for (int i_1 = 0; i_1 < pushed; i_1++) { PopScope(); } } decompiler.AddToken(Token.RB); // Now that we've accumulated any destructuring forms, // add expr to the call node; it's pushed on each iteration. call.AddChildToBack(expr); return body; }
private Node TransformArrayComp(ArrayComprehension node) { // An array comprehension expression such as // // [expr for (x in foo) for each ([y, z] in bar) if (cond)] // // is rewritten approximately as // // new Scope(ARRAYCOMP) { // new Node(BLOCK) { // let tmp1 = new Array; // for (let x in foo) { // for each (let tmp2 in bar) { // if (cond) { // tmp1.push([y, z] = tmp2, expr); // } // } // } // } // createName(tmp1) // } int lineno = node.GetLineno(); Scope scopeNode = CreateScopeNode(Token.ARRAYCOMP, lineno); string arrayName = currentScriptOrFn.GetNextTempName(); PushScope(scopeNode); try { DefineSymbol(Token.LET, arrayName, false); Node block = new Node(Token.BLOCK, lineno); Node newArray = CreateCallOrNew(Token.NEW, CreateName("Array")); Node init = new Node(Token.EXPR_VOID, CreateAssignment(Token.ASSIGN, CreateName(arrayName), newArray), lineno); block.AddChildToBack(init); block.AddChildToBack(ArrayCompTransformHelper(node, arrayName)); scopeNode.AddChildToBack(block); scopeNode.AddChildToBack(CreateName(arrayName)); return scopeNode; } finally { PopScope(); } }