package org.yinwang.pysonar.ast; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.yinwang.pysonar.Binding; import org.yinwang.pysonar.Indexer; import org.yinwang.pysonar.Scope; import org.yinwang.pysonar.types.FunType; import org.yinwang.pysonar.types.Type; import java.util.ArrayList; import java.util.List; public class FunctionDef extends Node { public Name name; public List<Node> args; public List<Node> defaults; @Nullable public List<Type> defaultTypes; public Name vararg; // *args public Name kwarg; // **kwarg public Node body; private List<Node> decoratorList; public boolean called = false; public FunctionDef(Name name, List<Node> args, Block body, List<Node> defaults, Name vararg, Name kwarg, int start, int end) { super(start, end); this.name = name; this.args = args; this.body = body; this.defaults = defaults; this.vararg = vararg; this.kwarg = kwarg; addChildren(name); addChildren(args); addChildren(defaults); addChildren(vararg, kwarg, this.body); } public void setDecoratorList(List<Node> decoratorList) { this.decoratorList = decoratorList; addChildren(decoratorList); } public List<Node> getDecoratorList() { if (decoratorList == null) { decoratorList = new ArrayList<Node>(); } return decoratorList; } @Override public boolean isFunctionDef() { return true; } @Override public boolean bindsName() { return true; } /** * Returns the name of the function for indexing/qname purposes. * Lambdas will return a generated name. */ @Nullable protected String getBindingName(Scope s) { return name.getId(); } public List<Node> getArgs() { return args; } public List<Node> getDefaults() { return defaults; } @Nullable public List<Type> getDefaultTypes() { return defaultTypes; } public Node getBody() { return body; } public Name getName() { return name; } /** * @return the vararg */ public Name getVararg() { return vararg; } /** * @param vararg the vararg to set */ public void setVararg(Name vararg) { this.vararg = vararg; } /** * @return the kwarg */ public Name getKwarg() { return kwarg; } /** * @param kwarg the kwarg to set */ public void setKwarg(Name kwarg) { this.kwarg = kwarg; } /** * A function's environment is not necessarily the enclosing scope. A * method's environment is the scope of the most recent scope that is not a * class. * * Be sure to distinguish the environment and the symbol table. The * function's table is only used for the function's attributes like * "im_class". Its parent should be the table of the enclosing scope, and * its path should be derived from that scope too for locating the names * "lexically". */ @NotNull @Override public Type resolve(@NotNull Scope outer, int tag) { resolveList(decoratorList, outer, tag); //XXX: not handling functional transformations yet FunType cl = new FunType(this, outer.getForwarding()); cl.getTable().setParent(outer); cl.getTable().setPath(outer.extendPath(getName().getId())); cl.setDefaultTypes(resolveAndConstructList(defaults, outer, tag)); Indexer.idx.addUncalled(cl); Binding.Kind funkind; if (outer.getScopeType() == Scope.ScopeType.CLASS) { if ("__init__".equals(name.getId())) { funkind = Binding.Kind.CONSTRUCTOR; } else { funkind = Binding.Kind.METHOD; } } else { funkind = Binding.Kind.FUNCTION; } Type outType = outer.getType(); if (outType != null && outType.isClassType()) { cl.setCls(outType.asClassType()); } NameBinder.bind(outer, name, cl, funkind, tag); return Indexer.idx.builtins.Cont; } @NotNull @Override public String toString() { return "<Function:" + start + ":" + name + ">"; } @Override public void visit(@NotNull NodeVisitor v) { if (v.visit(this)) { visitNode(name, v); visitNodeList(args, v); visitNodeList(defaults, v); visitNode(kwarg, v); visitNode(vararg, v); visitNode(body, v); } } @Override public boolean equals(Object obj) { if (obj instanceof FunctionDef) { FunctionDef fo = (FunctionDef)obj; return (fo.getFile().equals(getFile()) && fo.start == start); } else { return false; } } }