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.ListType;
import org.yinwang.pysonar.types.Type;
import org.yinwang.pysonar.types.UnionType;
import java.util.List;
/**
* Handles binding names to scopes, including destructuring assignment.
*/
public class NameBinder {
public static void bind(@NotNull Scope s, Node target, @NotNull Type rvalue, Binding.Kind kind, int tag) {
if (target instanceof Name) {
bindName(s, (Name)target, rvalue, kind, tag);
} else if (target instanceof Tuple) {
bind(s, ((Tuple)target).elts, rvalue, kind, tag);
} else if (target instanceof NList) {
bind(s, ((NList)target).elts, rvalue, kind, tag);
} else if (target instanceof Attribute) {
((Attribute)target).setAttr(s, rvalue, tag);
} else if (target instanceof Subscript) {
Subscript sub = (Subscript)target;
Type valueType = Node.resolveExpr(sub.value, s, tag);
if (sub.slice != null) Node.resolveExpr(sub.slice, s, tag);
if (valueType instanceof ListType) {
ListType t = (ListType)valueType;
t.setElementType(UnionType.union(t.getElementType(), rvalue));
}
} else if (target != null) {
Indexer.idx.putProblem(target, "invalid location for assignment");
}
}
/**
* Without specifying a kind, bind determines the kind according to the type
* of the scope.
*/
public static void bind(@NotNull Scope s, Node target, @NotNull Type rvalue, int tag) {
Binding.Kind kind;
if (s.getScopeType() == Scope.ScopeType.FUNCTION) {
kind = Binding.Kind.VARIABLE;
} else {
kind = Binding.Kind.SCOPE;
}
bind(s, target, rvalue, kind, tag);
}
public static void bind(@NotNull Scope s, @NotNull List<Node> xs, @NotNull Type rvalue, Binding.Kind kind, int tag) {
if (rvalue.isTupleType()) {
List<Type> vs = rvalue.asTupleType().getElementTypes();
if (xs.size() != vs.size()) {
reportUnpackMismatch(xs, vs.size());
} else {
for (int i = 0; i < xs.size(); i++) {
bind(s, xs.get(i), vs.get(i), kind, tag);
}
}
} else if (rvalue.isListType()) {
bind(s, xs, rvalue.asListType().toTupleType(xs.size()), kind, tag);
} else if (rvalue.isDictType()) {
bind(s, xs, rvalue.asDictType().toTupleType(xs.size()), kind, tag);
} else if (rvalue.isUnknownType()) {
for (Node x : xs) {
bind(s, x, Indexer.idx.builtins.unknown, kind, tag);
}
} else {
Indexer.idx.putProblem(xs.get(0).getFile(),
xs.get(0).start,
xs.get(xs.size() - 1).end,
"unpacking non-iterable: " + rvalue);
}
}
@Nullable
public static Binding bindName(@NotNull Scope s, @NotNull Name name, @NotNull Type rvalue,
Binding.Kind kind, int tag) {
Binding b;
if (s.isGlobalName(name.getId())) {
b = s.getGlobalTable().put(name.getId(), name, rvalue, kind, tag);
Indexer.idx.putRef(name, b);
} else {
b = s.put(name.getId(), name, rvalue, kind, tag);
}
Type nameType = b.getType();
if (nameType.isUnknownType()) {
nameType.getTable().setPath(b.getQname());
}
return b;
}
public static void bindIter(@NotNull Scope s, Node target, @NotNull Node iter, Binding.Kind kind, int tag) {
Type iterType = Node.resolveExpr(iter, s, tag);
if (iterType.isListType()) {
bind(s, target, iterType.asListType().getElementType(), kind, tag);
} else if (iterType.isTupleType()) {
bind(s, target, iterType.asTupleType().toListType().getElementType(), kind, tag);
} else {
Binding ent = iterType.getTable().lookupAttr("__iter__");
if (ent == null || !ent.getType().isFuncType()) {
if (!iterType.isUnknownType()) {
iter.addWarning("not an iterable type: " + iterType);
}
bind(s, target, Indexer.idx.builtins.unknown, kind, tag);
} else {
bind(s, target, ent.getType().asFuncType().getReturnType(), kind, tag);
}
}
}
private static void reportUnpackMismatch(@NotNull List<Node> xs, int vsize) {
int xsize = xs.size();
int beg = xs.get(0).start;
int end = xs.get(xs.size() - 1).end;
int diff = xsize - vsize;
String msg;
if (diff > 0) {
msg = "ValueError: need more than " + vsize + " values to unpack";
} else {
msg = "ValueError: too many values to unpack";
}
Indexer.idx.putProblem(xs.get(0).getFile(), beg, end, msg);
}
}