diff options
author | Jeremy Hylton <jeremy@alum.mit.edu> | 2001-09-17 18:03:55 (GMT) |
---|---|---|
committer | Jeremy Hylton <jeremy@alum.mit.edu> | 2001-09-17 18:03:55 (GMT) |
commit | 37c9351cf6efbe58e01fd879a1d60cc6073ced6b (patch) | |
tree | ea89cd6bdc54124e7b36b203269c1f68a89442e0 /Tools/compiler | |
parent | 09392b77a4d8be766e394177a466d51c2a64480f (diff) | |
download | cpython-37c9351cf6efbe58e01fd879a1d60cc6073ced6b.zip cpython-37c9351cf6efbe58e01fd879a1d60cc6073ced6b.tar.gz cpython-37c9351cf6efbe58e01fd879a1d60cc6073ced6b.tar.bz2 |
Handle more syntax errors.
Invoke compiler.syntax.check() after building AST. If a SyntaxError
occurs, print the error and exit without generating a .pyc file.
Refactor code to use compiler.misc.set_filename() rather than passing
filename argument around to each CodeGenerator instance.
Diffstat (limited to 'Tools/compiler')
-rw-r--r-- | Tools/compiler/compiler/pycodegen.py | 61 | ||||
-rw-r--r-- | Tools/compiler/compiler/syntax.py | 45 |
2 files changed, 77 insertions, 29 deletions
diff --git a/Tools/compiler/compiler/pycodegen.py b/Tools/compiler/compiler/pycodegen.py index a019828..04c115a 100644 --- a/Tools/compiler/compiler/pycodegen.py +++ b/Tools/compiler/compiler/pycodegen.py @@ -8,7 +8,7 @@ import sys import types from cStringIO import StringIO -from compiler import ast, parse, walk +from compiler import ast, parse, walk, syntax from compiler import pyassem, misc, future, symbols from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL from compiler.consts import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\ @@ -41,17 +41,19 @@ class BlockStack(misc.Stack): self.__super_init(self) self.loop = None - - def compile(filename, display=0): f = open(filename) buf = f.read() f.close() mod = Module(buf, filename) - mod.compile(display) - f = open(filename + "c", "wb") - mod.dump(f) - f.close() + try: + mod.compile(display) + except SyntaxError, err: + print "SyntaxError:", err + else: + f = open(filename + "c", "wb") + mod.dump(f) + f.close() class Module: def __init__(self, source, filename): @@ -61,7 +63,9 @@ class Module: def compile(self, display=0): tree = parse(self.source) - gen = ModuleCodeGenerator(self.filename, tree) + misc.set_filename(self.filename, tree) + syntax.check(tree) + gen = ModuleCodeGenerator(tree) if display: import pprint print pprint.pprint(tree) @@ -149,12 +153,11 @@ class CodeGenerator: __initialized = None class_name = None # provide default for instance variable - def __init__(self, filename): + def __init__(self): if self.__initialized is None: self.initClass() self.__class__.__initialized = 1 self.checkClass() - self.filename = filename self.locals = misc.Stack() self.setups = misc.Stack() self.curStack = 0 @@ -306,7 +309,7 @@ class CodeGenerator: self._visitFuncOrLambda(node, isLambda=1) def _visitFuncOrLambda(self, node, isLambda=0): - gen = self.FunctionGen(node, self.filename, self.scopes, isLambda, + gen = self.FunctionGen(node, self.scopes, isLambda, self.class_name, self.get_module()) walk(node.code, gen) gen.finish() @@ -324,7 +327,7 @@ class CodeGenerator: self.emit('MAKE_FUNCTION', len(node.defaults)) def visitClass(self, node): - gen = self.ClassGen(node, self.filename, self.scopes, + gen = self.ClassGen(node, self.scopes, self.get_module()) if node.doc: self.emit('LOAD_CONST', node.doc) @@ -430,14 +433,14 @@ class CodeGenerator: def visitBreak(self, node): if not self.setups: raise SyntaxError, "'break' outside loop (%s, %d)" % \ - (self.filename, node.lineno) + (node.filename, node.lineno) self.set_lineno(node) self.emit('BREAK_LOOP') def visitContinue(self, node): if not self.setups: raise SyntaxError, "'continue' outside loop (%s, %d)" % \ - (self.filename, node.lineno) + (node.filename, node.lineno) kind, block = self.setups.top() if kind == LOOP: self.set_lineno(node) @@ -454,12 +457,12 @@ class CodeGenerator: break if kind != LOOP: raise SyntaxError, "'continue' outside loop (%s, %d)" % \ - (self.filename, node.lineno) + (node.filename, node.lineno) self.emit('CONTINUE_LOOP', loop_block) self.nextBlock() elif kind == END_FINALLY: msg = "'continue' not allowed inside 'finally' clause (%s, %d)" - raise SyntaxError, msg % (self.filename, node.lineno) + raise SyntaxError, msg % (node.filename, node.lineno) def visitTest(self, node, jump): end = self.newBlock() @@ -1085,10 +1088,10 @@ class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator): scopes = None - def __init__(self, filename, tree): - self.graph = pyassem.PyFlowGraph("<module>", filename) + def __init__(self, tree): + self.graph = pyassem.PyFlowGraph("<module>", tree.filename) self.futures = future.find_futures(tree) - self.__super_init(filename) + self.__super_init() walk(tree, self) def get_module(self): @@ -1098,7 +1101,7 @@ class AbstractFunctionCode: optimized = 1 lambdaCount = 0 - def __init__(self, func, filename, scopes, isLambda, class_name, mod): + def __init__(self, func, scopes, isLambda, class_name, mod): self.class_name = class_name self.module = mod if isLambda: @@ -1108,10 +1111,10 @@ class AbstractFunctionCode: else: name = func.name args, hasTupleArg = generateArgList(func.argnames) - self.graph = pyassem.PyFlowGraph(name, filename, args, + self.graph = pyassem.PyFlowGraph(name, func.filename, args, optimized=1) self.isLambda = isLambda - self.super_init(filename) + self.super_init() if not isLambda and func.doc: self.setDocstring(func.doc) @@ -1162,10 +1165,10 @@ class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode, __super_init = AbstractFunctionCode.__init__ - def __init__(self, func, filename, scopes, isLambda, class_name, mod): + def __init__(self, func, scopes, isLambda, class_name, mod): self.scopes = scopes self.scope = scopes[func] - self.__super_init(func, filename, scopes, isLambda, class_name, mod) + self.__super_init(func, scopes, isLambda, class_name, mod) self.graph.setFreeVars(self.scope.get_free_vars()) self.graph.setCellVars(self.scope.get_cell_vars()) if self.graph.checkFlag(CO_GENERATOR_ALLOWED): @@ -1174,12 +1177,12 @@ class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode, class AbstractClassCode: - def __init__(self, klass, filename, scopes, module): + def __init__(self, klass, scopes, module): self.class_name = klass.name self.module = module - self.graph = pyassem.PyFlowGraph(klass.name, filename, + self.graph = pyassem.PyFlowGraph(klass.name, klass.filename, optimized=0, klass=1) - self.super_init(filename) + self.super_init() lnf = walk(klass.code, self.NameFinder(), verbose=0) self.locals.push(lnf.getLocals()) self.graph.setFlag(CO_NEWLOCALS) @@ -1200,10 +1203,10 @@ class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator): __super_init = AbstractClassCode.__init__ - def __init__(self, klass, filename, scopes, module): + def __init__(self, klass, scopes, module): self.scopes = scopes self.scope = scopes[klass] - self.__super_init(klass, filename, scopes, module) + self.__super_init(klass, scopes, module) self.graph.setFreeVars(self.scope.get_free_vars()) self.graph.setCellVars(self.scope.get_cell_vars()) ## self.graph.setFlag(CO_NESTED) diff --git a/Tools/compiler/compiler/syntax.py b/Tools/compiler/compiler/syntax.py new file mode 100644 index 0000000..14613ef --- /dev/null +++ b/Tools/compiler/compiler/syntax.py @@ -0,0 +1,45 @@ +"""Check for errs in the AST. + +The Python parser does not catch all syntax errors. Others, like +assignments with invalid targets, are caught in the code generation +phase. + +The compiler package catches some errors in the transformer module. +But it seems clearer to write checkers that use the AST to detect +errors. +""" + +from compiler import ast, walk + +def check(tree, multi=None): + v = SyntaxErrorChecker(multi) + walk(tree, v) + return v.errors + +class SyntaxErrorChecker: + """A visitor to find syntax errors in the AST.""" + + def __init__(self, multi=None): + """Create new visitor object. + + If optional argument multi is not None, then print messages + for each error rather than raising a SyntaxError for the + first. + """ + self.multi = multi + self.errors = 0 + + def error(self, node, msg): + self.errors = self.errors + 1 + if self.multi is not None: + print "%s:%s: %s" % (node.filename, node.lineno, msg) + else: + raise SyntaxError, "%s (%s:%s)" % (msg, node.filename, node.lineno) + + def visitAssign(self, node): + # the transformer module handles many of these + for target in node.nodes: + if isinstance(target, ast.AssList): + if target.lineno is None: + target.lineno = node.lineno + self.error(target, "can't assign to list comprehension") |