Pyrex merge: parser context refactoring + nogil blocks
authorStefan Behnel <scoder@users.berlios.de>
Fri, 6 Jun 2008 19:14:54 +0000 (21:14 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 6 Jun 2008 19:14:54 +0000 (21:14 +0200)
Cython/Compiler/Parsing.py

index 33ed9058aa719eb3b76ac67afb8925c43af6f9ad..74c670e8c8e0a1ca6a822f80cdd4df36d2809258 100644 (file)
@@ -12,6 +12,26 @@ from Errors import error, InternalError
 from Cython import Utils
 import Future
 
+class Ctx(object):
+    #  Parsing context
+    level = 'other'
+    visibility = 'private'
+    cdef_flag = 0
+    typedef_flag = 0
+    api = 0
+    overridable = 0
+    nogil = 0
+
+    def __init__(self, **kwds):
+        self.__dict__.update(kwds)
+
+    def __call__(self, **kwds):
+        ctx = Ctx()
+        d = ctx.__dict__
+        d.update(self.__dict__)
+        d.update(kwds)
+        return ctx
+
 def p_ident(s, message = "Expected an identifier"):
     if s.sy == 'IDENT':
         name = s.systring
@@ -37,9 +57,7 @@ def p_ident_list(s):
 #------------------------------------------
 
 def p_binop_expr(s, ops, p_sub_expr):
-    #print "p_binop_expr:", ops, p_sub_expr ###
     n1 = p_sub_expr(s)
-    #print "p_binop_expr(%s):" % p_sub_expr, s.sy ###
     while s.sy in ops:
         op = s.sy
         pos = s.position()
@@ -73,7 +91,6 @@ def p_test(s):
 #or_test: and_test ('or' and_test)*
 
 def p_or_test(s):
-    #return p_binop_expr(s, ('or',), p_and_test)
     return p_rassoc_binop_expr(s, ('or',), p_and_test)
 
 def p_rassoc_binop_expr(s, ops, p_subexpr):
@@ -1195,7 +1212,7 @@ def p_except_clause(s):
     return Nodes.ExceptClauseNode(pos,
         pattern = exc_type, target = exc_value, body = body)
 
-def p_include_statement(s, level):
+def p_include_statement(s, ctx):
     pos = s.position()
     s.next() # 'include'
     _, include_file_name = p_string_literal(s)
@@ -1208,7 +1225,7 @@ def p_include_statement(s, level):
             s2 = PyrexScanner(f, include_file_path, parent_scanner = s,
                               source_encoding=f.encoding)
             try:
-                tree = p_statement_list(s2, level)
+                tree = p_statement_list(s2, ctx)
             finally:
                 f.close()
             return tree
@@ -1258,7 +1275,7 @@ def p_simple_statement(s, first_statement = 0):
         node = p_expression_or_assignment(s)
     return node
 
-def p_simple_statement_list(s, first_statement = 0):
+def p_simple_statement_list(s, ctx, first_statement = 0):
     # Parse a series of simple statements on one line
     # separated by semicolons.
     stat = p_simple_statement(s, first_statement = first_statement)
@@ -1294,7 +1311,7 @@ def p_DEF_statement(s):
     s.expect_newline()
     return Nodes.PassStatNode(pos)
 
-def p_IF_statement(s, level, cdef_flag, visibility, api):
+def p_IF_statement(s, ctx):
     pos = s.position()
     saved_eval = s.compile_time_eval
     current_eval = saved_eval
@@ -1304,7 +1321,7 @@ def p_IF_statement(s, level, cdef_flag, visibility, api):
         s.next() # 'IF' or 'ELIF'
         expr = p_compile_time_expr(s)
         s.compile_time_eval = current_eval and bool(expr.compile_time_value(denv))
-        body = p_suite(s, level, cdef_flag, visibility, api = api)
+        body = p_suite(s, ctx)
         if s.compile_time_eval:
             result = body
             current_eval = 0
@@ -1313,7 +1330,7 @@ def p_IF_statement(s, level, cdef_flag, visibility, api):
     if s.sy == 'ELSE':
         s.next()
         s.compile_time_eval = current_eval
-        body = p_suite(s, level, cdef_flag, visibility, api = api)
+        body = p_suite(s, ctx)
         if current_eval:
             result = body
     if not result:
@@ -1321,18 +1338,18 @@ def p_IF_statement(s, level, cdef_flag, visibility, api):
     s.compile_time_eval = saved_eval
     return result
 
-def p_statement(s, level, cdef_flag = 0, visibility = 'private', api = 0,
-                first_statement = 0):
+def p_statement(s, ctx, first_statement = 0):
+    cdef_flag = ctx.cdef_flag
     if s.sy == 'ctypedef':
-        if level not in ('module', 'module_pxd'):
+        if ctx.level not in ('module', 'module_pxd'):
             s.error("ctypedef statement not allowed here")
-        if api:
+        if ctx.api:
             error(s.position(), "'api' not allowed with 'ctypedef'")
-        return p_ctypedef_statement(s, level, visibility, api)
+        return p_ctypedef_statement(s, ctx)
     elif s.sy == 'DEF':
         return p_DEF_statement(s)
     elif s.sy == 'IF':
-        return p_IF_statement(s, level, cdef_flag, visibility, api)
+        return p_IF_statement(s, ctx)
     else:
         overridable = 0
         if s.sy == 'cdef':
@@ -1343,36 +1360,32 @@ def p_statement(s, level, cdef_flag = 0, visibility = 'private', api = 0,
             overridable = 1
             s.next()
         if cdef_flag:
-            if level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_pxd'):
+            if ctx.level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_pxd'):
                 s.error('cdef statement not allowed here')
-            s.level = level
-            return p_cdef_statement(s, level, visibility = visibility,
-                                    api = api, overridable = overridable)
-    #    elif s.sy == 'cpdef':
-    #        s.next()
-    #        return p_c_func_or_var_declaration(s, level, s.position(), visibility = visibility, api = api, overridable = True)
+            s.level = ctx.level
+            return p_cdef_statement(s, ctx(overridable = overridable))
         else:
-            if api:
+            if ctx.api:
                 error(s.pos, "'api' not allowed with this statement")
             elif s.sy == 'def':
-                if level not in ('module', 'class', 'c_class', 'property'):
+                if ctx.level not in ('module', 'class', 'c_class', 'property'):
                     s.error('def statement not allowed here')
-                s.level = level
+                s.level = ctx.level
                 return p_def_statement(s)
             elif s.sy == 'class':
-                if level != 'module':
+                if ctx.level != 'module':
                     s.error("class definition not allowed here")
                 return p_class_statement(s)
             elif s.sy == 'include':
-                if level not in ('module', 'module_pxd'):
+                if ctx.level not in ('module', 'module_pxd'):
                     s.error("include statement not allowed here")
-                return p_include_statement(s, level)
-            elif level == 'c_class' and s.sy == 'IDENT' and s.systring == 'property':
+                return p_include_statement(s, ctx)
+            elif ctx.level == 'c_class' and s.sy == 'IDENT' and s.systring == 'property':
                 return p_property_decl(s)
-            elif s.sy == 'pass' and level != 'property':
+            elif s.sy == 'pass' and ctx.level != 'property':
                 return p_pass_statement(s, with_newline = 1)
             else:
-                if level in ('c_class_pxd', 'property'):
+                if ctx.level in ('c_class_pxd', 'property'):
                     s.error("Executable statement not allowed here")
                 if s.sy == 'if':
                     return p_if_statement(s)
@@ -1385,25 +1398,22 @@ def p_statement(s, level, cdef_flag = 0, visibility = 'private', api = 0,
                 elif s.sy == 'with':
                     return p_with_statement(s)
                 else:
-                    return p_simple_statement_list(s, first_statement = first_statement)
+                    return p_simple_statement_list(
+                        s, ctx, first_statement = first_statement)
 
-def p_statement_list(s, level,
-        cdef_flag = 0, visibility = 'private', api = 0, first_statement = 0):
+def p_statement_list(s, ctx, first_statement = 0):
     # Parse a series of statements separated by newlines.
     pos = s.position()
     stats = []
     while s.sy not in ('DEDENT', 'EOF'):
-        stats.append(p_statement(s, level,
-            cdef_flag = cdef_flag, visibility = visibility, api = api,
-            first_statement = first_statement))
+        stats.append(p_statement(s, ctx, first_statement = first_statement))
         first_statement = 0
     if len(stats) == 1:
         return stats[0]
     else:
         return Nodes.StatListNode(pos, stats = stats)
 
-def p_suite(s, level = 'other', cdef_flag = 0,
-        visibility = 'private', with_doc = 0, with_pseudo_doc = 0, api = 0):
+def p_suite(s, ctx = Ctx(), with_doc = 0, with_pseudo_doc = 0):
     pos = s.position()
     s.expect(':')
     doc = None
@@ -1413,17 +1423,13 @@ def p_suite(s, level = 'other', cdef_flag = 0,
         s.expect_indent()
         if with_doc or with_pseudo_doc:
             doc = p_doc_string(s)
-        body = p_statement_list(s, 
-            level = level,
-            cdef_flag = cdef_flag, 
-            visibility = visibility,
-            api = api)
+        body = p_statement_list(s, ctx)
         s.expect_dedent()
     else:
-        if api:
+        if ctx.api:
             error(s.pos, "'api' not allowed with this statement")
-        if level in ('module', 'class', 'function', 'other'):
-            body = p_simple_statement_list(s)
+        if ctx.level in ('module', 'class', 'function', 'other'):
+            body = p_simple_statement_list(s, ctx)
         else:
             body = p_pass_statement(s)
             s.expect_newline("Syntax error in declarations")
@@ -1556,8 +1562,9 @@ def p_opt_cname(s):
         cname = None
     return cname
 
-def p_c_declarator(s, empty = 0, is_type = 0, cmethod_flag = 0, assignable = 0,
-        nonempty = 0, calling_convention_allowed = 0):
+def p_c_declarator(s, ctx = Ctx(), empty = 0, is_type = 0, cmethod_flag = 0,
+                   assignable = 0, nonempty = 0,
+                   calling_convention_allowed = 0):
     # If empty is true, the declarator must be empty. If nonempty is true,
     # the declarator must be nonempty. Otherwise we don't care.
     # If cmethod_flag is true, then if this declarator declares
@@ -1567,13 +1574,16 @@ def p_c_declarator(s, empty = 0, is_type = 0, cmethod_flag = 0, assignable = 0,
         s.next()
         if s.sy == ')' or looking_at_type(s):
             base = Nodes.CNameDeclaratorNode(pos, name = "", cname = None)
-            result = p_c_func_declarator(s, pos, base, cmethod_flag)
+            result = p_c_func_declarator(s, pos, ctx, base, cmethod_flag)
         else:
-            result = p_c_declarator(s, empty, is_type, cmethod_flag, nonempty = nonempty,
-                calling_convention_allowed = 1)
+            result = p_c_declarator(s, ctx, empty = empty, is_type = is_type,
+                                    cmethod_flag = cmethod_flag,
+                                    nonempty = nonempty,
+                                    calling_convention_allowed = 1)
             s.expect(')')
     else:
-        result = p_c_simple_declarator(s, empty, is_type, cmethod_flag, assignable, nonempty)
+        result = p_c_simple_declarator(s, ctx, empty, is_type, cmethod_flag,
+                                       assignable, nonempty)
     if not calling_convention_allowed and result.calling_convention and s.sy != '(':
         error(s.position(), "%s on something that is not a function"
             % result.calling_convention)
@@ -1583,7 +1593,7 @@ def p_c_declarator(s, empty = 0, is_type = 0, cmethod_flag = 0, assignable = 0,
             result = p_c_array_declarator(s, result)
         else: # sy == '('
             s.next()
-            result = p_c_func_declarator(s, pos, result, cmethod_flag)
+            result = p_c_func_declarator(s, pos, ctx, result, cmethod_flag)
         cmethod_flag = 0
     return result
 
@@ -1597,10 +1607,10 @@ def p_c_array_declarator(s, base):
     s.expect(']')
     return Nodes.CArrayDeclaratorNode(pos, base = base, dimension = dim)
 
-def p_c_func_declarator(s, pos, base, cmethod_flag):
+def p_c_func_declarator(s, pos, ctx, base, cmethod_flag):
     #  Opening paren has already been skipped
-    args = p_c_arg_list(s, in_pyfunc = 0, cmethod_flag = cmethod_flag,
-        nonempty_declarators = 0)
+    args = p_c_arg_list(s, ctx, cmethod_flag = cmethod_flag,
+                        nonempty_declarators = 0)
     ellipsis = p_optional_ellipsis(s)
     s.expect(')')
     nogil = p_nogil(s)
@@ -1609,19 +1619,24 @@ def p_c_func_declarator(s, pos, base, cmethod_flag):
     return Nodes.CFuncDeclaratorNode(pos, 
         base = base, args = args, has_varargs = ellipsis,
         exception_value = exc_val, exception_check = exc_check,
-        nogil = nogil or with_gil, with_gil = with_gil)
+        nogil = nogil or ctx.nogil or with_gil, with_gil = with_gil)
 
-def p_c_simple_declarator(s, empty, is_type, cmethod_flag, assignable, nonempty):
+def p_c_simple_declarator(s, ctx, empty, is_type, cmethod_flag,
+                          assignable, nonempty):
     pos = s.position()
     calling_convention = p_calling_convention(s)
     if s.sy == '*':
         s.next()
-        base = p_c_declarator(s, empty, is_type, cmethod_flag, assignable, nonempty)
+        base = p_c_declarator(s, ctx, empty = empty, is_type = is_type,
+                              cmethod_flag = cmethod_flag,
+                              assignable = assignable, nonempty = nonempty)
         result = Nodes.CPtrDeclaratorNode(pos, 
             base = base)
     elif s.sy == '**': # scanner returns this as a single token
         s.next()
-        base = p_c_declarator(s, empty, is_type, cmethod_flag, assignable, nonempty)
+        base = p_c_declarator(s, ctx, empty = empty, is_type = is_type,
+                              cmethod_flag = cmethod_flag,
+                              assignable = assignable, nonempty = nonempty)
         result = Nodes.CPtrDeclaratorNode(pos,
             base = Nodes.CPtrDeclaratorNode(pos,
                 base = base))
@@ -1687,28 +1702,14 @@ def p_exception_value_clause(s):
 
 c_arg_list_terminators = ('*', '**', '.', ')')
 
-#def p_c_arg_list(s, in_pyfunc, cmethod_flag = 0, nonempty_declarators = 0,
-#              kw_only = 0):
-#      args = []
-#      if s.sy not in c_arg_list_terminators:
-#              args.append(p_c_arg_decl(s, in_pyfunc, cmethod_flag,
-#                      nonempty = nonempty_declarators, kw_only = kw_only))
-#              while s.sy == ',':
-#                      s.next()
-#                      if s.sy in c_arg_list_terminators:
-#                              break
-#                      args.append(p_c_arg_decl(s, in_pyfunc), nonempty = nonempty_declarators,
-#                              kw_only = kw_only)
-#      return args
-
-def p_c_arg_list(s, in_pyfunc, cmethod_flag = 0, nonempty_declarators = 0,
-        kw_only = 0):
+def p_c_arg_list(s, ctx = Ctx(), in_pyfunc = 0, cmethod_flag = 0,
+                 nonempty_declarators = 0, kw_only = 0):
     #  Comma-separated list of C argument declarations, possibly empty.
     #  May have a trailing comma.
     args = []
     is_self_arg = cmethod_flag
     while s.sy not in c_arg_list_terminators:
-        args.append(p_c_arg_decl(s, in_pyfunc, is_self_arg,
+        args.append(p_c_arg_decl(s, ctx, in_pyfunc, is_self_arg,
             nonempty = nonempty_declarators, kw_only = kw_only))
         if s.sy != ',':
             break
@@ -1723,12 +1724,12 @@ def p_optional_ellipsis(s):
     else:
         return 0
 
-def p_c_arg_decl(s, in_pyfunc, cmethod_flag = 0, nonempty = 0, kw_only = 0):
+def p_c_arg_decl(s, ctx, in_pyfunc, cmethod_flag = 0, nonempty = 0, kw_only = 0):
     pos = s.position()
     not_none = 0
     default = None
     base_type = p_c_base_type(s, cmethod_flag, nonempty = nonempty)
-    declarator = p_c_declarator(s, nonempty = nonempty)
+    declarator = p_c_declarator(s, ctx, nonempty = nonempty)
     if s.sy == 'not':
         s.next()
         if s.sy == 'IDENT' and s.systring == 'None':
@@ -1761,57 +1762,60 @@ def p_api(s):
     else:
         return 0
 
-def p_cdef_statement(s, level, visibility = 'private', api = 0,
-                     overridable = False):
+def p_cdef_statement(s, ctx):
     pos = s.position()
-    visibility = p_visibility(s, visibility)
-    api = api or p_api(s)
-    if api:
-        if visibility not in ('private', 'public'):
-            error(pos, "Cannot combine 'api' with '%s'" % visibility)
-    if (visibility == 'extern') and s.sy == 'from':
-            return p_cdef_extern_block(s, level, pos)
+    ctx.visibility = p_visibility(s, ctx.visibility)
+    ctx.api = ctx.api or p_api(s)
+    if ctx.api:
+        if ctx.visibility not in ('private', 'public'):
+            error(pos, "Cannot combine 'api' with '%s'" % ctx.visibility)
+    if (ctx.visibility == 'extern') and s.sy == 'from':
+        return p_cdef_extern_block(s, pos, ctx)
     elif s.sy == 'import':
         s.next()
-        return p_cdef_extern_block(s, level, pos)
-    elif s.sy == ':':
-        return p_cdef_block(s, level, visibility, api)
+        return p_cdef_extern_block(s, pos, ctx)
+    if p_nogil(s):
+        ctx.nogil = 1
+    if s.sy == ':':
+        return p_cdef_block(s, ctx)
     elif s.sy == 'class':
-        if level not in ('module', 'module_pxd'):
+        if ctx.level not in ('module', 'module_pxd'):
             error(pos, "Extension type definition not allowed here")
-        #if api:
+        #if ctx.api:
         #    error(pos, "'api' not allowed with extension class")
-        return p_c_class_definition(s, level, pos, visibility = visibility, api = api)
+        return p_c_class_definition(s, pos, ctx)
     elif s.sy == 'IDENT' and s.systring in struct_union_or_enum:
-        if level not in ('module', 'module_pxd'):
+        if ctx.level not in ('module', 'module_pxd'):
             error(pos, "C struct/union/enum definition not allowed here")
-        #if visibility == 'public':
+        #if ctx.visibility == 'public':
         #    error(pos, "Public struct/union/enum definition not implemented")
-        #if api:
+        #if ctx.api:
         #    error(pos, "'api' not allowed with '%s'" % s.systring)
         if s.systring == "enum":
-            return p_c_enum_definition(s, pos, level, visibility)
+            return p_c_enum_definition(s, pos, ctx)
         else:
-            return p_c_struct_or_union_definition(s, pos, level, visibility)
+            return p_c_struct_or_union_definition(s, pos, ctx)
     elif s.sy == 'pass':
         node = p_pass_statement(s)
         s.expect_newline('Expected a newline')
         return node
     else:
-        return p_c_func_or_var_declaration(s, level, pos, visibility, api,
-                                           overridable)
+        return p_c_func_or_var_declaration(s, pos, ctx)
 
-def p_cdef_block(s, level, visibility, api):
-    return p_suite(s, level, cdef_flag = 1, visibility = visibility, api = api)
+def p_cdef_block(s, ctx):
+    return p_suite(s, ctx(cdef_flag = 1))
 
-def p_cdef_extern_block(s, level, pos):
+def p_cdef_extern_block(s, pos, ctx):
     include_file = None
     s.expect('from')
     if s.sy == '*':
         s.next()
     else:
         _, include_file = p_string_literal(s)
-    body = p_suite(s, level, cdef_flag = 1, visibility = 'extern')
+    ctx = ctx(cdef_flag = 1, visibility = 'extern')
+    if p_nogil(s):
+        ctx.nogil = 1
+    body = p_suite(s, ctx)
     return Nodes.CDefExternNode(pos,
         include_file = include_file,
         body = body)
@@ -1820,7 +1824,7 @@ struct_union_or_enum = (
     "struct", "union", "enum"
 )
 
-def p_c_enum_definition(s, pos, level, visibility, typedef_flag = 0):
+def p_c_enum_definition(s, pos, ctx):
     # s.sy == ident 'enum'
     s.next()
     if s.sy == 'IDENT':
@@ -1842,9 +1846,10 @@ def p_c_enum_definition(s, pos, level, visibility, typedef_flag = 0):
         while s.sy not in ('DEDENT', 'EOF'):
             p_c_enum_line(s, items)
         s.expect_dedent()
-    return Nodes.CEnumDefNode(pos, name = name, cname = cname,
-        items = items, typedef_flag = typedef_flag, visibility = visibility,
-        in_pxd = level == 'module_pxd')
+    return Nodes.CEnumDefNode(
+        pos, name = name, cname = cname, items = items,
+        typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
+        in_pxd = ctx.level == 'module_pxd')
 
 def p_c_enum_line(s, items):
     if s.sy != 'pass':
@@ -1869,7 +1874,7 @@ def p_c_enum_item(s, items):
     items.append(Nodes.CEnumDefItemNode(pos, 
         name = name, cname = cname, value = value))
 
-def p_c_struct_or_union_definition(s, pos, level, visibility, typedef_flag = 0):
+def p_c_struct_or_union_definition(s, pos, ctx):
     # s.sy == ident 'struct' or 'union'
     kind = s.systring
     s.next()
@@ -1882,10 +1887,11 @@ def p_c_struct_or_union_definition(s, pos, level, visibility, typedef_flag = 0):
         s.expect('NEWLINE')
         s.expect_indent()
         attributes = []
+        body_ctx = Ctx()
         while s.sy != 'DEDENT':
             if s.sy != 'pass':
                 attributes.append(
-                    p_c_func_or_var_declaration(s, level = 'other', pos = s.position()))
+                    p_c_func_or_var_declaration(s, s.position(), body_ctx))
             else:
                 s.next()
                 s.expect_newline("Expected a newline")
@@ -1894,8 +1900,8 @@ def p_c_struct_or_union_definition(s, pos, level, visibility, typedef_flag = 0):
         s.expect_newline("Syntax error in struct or union definition")
     return Nodes.CStructOrUnionDefNode(pos, 
         name = name, cname = cname, kind = kind, attributes = attributes,
-        typedef_flag = typedef_flag, visibility = visibility,
-        in_pxd = level == 'module_pxd')
+        typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
+        in_pxd = ctx.level == 'module_pxd')
 
 def p_visibility(s, prev_visibility):
     pos = s.position()
@@ -1915,26 +1921,26 @@ def p_c_modifiers(s):
         return [modifier] + p_c_modifiers(s)
     return []
 
-def p_c_func_or_var_declaration(s, level, pos, visibility = 'private', api = 0,
-                                overridable = False):
-    cmethod_flag = level in ('c_class', 'c_class_pxd')
+def p_c_func_or_var_declaration(s, pos, ctx):
+    cmethod_flag = ctx.level in ('c_class', 'c_class_pxd')
     modifiers = p_c_modifiers(s)
     base_type = p_c_base_type(s, nonempty = 1)
-    declarator = p_c_declarator(s, cmethod_flag = cmethod_flag, assignable = 1, nonempty = 1)
-    declarator.overridable = overridable
+    declarator = p_c_declarator(s, ctx, cmethod_flag = cmethod_flag,
+                                assignable = 1, nonempty = 1)
+    declarator.overridable = ctx.overridable
     if s.sy == ':':
-        if level not in ('module', 'c_class'):
+        if ctx.level not in ('module', 'c_class'):
             s.error("C function definition not allowed here")
-        doc, suite = p_suite(s, 'function', with_doc = 1)
+        doc, suite = p_suite(s, Ctx(level = 'function'), with_doc = 1)
         result = Nodes.CFuncDefNode(pos,
-            visibility = visibility,
+            visibility = ctx.visibility,
             base_type = base_type,
             declarator = declarator, 
             body = suite,
             doc = doc,
             modifiers = modifiers,
-            api = api,
-            overridable = overridable)
+            api = ctx.api,
+            overridable = ctx.overridable)
     else:
         #if api:
         #    error(s.pos, "'api' not allowed with variable declaration")
@@ -1943,39 +1949,40 @@ def p_c_func_or_var_declaration(s, level, pos, visibility = 'private', api = 0,
             s.next()
             if s.sy == 'NEWLINE':
                 break
-            declarator = p_c_declarator(s, cmethod_flag = cmethod_flag, assignable = 1, nonempty = 1)
+            declarator = p_c_declarator(s, ctx, cmethod_flag = cmethod_flag,
+                                        assignable = 1, nonempty = 1)
             declarators.append(declarator)
         s.expect_newline("Syntax error in C variable declaration")
         result = Nodes.CVarDefNode(pos, 
-            visibility = visibility,
-            base_type = base_type, 
+            visibility = ctx.visibility,
+            base_type = base_type,
             declarators = declarators,
-            in_pxd = level == 'module_pxd',
-            api = api,
-            overridable = overridable)
+            in_pxd = ctx.level == 'module_pxd',
+            api = ctx.api,
+            overridable = ctx.overridable)
     return result
 
-def p_ctypedef_statement(s, level, visibility = 'private', api = 0):
+def p_ctypedef_statement(s, ctx):
     # s.sy == 'ctypedef'
     pos = s.position()
     s.next()
-    visibility = p_visibility(s, visibility)
+    visibility = p_visibility(s, ctx.visibility)
+    ctx = ctx(typedef_flag = 1, visibility = visibility)
     if s.sy == 'class':
-        return p_c_class_definition(s, level, pos,
-            visibility = visibility, typedef_flag = 1, api = api)
+        return p_c_class_definition(s, pos, ctx)
     elif s.sy == 'IDENT' and s.systring in ('struct', 'union', 'enum'):
         if s.systring == 'enum':
-            return p_c_enum_definition(s, pos, level, visibility, typedef_flag = 1)
+            return p_c_enum_definition(s, pos, ctx)
         else:
-            return p_c_struct_or_union_definition(s, pos, level, visibility,
-                typedef_flag = 1)
+            return p_c_struct_or_union_definition(s, pos, ctx)
     else:
         base_type = p_c_base_type(s, nonempty = 1)
-        declarator = p_c_declarator(s, is_type = 1, nonempty = 1)
+        declarator = p_c_declarator(s, ctx, is_type = 1, nonempty = 1)
         s.expect_newline("Syntax error in ctypedef statement")
-        return Nodes.CTypeDefNode(pos,
-            base_type = base_type, declarator = declarator, visibility = visibility,
-            in_pxd = level == 'module_pxd')
+        return Nodes.CTypeDefNode(
+            pos, base_type = base_type,
+            declarator = declarator, visibility = visibility,
+            in_pxd = ctx.level == 'module_pxd')
 
 def p_def_statement(s):
     # s.sy == 'def'
@@ -2003,7 +2010,7 @@ def p_def_statement(s):
     s.expect(')')
     if p_nogil(s):
         error(s.pos, "Python function cannot be declared nogil")
-    doc, body = p_suite(s, 'function', with_doc = 1)
+    doc, body = p_suite(s, Ctx(level = 'function'), with_doc = 1)
     return Nodes.DefNode(pos, name = name, args = args, 
         star_arg = star_arg, starstar_arg = starstar_arg,
         doc = doc, body = body)
@@ -2025,14 +2032,13 @@ def p_class_statement(s):
         s.expect(')')
     else:
         base_list = []
-    doc, body = p_suite(s, 'class', with_doc = 1)
+    doc, body = p_suite(s, Ctx(level = 'class'), with_doc = 1)
     return Nodes.PyClassDefNode(pos,
         name = class_name,
         bases = ExprNodes.TupleNode(pos, args = base_list),
         doc = doc, body = body)
 
-def p_c_class_definition(s, level, pos, 
-        visibility = 'private', typedef_flag = 0, api = 0):
+def p_c_class_definition(s, pos,  ctx):
     # s.sy == 'class'
     s.next()
     module_path = []
@@ -2041,7 +2047,7 @@ def p_c_class_definition(s, level, pos,
         s.next()
         module_path.append(class_name)
         class_name = p_ident(s)
-    if module_path and visibility != 'extern':
+    if module_path and ctx.visibility != 'extern':
         error(pos, "Qualified class name only allowed for 'extern' C class")
     if module_path and s.sy == 'IDENT' and s.systring == 'as':
         s.next()
@@ -2065,38 +2071,38 @@ def p_c_class_definition(s, level, pos,
         base_class_module = ".".join(base_class_path[:-1])
         base_class_name = base_class_path[-1]
     if s.sy == '[':
-        if visibility not in ('public', 'extern'):
+        if ctx.visibility not in ('public', 'extern'):
             error(s.position(), "Name options only allowed for 'public' or 'extern' C class")
         objstruct_name, typeobj_name = p_c_class_options(s)
     if s.sy == ':':
-        if level == 'module_pxd':
+        if ctx.level == 'module_pxd':
             body_level = 'c_class_pxd'
         else:
             body_level = 'c_class'
-        doc, body = p_suite(s, body_level, with_doc = 1)
+        doc, body = p_suite(s, Ctx(level = body_level), with_doc = 1)
     else:
         s.expect_newline("Syntax error in C class definition")
         doc = None
         body = None
-    if visibility == 'extern':
+    if ctx.visibility == 'extern':
         if not module_path:
             error(pos, "Module name required for 'extern' C class")
         if typeobj_name:
             error(pos, "Type object name specification not allowed for 'extern' C class")
-    elif visibility == 'public':
+    elif ctx.visibility == 'public':
         if not objstruct_name:
             error(pos, "Object struct name specification required for 'public' C class")
         if not typeobj_name:
             error(pos, "Type object name specification required for 'public' C class")
-    elif visibility == 'private':
-        if api:
+    elif ctx.visibility == 'private':
+        if ctx.api:
             error(pos, "Only 'public' C class can be declared 'api'")
     else:
-        error(pos, "Invalid class visibility '%s'" % visibility)
+        error(pos, "Invalid class visibility '%s'" % ctx.visibility)
     return Nodes.CClassDefNode(pos,
-        visibility = visibility,
-        typedef_flag = typedef_flag,
-        api = api,
+        visibility = ctx.visibility,
+        typedef_flag = ctx.typedef_flag,
+        api = ctx.api,
         module_name = ".".join(module_path),
         class_name = class_name,
         as_name = as_name,
@@ -2104,7 +2110,7 @@ def p_c_class_definition(s, level, pos,
         base_class_name = base_class_name,
         objstruct_name = objstruct_name,
         typeobj_name = typeobj_name,
-        in_pxd = level == 'module_pxd',
+        in_pxd = ctx.level == 'module_pxd',
         doc = doc,
         body = body)
 
@@ -2131,7 +2137,7 @@ def p_property_decl(s):
     pos = s.position()
     s.next() # 'property'
     name = p_ident(s)
-    doc, body = p_suite(s, 'property', with_doc = 1)
+    doc, body = p_suite(s, Ctx(level = 'property'), with_doc = 1)
     return Nodes.PropertyNode(pos, name = name, doc = doc, body = body)
 
 def p_doc_string(s):
@@ -2152,7 +2158,7 @@ def p_module(s, pxd, full_module_name):
         level = 'module_pxd'
     else:
         level = 'module'
-    body = p_statement_list(s, level, first_statement = 1)
+    body = p_statement_list(s, Ctx(level = level), first_statement = 1)
     if s.sy != 'EOF':
         s.error("Syntax error in statement [%s,%s]" % (
             repr(s.sy), repr(s.systring)))