make parse errors behave like type errors
authorChris Jones <jones.chris.g@gmail.com>
Mon, 13 Jul 2009 11:12:08 -0500
changeset 35774 cb8189926a69872c73508fba50830f0d07af341f
parent 35773 91daceed8b15d73d1616e59343dad43d00365075
child 35775 17c4987e08e3ee0ff29fc766cf51f41c1f07c651
push idunknown
push userunknown
push dateunknown
milestone1.9.2a1pre
make parse errors behave like type errors
ipc/ipdl/ipdl.py
ipc/ipdl/ipdl/__init__.py
ipc/ipdl/ipdl/parser.py
--- a/ipc/ipdl/ipdl.py
+++ b/ipc/ipdl/ipdl.py
@@ -72,16 +72,19 @@ for f in files:
     else:
         fd = open(f)
         filename = f
 
     specstring = fd.read()
     fd.close()
 
     ast = ipdl.parse(specstring, filename, includedirs=includedirs)
+    if ast is None:
+        print >>sys.stderr, 'Specification could not be parsed.'
+        sys.exit(1)
 
     allprotocols.append,('%sProtocolMsgStart' % ast.protocol.name)
 
     log(2, 'checking types')
     if not ipdl.typecheck(ast):
         print >>sys.stderr, 'Specification is not well typed.'
         sys.exit(1)
 
--- a/ipc/ipdl/ipdl/__init__.py
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -37,21 +37,23 @@ from cStringIO import StringIO
 
 from ipdl.cgen import IPDLCodeGen
 from ipdl.lower import LowerToCxx
 from ipdl.parser import Parser
 from ipdl.type import TypeCheck
 
 from ipdl.cxx.cgen import CxxCodeGen
 
-def parse(specstring, filename='/stdin', includedirs=[ ]):
-    return Parser().parse(specstring, os.path.abspath(filename), includedirs)
+def parse(specstring, filename='/stdin', includedirs=[ ], errout=sys.stderr):
+    '''Return an IPDL AST if parsing was successful.  Print errors to |errout|
+    if it is not.'''
+    return Parser().parse(specstring, os.path.abspath(filename), includedirs, errout)
 
 def typecheck(ast, errout=sys.stderr):
-    '''Returns True iff |ast| is well typed.  Print errors to |errout| if
+    '''Return True iff |ast| is well typed.  Print errors to |errout| if
     it is not.'''
     return TypeCheck().check(ast, errout)
 
 def gencxx(ast, outdir):
     for hdr in LowerToCxx().lower(ast):
         file = os.path.join(outdir,
                             *([ns.namespace for ns in ast.protocol.namespaces] + [hdr.filename]))
 
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -41,16 +41,23 @@ def _getcallerpath():
     return os.path.abspath(sys._getframe(1).f_code.co_filename)
 
 # we want PLY to generate its output in the module directory, not wherever
 # the user chooses to run ipdlc from
 _thisdir, _ = os.path.split(_getcallerpath())
 
 ##-----------------------------------------------------------------------------
 
+class ParseError(Exception):
+    def __init__(self, loc, fmt, *args):
+        self.loc = loc
+        self.error = ('%s: error: %s'% (loc, fmt)) % args
+    def __str__(self):
+        return self.error
+
 class Parser:
     # when we reach an |include protocol "foo.ipdl";| statement, we need to
     # save the current parser state and create a new one.  this "stack" is
     # where that state is saved
     #
     # there is one Parser per file
     current = None
     parseStack = [ ]
@@ -61,17 +68,17 @@ class Parser:
         self.filename = None
         self.includedirs = None
         self.loc = None         # not always up to date
         self.lexer = None
         self.parser = None
         self.tu = TranslationUnit()
         self.direction = None
 
-    def parse(self, input, filename, includedirs):
+    def parse(self, input, filename, includedirs, errout):
         assert os.path.isabs(filename)
 
         if filename in Parser.parsed:
             return Parser.parsed[filename].tu
 
         self.lexer = lex.lex(debug=self.debug,
                              optimize=not self.debug,
                              lextab="ipdl_lextab",
@@ -83,18 +90,22 @@ class Parser:
         self.filename = filename
         self.includedirs = includedirs
         self.tu.filename = filename
 
         Parser.parsed[filename] = self
         Parser.parseStack.append(Parser.current)
         Parser.current = self
 
-        ast = self.parser.parse(input=input, lexer=self.lexer,
-                                debug=self.debug)
+        try:
+            ast = self.parser.parse(input=input, lexer=self.lexer,
+                                    debug=self.debug)
+        except Exception, p:
+            print >>errout, p
+            return None
 
         Parser.current = Parser.parseStack.pop()
         return ast
 
     def resolveIncludePath(self, filepath):
         '''Return the absolute path from which the possibly partial
 |filepath| should be read, or |None| if |filepath| can't be located.'''
         for incdir in self.includedirs +[ '' ]:
@@ -172,18 +183,19 @@ def t_ID(t):
 
 def t_STRING(t):
     r'"[^"\n]*"'
     t.value = t.value[1:-1]
     return t
 
 def t_error(t):
     includeStackStr = Parser.includeStackString()
-    raise Exception, '%s%s: error: lexically invalid characters %s'% (
-        includeStackStr, Loc(Parser.current.filename, t.lineno), str(t))
+    raise ParseError(
+        Loc(Parser.current.filename, t.lineno),
+        "%s: lexically invalid characters `%s'"% (includeStackStr, t.value))
 
 ##-----------------------------------------------------------------------------
 
 def p_TranslationUnit(p):
     """TranslationUnit : Preamble NamespacedProtocolDefn"""
     tu = Parser.current.tu
     for stmt in p[1]:
         if isinstance(stmt, CxxInclude):  tu.addCxxInclude(stmt)
@@ -218,20 +230,20 @@ def p_CxxIncludeStmt(p):
 def p_ProtocolIncludeStmt(p):
     """ProtocolIncludeStmt : INCLUDE PROTOCOL STRING"""
     loc = locFromTok(p, 1)
     Parser.current.loc = loc
     inc = ProtocolInclude(loc, p[3])
 
     path = Parser.current.resolveIncludePath(inc.file)
     if path is None:
-        print >>sys.stderr, "error: can't locate protocol include file `%s'"% (inc.file)
-        inc.tu = TranslationUnit()
-    else:
-        inc.tu = Parser().parse(open(path).read(), path, Parser.current.includedirs)
+        raise ParseError(loc, "can't locate protocol include file `%s'"% (
+                inc.file))
+    
+    inc.tu = Parser().parse(open(path).read(), path, Parser.current.includedirs)
     p[0] = inc
 
 def p_UsingStmt(p):
     """UsingStmt : USING CxxType"""
     p[0] = UsingStmt(locFromTok(p, 1), p[2])
 
 ##--------------------
 ## Protocol definition
@@ -461,10 +473,11 @@ def p_QualifiedID(p):
     if isinstance(p[1], QualifiedId):
         p[1].qualify(p[3])
         p[0] = p[1]
     else:
         p[0] = QualifiedId(locFromTok(p, 1), p[3])
 
 def p_error(t):
     includeStackStr = Parser.includeStackString()
-    raise Exception, '%s%s: error: bad syntax near "%s"'% (
-        includeStackStr, Loc(Parser.current.filename, t.lineno), t.value)
+    raise ParseError(
+        Loc(Parser.current.filename, t.lineno),
+        "%s: bad syntax near `%s'"% (includeStackStr, t.value))