org.python.indexer.ast.NameBinder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython-slim Show documentation
Show all versions of jython-slim Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
/**
* Copyright 2009, Google Inc. All rights reserved.
* Licensed to PSF under a Contributor Agreement.
*/
package org.python.indexer.ast;
import org.python.indexer.Indexer;
import org.python.indexer.NBinding;
import org.python.indexer.types.NListType;
import org.python.indexer.types.NTupleType;
import org.python.indexer.types.NType;
import org.python.indexer.types.NUnknownType;
import org.python.indexer.Scope;
import java.util.List;
import static org.python.indexer.NBinding.Kind.ATTRIBUTE;
import static org.python.indexer.NBinding.Kind.CLASS;
import static org.python.indexer.NBinding.Kind.CONSTRUCTOR;
import static org.python.indexer.NBinding.Kind.FUNCTION;
import static org.python.indexer.NBinding.Kind.METHOD;
import static org.python.indexer.NBinding.Kind.MODULE;
import static org.python.indexer.NBinding.Kind.PARAMETER;
import static org.python.indexer.NBinding.Kind.SCOPE;
import static org.python.indexer.NBinding.Kind.VARIABLE;
/**
* Handles binding names to scopes, including destructuring assignment.
*/
public class NameBinder {
static private final NameBinder DEFAULT_BINDER = new NameBinder();
static private final NameBinder ATTRIBUTE_BINDER = new NameBinder(ATTRIBUTE);
static private final NameBinder CLASS_BINDER = new NameBinder(CLASS);
static private final NameBinder CONSTRUCTOR_BINDER = new NameBinder(CONSTRUCTOR);
static private final NameBinder FUNCTION_BINDER = new NameBinder(FUNCTION);
static private final NameBinder METHOD_BINDER = new NameBinder(METHOD);
static private final NameBinder MODULE_BINDER = new NameBinder(MODULE);
static private final NameBinder PARAMETER_BINDER = new NameBinder(PARAMETER);
static private final NameBinder VARIABLE_BINDER = new NameBinder(VARIABLE);
private NBinding.Kind kind;
/**
* Factory method for creating instances.
*/
public static NameBinder make() {
return DEFAULT_BINDER;
}
/**
* Factory method for creating instances.
* @return a {@code NameBinder} that will create bindings of {@code kind},
* overriding the default choices.
*/
public static NameBinder make(NBinding.Kind kind) {
switch (kind) {
case ATTRIBUTE:
return ATTRIBUTE_BINDER;
case CLASS:
return CLASS_BINDER;
case CONSTRUCTOR:
return CONSTRUCTOR_BINDER;
case FUNCTION:
return FUNCTION_BINDER;
case METHOD:
return METHOD_BINDER;
case MODULE:
return MODULE_BINDER;
case PARAMETER:
return PARAMETER_BINDER;
case VARIABLE:
return VARIABLE_BINDER;
default:
return DEFAULT_BINDER;
}
}
private NameBinder() {
}
private NameBinder(NBinding.Kind kind) {
this.kind = kind;
}
public void bind(Scope s, NNode target, NType rvalue) throws Exception {
if (target instanceof NName) {
bindName(s, (NName)target, rvalue);
return;
}
if (target instanceof NTuple) {
bind(s, ((NTuple)target).elts, rvalue);
return;
}
if (target instanceof NList) {
bind(s, ((NList)target).elts, rvalue);
return;
}
if (target instanceof NAttribute) {
// This causes various problems if we let it happen during the
// name-binding pass. I believe the only name-binding context
// in which an NAttribute can be an lvalue is in an assignment.
// Assignments are statements, so they can only appear in blocks.
// Hence the scope for the top-level name is unambiguous; we can
// safely leave binding it until the resolve pass.
if (!s.isNameBindingPhase()) {
((NAttribute)target).setAttr(s, rvalue);
}
return;
}
if (target instanceof NSubscript) {
// Ditto. No resolving is allowed during the name-binding phase.
if (!s.isNameBindingPhase()) {
target.resolveExpr(target, s);
}
return;
}
Indexer.idx.putProblem(target, "invalid location for assignment");
}
public void bind(Scope s, List xs, NType rvalue) throws Exception {
if (rvalue.isTupleType()) {
List 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));
}
}
return;
}
if (rvalue.isListType()) {
bind(s, xs, rvalue.asListType().toTupleType(xs.size()));
return;
}
if (rvalue.isDictType()) {
bind(s, xs, rvalue.asDictType().toTupleType(xs.size()));
return;
}
if (!rvalue.isUnknownType()) {
Indexer.idx.putProblem(xs.get(0).getFile(),
xs.get(0).start(),
xs.get(xs.size()-1).end(),
"unpacking non-iterable: " + rvalue);
}
for (int i = 0; i < xs.size(); i++) {
bind(s, xs.get(i), new NUnknownType());
}
}
public NBinding bindName(Scope s, NName name, NType rvalue) throws Exception {
NBinding b;
if (s.isGlobalName(name.id)) {
b = s.getGlobalTable().put(name.id, name, rvalue, kindOr(SCOPE));
Indexer.idx.putLocation(name, b);
} else {
Scope bindingScope = s.getScopeSymtab();
b = bindingScope.put(name.id, name, rvalue,
kindOr(bindingScope.isFunctionScope() ? VARIABLE : SCOPE));
}
name.setType(b.followType());
// XXX: this seems like a bit of a hack; should at least figure out
// and document what use cases require it.
NType nameType = name.getType();
if (!(nameType.isModuleType() || nameType.isClassType())) {
nameType.getTable().setPath(b.getQname());
}
return b;
}
public void bindIter(Scope s, NNode target, NNode iter) throws Exception {
NType iterType = NNode.resolveExpr(iter, s);
if (iterType.isListType()) {
bind(s, target, iterType.asListType().getElementType());
} else if (iterType.isTupleType()) {
bind(s, target, iterType.asTupleType().toListType().getElementType());
} else {
NBinding ent = iterType.getTable().lookupAttr("__iter__");
if (ent == null || !ent.getType().isFuncType()) {
if (!iterType.isUnknownType()) {
iter.addWarning("not an iterable type: " + iterType);
}
bind(s, target, new NUnknownType());
} else {
bind(s, target, ent.getType().asFuncType().getReturnType());
}
}
}
private void reportUnpackMismatch(List 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);
}
private NBinding.Kind kindOr(NBinding.Kind k) {
return kind != null ? kind : k;
}
}