/**
* Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.parser.grammarcommon;
import java.util.ArrayList;
import org.python.pydev.core.log.Log;
import org.python.pydev.parser.jython.ISpecialStr;
import org.python.pydev.parser.jython.ParseException;
import org.python.pydev.parser.jython.SimpleNode;
import org.python.pydev.parser.jython.Token;
import org.python.pydev.parser.jython.ast.Attribute;
import org.python.pydev.parser.jython.ast.AugAssign;
import org.python.pydev.parser.jython.ast.BinOp;
import org.python.pydev.parser.jython.ast.BoolOp;
import org.python.pydev.parser.jython.ast.Break;
import org.python.pydev.parser.jython.ast.Compare;
import org.python.pydev.parser.jython.ast.Comprehension;
import org.python.pydev.parser.jython.ast.Continue;
import org.python.pydev.parser.jython.ast.Delete;
import org.python.pydev.parser.jython.ast.Dict;
import org.python.pydev.parser.jython.ast.DictComp;
import org.python.pydev.parser.jython.ast.Exec;
import org.python.pydev.parser.jython.ast.Expr;
import org.python.pydev.parser.jython.ast.ExtSlice;
import org.python.pydev.parser.jython.ast.For;
import org.python.pydev.parser.jython.ast.If;
import org.python.pydev.parser.jython.ast.Import;
import org.python.pydev.parser.jython.ast.ImportFrom;
import org.python.pydev.parser.jython.ast.Module;
import org.python.pydev.parser.jython.ast.Name;
import org.python.pydev.parser.jython.ast.NameTok;
import org.python.pydev.parser.jython.ast.Num;
import org.python.pydev.parser.jython.ast.Pass;
import org.python.pydev.parser.jython.ast.Set;
import org.python.pydev.parser.jython.ast.SetComp;
import org.python.pydev.parser.jython.ast.Starred;
import org.python.pydev.parser.jython.ast.Str;
import org.python.pydev.parser.jython.ast.StrJoin;
import org.python.pydev.parser.jython.ast.Suite;
import org.python.pydev.parser.jython.ast.UnaryOp;
import org.python.pydev.parser.jython.ast.With;
import org.python.pydev.parser.jython.ast.WithItem;
import org.python.pydev.parser.jython.ast.Yield;
import org.python.pydev.parser.jython.ast.aliasType;
import org.python.pydev.parser.jython.ast.comprehensionType;
import org.python.pydev.parser.jython.ast.decoratorsType;
import org.python.pydev.parser.jython.ast.exprType;
import org.python.pydev.parser.jython.ast.sliceType;
import org.python.pydev.parser.jython.ast.stmtType;
import org.python.pydev.parser.jython.ast.suiteType;
import org.python.pydev.shared_core.string.FastStringBuffer;
/**
* Provides the basic behavior for a tree builder (opening and closing node scopes).
*
* Subclasses must provide actions where it's not common.
*
* @author Fabio
*/
public abstract class AbstractTreeBuilder extends AbstractTreeBuilderHelpers {
/**
* Keeps the last opened node.
*/
private SimpleNode lastOpened;
/**
* @return the last opened node.
*/
@Override
public final SimpleNode getLastOpened() {
return lastOpened;
}
private final FastStringBuffer tempBuffer = new FastStringBuffer(20);
/**
* Constructor
*/
public AbstractTreeBuilder(JJTPythonGrammarState stack) {
super(stack);
if (!stack.getGrammar().generateTree) {
throw new AssertionError("Should not create a tree builder if the grammar won't generate the AST.");
}
}
/**
* Subclasses must implement this method to deal with any node that's not properly handled in this base.
* @param n the node that should be closed
* @param arity the current number of nodes in the stack (found after the context was opened)
* @return a new node representing the node that's having it's context closed.
* @throws Exception
*/
protected abstract SimpleNode onCloseNode(SimpleNode n, int arity) throws Exception;
/**
* Opens a new scope and returns a node to be used in this scope. This same node will later be called
* in {@link #closeNode(SimpleNode, int)} to have its scope closed (and at that time it may be changed
* for a new node that represents the scope more accurately.
*/
@Override
public final SimpleNode openNode(final int id) {
SimpleNode ret;
switch (id) {
case JJTFALSE:
ret = new Name("False", Name.Load, true);
break;
case JJTTRUE:
ret = new Name("True", Name.Load, true);
break;
case JJTNONE:
ret = new Name("None", Name.Load, true);
break;
case JJTNAME:
case JJTDOTTED_NAME:
//the actual name will be set during the parsing (token image) -- see Name construct
ret = new Name(null, Name.Load, false);
break;
case JJTNUM://the actual number will be set during the parsing (token image) -- see Num construct
ret = new Num(null, -1, null);
break;
case JJTSTRING:
case JJTUNICODE:
case JJTBINARY:
case JJTFSTRING:
//the actual number will be set during the parsing (token image) -- see Num construct
ret = new Str(null, -1, false, false, false, false);
break;
case JJTFOR_STMT:
ret = new For(null, null, null, null, stack.getGrammar().getInsideAsync());
break;
case JJTEXEC_STMT:
ret = new Exec(null, null, null);
break;
case JJTPASS_STMT:
ret = new Pass();
break;
case JJTBREAK_STMT:
ret = new Break();
break;
case JJTCONTINUE_STMT:
ret = new Continue();
break;
case JJTBEGIN_DECORATOR:
ret = new decoratorsType(null, null, null, null, null, false);
break;
case JJTIF_STMT:
ret = new If(null, null, null);
break;
case JJTAUG_PLUS:
ret = new AugAssign(null, AugAssign.Add, null);
break;
case JJTAUG_MINUS:
ret = new AugAssign(null, AugAssign.Sub, null);
break;
case JJTAUG_MULTIPLY:
ret = new AugAssign(null, AugAssign.Mult, null);
break;
case JJTAUG_DOT:
ret = new AugAssign(null, AugAssign.Dot, null);
break;
case JJTAUG_DIVIDE:
ret = new AugAssign(null, AugAssign.Div, null);
break;
case JJTAUG_MODULO:
ret = new AugAssign(null, AugAssign.Mod, null);
break;
case JJTAUG_AND:
ret = new AugAssign(null, AugAssign.BitAnd, null);
break;
case JJTAUG_OR:
ret = new AugAssign(null, AugAssign.BitOr, null);
break;
case JJTAUG_XOR:
ret = new AugAssign(null, AugAssign.BitXor, null);
break;
case JJTAUG_LSHIFT:
ret = new AugAssign(null, AugAssign.LShift, null);
break;
case JJTAUG_RSHIFT:
ret = new AugAssign(null, AugAssign.RShift, null);
break;
case JJTAUG_POWER:
ret = new AugAssign(null, AugAssign.Pow, null);
break;
case JJTAUG_FLOORDIVIDE:
ret = new AugAssign(null, AugAssign.FloorDiv, null);
break;
case JJTOR_2OP:
ret = new BinOp(null, BinOp.BitOr, null);
break;
case JJTXOR_2OP:
ret = new BinOp(null, BinOp.BitXor, null);
break;
case JJTAND_2OP:
ret = new BinOp(null, BinOp.BitAnd, null);
break;
case JJTLSHIFT_2OP:
ret = new BinOp(null, BinOp.LShift, null);
break;
case JJTRSHIFT_2OP:
ret = new BinOp(null, BinOp.RShift, null);
break;
case JJTADD_2OP:
ret = new BinOp(null, BinOp.Add, null);
break;
case JJTSUB_2OP:
ret = new BinOp(null, BinOp.Sub, null);
break;
case JJTMUL_2OP:
ret = new BinOp(null, BinOp.Mult, null);
break;
case JJTDOT_2OP:
ret = new BinOp(null, BinOp.Dot, null);
break;
case JJTDIV_2OP:
ret = new BinOp(null, BinOp.Div, null);
break;
case JJTMOD_2OP:
ret = new BinOp(null, BinOp.Mod, null);
break;
case JJTPOW_2OP:
ret = new BinOp(null, BinOp.Pow, null);
break;
case JJTFLOORDIV_2OP:
ret = new BinOp(null, BinOp.FloorDiv, null);
break;
case JJTPOS_1OP:
ret = new UnaryOp(UnaryOp.UAdd, null);
break;
case JJTNEG_1OP:
ret = new UnaryOp(UnaryOp.USub, null);
break;
case JJTINVERT_1OP:
ret = new UnaryOp(UnaryOp.Invert, null);
break;
case JJTNOT_1OP:
ret = new UnaryOp(UnaryOp.Not, null);
break;
case JJTIMPORT:
ret = new Import(null);
break;
case JJTDOT_OP:
ret = new Attribute(null, null, Attribute.Load);
break;
case JJTSTAR_EXPR:
ret = new Starred(null, stack.getGrammar().getGrammarActions().getStarExprScope());
break;
case JJTFILE_INPUT:
ret = new Module(null);
break;
case JJTEVAL_INPUT:
ret = new Expr(null);
break;
default:
ret = new IdentityNode(id);
break;
}
ret.setId(id);
lastOpened = ret;
return ret;
}
/**
* Subclasses must implement this method to deal with any node that's not properly handled in this base.
* @param n the node that should be closed
* @param arity the current number of nodes in the stack (found after the context was opened)
* @return a new node representing the node that's having it's context closed.
* @throws Exception
*/
@Override
public final SimpleNode closeNode(final SimpleNode n, final int arity) throws Exception {
exprType value;
suiteType orelseSuite;
stmtType[] body;
exprType iter;
exprType target;
if (DEBUG_TREE_BUILDER) {
System.out.println("\n\n\n---------------------------");
System.out.println("Closing node scope: " + n);
System.out.println("Arity: " + arity);
if (arity > 0) {
System.out.println("Nodes in scope: ");
for (int i = 0; i < arity; i++) {
System.out.println(stack.peekNode(i));
}
}
}
exprType[] exprs;
switch (n.getId()) {
case -1:
throw new ParseException("Illegal node found: " + n, n);
case JJTFILE_INPUT:
Module m = (Module) n;
m.body = makeStmts(arity);
return m;
case JJTFALSE:
case JJTTRUE:
case JJTNONE:
case JJTNAME:
case JJTNUM:
case JJTPASS_STMT:
case JJTBREAK_STMT:
case JJTCONTINUE_STMT:
case JJTSTRING:
case JJTUNICODE:
case JJTBINARY:
case JJTFSTRING:
case JJTBEGIN_DECORATOR:
case JJTCOMMA:
case JJTCOLON:
return n; //it's already the correct node (and it's value is already properly set)
case JJTSUITE:
stmtType[] stmts = new stmtType[arity];
for (int i = arity - 1; i >= 0; i--) {
SimpleNode yield_or_stmt = stack.popNode();
if (yield_or_stmt instanceof Yield) {
stmts[i] = new Expr((Yield) yield_or_stmt);
} else {
try {
stmts[i] = (stmtType) yield_or_stmt;
} catch (ClassCastException e) {
recoverFromClassCastException(yield_or_stmt, e);
stmts[i] = new Pass(); //recover from it with a valid node!
}
}
}
return new Suite(stmts);
case JJTFOR_STMT:
orelseSuite = null;
if (stack.nodeArity() == 5) {
orelseSuite = popSuiteAndSuiteType();
}
body = popSuite();
iter = (exprType) stack.popNode();
target = (exprType) stack.popNode();
ctx.setStore(target);
For forStmt = (For) n;
forStmt.target = target;
forStmt.iter = iter;
forStmt.body = body;
forStmt.orelse = orelseSuite;
return forStmt;
case JJTBEGIN_ELIF_STMT:
return new If(null, null, null);
case JJTIF_STMT:
return handleIfConstruct(n, arity);
case JJTEXEC_STMT:
exprType locals = arity >= 3 ? ((exprType) stack.popNode()) : null;
exprType globals = arity >= 2 ? ((exprType) stack.popNode()) : null;
value = (exprType) stack.popNode();
Exec exec = (Exec) n;
exec.body = value;
exec.locals = locals;
exec.globals = globals;
return exec;
case JJTDECORATORS:
ArrayList<SimpleNode> list2 = new ArrayList<SimpleNode>();
ArrayList<SimpleNode> listArgs = new ArrayList<SimpleNode>();
while (stack.nodeArity() > 0) {
SimpleNode node = stack.popNode();
while (!(node instanceof decoratorsType)) {
if (node instanceof comprehensionType) {
listArgs.add(node);
listArgs.add(stack.popNode()); //target
} else if (node instanceof ComprehensionCollection) {
listArgs.add(((ComprehensionCollection) node).getGenerators()[0]);
listArgs.add(stack.popNode()); //target
} else {
listArgs.add(node);
}
node = stack.popNode();
}
listArgs.add(node);//the decoratorsType
list2.add(0, makeDecorator(listArgs));
listArgs.clear();
}
return new Decorators(list2.toArray(new decoratorsType[0]), JJTDECORATORS);
case JJTSUBSCRIPTLIST:
sliceType[] dims = new sliceType[arity];
for (int i = arity - 1; i >= 0; i--) {
SimpleNode sliceNode = stack.popNode();
if (sliceNode instanceof sliceType) {
dims[i] = (sliceType) sliceNode;
} else if (sliceNode instanceof IdentityNode) {
//this should be ignored...
//this happens when parsing something like a[1,], whereas a[1,2] would not have this.
} else {
throw new RuntimeException("Expected a sliceType or an IdentityNode. Received :"
+ sliceNode.getClass());
}
}
return new ExtSlice(dims);
case JJTAUG_PLUS:
case JJTAUG_MINUS:
case JJTAUG_MULTIPLY:
case JJTAUG_DOT:
case JJTAUG_DIVIDE:
case JJTAUG_MODULO:
case JJTAUG_AND:
case JJTAUG_OR:
case JJTAUG_XOR:
case JJTAUG_LSHIFT:
case JJTAUG_RSHIFT:
case JJTAUG_POWER:
case JJTAUG_FLOORDIVIDE:
AugAssign augAssign = (AugAssign) n;
exprType value1 = (exprType) stack.popNode();
exprType target1 = (exprType) stack.popNode();
ctx.setAugStore(target1);
augAssign.target = target1;
augAssign.value = value1;
return n;
case JJTOR_BOOLEAN:
return new BoolOp(BoolOp.Or, makeExprs());
case JJTAND_BOOLEAN:
return new BoolOp(BoolOp.And, makeExprs());
case JJTCOMPARISION:
if (arity <= 2) {
throw new ParseException("Internal error: To make a compare, at least 3 nodes are needed.", n);
}
int l = arity / 2;
exprType[] comparators = new exprType[l];
int[] ops = new int[l];
for (int i = l - 1; i >= 0; i--) {
comparators[i] = (exprType) stack.popNode();
SimpleNode op = stack.popNode();
switch (op.getId()) {
case JJTLESS_CMP:
ops[i] = Compare.Lt;
break;
case JJTGREATER_CMP:
ops[i] = Compare.Gt;
break;
case JJTEQUAL_CMP:
ops[i] = Compare.Eq;
break;
case JJTGREATER_EQUAL_CMP:
ops[i] = Compare.GtE;
break;
case JJTLESS_EQUAL_CMP:
ops[i] = Compare.LtE;
break;
case JJTNOTEQUAL_CMP:
ops[i] = Compare.NotEq;
break;
case JJTIN_CMP:
ops[i] = Compare.In;
break;
case JJTNOT_IN_CMP:
ops[i] = Compare.NotIn;
break;
case JJTIS_NOT_CMP:
ops[i] = Compare.IsNot;
break;
case JJTIS_CMP:
ops[i] = Compare.Is;
break;
default:
throw new RuntimeException("Unknown cmp op:" + op.getId());
}
}
return new Compare(((exprType) stack.popNode()), ops, comparators);
case JJTLESS_CMP:
case JJTGREATER_CMP:
case JJTEQUAL_CMP:
case JJTGREATER_EQUAL_CMP:
case JJTLESS_EQUAL_CMP:
case JJTNOTEQUAL_CMP:
case JJTIN_CMP:
case JJTNOT_IN_CMP:
case JJTIS_NOT_CMP:
case JJTIS_CMP:
return n;
case JJTOR_2OP:
case JJTXOR_2OP:
case JJTAND_2OP:
case JJTLSHIFT_2OP:
case JJTRSHIFT_2OP:
case JJTADD_2OP:
case JJTSUB_2OP:
case JJTMUL_2OP:
case JJTDOT_2OP:
case JJTDIV_2OP:
case JJTMOD_2OP:
case JJTPOW_2OP:
case JJTFLOORDIV_2OP:
BinOp op = (BinOp) n;
exprType right = (exprType) stack.popNode();
exprType left = (exprType) stack.popNode();
op.right = right;
op.left = left;
return n;
case JJTPOS_1OP:
case JJTNEG_1OP:
case JJTINVERT_1OP:
case JJTNOT_1OP:
((UnaryOp) n).operand = ((exprType) stack.popNode());
return n;
case JJTIMPORT:
((Import) n).names = makeAliases(arity);
return n;
case JJTDOT_OP:
NameTok attr = makeName(NameTok.Attrib);
value = (exprType) stack.popNode();
Attribute attribute = (Attribute) n;
attribute.value = value;
attribute.attr = attr;
return n;
case JJTBEGIN_DEL_STMT:
return new Delete(null);
case JJTDEL_STMT:
exprs = makeExprs(arity - 1);
ctx.setDelete(exprs);
Delete d = (Delete) stack.popNode();
d.targets = exprs;
return d;
case JJTDOTTED_NAME:
Name name = (Name) n;
FastStringBuffer sb = tempBuffer.clear();
for (int i = 0; i < arity; i++) {
if (i > 0) {
sb.insert(0, '.');
}
Name name0 = (Name) stack.popNode();
sb.insert(0, name0.id);
addSpecials(name0, name);
//we have to set that, because if we later add things to the previous Name, we will now want it to be added to
//the new name (comments will only appear later and may be added to the previous name -- so, we replace the previous
//name specials list).
name0.specialsBefore = name.getSpecialsBefore();
name0.specialsAfter = name.getSpecialsAfter();
}
name.id = sb.toString();
return name;
case JJTDOTTED_AS_NAME:
NameTok asname = null;
if (arity > 1) {
asname = makeName(NameTok.ImportName);
}
return new aliasType(makeName(NameTok.ImportName), asname);
case JJTIMPORT_AS_NAME:
asname = null;
if (arity > 1) {
asname = makeName(NameTok.ImportName);
}
return new aliasType(makeName(NameTok.ImportName), asname);
case JJTSTRJOIN:
Str str2 = (Str) stack.popNode();
Object o = stack.popNode();
StrJoin ret;
if (o instanceof Str) {
Str str1 = (Str) o;
ret = new StrJoin(new exprType[] { str1, str2 });
} else {
StrJoin strJ = (StrJoin) o;
exprType[] newStrs = new exprType[strJ.strs.length + 1];
System.arraycopy(strJ.strs, 0, newStrs, 0, strJ.strs.length);
newStrs[strJ.strs.length] = str2;
strJ.strs = newStrs;
ret = strJ;
}
ret.beginLine = ret.strs[0].beginLine;
ret.beginColumn = ret.strs[0].beginColumn;
return ret;
}
//if we found a node not expected in the base, let's give subclasses an opportunity for dealing with it.
return onCloseNode(n, arity);
}
/**
* Handles a found if construct.
*
* @param n the node that opened the if scope.
* @param arity the current number of nodes in the stack.
* @return the If node that should close this context.
*/
private final SimpleNode handleIfConstruct(final SimpleNode n, int arity) {
stmtType[] body;
exprType test;
suiteType orelse = null;
if (arity % 3 == 1) {
arity -= 2;
orelse = this.popSuiteAndSuiteType();
}
//make the suite
Suite suite = (Suite) stack.popNode();
arity--;
body = suite.body;
test = (exprType) stack.popNode();
arity--;
//make the if
If last;
if (arity == 0) {
//last If found
last = (If) n;
} else {
last = (If) stack.popNode();
arity--;
}
last.test = test;
last.body = body;
last.orelse = orelse;
addSpecialsAndClearOriginal(suite, last);
while (arity > 0) {
suite = (Suite) stack.popNode();
arity--;
body = suite.body;
test = (exprType) stack.popNode();
arity--;
suiteType newOrElse = new Suite(new stmtType[] { last });
if (arity == 0) {
//last If found
last = (If) n;
} else {
last = (If) stack.popNode();
arity--;
}
last.test = test;
last.body = body;
last.orelse = newOrElse;
addSpecialsAndClearOriginal(suite, last);
}
return last;
}
protected final SimpleNode makeImportFrom25Onwards(int arity) throws ParseException {
ArrayList<aliasType> aliastL = new ArrayList<aliasType>();
while (arity > 0 && stack.peekNode() instanceof aliasType) {
aliastL.add(0, (aliasType) stack.popNode());
arity--;
}
NameTok nT;
if (arity > 0) {
nT = makeName(NameTok.ImportModule);
} else {
nT = new NameTok("", NameTok.ImportModule);
Object temporaryTok = this.stack.getGrammar().temporaryToken;
ISpecialStr temporaryToken;
if (temporaryTok instanceof ISpecialStr) {
temporaryToken = (ISpecialStr) temporaryTok;
} else {
//must be a Token
temporaryToken = ((Token) temporaryTok).asSpecialStr();
}
if (temporaryToken.toString().equals("from")) {
nT.beginColumn = temporaryToken.getBeginCol();
nT.beginLine = temporaryToken.getBeginLine();
} else {
Log.log("Expected to find 'from' token as the current temporary token (begin col/line can be wrong)!");
}
}
return new ImportFrom(nT, aliastL.toArray(new aliasType[0]), 0);
}
protected final ComprehensionCollection makeCompFor(int arity) throws Exception {
ComprehensionCollection col = null;
if (stack.peekNode() instanceof ComprehensionCollection) {
col = (ComprehensionCollection) stack.popNode();
arity--;
} else {
col = new ComprehensionCollection();
}
ArrayList<exprType> ifs = new ArrayList<exprType>();
for (int i = arity - 3; i >= 0; i--) {
SimpleNode ifsNode = stack.popNode();
ifs.add((exprType) ifsNode);
}
exprType iter = (exprType) stack.popNode();
exprType target = (exprType) stack.popNode();
ctx.setStore(target);
col.added.add(new Comprehension(target, iter, ifs.toArray(new exprType[0])));
return col;
}
protected final SimpleNode makeDictionaryOrSet(int arity) {
if (arity == 0) {
return new Dict(new exprType[0], new exprType[0]);
}
SimpleNode dictNode0 = stack.popNode();
if (dictNode0 instanceof Set) {
Set set = (Set) dictNode0;
exprType[] elts = new exprType[arity - 1]; //-1 because the set was already taken from there
for (int i = arity - 2; i >= 0; i--) { //same thing here
elts[i] = (exprType) stack.popNode();
}
set.elts = elts;
return set;
}
if (dictNode0 instanceof ComprehensionCollection) {
if (arity == 2) {
ComprehensionCollection comp = (ComprehensionCollection) dictNode0;
return new SetComp((exprType) stack.popNode(), comp.getGenerators());
} else if (arity == 3) {
SimpleNode dictNode1 = stack.popNode(); //we must inverse things here...
ComprehensionCollection comp = (ComprehensionCollection) dictNode0;
return new DictComp((exprType) stack.popNode(), (exprType) dictNode1, comp.getGenerators());
}
}
boolean isDictComplete = arity % 2 == 0;
int l = arity / 2;
exprType[] keys;
if (isDictComplete) {
keys = new exprType[l];
} else {
keys = new exprType[l + 1]; //we have 1 additional entry in the keys (parse error actually, but let's recover at this point!)
}
boolean node0Used = false;
exprType[] vals = new exprType[l];
for (int i = l - 1; i >= 0; i--) {
if (!node0Used) {
node0Used = true;
vals[i] = (exprType) dictNode0;
keys[i] = (exprType) stack.popNode();
} else {
vals[i] = (exprType) stack.popNode();
keys[i] = (exprType) stack.popNode();
}
}
if (!isDictComplete) {
if (node0Used) {
keys[keys.length - 1] = (exprType) stack.popNode();
} else {
keys[keys.length - 1] = (exprType) dictNode0;
}
}
return new Dict(keys, vals);
}
protected final SimpleNode makeWithItem(int arity) throws Exception {
exprType expr = (exprType) stack.popNode(); //expr
arity--;
exprType asExpr = null;
if (arity > 0) {
asExpr = expr;
expr = (exprType) stack.popNode();
ctx.setStore(asExpr);
}
return new WithItem(expr, asExpr);
}
protected final SimpleNode makeWithStmt(int arity) {
Suite suite = (Suite) stack.popNode();
arity--;
WithItem[] items = new WithItem[arity];
while (arity > 0) {
items[arity - 1] = (WithItem) stack.popNode();
arity--;
}
suiteType s = new Suite(suite.body);
addSpecialsAndClearOriginal(suite, s);
return new With(items, s, stack.getGrammar().getInsideAsync());
}
}