Commits

Eli Bendersky committed 20b91c8

Issue #83: Distinguish initializer lists from expression lists

Comments (0)

Files changed (8)

   - pycparser now carries its PLY dependency along. The pycparser/ply directory
     contains the source of PLY for the currently supported version. This makes
     distribution and testing easier.
+  - Issue #83: fix parsing and C generation to distinguish between initializer
+    lists in declarations and initializing variables with parenthesized
+    comma-separated expressions.
   - Issue #84: fix C generation for some statements.
   - Issues #86 and #87: improve location reporting for parse errors.
   - Issue #89: fix C generation for K&R-style function definitions.

pycparser/_c_ast.cfg

 # Compound literal (anonymous aggregate) for C99.
 # (type-name) {initializer_list}
 # type: the typename
-# init: ExprList for the initializer list
+# init: InitExprList for the initializer list
 #
 CompoundLiteral: [type*, init*]
 
 #
 EnumeratorList: [enumerators**]
 
-# a list of comma separated expressions
+# A list of expressions separated by the comma operator.
 #
 ExprList: [exprs**]
 
 
 If: [cond*, iftrue*, iffalse*]
 
+# An initialization list used for compound literals.
+#
+InitList: [exprs**]
+
 Label: [name, stmt*]
 
 # A named initializer for C99.

pycparser/c_ast.py

 
     attr_names = ()
 
+class InitList(Node):
+    def __init__(self, exprs, coord=None):
+        self.exprs = exprs
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.exprs or []):
+            nodelist.append(("exprs[%d]" % i, child))
+        return tuple(nodelist)
+
+    attr_names = ()
+
 class Label(Node):
     def __init__(self, name, stmt, coord=None):
         self.name = name

pycparser/c_generator.py

         rval_str = self._parenthesize_if(n.right, 
                             lambda d: not self._is_simple_node(d))
         return '%s %s %s' % (lval_str, n.op, rval_str)
-    
+
     def visit_Assignment(self, n):
         rval_str = self._parenthesize_if(
                             n.rvalue, 
                             lambda n: isinstance(n, c_ast.Assignment))
         return '%s %s %s' % (self.visit(n.lvalue), n.op, rval_str)
-    
+
     def visit_IdentifierType(self, n):
         return ' '.join(n.names)
-    
+
     def visit_Decl(self, n, no_type=False):
         # no_type is used when a Decl is part of a DeclList, where the type is
         # explicitly only for the first delaration in a list.
         s = n.name if no_type else self._generate_decl(n)
         if n.bitsize: s += ' : ' + self.visit(n.bitsize)
         if n.init:
-            if isinstance(n.init, c_ast.ExprList):
+            if isinstance(n.init, c_ast.InitList):
                 s += ' = {' + self.visit(n.init) + '}'
+            elif isinstance(n.init, c_ast.ExprList):
+                s += ' = (' + self.visit(n.init) + ')'
             else:
                 s += ' = ' + self.visit(n.init)
         return s
-    
+
     def visit_DeclList(self, n):
         s = self.visit(n.decls[0])
         if len(n.decls) > 1:
             s += ', ' + ', '.join(self.visit_Decl(decl, no_type=True) 
                                     for decl in n.decls[1:])
         return s
-    
+
     def visit_Typedef(self, n):
         s = ''
         if n.storage: s += ' '.join(n.storage) + ' '
         s += self._generate_type(n.type)
         return s
-    
+
     def visit_Cast(self, n):
         s = '(' + self._generate_type(n.to_type) + ')' 
         return s + ' ' + self._parenthesize_unless_simple(n.expr)
-    
+
     def visit_ExprList(self, n):
         visited_subexprs = []
         for expr in n.exprs:
             else:
                 visited_subexprs.append(self.visit(expr))
         return ', '.join(visited_subexprs)
-    
+
+    def visit_InitList(self, n):
+        visited_subexprs = []
+        for expr in n.exprs:
+            if isinstance(expr, c_ast.InitList):
+                visited_subexprs.append('(' + self.visit(expr) + ')')
+            else:
+                visited_subexprs.append(self.visit(expr))
+        return ', '.join(visited_subexprs)
+
     def visit_Enum(self, n):
         s = 'enum'
         if n.name: s += ' ' + n.name
                     s += ', '
             s += '}'
         return s
-        
+
     def visit_FuncDef(self, n):
         decl = self.visit(n.decl)
         self.indent_level = 0
         self.indent_level -= 2
         s += self._make_indent() + '}\n'
         return s
-    
+
     def visit_EmptyStatement(self, n):
         return ';'
-    
+
     def visit_ParamList(self, n):
         return ', '.join(self.visit(param) for param in n.params)
 
 
     def visit_Break(self, n):
         return 'break;'
-        
+
     def visit_Continue(self, n):
         return 'continue;'
-    
+
     def visit_TernaryOp(self, n):
         s = self.visit(n.cond) + ' ? '
         s += self.visit(n.iftrue) + ' : '
         s += self.visit(n.iffalse)
         return s
-    
+
     def visit_If(self, n):
         s = 'if ('
         if n.cond: s += self.visit(n.cond)
             s += self._make_indent() + 'else\n'
             s += self._generate_stmt(n.iffalse, add_indent=True)
         return s
-    
+
     def visit_For(self, n):
         s = 'for ('
         if n.init: s += self.visit(n.init)
         s = 'switch (' + self.visit(n.cond) + ')\n'
         s += self._generate_stmt(n.stmt, add_indent=True)
         return s
-    
+
     def visit_Case(self, n):
         s = 'case ' + self.visit(n.expr) + ':\n'
         s += self._generate_stmt(n.stmt, add_indent=True)
         return s
-    
+
     def visit_Default(self, n):
         return 'default:\n' + self._generate_stmt(n.stmt, add_indent=True)
 

pycparser/c_parser.py

         """
         if len(p) == 3: # single initializer
             init = p[2] if p[1] is None else c_ast.NamedInitializer(p[1], p[2])
-            p[0] = c_ast.ExprList([init], p[2].coord)
+            p[0] = c_ast.InitList([init], p[2].coord)
         else:
             init = p[4] if p[3] is None else c_ast.NamedInitializer(p[3], p[4])
             p[1].exprs.append(init)

tests/test_c_generator.py

               return 0;
             }''')
 
+    def test_issue83(self):
+        self._assert_ctoc_correct(r'''
+            void x(void) {
+                int i = (9, k);
+            }
+            ''')
+
     def test_issue84(self):
         self._assert_ctoc_correct(r'''
             void x(void) {

tests/test_c_parser.py

     if typ == NamedInitializer:
         des = [expand_init(dp) for dp in init.name]
         return (des, expand_init(init.expr))
-    elif typ == ExprList:
+    elif typ in (InitList, ExprList):
         return [expand_init(expr) for expr in init.exprs]
     elif typ == Constant:
         return ['Constant', init.type, init.value]
 
 
 if __name__ == "__main__":    
-    source_code = '''
-'''
-
-
-    #--------------- Lexing 
-    #~ def errfoo(msg, a, b):
-        #~ printme(msg)
-        #~ sys.exit()
-    #~ clex = CLexer(errfoo, lambda t: False)
-    #~ clex.build()
-    #~ clex.input(source_code)
-    
-    #~ while 1:
-        #~ tok = clex.token()
-        #~ if not tok: break
-            
-        #~ printme([tok.value, tok.type, tok.lineno, clex.filename, tok.lexpos])
-
-    #--------------- Parsing
     source_code = r'''
-    typedef int int8_t;
-    int boo, 8sd;
-    
-    
+    void main(void) {
+        i = (a, b);
+    }
     '''
 
     parser = CParser(lex_optimize=False, yacc_optimize=False, yacc_debug=True)