package org.yinwang.pysonar.ast;
import org.jetbrains.annotations.NotNull;
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;
public class Subscript extends Node {
@NotNull
public Node value;
@NotNull
public Node slice; // an NIndex or NSlice
public Subscript(@NotNull Node value, @NotNull Node slice, int start, int end) {
super(start, end);
this.value = value;
this.slice = slice;
addChildren(value, slice);
}
@NotNull
@Override
public Type resolve(Scope s, int tag) {
Type vt = resolveExpr(value, s, tag);
Type st = resolveExpr(slice, s, tag);
if (vt.isUnionType()) {
Type retType = Indexer.idx.builtins.unknown;
for (Type t: vt.asUnionType().getTypes()) {
retType = UnionType.union(retType, getSubscript(t, st, s, tag));
}
return retType;
} else {
return getSubscript(vt, st, s, tag);
}
}
@NotNull
private Type getSubscript(@NotNull Type vt, @NotNull Type st, Scope s, int tag) {
if (vt.isUnknownType()) {
return Indexer.idx.builtins.unknown;
} else if (vt.isListType()) {
return getListSubscript(vt, st, s, tag);
} else if (vt.isTupleType()) {
return getListSubscript(vt.asTupleType().toListType(), st, s, tag);
} else if (vt.isDictType()) {
ListType nl = new ListType(vt.asDictType().valueType);
return getListSubscript(nl, st, s, tag);
} else if (vt.isStrType()) {
if (st.isListType() || st.isNumType()) {
return vt;
} else {
addWarning("Possible KeyError (wrong type for subscript)");
return Indexer.idx.builtins.unknown;
}
} else {
return Indexer.idx.builtins.unknown;
}
}
@NotNull
private Type getListSubscript(@NotNull Type vt, @NotNull Type st, Scope s, int tag) {
if (vt.isListType()) {
if (st.isListType()) {
return vt;
} else if (st.isNumType()) {
return vt.asListType().getElementType();
} else {
Type sliceFunc = vt.getTable().lookupAttrType("__getslice__");
if (sliceFunc == null) {
addError("The type can't be sliced: " + vt);
return Indexer.idx.builtins.unknown;
} else if (sliceFunc.isFuncType()) {
return Call.apply(sliceFunc.asFuncType(), null, null, null, null, this, tag);
} else {
addError("The type's __getslice__ method is not a function: " + sliceFunc);
return Indexer.idx.builtins.unknown;
}
}
} else {
return Indexer.idx.builtins.unknown;
}
}
@NotNull
@Override
public String toString() {
return "<Subscript:" + value + ":" + slice + ">";
}
@Override
public void visit(@NotNull NodeVisitor v) {
if (v.visit(this)) {
visitNode(value, v);
visitNode(slice, v);
}
}
}