remove trailing whitespace
[cython.git] / Cython / Compiler / Parsing.py
index 4995bbdf1a3c9641872f3b46745a7e988f750863..e111bc13862a6703c6198408f62a8dd1c1cfa358 100644 (file)
@@ -1,4 +1,4 @@
-# cython: auto_cpdef=True, infer_types=True
+# cython: auto_cpdef=True, infer_types=True, language_level=3, py2_import=True
 #
 #   Pyrex Parser
 #
@@ -178,7 +178,7 @@ def p_comparison(s):
         pos = s.position()
         op = p_cmp_op(s)
         n2 = p_starred_expr(s)
-        n1 = ExprNodes.PrimaryCmpNode(pos, 
+        n1 = ExprNodes.PrimaryCmpNode(pos,
             operator = op, operand1 = n1, operand2 = n2)
         if s.sy in comparison_ops:
             n1.cascade = p_cascaded_cmp(s)
@@ -206,7 +206,7 @@ def p_cascaded_cmp(s):
     pos = s.position()
     op = p_cmp_op(s)
     n2 = p_starred_expr(s)
-    result = ExprNodes.CascadedCmpNode(pos, 
+    result = ExprNodes.CascadedCmpNode(pos,
         operator = op, operand2 = n2)
     if s.sy in comparison_ops:
         result.cascade = p_cascaded_cmp(s)
@@ -230,9 +230,9 @@ def p_cmp_op(s):
     if op == '<>':
         op = '!='
     return op
-    
+
 comparison_ops = (
-    '<', '>', '==', '>=', '<=', '<>', '!=', 
+    '<', '>', '==', '>=', '<=', '<>', '!=',
     'in', 'is', 'not'
 )
 
@@ -269,6 +269,10 @@ def p_term(s):
 #factor: ('+'|'-'|'~'|'&'|typecast|sizeof) factor | power
 
 def p_factor(s):
+    # little indirection for C-ification purposes
+    return _p_factor(s)
+
+def _p_factor(s):
     sy = s.sy
     if sy in ('+', '-', '~'):
         op = s.sy
@@ -302,8 +306,8 @@ def p_typecast(s):
         typecheck = 0
     s.expect(">")
     operand = p_factor(s)
-    return ExprNodes.TypecastNode(pos, 
-        base_type = base_type, 
+    return ExprNodes.TypecastNode(pos,
+        base_type = base_type,
         declarator = declarator,
         operand = operand,
         typecheck = typecheck)
@@ -314,15 +318,15 @@ def p_sizeof(s):
     s.next()
     s.expect('(')
     # Here we decide if we are looking at an expression or type
-    # If it is actually a type, but parsable as an expression, 
-    # we treat it as an expression here. 
+    # If it is actually a type, but parsable as an expression,
+    # we treat it as an expression here.
     if looking_at_expr(s):
         operand = p_test(s)
         node = ExprNodes.SizeofVarNode(pos, operand = operand)
     else:
         base_type = p_c_base_type(s)
         declarator = p_c_declarator(s, empty = 1)
-        node = ExprNodes.SizeofTypeNode(pos, 
+        node = ExprNodes.SizeofTypeNode(pos,
             base_type = base_type, declarator = declarator)
     s.expect(')')
     return node
@@ -375,7 +379,7 @@ def p_trailer(s, node1):
     else: # s.sy == '.'
         s.next()
         name = EncodedString( p_ident(s) )
-        return ExprNodes.AttributeNode(pos, 
+        return ExprNodes.AttributeNode(pos,
             obj = node1, attribute = name)
 
 # arglist:  argument (',' argument)* [',']
@@ -465,7 +469,7 @@ def p_call(s, function):
     else:
         arg_tuple, keyword_dict = p_call_build_packed_args(
             pos, positional_args, keyword_args, star_arg)
-        return ExprNodes.GeneralCallNode(pos, 
+        return ExprNodes.GeneralCallNode(pos,
             function = function,
             positional_args = arg_tuple,
             keyword_args = keyword_dict,
@@ -482,7 +486,7 @@ def p_index(s, base):
     subscripts = p_subscript_list(s)
     if len(subscripts) == 1 and len(subscripts[0]) == 2:
         start, stop = subscripts[0]
-        result = ExprNodes.SliceIndexNode(pos, 
+        result = ExprNodes.SliceIndexNode(pos,
             base = base, start = start, stop = stop)
     else:
         indexes = make_slice_nodes(pos, subscripts)
@@ -611,7 +615,7 @@ def p_atom(s):
             return ExprNodes.BoolNode(pos, value=True)
         elif name == "False":
             return ExprNodes.BoolNode(pos, value=False)
-        elif name == "NULL":
+        elif name == "NULL" and not s.in_python_file:
             return ExprNodes.NullNode(pos)
         else:
             return p_name(s, name)
@@ -762,17 +766,10 @@ def p_string_literal(s, kind_override=None):
                 has_non_ASCII_literal_characters = True
         elif sy == 'ESCAPE':
             if is_raw:
-                if systr == u'\\\n':
-                    chars.append(u'\\\n')
-                elif systr == u'\\\"':
-                    chars.append(u'"')
-                elif systr == u'\\\'':
-                    chars.append(u"'")
-                else:
-                    chars.append(systr)
-                    if is_python3_source and not has_non_ASCII_literal_characters \
-                           and check_for_non_ascii_characters(systr):
-                        has_non_ASCII_literal_characters = True
+                chars.append(systr)
+                if is_python3_source and not has_non_ASCII_literal_characters \
+                       and check_for_non_ascii_characters(systr):
+                    has_non_ASCII_literal_characters = True
             else:
                 c = systr[1]
                 if c in u"01234567":
@@ -785,15 +782,20 @@ def p_string_literal(s, kind_override=None):
                 elif c == u'\n':
                     pass
                 elif c == u'x':
-                    chars.append_charval( int(systr[2:], 16) )
+                    if len(systr) == 4:
+                        chars.append_charval( int(systr[2:], 16) )
+                    else:
+                        s.error("Invalid hex escape '%s'" % systr)
                 elif c in u'Uu':
                     if kind in ('u', ''):
-                        chrval = int(systr[2:], 16)
-                        if chrval > 1114111: # sys.maxunicode:
-                            s.error("Invalid unicode escape '%s'" % systr,
-                                    pos = pos)
+                        if len(systr) in (6,10):
+                            chrval = int(systr[2:], 16)
+                            if chrval > 1114111: # sys.maxunicode:
+                                s.error("Invalid unicode escape '%s'" % systr)
+                        else:
+                            s.error("Invalid unicode escape '%s'" % systr)
                     else:
-                        # unicode escapes in plain byte strings are not unescaped
+                        # unicode escapes in byte strings are not unescaped
                         chrval = None
                     chars.append_uescape(chrval, systr)
                 else:
@@ -831,7 +833,7 @@ def p_string_literal(s, kind_override=None):
 # comp_iter     ::=     comp_for | comp_if
 # comp_for     ::=     "for" expression_list "in" testlist [comp_iter]
 # comp_if     ::=     "if" test [comp_iter]
-        
+
 def p_list_maker(s):
     # s.sy == '['
     pos = s.position()
@@ -858,7 +860,7 @@ def p_list_maker(s):
             exprs = [expr]
         s.expect(']')
         return ExprNodes.ListNode(pos, args = exprs)
-        
+
 def p_comp_iter(s, body):
     if s.sy == 'for':
         return p_comp_for(s, body)
@@ -873,16 +875,15 @@ def p_comp_for(s, body):
     pos = s.position()
     s.next()
     kw = p_for_bounds(s, allow_testlist=False)
-    kw['else_clause'] = None
-    kw['body'] = p_comp_iter(s, body)
+    kw.update(dict(else_clause = None, body = p_comp_iter(s, body)))
     return Nodes.ForStatNode(pos, **kw)
-        
+
 def p_comp_if(s, body):
     # s.sy == 'if'
     pos = s.position()
     s.next()
     test = p_test_nocond(s)
-    return Nodes.IfStatNode(pos, 
+    return Nodes.IfStatNode(pos,
         if_clauses = [Nodes.IfClauseNode(pos, condition = test,
                                          body = p_comp_iter(s, body))],
         else_clause = None )
@@ -983,7 +984,7 @@ def p_test_or_starred_expr_list(s, expr=None):
             break
         s.next()
     return exprs
-    
+
 
 #testlist: test (',' test)* [',']
 
@@ -1044,6 +1045,12 @@ def p_global_statement(s):
     names = p_ident_list(s)
     return Nodes.GlobalNode(pos, names = names)
 
+def p_nonlocal_statement(s):
+    pos = s.position()
+    s.next()
+    names = p_ident_list(s)
+    return Nodes.NonlocalNode(pos, names = names)
+
 def p_expression_or_assignment(s):
     expr_list = [p_testlist_star_expr(s)]
     while s.sy == '=':
@@ -1073,7 +1080,7 @@ def p_expression_or_assignment(s):
 
     rhs = expr_list[-1]
     if len(expr_list) == 2:
-        return Nodes.SingleAssignmentNode(rhs.pos, 
+        return Nodes.SingleAssignmentNode(rhs.pos,
             lhs = expr_list[0], rhs = rhs)
     else:
         return Nodes.CascadedAssignmentNode(rhs.pos,
@@ -1174,7 +1181,7 @@ def p_raise_statement(s):
                 s.next()
                 exc_tb = p_test(s)
     if exc_type or exc_value or exc_tb:
-        return Nodes.RaiseStatNode(pos, 
+        return Nodes.RaiseStatNode(pos,
             exc_type = exc_type,
             exc_value = exc_value,
             exc_tb = exc_tb)
@@ -1194,7 +1201,7 @@ def p_import_statement(s):
     for pos, target_name, dotted_name, as_name in items:
         dotted_name = EncodedString(dotted_name)
         if kind == 'cimport':
-            stat = Nodes.CImportStatNode(pos, 
+            stat = Nodes.CImportStatNode(pos,
                 module_name = dotted_name,
                 as_name = as_name)
         else:
@@ -1204,11 +1211,12 @@ def p_import_statement(s):
             else:
                 name_list = None
             stat = Nodes.SingleAssignmentNode(pos,
-                lhs = ExprNodes.NameNode(pos, 
+                lhs = ExprNodes.NameNode(pos,
                     name = as_name or target_name),
-                rhs = ExprNodes.ImportNode(pos, 
+                rhs = ExprNodes.ImportNode(pos,
                     module_name = ExprNodes.IdentifierStringNode(
                         pos, value = dotted_name),
+                    level = None,
                     name_list = name_list))
         stats.append(stat)
     return Nodes.StatListNode(pos, stats = stats)
@@ -1217,13 +1225,31 @@ def p_from_import_statement(s, first_statement = 0):
     # s.sy == 'from'
     pos = s.position()
     s.next()
-    (dotted_name_pos, _, dotted_name, _) = \
-        p_dotted_name(s, as_allowed = 0)
+    if s.sy == '.':
+        # count relative import level
+        level = 0
+        while s.sy == '.':
+            level += 1
+            s.next()
+        if s.sy == 'cimport':
+            s.error("Relative cimport is not supported yet")
+    else:
+        level = None
+    if level is not None and s.sy == 'import':
+        # we are dealing with "from .. import foo, bar"
+        dotted_name_pos, dotted_name = s.position(), ''
+    elif level is not None and s.sy == 'cimport':
+        # "from .. cimport"
+        s.error("Relative cimport is not supported yet")
+    else:
+        (dotted_name_pos, _, dotted_name, _) = \
+            p_dotted_name(s, as_allowed = 0)
     if s.sy in ('import', 'cimport'):
         kind = s.sy
         s.next()
     else:
         s.error("Expected 'import' or 'cimport'")
+
     is_cimport = kind == 'cimport'
     is_parenthesized = False
     if s.sy == '*':
@@ -1245,6 +1271,8 @@ def p_from_import_statement(s, first_statement = 0):
     if dotted_name == '__future__':
         if not first_statement:
             s.error("from __future__ imports must occur at the beginning of the file")
+        elif level is not None:
+            s.error("invalid syntax")
         else:
             for (name_pos, name, as_name, kind) in imported_names:
                 if name == "braces":
@@ -1270,7 +1298,7 @@ def p_from_import_statement(s, first_statement = 0):
                 ExprNodes.IdentifierStringNode(name_pos, value = encoded_name))
             items.append(
                 (name,
-                 ExprNodes.NameNode(name_pos, 
+                 ExprNodes.NameNode(name_pos,
                                     name = as_name or name)))
         import_list = ExprNodes.ListNode(
             imported_names[0][0], args = imported_name_strings)
@@ -1278,6 +1306,7 @@ def p_from_import_statement(s, first_statement = 0):
         return Nodes.FromImportStatNode(pos,
             module = ExprNodes.ImportNode(dotted_name_pos,
                 module_name = ExprNodes.IdentifierStringNode(pos, value = dotted_name),
+                level = level,
                 name_list = import_list),
             items = items)
 
@@ -1359,8 +1388,8 @@ def p_while_statement(s):
     test = p_test(s)
     body = p_suite(s)
     else_clause = p_else_clause(s)
-    return Nodes.WhileStatNode(pos, 
-        condition = test, body = body, 
+    return Nodes.WhileStatNode(pos,
+        condition = test, body = body,
         else_clause = else_clause)
 
 def p_for_statement(s):
@@ -1368,16 +1397,17 @@ def p_for_statement(s):
     pos = s.position()
     s.next()
     kw = p_for_bounds(s, allow_testlist=True)
-    kw['body'] = p_suite(s)
-    kw['else_clause'] = p_else_clause(s)
+    body = p_suite(s)
+    else_clause = p_else_clause(s)
+    kw.update(dict(body = body, else_clause = else_clause))
     return Nodes.ForStatNode(pos, **kw)
-            
+
 def p_for_bounds(s, allow_testlist=True):
     target = p_for_target(s)
     if s.sy == 'in':
         s.next()
         iterator = p_for_iterator(s, allow_testlist)
-        return { 'target': target, 'iterator': iterator }
+        return dict( target = target, iterator = iterator )
     elif not s.in_python_file:
         if s.sy == 'from':
             s.next()
@@ -1396,7 +1426,7 @@ def p_for_bounds(s, allow_testlist=True):
             target = ExprNodes.NameNode(name2_pos, name = name2)
         else:
             if not target.is_name:
-                error(target.pos, 
+                error(target.pos,
                     "Target of for-from statement must be a variable name")
             elif name2 != target.name:
                 error(name2_pos,
@@ -1404,12 +1434,13 @@ def p_for_bounds(s, allow_testlist=True):
         if rel1[0] != rel2[0]:
             error(rel2_pos,
                 "Relation directions in for-from do not match")
-        return {'target': target, 
-                'bound1': bound1, 
-                'relation1': rel1, 
-                'relation2': rel2,
-                'bound2': bound2,
-                'step': step }
+        return dict(target = target,
+                    bound1 = bound1,
+                    relation1 = rel1,
+                    relation2 = rel2,
+                    bound2 = bound2,
+                    step = step,
+                    )
     else:
         s.expect('in')
         return {}
@@ -1423,7 +1454,7 @@ def p_for_from_relation(s):
         s.error("Expected one of '<', '<=', '>' '>='")
 
 def p_for_from_step(s):
-    if s.sy == 'by':
+    if s.sy == 'IDENT' and s.systring == 'by':
         s.next()
         step = p_bit_expr(s)
         return step
@@ -1564,7 +1595,7 @@ def p_with_items(s):
             body = p_with_items(s)
         else:
             body = p_suite(s)
-    return Nodes.WithStatNode(pos, manager = manager, 
+    return Nodes.WithStatNode(pos, manager = manager,
                               target = target, body = body)
 
 def p_with_template(s):
@@ -1595,6 +1626,8 @@ def p_simple_statement(s, first_statement = 0):
     #print "p_simple_statement:", s.sy, s.systring ###
     if s.sy == 'global':
         node = p_global_statement(s)
+    elif s.sy == 'nonlocal':
+        node = p_nonlocal_statement(s)
     elif s.sy == 'print':
         node = p_print_statement(s)
     elif s.sy == 'exec':
@@ -1627,15 +1660,27 @@ 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)
-    if s.sy == ';':
-        stats = [stat]
-        while s.sy == ';':
-            #print "p_simple_statement_list: maybe more to follow" ###
-            s.next()
-            if s.sy in ('NEWLINE', 'EOF'):
-                break
-            stats.append(p_simple_statement(s))
-        stat = Nodes.StatListNode(stats[0].pos, stats = stats)
+    pos = stat.pos
+    stats = []
+    if not isinstance(stat, Nodes.PassStatNode):
+        stats.append(stat)
+    while s.sy == ';':
+        #print "p_simple_statement_list: maybe more to follow" ###
+        s.next()
+        if s.sy in ('NEWLINE', 'EOF'):
+            break
+        stat = p_simple_statement(s, first_statement = first_statement)
+        if isinstance(stat, Nodes.PassStatNode):
+            continue
+        stats.append(stat)
+        first_statement = False
+
+    if not stats:
+        stat = Nodes.PassStatNode(pos)
+    elif len(stats) == 1:
+        stat = stats[0]
+    else:
+        stat = Nodes.StatListNode(pos, stats = stats)
     s.expect_newline("Syntax error in simple statement list")
     return stat
 
@@ -1701,7 +1746,6 @@ def p_statement(s, ctx, first_statement = 0):
         return p_IF_statement(s, ctx)
     elif s.sy == 'DECORATOR':
         if ctx.level not in ('module', 'class', 'c_class', 'function', 'property', 'module_pxd', 'c_class_pxd'):
-            print ctx.level
             s.error('decorator not allowed here')
         s.level = ctx.level
         decorators = p_decorators(s)
@@ -1773,9 +1817,14 @@ def p_statement_list(s, ctx, first_statement = 0):
     pos = s.position()
     stats = []
     while s.sy not in ('DEDENT', 'EOF'):
-        stats.append(p_statement(s, ctx, first_statement = first_statement))
-        first_statement = 0
-    if len(stats) == 1:
+        stat = p_statement(s, ctx, first_statement = first_statement)
+        if isinstance(stat, Nodes.PassStatNode):
+            continue
+        stats.append(stat)
+        first_statement = False
+    if not stats:
+        return Nodes.PassStatNode(pos)
+    elif len(stats) == 1:
         return stats[0]
     else:
         return Nodes.StatListNode(pos, stats = stats)
@@ -1831,21 +1880,21 @@ def p_positional_and_keyword_args(s, end_sy_set, templates = None):
             else:
                 base_type = p_c_base_type(s, templates = templates)
                 declarator = p_c_declarator(s, empty = 1)
-                arg = Nodes.CComplexBaseTypeNode(base_type.pos, 
+                arg = Nodes.CComplexBaseTypeNode(base_type.pos,
                     base_type = base_type, declarator = declarator)
                 parsed_type = True
             keyword_node = ExprNodes.IdentifierStringNode(
                 arg.pos, value = EncodedString(ident))
             keyword_args.append((keyword_node, arg))
             was_keyword = True
-                
+
         else:
             if looking_at_expr(s):
                 arg = p_test(s)
             else:
                 base_type = p_c_base_type(s, templates = templates)
                 declarator = p_c_declarator(s, empty = 1)
-                arg = Nodes.CComplexBaseTypeNode(base_type.pos, 
+                arg = Nodes.CComplexBaseTypeNode(base_type.pos,
                     base_type = base_type, declarator = declarator)
                 parsed_type = True
             positional_args.append(arg)
@@ -1887,7 +1936,7 @@ def p_c_complex_base_type(s):
     base_type = p_c_base_type(s)
     declarator = p_c_declarator(s, empty = 1)
     s.expect(')')
-    return Nodes.CComplexBaseTypeNode(pos, 
+    return Nodes.CComplexBaseTypeNode(pos,
         base_type = base_type, declarator = declarator)
 
 def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
@@ -1913,7 +1962,7 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
                 name = s.systring
                 s.next()
             else:
-                name = 'int'
+                name = 'int'  # long [int], short [int], long [int] complex, etc.
         if s.sy == 'IDENT' and s.systring == 'complex':
             complex = 1
             s.next()
@@ -1929,7 +1978,7 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
         name = s.systring
         s.next()
         if nonempty and s.sy != 'IDENT':
-            # Make sure this is not a declaration of a variable or function.  
+            # Make sure this is not a declaration of a variable or function.
             if s.sy == '(':
                 s.next()
                 if s.sy == '*' or s.sy == '**' or s.sy == '&':
@@ -1942,28 +1991,28 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
                 s.put_back('IDENT', name)
                 name = None
 
-    type_node = Nodes.CSimpleBaseTypeNode(pos, 
+    type_node = Nodes.CSimpleBaseTypeNode(pos,
         name = name, module_path = module_path,
         is_basic_c_type = is_basic, signed = signed,
-        complex = complex, longness = longness, 
+        complex = complex, longness = longness,
         is_self_arg = self_flag, templates = templates)
 
     if s.sy == '[':
         type_node = p_buffer_or_template(s, type_node, templates)
-    
+
     if s.sy == '.':
         s.next()
         name = p_ident(s)
         type_node = Nodes.CNestedBaseTypeNode(pos, base_type = type_node, name = name)
-    
+
     return type_node
 
 def p_buffer_or_template(s, base_type_node, templates):
     # s.sy == '['
     pos = s.position()
     s.next()
-    # Note that buffer_positional_options_count=1, so the only positional argument is dtype. 
-    # For templated types, all parameters are types. 
+    # Note that buffer_positional_options_count=1, so the only positional argument is dtype.
+    # For templated types, all parameters are types.
     positional_args, keyword_args = (
         p_positional_and_keyword_args(s, (']',), templates)
     )
@@ -1979,7 +2028,7 @@ def p_buffer_or_template(s, base_type_node, templates):
         keyword_args = keyword_dict,
         base_type_node = base_type_node)
     return result
-    
+
 
 def looking_at_name(s):
     return s.sy == 'IDENT' and not s.systring in calling_convention_words
@@ -2001,7 +2050,7 @@ def looking_at_expr(s):
             is_type = True
         elif s.sy == '*' or s.sy == '**':
             s.next()
-            is_type = s.sy == ')'
+            is_type = s.sy in (')', ']')
             s.put_back(*saved)
         elif s.sy == '(':
             s.next()
@@ -2039,6 +2088,7 @@ basic_c_type_names = ("void", "char", "int", "float", "double", "bint")
 special_basic_c_types = {
     # name : (signed, longness)
     "Py_UNICODE" : (0, 0),
+    "Py_UCS4"    : (0, 0),
     "Py_ssize_t" : (2, 0),
     "ssize_t"    : (2, 0),
     "size_t"     : (0, 0),
@@ -2127,13 +2177,13 @@ def p_c_func_declarator(s, pos, ctx, base, cmethod_flag):
     nogil = p_nogil(s)
     exc_val, exc_check = p_exception_value_clause(s)
     with_gil = p_with_gil(s)
-    return Nodes.CFuncDeclaratorNode(pos, 
+    return Nodes.CFuncDeclaratorNode(pos,
         base = base, args = args, has_varargs = ellipsis,
         exception_value = exc_val, exception_check = exc_check,
         nogil = nogil or ctx.nogil or with_gil, with_gil = with_gil)
 
 supported_overloaded_operators = cython.set([
-    '+', '-', '*', '/', '%', 
+    '+', '-', '*', '/', '%',
     '++', '--', '~', '|', '&', '^', '<<', '>>', ',',
     '==', '!=', '>=', '>', '<=', '<',
     '[]', '()',
@@ -2148,7 +2198,7 @@ def p_c_simple_declarator(s, ctx, empty, is_type, cmethod_flag,
         base = p_c_declarator(s, ctx, empty = empty, is_type = is_type,
                               cmethod_flag = cmethod_flag,
                               assignable = assignable, nonempty = nonempty)
-        result = Nodes.CPtrDeclaratorNode(pos, 
+        result = Nodes.CPtrDeclaratorNode(pos,
             base = base)
     elif s.sy == '**': # scanner returns this as a single token
         s.next()
@@ -2417,7 +2467,7 @@ def p_c_enum_definition(s, pos, ctx):
     return Nodes.CEnumDefNode(
         pos, name = name, cname = cname, items = items,
         typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
-        in_pxd = ctx.level == 'module_pxd')
+        api = ctx.api, in_pxd = ctx.level == 'module_pxd')
 
 def p_c_enum_line(s, ctx, items):
     if s.sy != 'pass':
@@ -2441,7 +2491,7 @@ def p_c_enum_item(s, ctx, items):
     if s.sy == '=':
         s.next()
         value = p_test(s)
-    items.append(Nodes.CEnumDefItemNode(pos, 
+    items.append(Nodes.CEnumDefItemNode(pos,
         name = name, cname = cname, value = value))
 
 def p_c_struct_or_union_definition(s, pos, ctx):
@@ -2475,10 +2525,10 @@ def p_c_struct_or_union_definition(s, pos, ctx):
         s.expect_dedent()
     else:
         s.expect_newline("Syntax error in struct or union definition")
-    return Nodes.CStructOrUnionDefNode(pos, 
+    return Nodes.CStructOrUnionDefNode(pos,
         name = name, cname = cname, kind = kind, attributes = attributes,
         typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
-        in_pxd = ctx.level == 'module_pxd', packed = packed)
+        api = ctx.api, in_pxd = ctx.level == 'module_pxd', packed = packed)
 
 def p_visibility(s, prev_visibility):
     pos = s.position()
@@ -2490,7 +2540,7 @@ def p_visibility(s, prev_visibility):
                 % (prev_visibility, visibility))
         s.next()
     return visibility
-    
+
 def p_c_modifiers(s):
     if s.sy == 'IDENT' and s.systring in ('inline',):
         modifier = s.systring
@@ -2512,7 +2562,7 @@ def p_c_func_or_var_declaration(s, pos, ctx):
         result = Nodes.CFuncDefNode(pos,
             visibility = ctx.visibility,
             base_type = base_type,
-            declarator = declarator, 
+            declarator = declarator,
             body = suite,
             doc = doc,
             modifiers = modifiers,
@@ -2530,7 +2580,7 @@ def p_c_func_or_var_declaration(s, pos, ctx):
                                         assignable = 1, nonempty = 1)
             declarators.append(declarator)
         s.expect_newline("Syntax error in C variable declaration")
-        result = Nodes.CVarDefNode(pos, 
+        result = Nodes.CVarDefNode(pos,
             visibility = ctx.visibility,
             base_type = base_type,
             declarators = declarators,
@@ -2563,7 +2613,8 @@ def p_ctypedef_statement(s, ctx):
         s.expect_newline("Syntax error in ctypedef statement")
         return Nodes.CTypeDefNode(
             pos, base_type = base_type,
-            declarator = declarator, visibility = visibility,
+            declarator = declarator,
+            visibility = visibility, api = api,
             in_pxd = ctx.level == 'module_pxd')
 
 def p_decorators(s):
@@ -2599,7 +2650,7 @@ def p_def_statement(s, decorators=None):
         s.next()
         return_type_annotation = p_test(s)
     doc, body = p_suite(s, Ctx(level = 'function'), with_doc = 1)
-    return Nodes.DefNode(pos, name = name, args = args, 
+    return Nodes.DefNode(pos, name = name, args = args,
         star_arg = star_arg, starstar_arg = starstar_arg,
         doc = doc, body = body, decorators = decorators,
         return_type_annotation = return_type_annotation)
@@ -2690,8 +2741,8 @@ def p_c_class_definition(s, pos,  ctx):
         base_class_module = ".".join(base_class_path[:-1])
         base_class_name = base_class_path[-1]
     if s.sy == '[':
-        if ctx.visibility not in ('public', 'extern'):
-            error(s.position(), "Name options only allowed for 'public' or 'extern' C class")
+        if ctx.visibility not in ('public', 'extern') and not ctx.api:
+            error(s.position(), "Name options only allowed for 'public', 'api', or 'extern' C class")
         objstruct_name, typeobj_name = p_c_class_options(s)
     if s.sy == ':':
         if ctx.level == 'module_pxd':
@@ -2715,7 +2766,10 @@ def p_c_class_definition(s, pos,  ctx):
             error(pos, "Type object name specification required for 'public' C class")
     elif ctx.visibility == 'private':
         if ctx.api:
-            error(pos, "Only 'public' C class can be declared 'api'")
+            if not objstruct_name:
+                error(pos, "Object struct name specification required for 'api' C class")
+            if not typeobj_name:
+                error(pos, "Type object name specification required for 'api' C class")
     else:
         error(pos, "Invalid class visibility '%s'" % ctx.visibility)
     return Nodes.CClassDefNode(pos,
@@ -2779,7 +2833,7 @@ def p_code(s, level=None):
             repr(s.sy), repr(s.systring)))
     return body
 
-COMPILER_DIRECTIVE_COMMENT_RE = re.compile(r"^#\s*cython:\s*((\w|[.])+\s*=.*)$")
+COMPILER_DIRECTIVE_COMMENT_RE = re.compile(r"^#\s*cython\s*:\s*((\w|[.])+\s*=.*)$")
 
 def p_compiler_directive_comments(s):
     result = {}