package org.yinwang.pysonar.ast;
import org.jetbrains.annotations.NotNull;
import org.yinwang.pysonar.Binding;
import org.yinwang.pysonar.Builtins;
import org.yinwang.pysonar.Indexer;
import org.yinwang.pysonar.Scope;
import org.yinwang.pysonar.types.ClassType;
import org.yinwang.pysonar.types.DictType;
import org.yinwang.pysonar.types.TupleType;
import org.yinwang.pysonar.types.Type;
import java.util.ArrayList;
import java.util.List;
public class ClassDef extends Node {
@NotNull
public Name name;
public List<Node> bases;
public Block body;
public ClassDef(@NotNull Name name, List<Node> bases, Block body, int start, int end) {
super(start, end);
this.name = name;
this.bases = bases;
this.body = body;
addChildren(name, this.body);
addChildren(bases);
}
@Override
public boolean isClassDef() {
return true;
}
@NotNull
public Name getName() {
return name;
}
@Override
public boolean bindsName() {
return true;
}
@NotNull
@Override
public Type resolve(@NotNull Scope s, int tag) {
ClassType classType = new ClassType(getName().getId(), s);
List<Type> baseTypes = new ArrayList<>();
for (Node base : bases) {
Type baseType = resolveExpr(base, s, tag);
if (baseType.isClassType()) {
classType.addSuper(baseType);
} else if (baseType.isUnionType()) {
for (Type b : baseType.asUnionType().getTypes()) {
classType.addSuper(b);
break;
}
} else {
Indexer.idx.putProblem(base, base + " is not a class");
}
baseTypes.add(baseType);
}
// XXX: Not sure if we should add "bases", "name" and "dict" here. They
// must be added _somewhere_ but I'm just not sure if it should be HERE.
Builtins builtins = Indexer.idx.builtins;
addSpecialAttribute(classType.getTable(), "__bases__", new TupleType(baseTypes));
addSpecialAttribute(classType.getTable(), "__name__", builtins.BaseStr);
addSpecialAttribute(classType.getTable(), "__dict__", new DictType(builtins.BaseStr, Indexer.idx.builtins.unknown));
addSpecialAttribute(classType.getTable(), "__module__", builtins.BaseStr);
addSpecialAttribute(classType.getTable(), "__doc__", builtins.BaseStr);
// Bind ClassType to name here before resolving the body because the
// methods need this type as self.
NameBinder.bind(s, name, classType, Binding.Kind.CLASS, tag);
resolveExpr(body, classType.getTable(), tag);
return Indexer.idx.builtins.Cont;
}
private void addSpecialAttribute(@NotNull Scope s, String name, Type proptype) {
Binding b = s.update(name, Builtins.newTutUrl("classes.html"), proptype, Binding.Kind.ATTRIBUTE);
b.markSynthetic();
b.markStatic();
b.markReadOnly();
}
@NotNull
@Override
public String toString() {
return "<ClassDef:" + name.getId() + ":" + start + ">";
}
@Override
public void visit(@NotNull NodeVisitor v) {
if (v.visit(this)) {
visitNode(name, v);
visitNodeList(bases, v);
visitNode(body, v);
}
}
}