bug 510344: each translation unit needs its own symbol table. also fixed some parser error reporting and removed debugging |print|s
authorChris Jones <jones.chris.g@gmail.com>
Thu, 13 Aug 2009 21:22:55 -0500
changeset 35852 f5fc729e59a734dce1cb6362a2ac26ee7f9c2f06
parent 35851 f2966b784f9bdfd679517753b07bd85ee63b0f56
child 35853 99866f468c60518e68303ac1c3f04aa5ef085d19
push idunknown
push userunknown
push dateunknown
bugs510344
milestone1.9.2a1pre
bug 510344: each translation unit needs its own symbol table. also fixed some parser error reporting and removed debugging |print|s
ipc/ipdl/ipdl/cxx/ast.py
ipc/ipdl/ipdl/cxx/cgen.py
ipc/ipdl/ipdl/parser.py
ipc/ipdl/ipdl/type.py
--- a/ipc/ipdl/ipdl/cxx/ast.py
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -303,17 +303,16 @@ class Inherit(Node):
     def __init__(self, name, viz='public'):
         Node.__init__(self)
         self.name = name
         self.viz = viz
 
 class FriendClassDecl(Node):
     def __init__(self, friend):
         Node.__init__(self)
-        print "FriendClassDecl: %r" % friend
         self.friend = friend
 
 class MethodDecl(Node):
     def __init__(self, name, params=[ ], ret=Type('void'),
                  virtual=False, const=False, pure=False, static=False):
         assert not (virtual and static)
         assert not pure or virtual # pure => virtual
 
--- a/ipc/ipdl/ipdl/cxx/cgen.py
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -129,17 +129,16 @@ class CxxCodeGen(CodePrinter, Visitor):
 
         self.dedent()
         self.printdentln('};')
 
     def visitInherit(self, inh):
         self.write(inh.viz +' '+ inh.name)
 
     def visitFriendClassDecl(self, fcd):
-        print >>sys.stderr, "visitFriendClassDecl: %r" % fcd
         self.printdentln('friend class '+ fcd.friend +';')
 
     def visitMethodDecl(self, md):
         assert not (md.static and md.virtual)
         if md.static:
             self.write('static ')
         if md.virtual:
             self.write('virtual ')
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -44,20 +44,30 @@ def _getcallerpath():
 # 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
+        self.error = ('%s%s: error: %s'% (
+            Parser.includeStackString(), loc, fmt)) % args
     def __str__(self):
         return self.error
 
+def _safeLinenoValue(t):
+    lineno, value = 0, '???'
+    if hasattr(t, 'lineno'): lineno = t.lineno
+    if hasattr(t, 'value'):  value = t.value
+    return lineno, value
+
+def _error(loc, fmt, *args):
+    raise ParseError(loc, fmt, *args)
+
 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 = [ ]
@@ -104,17 +114,17 @@ class Parser:
             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.'''
+|filepath| should be read, or |None| if |filepath| cannot be located.'''
         for incdir in self.includedirs +[ '' ]:
             realpath = os.path.join(incdir, filepath)
             if os.path.isfile(realpath):
                 return os.path.abspath(realpath)
         return None
 
     # returns a GCC-style string representation of the include stack.
     # e.g.,
@@ -184,20 +194,18 @@ def t_ID(t):
     return t
 
 def t_STRING(t):
     r'"[^"\n]*"'
     t.value = t.value[1:-1]
     return t
 
 def t_error(t):
-    includeStackStr = Parser.includeStackString()
-    raise ParseError(
-        Loc(Parser.current.filename, t.lineno),
-        "%s: lexically invalid characters `%s'"% (includeStackStr, t.value))
+    _error(Loc(Parser.current.filename, t.lineno),
+           'lexically invalid characters `%s', 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)
@@ -319,17 +327,17 @@ def p_MessageDirectionLabel(p):
     elif p[1] == 'both':
         Parser.current.direction = INOUT
     else:
         assert 0
 
 def p_MessageDecl(p):
     """MessageDecl : OptionalSendSemanticsQual MessageBody"""
     if Parser.current.direction is None:
-        p_error(p[1])
+        _error(locFromTok(p, 1), 'missing message direction')
 
     if 2 == len(p):
         msg = p[1]
         msg.sendSemantics = ASYNC
     else:
         msg = p[2]
         msg.sendSemantics = p[1]
 
@@ -494,12 +502,11 @@ def p_CxxID(p):
     else:
         p[0] = (locFromTok(p, 1), str(p[1]))
 
 def p_CxxTemplateInst(p):
     """CxxTemplateInst : ID '<' ID '>'"""
     p[0] = (locFromTok(p, 1), str(p[1]) +'<'+ str(p[3]) +'>')
 
 def p_error(t):
-    includeStackStr = Parser.includeStackString()
-    raise ParseError(
-        Loc(Parser.current.filename, t.lineno),
-        "%s: bad syntax near `%s'"% (includeStackStr, t.value))
+    lineno, value = _safeLinenoValue(t)
+    _error(Loc(Parser.current.filename, lineno),
+           "bad syntax near `%s'", value)
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -293,39 +293,38 @@ a decl says where, with what type, and u
 declared.
 
 With this information, it finally type checks the AST.'''
 
     def __init__(self):
         # NB: no IPDL compile will EVER print a warning.  A program has
         # one of two attributes: it is either well typed, or not well typed.
         self.errors = [ ]       # [ string ]
-        self.symtab = SymbolTable(self.errors)
 
     def check(self, tu, errout=sys.stderr):
         def runpass(tcheckpass):
             tu.accept(tcheckpass)
             if len(self.errors):
                 self.reportErrors(errout)
                 return False
             return True
 
         tu.cxxIncludes = builtinIncludes + tu.cxxIncludes
 
         # tag each relevant node with "decl" information, giving type, name,
         # and location of declaration
-        if not runpass(GatherDecls(builtinUsing, self.symtab, self.errors)):
+        if not runpass(GatherDecls(builtinUsing, self.errors)):
             return False
 
         # now that the nodes have decls, type checking is much easier.
-        if not runpass(CheckTypes(self.symtab, self.errors)):
+        if not runpass(CheckTypes(self.errors)):
             return False
 
         if (tu.protocol.startState is not None
-            and not runpass(CheckStateMachine(self.symtab, self.errors))):
+            and not runpass(CheckStateMachine(self.errors))):
             return False
 
         return True
 
     def reportErrors(self, errout):
         for error in self.errors:
             print >>errout, error
 
@@ -343,45 +342,45 @@ class TcheckVisitor(Visitor):
         d.type = type
         d.progname = progname
         d.shortname = shortname
         d.fullname = fullname
         self.symtab.declare(d)
         return d
 
 class GatherDecls(TcheckVisitor):
-    def __init__(self, builtinUsing, symtab, errors):
-        TcheckVisitor.__init__(self, symtab, errors)
+    def __init__(self, builtinUsing, errors):
+        # |self.symtab| is the symbol table for the translation unit
+        # currently being visited
+        TcheckVisitor.__init__(self, None, errors)
         self.builtinUsing = builtinUsing
-        self.depth = 0
+
+    def declareBuiltins(self):
+        for using in self.builtinUsing:
+            fullname = str(using.type)
+            if using.type.basename() == fullname:
+                fullname = None
+            using.decl = self.declareLocalGlobal(
+                loc=using.loc,
+                type=BuiltinCxxType(using.type.spec),
+                shortname=using.type.basename(),
+                fullname=fullname)
+            self.symtab.declare(using.decl)
 
     def visitTranslationUnit(self, tu):
         # all TranslationUnits declare symbols in global scope
-        if hasattr(tu, '_tchecked') and tu._tchecked:
+        if hasattr(tu, 'symtab'):
             return
-        tu._tchecked = True
-        self.depth += 1
+        tu.symtab = SymbolTable(self.errors)
+        savedSymtab = self.symtab
+        self.symtab = tu.symtab
 
-        # bit of a hack here --- we want the builtin |using|
-        # statements to be added to the symbol table before anything
-        # else, but we also want them in the translation units' list
-        # of using stmts so that we can use them later down the pipe.
-        # so we add them to the symbol table before anything else, and
-        # prepend them to the TUs after visiting all their |using|
-        # decls
-        if 1 == self.depth:
-            for using in self.builtinUsing:
-                fullname = str(using.type)
-                if using.type.basename() == fullname:
-                    fullname = None
-                using.decl = self.declare(
-                    loc=using.loc,
-                    type=BuiltinCxxType(using.type.spec),
-                    shortname=using.type.basename(),
-                    fullname=fullname)
+        # pretend like the translation unit "using"-ed these for the
+        # sake of type checking and C++ code generation
+        tu.using = self.builtinUsing + tu.using
 
         p = tu.protocol
 
         # FIXME/cjones: it's a little weird and counterintuitive to put
         # both the namespace and non-namespaced name in the global scope.
         # try to figure out something better; maybe a type-neutral |using|
         # that works for C++ and protocol types?
         qname = QualifiedId(p.loc, p.name,
@@ -395,36 +394,31 @@ class GatherDecls(TcheckVisitor):
             type=ProtocolType(qname, p.sendSemantics),
             shortname=p.name,
             fullname=fullname)
 
         # make sure we have decls for all dependent protocols
         for pinc in tu.protocolIncludes:
             pinc.accept(self)
 
-        # each protocol has its own scope for types brought in through |using|
-        self.symtab.enterScope(tu)
-
-        # and for all imported C++ types
+        # declare imported (and builtin) C++ types
         for using in tu.using:
             using.accept(self)
 
-        # (see long comment above)
-        tu.using = self.builtinUsing + tu.using
-
         # grab symbols in the protocol itself
         p.accept(self)
 
-        self.symtab.exitScope(tu)
+        tu.type = VOID
 
-        tu.type = VOID
-        self.depth -= 1
+        self.symtab = savedSymtab
+
 
     def visitProtocolInclude(self, pi):
         pi.tu.accept(self)
+        self.symtab.declare(pi.tu.protocol.decl)
 
     def visitUsingStmt(self, using):
         fullname = str(using.type)
         if using.type.basename() == fullname:
             fullname = None
         using.decl = self.declare(
             loc=using.loc,
             type=ImportedCxxType(using.type.spec),
@@ -674,18 +668,19 @@ class GatherDecls(TcheckVisitor):
                 "`%s' should have message type, but instead has type `%s'",
                 mname, mdecl.type.typename())
         else:
             t.msg = mdecl
 
 ##-----------------------------------------------------------------------------
 
 class CheckTypes(TcheckVisitor):
-    def __init__(self, symtab, errors):
-        TcheckVisitor.__init__(self, symtab, errors)
+    def __init__(self, errors):
+        # don't need the symbol table, we just want the error reporting
+        TcheckVisitor.__init__(self, None, errors)
         self.visited = set()
 
     def visitProtocolInclude(self, inc):
         if inc.tu.filename in self.visited:
             return
         self.visited.add(inc.tu.filename)
         inc.tu.protocol.accept(self)
 
@@ -792,18 +787,19 @@ class CheckTypes(TcheckVisitor):
                 loc, "%s %s message `%s' is not `%s'd",
                 mtype.sendSemantics.pretty, mtype.direction.pretty,
                 t.msg.progname,
                 t.trigger.pretty)
 
 ##-----------------------------------------------------------------------------
 
 class CheckStateMachine(TcheckVisitor):
-    def __init__(self, symtab, errors):
-        TcheckVisitor.__init__(self, symtab, errors)
+    def __init__(self, errors):
+        # don't need the symbol table, we just want the error reporting
+        TcheckVisitor.__init__(self, None, errors)
         self.p = None
 
     def visitProtocol(self, p):
         self.p = p
         self.checkReachability(p)
         for ts in p.transitionStmts:
             ts.accept(self)