// (C) Copyright 2001 Samuele Pedroni package org.python.compiler; import org.python.antlr.Visitor; import org.python.antlr.PythonTree; import org.python.antlr.ast.ClassDef; import org.python.antlr.ast.Exec; import org.python.antlr.ast.Expression; import org.python.antlr.ast.FunctionDef; import org.python.antlr.ast.GeneratorExp; import org.python.antlr.ast.Global; import org.python.antlr.ast.Import; import org.python.antlr.ast.ImportFrom; import org.python.antlr.ast.Interactive; import org.python.antlr.ast.Lambda; import org.python.antlr.ast.ListComp; import org.python.antlr.ast.Name; import org.python.antlr.ast.Return; import org.python.antlr.ast.With; import org.python.antlr.ast.Yield; import org.python.antlr.ast.arguments; import org.python.antlr.ast.expr_contextType; import org.python.antlr.base.expr; import org.python.antlr.base.stmt; import org.python.core.ParserFacade; import java.util.ArrayList; import java.util.Hashtable; import java.util.Stack; import java.util.List; public class ScopesCompiler extends Visitor implements ScopeConstants { private CompilationContext code_compiler; private Stack<ScopeInfo> scopes; private ScopeInfo cur = null; private Hashtable<PythonTree,ScopeInfo> nodeScopes; private int level = 0; private int func_level = 0; public ScopesCompiler(CompilationContext code_compiler, Hashtable<PythonTree,ScopeInfo> nodeScopes) { this.code_compiler = code_compiler; this.nodeScopes = nodeScopes; scopes = new Stack<ScopeInfo>(); } public void beginScope(String name, int kind, PythonTree node, ArgListCompiler ac) { if (cur != null) { scopes.push(cur); } if (kind == FUNCSCOPE) { func_level++; } cur = new ScopeInfo(name, node, level++, kind, func_level, ac); nodeScopes.put(node, cur); } public void endScope() throws Exception { if (cur.kind == FUNCSCOPE) { func_level--; } level--; ScopeInfo up = null; if (!scopes.empty()) { up = scopes.pop(); } //Go into the stack to find a non class containing scope to use making the closure //See PEP 227 int dist = 1; ScopeInfo referenceable = up; for (int i = scopes.size() - 1; i >= 0 && referenceable.kind == CLASSSCOPE; i--, dist++) { referenceable = (scopes.get(i)); } cur.cook(referenceable, dist, code_compiler); cur.dump(); // debug cur = up; } public void parse(PythonTree node) throws Exception { try { visit(node); } catch (Throwable t) { throw ParserFacade.fixParseError(null, t, code_compiler.getFilename()); } } @Override public Object visitInteractive(Interactive node) throws Exception { beginScope("<single-top>", TOPSCOPE, node, null); suite(node.getInternalBody()); endScope(); return null; } @Override public Object visitModule(org.python.antlr.ast.Module node) throws Exception { beginScope("<file-top>", TOPSCOPE, node, null); suite(node.getInternalBody()); endScope(); return null; } @Override public Object visitExpression(Expression node) throws Exception { beginScope("<eval-top>", TOPSCOPE, node, null); visit(new Return(node,node.getInternalBody())); endScope(); return null; } private void def(String name) { cur.addBound(name); } @Override public Object visitFunctionDef(FunctionDef node) throws Exception { def(node.getInternalName()); ArgListCompiler ac = new ArgListCompiler(); ac.visitArgs(node.getInternalArgs()); List<expr> defaults = ac.getDefaults(); for (int i = 0; i < defaults.size(); i++) { visit(defaults.get(i)); } List<expr> decs = node.getInternalDecorator_list(); for (int i = decs.size() - 1; i >= 0; i--) { visit(decs.get(i)); } beginScope(node.getInternalName(), FUNCSCOPE, node, ac); int n = ac.names.size(); for (int i = 0; i < n; i++) { cur.addParam(ac.names.get(i)); } for (int i = 0; i < ac.init_code.size(); i++) { visit(ac.init_code.get(i)); } cur.markFromParam(); suite(node.getInternalBody()); endScope(); return null; } @Override public Object visitLambda(Lambda node) throws Exception { ArgListCompiler ac = new ArgListCompiler(); ac.visitArgs(node.getInternalArgs()); List<? extends PythonTree> defaults = ac.getDefaults(); for (int i = 0; i < defaults.size(); i++) { visit(defaults.get(i)); } beginScope("<lambda>", FUNCSCOPE, node, ac); for (Object o : ac.names) { cur.addParam((String) o); } for (Object o : ac.init_code) { visit((stmt) o); } cur.markFromParam(); visit(node.getInternalBody()); endScope(); return null; } public void suite(List<stmt> stmts) throws Exception { for (int i = 0; i < stmts.size(); i++) visit(stmts.get(i)); } @Override public Object visitImport(Import node) throws Exception { for (int i = 0; i < node.getInternalNames().size(); i++) { if (node.getInternalNames().get(i).getInternalAsname() != null) { cur.addBound(node.getInternalNames().get(i).getInternalAsname()); } else { String name = node.getInternalNames().get(i).getInternalName(); if (name.indexOf('.') > 0) { name = name.substring(0, name.indexOf('.')); } cur.addBound(name); } } return null; } @Override public Object visitImportFrom(ImportFrom node) throws Exception { Future.checkFromFuture(node); // future stmt support int n = node.getInternalNames().size(); if (n == 0) { cur.from_import_star = true; return null; } for (int i = 0; i < n; i++) { if (node.getInternalNames().get(i).getInternalAsname() != null) { cur.addBound(node.getInternalNames().get(i).getInternalAsname()); } else { cur.addBound(node.getInternalNames().get(i).getInternalName()); } } return null; } @Override public Object visitGlobal(Global node) throws Exception { int n = node.getInternalNames().size(); for (int i = 0; i < n; i++) { String name = node.getInternalNames().get(i); int prev = cur.addGlobal(name); if (prev >= 0) { if ((prev & FROM_PARAM) != 0) { code_compiler.error("name '" + name + "' is local and global", true, node); } if ((prev & GLOBAL) != 0) { continue; } String what; if ((prev & BOUND) != 0) { what = "assignment"; } else { what = "use"; } code_compiler.error("name '" + name + "' declared global after " + what, false, node); } } return null; } @Override public Object visitExec(Exec node) throws Exception { cur.exec = true; if (node.getInternalGlobals() == null && node.getInternalLocals() == null) { cur.unqual_exec = true; } traverse(node); return null; } @Override public Object visitClassDef(ClassDef node) throws Exception { def(node.getInternalName()); int n = node.getInternalBases().size(); for (int i = 0; i < n; i++) { visit(node.getInternalBases().get(i)); } beginScope(node.getInternalName(), CLASSSCOPE, node, null); suite(node.getInternalBody()); endScope(); return null; } @Override public Object visitName(Name node) throws Exception { String name = node.getInternalId(); if (node.getInternalCtx() != expr_contextType.Load) { if (name.equals("__debug__")) { code_compiler.error("can not assign to __debug__", true, node); } cur.addBound(name); } else { cur.addUsed(name); } return null; } @Override public Object visitListComp(ListComp node) throws Exception { String tmp = "_[" + node.getLine() + "_" + node.getCharPositionInLine() + "]"; cur.addBound(tmp); traverse(node); return null; } @Override public Object visitYield(Yield node) throws Exception { cur.defineAsGenerator(node); cur.yield_count++; traverse(node); return null; } @Override public Object visitReturn(Return node) throws Exception { if (node.getInternalValue() != null) { cur.noteReturnValue(node); } traverse(node); return null; } @Override public Object visitGeneratorExp(GeneratorExp node) throws Exception { // The first iterator is evaluated in the outer scope if (node.getInternalGenerators() != null && node.getInternalGenerators().size() > 0) { visit(node.getInternalGenerators().get(0).getInternalIter()); } String bound_exp = "_(x)"; String tmp = "_(" + node.getLine() + "_" + node.getCharPositionInLine() + ")"; def(tmp); ArgListCompiler ac = new ArgListCompiler(); List<expr> args = new ArrayList<expr>(); args.add(new Name(node.getToken(), bound_exp, expr_contextType.Param)); ac.visitArgs(new arguments(node, args, null, null, new ArrayList<expr>())); beginScope(tmp, FUNCSCOPE, node, ac); cur.addParam(bound_exp); cur.markFromParam(); cur.defineAsGenerator(node); cur.yield_count++; // The reset of the iterators are evaluated in the inner scope if (node.getInternalElt() != null) { visit(node.getInternalElt()); } if (node.getInternalGenerators() != null) { for (int i = 0; i < node.getInternalGenerators().size(); i++) { if (node.getInternalGenerators().get(i) != null) { if (i == 0) { visit(node.getInternalGenerators().get(i).getInternalTarget()); if (node.getInternalGenerators().get(i).getInternalIfs() != null) { for (expr cond : node.getInternalGenerators().get(i).getInternalIfs()) { if (cond != null) { visit(cond); } } } } else { visit(node.getInternalGenerators().get(i)); } } } } endScope(); return null; } @Override public Object visitWith(With node) throws Exception { cur.max_with_count++; traverse(node); return null; } }