summaryrefslogtreecommitdiffstats
path: root/Tools/compiler
diff options
context:
space:
mode:
authorJeremy Hylton <jeremy@alum.mit.edu>2001-09-17 18:03:55 (GMT)
committerJeremy Hylton <jeremy@alum.mit.edu>2001-09-17 18:03:55 (GMT)
commit37c9351cf6efbe58e01fd879a1d60cc6073ced6b (patch)
treeea89cd6bdc54124e7b36b203269c1f68a89442e0 /Tools/compiler
parent09392b77a4d8be766e394177a466d51c2a64480f (diff)
downloadcpython-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.py61
-rw-r--r--Tools/compiler/compiler/syntax.py45
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")