Repository: inducer/pycparserext

Issue:
add support for __volatile__, and __restrict__
I added some support to handle Snapdragon processors, header files.  Basically, trivial support for __volatile__ I could have used a #define but this seems to be a better method.  I think GCC supports both __XXX and __XXX__ for all of those key words so this is really a missing feature. 

mhoffman-linux02$ git diff
```
diff --git a/pycparserext/ext_c_lexer.py b/pycparserext/ext_c_lexer.py
index e501a61..ed7ae67 100644
--- a/pycparserext/ext_c_lexer.py
+++ b/pycparserext/ext_c_lexer.py
@@ -63,8 +63,8 @@ def add_lexer_keywords(cls, keywords):
 add_lexer_keywords(GnuCLexer, [
     '__attribute__', '__asm__', '__asm', '__typeof__',
     '__real__', '__imag__', '__builtin_types_compatible_p',
-    '__const', '__restrict', '__inline', '__inline__',
-    '__extension__', 'asm', '__attribute'])
+    '__const', '__restrict', '__restrict__', '__inline', '__inline__',
+    '__extension__', 'asm', '__attribute', '__volatile__'])

 _CL_KEYWORDS = ['kernel', 'constant', 'global', 'local', 'private',
         "read_only", "write_only", "read_write"]
diff --git a/pycparserext/ext_c_parser.py b/pycparserext/ext_c_parser.py
index c9a014a..218d118 100644
--- a/pycparserext/ext_c_parser.py
+++ b/pycparserext/ext_c_parser.py
@@ -359,6 +359,7 @@ class _AsmMixin(object):

     def p_asm_volatile(self, p):
         """ asm_volatile : VOLATILE
+                         | __VOLATILE__
                          | empty
         """
         p[0] = p[1]
@@ -434,6 +435,7 @@ class GnuCParser(_AsmAndAttributesMixin, CParserBase):
     def p_type_qualifier_gnu(self, p):
         """ type_qualifier  : __CONST
                             | __RESTRICT
+                            | __RESTRICT__
                             | __EXTENSION__
         """
         p[0] = p[1]
```

Comments:

Patch:
diff --git a/pycparserext/ext_c_generator.py b/pycparserext/ext_c_generator.py
index 3ecccad..297793d 100644
--- a/pycparserext/ext_c_generator.py
+++ b/pycparserext/ext_c_generator.py
@@ -63,7 +63,13 @@ class AsmAndAttributesMixin(object):
                 if isinstance(modifier, c_ast.ArrayDecl):
                     if (i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl)):
                         nstr = '(' + nstr + ')'
-                    nstr += '[' + self.visit(modifier.dim) + ']'
+
+                    # BUG FIX: pycparser ignores quals
+                    dim_quals = (' '.join(modifier.dim_quals) + ' '
+                                 if modifier.dim_quals else '')
+
+                    nstr += '[' + dim_quals + self.visit(modifier.dim) + ']'
+
                 elif isinstance(modifier, c_ast.FuncDecl):
                     if (i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl)):
                         nstr = '(' + nstr + ')'
diff --git a/pycparserext/ext_c_lexer.py b/pycparserext/ext_c_lexer.py
index 1f8126c..5acff45 100644
--- a/pycparserext/ext_c_lexer.py
+++ b/pycparserext/ext_c_lexer.py
@@ -61,16 +61,30 @@ def add_lexer_keywords(cls, keywords):
             kw.upper() for kw in keywords)
 
 
-add_lexer_keywords(GnuCLexer, [
-    '__attribute__', '__asm__', '__asm', '__typeof__',
-    '__real__', '__imag__', '__builtin_types_compatible_p',
-    '__const', '__restrict', '__inline', '__inline__',
-    '__extension__', 'asm', '__attribute'])
-
-_CL_KEYWORDS = ['kernel', 'constant', 'global', 'local', 'private',
-        "read_only", "write_only", "read_write"]
-add_lexer_keywords(OpenCLCLexer, [
-    '__attribute__', '__attribute', '__asm__', '__asm', 'asm']
-    + _CL_KEYWORDS + ["__"+kw for kw in _CL_KEYWORDS])
+_COMMON_KEYWORDS = [
+    '__attribute__', '__attribute',
+    '__asm__', '__asm', 'asm']
+
+_GNU_KEYWORDS = [
+    '__typeof__',
+    '__real__', '__imag__',
+    '__builtin_types_compatible_p',
+    '__const',
+    '__restrict__', '__restrict',
+    '__inline__', '__inline',
+    '__extension__',
+    '__volatile', '__volatile__']
+
+add_lexer_keywords(GnuCLexer, _COMMON_KEYWORDS + _GNU_KEYWORDS)
+
+# These will be added as unadorned keywords and keywords with '__' prepended
+_CL_BASE_KEYWORDS = [
+    'kernel', 'constant', 'global', 'local', 'private',
+    'read_only', 'write_only', 'read_write']
+
+_CL_KEYWORDS = _COMMON_KEYWORDS
+_CL_KEYWORDS += _CL_BASE_KEYWORDS + ["__"+kw for kw in _CL_BASE_KEYWORDS]
+
+add_lexer_keywords(OpenCLCLexer, _CL_KEYWORDS)
 
 # vim: fdm=marker
diff --git a/pycparserext/ext_c_parser.py b/pycparserext/ext_c_parser.py
index 00fad2a..abbb381 100644
--- a/pycparserext/ext_c_parser.py
+++ b/pycparserext/ext_c_parser.py
@@ -387,17 +387,17 @@ class _AsmMixin(object):
         p[0] = Asm(p[1], p[3], p[5], p[7], p[9], coord=self._coord(p.lineno(2)))
 
     def p_asm_keyword(self, p):
-        """ asm_keyword : __ASM__ asm_volatile
-                        | __ASM asm_volatile
-                        | ASM asm_volatile
+        """ asm_keyword : __ASM__ asm_volatile_opt
+                        | __ASM asm_volatile_opt
+                        | ASM asm_volatile_opt
         """
         p[0] = p[1]
         if p[2]:
             p[0] += ' ' + p[2]
 
-    def p_asm_volatile(self, p):
-        """ asm_volatile : VOLATILE
-                         | empty
+    def p_asm_volatile_opt(self, p):
+        """ asm_volatile_opt : unified_volatile
+                             | empty
         """
         p[0] = p[1]
 
@@ -475,7 +475,10 @@ class GnuCParser(_AsmAndAttributesMixin, CParserBase):
     def p_type_qualifier_gnu(self, p):
         """ type_qualifier  : __CONST
                             | __RESTRICT
+                            | __RESTRICT__
                             | __EXTENSION__
+                            | __VOLATILE
+                            | __VOLATILE__
         """
         p[0] = p[1]
 
@@ -540,6 +543,12 @@ class GnuCParser(_AsmAndAttributesMixin, CParserBase):
         """
         p[0] = RangeExpression(p[2], p[4], coord=self._coord(p.lineno(1)))
 
+    def p_unified_volatile_gnu(self, p):
+        """ unified_volatile : VOLATILE
+                             | __VOLATILE
+                             | __VOLATILE__
+        """
+        p[0] = p[1]
 # }}}
 
 
@@ -615,4 +624,9 @@ class OpenCLCParser(_AsmAndAttributesMixin, CParserBase):
         """
         p[0] = p[1]
 
+    def p_unified_volatile_cl(self, p):
+        """ unified_volatile : VOLATILE
+        """
+        p[0] = p[1]
+
 # vim: fdm=marker


Test patch:
diff --git a/test/test_pycparserext.py b/test/test_pycparserext.py
index 0ab21f8..d39f285 100644
--- a/test/test_pycparserext.py
+++ b/test/test_pycparserext.py
@@ -94,6 +94,20 @@ def test_asm_volatile_3():
     print(GnuCGenerator().visit(ast))
 
 
+def test_asm_volatile_4():
+    src = """
+    void barrier(void) {
+        __asm__ __volatile__("": : :"memory");
+    }    """
+    from pycparserext.ext_c_parser import GnuCParser
+    p = GnuCParser()
+    ast = p.parse(src)
+    ast.show()
+
+    from pycparserext.ext_c_generator import GnuCGenerator
+    print(GnuCGenerator().visit(ast))
+
+
 def test_funky_header_code():
     src = """
         extern __inline int __attribute__ ((__nothrow__)) __signbitf (float __x)
@@ -438,6 +452,19 @@ def test_designated_initializers():
     assert _round_trip_matches(src)
 
 
+@pytest.mark.parametrize("restrict_kw", ["restrict", "__restrict__", "__restrict"])
+def test_restrict(restrict_kw):
+    src = """
+    void f(int n, int * {0} p, int * {0} q)
+    {{
+    }}
+    typedef int *array_t[10];
+    {0} array_t a;
+    void f(int m, int n, float a[{0} m][n], float b[{0} m][n]);
+    """ .format(restrict_kw)
+    assert _round_trip_matches(src)
+
+
 def test_node_visitor():
     from pycparser.c_ast import NodeVisitor