package org.python.pydev.parser.jython.ast.factory; import java.util.ArrayList; import java.util.List; import org.python.pydev.core.FullRepIterable; import org.python.pydev.core.docutils.StringUtils; import org.python.pydev.core.log.Log; import org.python.pydev.core.structure.FastStack; import org.python.pydev.parser.jython.SimpleNode; import org.python.pydev.parser.jython.ast.Assign; import org.python.pydev.parser.jython.ast.Attribute; import org.python.pydev.parser.jython.ast.Call; import org.python.pydev.parser.jython.ast.ClassDef; import org.python.pydev.parser.jython.ast.Expr; import org.python.pydev.parser.jython.ast.FunctionDef; import org.python.pydev.parser.jython.ast.Name; import org.python.pydev.parser.jython.ast.NameTok; import org.python.pydev.parser.jython.ast.Pass; import org.python.pydev.parser.jython.ast.Return; import org.python.pydev.parser.jython.ast.Str; import org.python.pydev.parser.jython.ast.VisitorBase; import org.python.pydev.parser.jython.ast.argumentsType; import org.python.pydev.parser.jython.ast.decoratorsType; import org.python.pydev.parser.jython.ast.exprType; import org.python.pydev.parser.jython.ast.keywordType; import org.python.pydev.parser.jython.ast.stmtType; import org.python.pydev.parser.visitors.NodeUtils; public class PyAstFactory { NodeHelper nodeHelper; public PyAstFactory(AdapterPrefs adapterPrefs) { nodeHelper = new NodeHelper(adapterPrefs); } public FunctionDef createFunctionDef(String name) { FunctionDef functionDef = new FunctionDef(new NameTok(name, NameTok.FunctionName), null, null, null, null); return functionDef; } public ClassDef createClassDef(String name) { exprType[] bases = null; stmtType[] body = null; decoratorsType[] decs = null; keywordType[] keywords = null; exprType starargs = null; exprType kwargs = null; ClassDef def = new ClassDef(new NameTok(name, NameTok.ClassName), bases, body, decs, keywords, starargs, kwargs); return def; } public void setBaseClasses(ClassDef classDef, String... baseClasses) { ArrayList<exprType> bases = new ArrayList<exprType>(); for (String s : baseClasses) { Name n = createName(s); bases.add(n); } classDef.bases = bases.toArray(new exprType[bases.size()]); } public Name createName(String s) { Name name = new Name(s, Name.Load, false); return name; } public FunctionDef createSetterFunctionDef(String accessorName, String attributeName) { NameTok functionName = new NameTok(accessorName, NameTok.FunctionName); argumentsType args = createArguments(true, "value"); stmtType[] body = createSetterBody(attributeName); return new FunctionDef(functionName, args, body, null, null); } public argumentsType createArguments(boolean addSelf, String... simpleParams) { List<exprType> params = new ArrayList<exprType>(); if (addSelf) { params.add(new Name("self", Name.Param, true)); } for (String s : simpleParams) { params.add(new Name(s, Name.Param, false)); } return new argumentsType(params.toArray(new exprType[params.size()]), null, null, null, null, null, null, null, null, null); } private stmtType[] createSetterBody(String attributeName) { Name self = new Name("self", Name.Load, true); NameTok name = new NameTok(nodeHelper.getPrivateAttr(attributeName), NameTok.Attrib); Attribute attribute = new Attribute(self, name, Attribute.Store); Name value = new Name("value", Name.Load, false); Assign assign = new Assign(new exprType[] { attribute }, value); return new stmtType[] { assign }; } public Call createCall(String call, String... params) { List<exprType> lst = createParamsList(params); return createCall(call, lst, null, null, null); } public List<exprType> createParamsList(String... params) { ArrayList<exprType> lst = new ArrayList<exprType>(); for (String p : params) { lst.add(new Name(p, Name.Param, false)); } return lst; } public Call createCall(String call, List<exprType> params, keywordType[] keywords, exprType starargs, exprType kwargs) { exprType[] array = params != null ? params.toArray(new Name[params.size()]) : new exprType[0]; if (call.indexOf(".") != -1) { return new Call(createAttribute(call), array, keywords, starargs, kwargs); } return new Call(new Name(call, Name.Load, false), array, keywords, starargs, kwargs); } public Attribute createAttribute(String attribute) { List<String> splitted = StringUtils.split(attribute, '.'); if (splitted.size() <= 1) { throw new RuntimeException("Cannot create attribute without dot access."); } if (splitted.size() == 2) { return new Attribute(new Name(splitted.get(0), Name.Load, false), new NameTok(splitted.get(1), NameTok.Attrib), Attribute.Load); } //>2 return new Attribute(createAttribute(FullRepIterable.getWithoutLastPart(attribute)), new NameTok( splitted.get(splitted.size() - 1), NameTok.Attrib), Attribute.Load); } public Assign createAssign(exprType... targetsAndVal) { exprType[] targets = new exprType[targetsAndVal.length - 1]; System.arraycopy(targetsAndVal, 0, targets, 0, targets.length); exprType value = targetsAndVal[targetsAndVal.length - 1]; return new Assign(targets, value); } public void setBody(FunctionDef functionDef, Object... body) { functionDef.body = createStmtArray(body); } public void setBody(ClassDef def, Object... body) { def.body = createStmtArray(body); } private stmtType[] createStmtArray(Object... body) { ArrayList<stmtType> newBody = new ArrayList<stmtType>(); for (Object o : body) { if (o instanceof exprType) { newBody.add(new Expr((exprType) o)); } else if (o instanceof stmtType) { newBody.add((stmtType) o); } else { throw new RuntimeException("Unhandled: " + o); } } stmtType[] bodyArray = newBody.toArray(new stmtType[newBody.size()]); return bodyArray; } public Str createString(String string) { return new Str(string, Str.TripleSingle, false, false, false); } public Pass createPass() { return new Pass(); } private static final RuntimeException stopVisitingException = new RuntimeException("stop visiting"); /** * @param functionDef the function for the override body * @param currentClassName */ public stmtType createOverrideBody(FunctionDef functionDef, String parentClassName, String currentClassName) { //create a copy because we do not want to retain the original line/col and we may change the originals here. final boolean[] addReturn = new boolean[] { false }; VisitorBase visitor = new VisitorBase() { public Object visitClassDef(ClassDef node) throws Exception { return null; } public Object visitFunctionDef(FunctionDef node) throws Exception { return null; //don't visit internal scopes. } @Override protected Object unhandled_node(SimpleNode node) throws Exception { if (node instanceof Return) { addReturn[0] = true; throw stopVisitingException; } return null; } @Override public void traverse(SimpleNode node) throws Exception { node.traverse(this); } }; try { visitor.traverse(functionDef); } catch (Exception e) { if (e != stopVisitingException) { Log.log(e); } } boolean isClassMethod = false; if (functionDef.decs != null) { for (decoratorsType dec : functionDef.decs) { String rep = NodeUtils.getRepresentationString(dec.func); if ("classmethod".equals(rep)) { isClassMethod = true; break; } } } argumentsType args = functionDef.args.createCopy(false); List<exprType> params = new ArrayList<exprType>(); for (exprType expr : args.args) { //note: self should be there already! params.add((exprType) expr); } exprType starargs = args.vararg != null ? new Name(((NameTok) args.vararg).id, Name.Load, false) : null; exprType kwargs = args.kwarg != null ? new Name(((NameTok) args.kwarg).id, Name.Load, false) : null; List<keywordType> keywords = new ArrayList<keywordType>(); if (args.defaults != null) { int diff = args.args.length - args.defaults.length; FastStack<Integer> removePositions = new FastStack<Integer>(args.defaults.length); for (int i = 0; i < args.defaults.length; i++) { exprType expr = args.defaults[i]; if (expr != null) { exprType name = params.get(i + diff); if (name instanceof Name) { removePositions.push(i + diff); //it's removed backwards, that's why it's a stack keywords.add(new keywordType(new NameTok(((Name) name).id, NameTok.KeywordName), name, false)); } else { Log.log("Expected: " + name + " to be a Name instance."); } } } while (removePositions.size() > 0) { Integer pop = removePositions.pop(); params.remove((int) pop); } } Call call; if (isClassMethod && params.size() > 0) { //We need to use the super() construct //Something as: //Expr[value= // Call[func= // Attribute[value= // Call[func=Name[id=super, ctx=Load, reserved=false], args=[Name[id=Current, ctx=Load, reserved=false], Name[id=cls, ctx=Load, reserved=false]], keywords=[], starargs=null, kwargs=null], // attr=NameTok[id=test, ctx=Attrib], ctx=Load], // args=[], keywords=[], starargs=null, kwargs=null] //] exprType firstParam = params.remove(0); Call innerCall = createCall("super", currentClassName, NodeUtils.getRepresentationString(firstParam)); Attribute attr = new Attribute(innerCall, new NameTok(NodeUtils.getRepresentationString(functionDef), NameTok.Attrib), Attribute.Load); call = new Call(attr, params.toArray(new Name[params.size()]), keywords.toArray(new keywordType[keywords .size()]), starargs, kwargs); } else { call = createCall(parentClassName + "." + NodeUtils.getRepresentationString(functionDef), params, keywords.toArray(new keywordType[keywords.size()]), starargs, kwargs); } if (addReturn[0]) { return new Return(call); } else { return new Expr(call); } } }