import Future
import Options
+
class Ctx(object):
- # Parsing context
+ """ Parsing context
+
+ * namespace (string): C++ namespace of the source (`None` for C
+ objects, set if the symbol is external)
+ * cdef_flag (boolean): Symbol (data) has a C definition.
+ * c_visibility ('private'|'public'|'extern'|'ignore'):
+
+ * private: Symbol is not accessible to external C code
+ * public: Symbol is accessible to external C code
+ * extern: Symbol is defined elsewhere (otherwise a local
+ definition is created).
+ * ignore: ? something about symbol re-definition?
+
+ * api (boolean): Add to generated header file
+
+
+ * visibility ('private'|'public'|'readonly'):
+
+ * private: Object is not exposed to Python code.
+ * public: Python can read/write to the object's data.
+ * readonly: Python can read (but nut write) the object's data.
+
+ * overridable (boolean): Python references can be overridden in
+ Python (if the object is visible). This is only supported in
+ class methods.
+ """
level = 'other'
- visibility = 'private'
- cdef_flag = 0
typedef_flag = 0
- api = 0
- overridable = 0
nogil = 0
- namespace = None
templates = None
- def __init__(self, **kwds):
- self.__dict__.update(kwds)
+ namespace = None
+ cdef_flag = 0
+ c_visibility = 'private'
+ api = 0
+
+ visibility = 'public'
+ overridable = 0
+
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
def __call__(self, **kwds):
ctx = Ctx()
return result
def p_statement(s, ctx, first_statement = 0):
- cdef_flag = ctx.cdef_flag
decorators = None
- if s.sy == 'ctypedef':
- if ctx.level not in ('module', 'module_pxd'):
- s.error("ctypedef statement not allowed here")
- #if ctx.api:
- # error(s.position(), "'api' not allowed with 'ctypedef'")
- return p_ctypedef_statement(s, ctx)
- elif s.sy == 'DEF':
+ pos = s.position()
+ if s.sy == 'DEF':
return p_DEF_statement(s)
elif s.sy == 'IF':
return p_IF_statement(s, ctx)
decorators = p_decorators(s)
if s.sy not in ('def', 'cdef', 'cpdef', 'class'):
s.error("Decorators can only be followed by functions or classes")
- elif s.sy == 'pass' and cdef_flag:
+ elif s.sy == 'pass' and ctx.cdef_flag:
# empty cdef block
return p_pass_statement(s, with_newline = 1)
-
- overridable = 0
- if s.sy == 'cdef':
- cdef_flag = 1
- s.next()
- elif s.sy == 'cpdef':
- cdef_flag = 1
- overridable = 1
- s.next()
- if cdef_flag:
+ sy = s.sy
+ ctx = p_binding(s, ctx)
+ if sy == 'ctypedef':
+ if ctx.level not in ('module', 'module_pxd'):
+ s.error("ctypedef statement not allowed here")
+ return p_ctypedef_statement(s, pos, ctx)
+ elif ctx.cdef_flag:
if ctx.level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_pxd'):
s.error('cdef statement not allowed here')
s.level = ctx.level
- node = p_cdef_statement(s, ctx(overridable = overridable))
- if decorators is not None:
- if not isinstance(node, (Nodes.CFuncDefNode, Nodes.CVarDefNode, Nodes.CClassDefNode)):
- s.error("Decorators can only be followed by functions or classes")
- node.decorators = decorators
- return node
- else:
- if ctx.api:
+ if ctx.api:
+ if ctx.cdef_flag:
+ if ctx.c_visibility == 'extern':
+ error(pos, "Cannot combine 'api' with 'extern'")
+ else:
s.error("'api' not allowed with this statement")
- elif s.sy == 'def':
- # def statements aren't allowed in pxd files, except
- # as part of a cdef class
- if ('pxd' in ctx.level) and (ctx.level != 'c_class_pxd'):
- s.error('def statement not allowed here')
- s.level = ctx.level
- return p_def_statement(s, decorators)
- elif s.sy == 'class':
+ if ctx.cdef_flag and p_nogil(s):
+ ctx.nogil = 1
+ if ctx.overridable:
+ error(pos, "cdef blocks cannot be declared cpdef")
+ return p_cdef_block(s, ctx)
+ elif s.sy == ':' and ctx.cdef_flag:
+ if ctx.overridable:
+ error(pos, "cdef blocks cannot be declared cpdef")
+ return p_cdef_block(s, ctx)
+ elif s.sy == 'def':
+ # def statements aren't allowed in pxd files, except
+ # as part of a cdef class
+ if ('pxd' in ctx.level) and (ctx.level != 'c_class_pxd'):
+ s.error('def statement not allowed here')
+ s.level = ctx.level
+ return p_def_statement(s, decorators)
+ elif s.sy == 'class':
+ if ctx.cdef_flag:
+ if ctx.level not in ('module', 'module_pxd'):
+ error(pos, "Extension type definition not allowed here")
+ if ctx.overridable:
+ error(pos, "Extension types cannot be declared cpdef")
+ return p_c_class_definition(s, pos, ctx, decorators)
+ else:
if ctx.level not in ('module', 'function', 'class', 'other'):
s.error("class definition not allowed here")
- return p_class_statement(s, decorators)
- elif s.sy == 'include':
+ return p_class_statement(s, decorators)
+ elif s.sy == 'from' and ctx.c_visibility == 'extern':
+ return p_cdef_extern_block(s, pos, ctx)
+ elif s.sy == 'import' and ctx.cdef_flag:
+ s.next()
+ return p_cdef_extern_block(s, pos, ctx)
+ elif s.sy == 'include':
+ if ctx.level not in ('module', 'module_pxd'):
+ s.error("include statement not allowed here")
+ 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 ctx.cdef_flag:
+ if s.sy == 'IDENT' and s.systring == 'cppclass':
+ if ctx.c_visibility != 'extern':
+ error(pos, "C++ classes need to be declared extern")
+ return p_cpp_class_definition(s, pos, ctx)
+ elif s.sy == 'IDENT' and s.systring in ("struct", "union", "enum", "packed"):
if ctx.level not in ('module', 'module_pxd'):
- s.error("include statement not allowed here")
- 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 ctx.level != 'property':
- return p_pass_statement(s, with_newline = 1)
- else:
- if ctx.level in ('c_class_pxd', 'property'):
- s.error("Executable statement not allowed here")
- if s.sy == 'if':
- return p_if_statement(s)
- elif s.sy == 'while':
- return p_while_statement(s)
- elif s.sy == 'for':
- return p_for_statement(s)
- elif s.sy == 'try':
- return p_try_statement(s)
- elif s.sy == 'with':
- return p_with_statement(s)
+ error(pos, "C struct/union/enum definition not allowed here")
+ if ctx.overridable:
+ error(pos, "C struct/union/enum cannot be declared cpdef")
+ if s.systring == "enum":
+ return p_c_enum_definition(s, pos, ctx)
else:
- return p_simple_statement_list(
- s, ctx, first_statement = first_statement)
+ return p_c_struct_or_union_definition(s, pos, ctx)
+ return p_c_func_or_var_declaration(s, pos, ctx, decorators)
+ elif s.sy == 'pass' and ctx.level != 'property':
+ return p_pass_statement(s, with_newline = 1)
+ elif ctx.level in ('c_class_pxd', 'property'):
+ s.error("Executable statement not allowed here")
+ elif s.sy == 'if':
+ return p_if_statement(s)
+ elif s.sy == 'while':
+ return p_while_statement(s)
+ elif s.sy == 'for':
+ return p_for_statement(s)
+ elif s.sy == 'try':
+ return p_try_statement(s)
+ elif s.sy == 'with':
+ return p_with_statement(s)
+ return p_simple_statement_list(
+ s, ctx, first_statement = first_statement)
def p_statement_list(s, ctx, first_statement = 0):
# Parse a series of statements separated by newlines.
base_type = base_type, declarator = declarator)
def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
- #print "p_c_simple_base_type: self_flag =", self_flag, nonempty
is_basic = 0
signed = 1
longness = 0
return True
def looking_at_base_type(s):
- #print "looking_at_base_type?", s.sy, s.systring, s.position()
return s.sy == 'IDENT' and s.systring in base_type_start_words
def looking_at_dotted_name(s):
error(s.position(), "Empty declarator")
name = ""
cname = None
+ if name == 'Rectangle':
+ pass
if cname is None and ctx.namespace is not None and nonempty:
cname = ctx.namespace + "::" + name
- if name == 'operator' and ctx.visibility == 'extern' and nonempty:
+ if name == 'operator' and ctx.c_visibility == 'extern' and nonempty:
op = s.sy
if [1 for c in op if c in '+-*/<=>!%&|([^~,']:
s.next()
else:
return 0
-def p_cdef_statement(s, ctx):
- pos = s.position()
- 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, pos, ctx)
- elif p_nogil(s):
- ctx.nogil = 1
- if ctx.overridable:
- error(pos, "cdef blocks cannot be declared cpdef")
- return p_cdef_block(s, ctx)
- elif s.sy == ':':
- if ctx.overridable:
- error(pos, "cdef blocks cannot be declared cpdef")
- return p_cdef_block(s, ctx)
- elif s.sy == 'class':
- if ctx.level not in ('module', 'module_pxd'):
- error(pos, "Extension type definition not allowed here")
- if ctx.overridable:
- error(pos, "Extension types cannot be declared cpdef")
- return p_c_class_definition(s, pos, ctx)
- elif s.sy == 'IDENT' and s.systring == 'cppclass':
- if ctx.visibility != 'extern':
- error(pos, "C++ classes need to be declared extern")
- return p_cpp_class_definition(s, pos, ctx)
- elif s.sy == 'IDENT' and s.systring in ("struct", "union", "enum", "packed"):
- if ctx.level not in ('module', 'module_pxd'):
- error(pos, "C struct/union/enum definition not allowed here")
- if ctx.overridable:
- error(pos, "C struct/union/enum cannot be declared cpdef")
- if s.systring == "enum":
- return p_c_enum_definition(s, pos, ctx)
- else:
- return p_c_struct_or_union_definition(s, pos, ctx)
- else:
- return p_c_func_or_var_declaration(s, pos, ctx)
-
def p_cdef_block(s, ctx):
return p_suite(s, ctx(cdef_flag = 1))
s.next()
else:
include_file = p_string_literal(s, 'u')[2]
- ctx = ctx(cdef_flag = 1, visibility = 'extern')
+ ctx = ctx()
+ ctx.cdef_flag = 1
+ ctx.c_visibility = 'extern'
if s.systring == "namespace":
s.next()
ctx.namespace = p_string_literal(s, 'u')[2]
items = None
s.expect(':')
items = []
+ # Work around overloading of the 'public' keyword.
+ item_ctx = ctx()
+ if item_ctx.c_visibility in ('public', 'extern'):
+ item_ctx.visibility = 'public'
+ else:
+ item_ctx.c_visibility = 'public'
+ item_ctx.visibility = 'private'
if s.sy != 'NEWLINE':
- p_c_enum_line(s, ctx, items)
+ p_c_enum_line(s, item_ctx, items)
else:
s.next() # 'NEWLINE'
s.expect_indent()
while s.sy not in ('DEDENT', 'EOF'):
- p_c_enum_line(s, ctx, items)
+ p_c_enum_line(s, item_ctx, items)
s.expect_dedent()
return Nodes.CEnumDefNode(
pos, name = name, cname = cname, items = items,
- typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
- api = ctx.api, in_pxd = ctx.level == 'module_pxd')
+ typedef_flag = ctx.typedef_flag, c_visibility = ctx.c_visibility,
+ visibility = ctx.visibility, api = ctx.api,
+ in_pxd = ctx.level == 'module_pxd')
def p_c_enum_line(s, ctx, items):
if s.sy != 'pass':
def p_c_enum_item(s, ctx, items):
pos = s.position()
+ ctx = p_binding(s, ctx)
name = p_ident(s)
cname = p_opt_cname(s)
if cname is None and ctx.namespace is not None:
s.next()
value = p_test(s)
items.append(Nodes.CEnumDefItemNode(pos,
- name = name, cname = cname, value = value))
+ name = name, cname = cname, value = value,
+ c_visibility = ctx.c_visibility, visibility = ctx.visibility,
+ in_pxd = ctx.level == 'module_pxd'))
+
def p_c_struct_or_union_definition(s, pos, ctx):
packed = False
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, s.position(), body_ctx))
- else:
+ if s.sy == 'pass':
s.next()
s.expect_newline("Expected a newline")
+ else:
+ body_ctx = p_binding(s, Ctx())
+ attributes.append(
+ p_c_func_or_var_declaration(s, s.position(), body_ctx))
s.expect_dedent()
else:
s.expect_newline("Syntax error in struct or union definition")
- return Nodes.CStructOrUnionDefNode(pos,
- name = name, cname = cname, kind = kind, attributes = attributes,
- typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
- api = ctx.api, in_pxd = ctx.level == 'module_pxd', packed = packed)
+ return Nodes.CStructOrUnionDefNode(pos = pos,
+ name = name,
+ cname = cname,
+ kind = kind,
+ attributes = attributes,
+ typedef_flag = ctx.typedef_flag,
+ cdef_flag = ctx.cdef_flag,
+ overridable = ctx.overridable,
+ c_visibility = ctx.c_visibility,
+ visibility = ctx.visibility,
+ api = ctx.api,
+ in_pxd = ctx.level == 'module_pxd',
+ packed = packed)
-def p_visibility(s, prev_visibility):
+def p_visibility(s, ctx):
pos = s.position()
- visibility = prev_visibility
if s.sy == 'IDENT' and s.systring in ('extern', 'public', 'readonly'):
visibility = s.systring
- if prev_visibility != 'private' and visibility != prev_visibility:
- s.error("Conflicting visibility options '%s' and '%s'"
- % (prev_visibility, visibility))
+ outer_scope = ctx.level in ('module', 'module_pxd')
+ if visibility == 'extern':
+ #if prev_visibility != 'private' and visibility != prev_visibility:
+ # s.error("Conflicting visibility options '%s' and '%s'"
+ # % (prev_visibility, visibility))
+ ctx.c_visibility = 'extern'
+ # Need to restore/set default value for Python visibility?
+ elif outer_scope and visibility not in ('readonly',):
+ ctx.c_visibility = visibility
+ else:
+ ctx.visibility = visibility
+ if ctx.visibility != 'private':
+ ctx.c_visibility = 'public'
s.next()
- return visibility
+ return ctx
def p_c_modifiers(s):
if s.sy == 'IDENT' and s.systring in ('inline',):
return [modifier] + p_c_modifiers(s)
return []
-def p_c_func_or_var_declaration(s, pos, ctx):
+def p_binding(s, ctx):
+ new_ctx = ctx()
+ new_ctx.overridable = 0
+ if s.sy == 'cdef':
+ new_ctx.cdef_flag = 1
+ s.next()
+ elif s.sy == 'cpdef':
+ new_ctx.cdef_flag = 1
+ new_ctx.overridable = 1
+ s.next()
+ elif s.sy == 'ctypedef':
+ new_ctx.typedef_flag = 1
+ new_ctx.cdef_flag = 1
+ s.next()
+ if new_ctx.cdef_flag:
+ new_ctx = p_visibility(s, new_ctx)
+ new_ctx.api = ctx.api or p_api(s)
+ return new_ctx
+
+def p_c_func_or_var_declaration(s, pos, ctx, decorators=None):
cmethod_flag = ctx.level in ('c_class', 'c_class_pxd')
modifiers = p_c_modifiers(s)
base_type = p_c_base_type(s, nonempty = 1, templates = ctx.templates)
s.error("C function definition not allowed here")
doc, suite = p_suite(s, Ctx(level = 'function'), with_doc = 1)
result = Nodes.CFuncDefNode(pos,
+ cdef_flag = ctx.cdef_flag,
+ overridable = ctx.overridable,
+ c_visibility = ctx.c_visibility,
visibility = ctx.visibility,
base_type = base_type,
declarator = declarator,
+ decorators = decorators,
body = suite,
doc = doc,
modifiers = modifiers,
- api = ctx.api,
- overridable = ctx.overridable)
+ api = ctx.api)
else:
#if api:
# s.error("'api' not allowed with variable declaration")
declarators.append(declarator)
s.expect_newline("Syntax error in C variable declaration")
result = Nodes.CVarDefNode(pos,
+ c_visibility = ctx.c_visibility,
visibility = ctx.visibility,
+ api = ctx.api,
+ overridable = ctx.overridable,
base_type = base_type,
declarators = declarators,
- in_pxd = ctx.level == 'module_pxd',
- api = ctx.api,
- overridable = ctx.overridable)
+ decorators = decorators,
+ in_pxd = ctx.level == 'module_pxd')
return result
-def p_ctypedef_statement(s, ctx):
- # s.sy == 'ctypedef'
- pos = s.position()
- s.next()
- visibility = p_visibility(s, ctx.visibility)
- api = p_api(s)
- ctx = ctx(typedef_flag = 1, visibility = visibility)
- if api:
- ctx.api = 1
+def p_ctypedef_statement(s, pos, ctx):
if s.sy == 'class':
return p_c_class_definition(s, pos, ctx)
elif s.sy == 'IDENT' and s.systring in ('packed', 'struct', 'union', 'enum'):
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, api = api,
- in_pxd = ctx.level == 'module_pxd')
+ pos, base_type = base_type, declarator = declarator,
+ c_visibility = ctx.c_visibility, visibility = ctx.visibility,
+ api = ctx.api, in_pxd = ctx.level == 'module_pxd')
def p_decorators(s):
decorators = []
starstar_arg = starstar_arg,
doc = doc, body = body, decorators = decorators)
-def p_c_class_definition(s, pos, ctx):
+def p_c_class_definition(s, pos, ctx, decorators=None):
# s.sy == 'class'
s.next()
module_path = []
s.next()
module_path.append(class_name)
class_name = p_ident(s)
- if module_path and ctx.visibility != 'extern':
+ if module_path and ctx.c_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()
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') and not ctx.api:
+ if ctx.c_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 == ':':
body_level = 'c_class_pxd'
else:
body_level = 'c_class'
- doc, body = p_suite(s, Ctx(level = body_level), with_doc = 1)
+ body_ctx = Ctx(
+ level = body_level,
+ visibility = 'private',
+ c_visibility = 'private')
+ doc, body = p_suite(s, body_ctx, with_doc = 1)
else:
s.expect_newline("Syntax error in C class definition")
doc = None
body = None
- if ctx.visibility == 'extern':
+ if ctx.c_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 ctx.visibility == 'public':
+ elif ctx.c_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 ctx.visibility == 'private':
+ elif ctx.c_visibility == 'private':
if ctx.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)
+ error(pos, "Invalid class visibility '%s'" % ctx.visibility_string())
return Nodes.CClassDefNode(pos,
+ c_visibility = ctx.c_visibility,
visibility = ctx.visibility,
typedef_flag = ctx.typedef_flag,
api = ctx.api,
base_class_name = base_class_name,
objstruct_name = objstruct_name,
typeobj_name = typeobj_name,
+ decorators = decorators,
in_pxd = ctx.level == 'module_pxd',
doc = doc,
body = body)
else:
level = 'module'
- body = p_statement_list(s, Ctx(level = level), first_statement = 1)
+ ctx = Ctx(level = level)
+ body = p_statement_list(s, ctx, first_statement = 1)
if s.sy != 'EOF':
s.error("Syntax error in statement [%s,%s]" % (
repr(s.sy), repr(s.systring)))
s.expect('NEWLINE')
s.expect_indent()
attributes = []
- body_ctx = Ctx(visibility = ctx.visibility)
+ body_ctx = Ctx()
+ #body_ctx = p_visibility(s, body_ctx)
+ body_ctx.c_visibility = ctx.c_visibility
+ body_ctx.visibility = ctx.visibility
body_ctx.templates = templates
while s.sy != 'DEDENT':
if s.systring == 'cppclass':
name = class_name,
cname = cname,
base_classes = base_classes,
+ c_visibility = ctx.c_visibility,
visibility = ctx.visibility,
in_pxd = ctx.level == 'module_pxd',
attributes = attributes,
f.write("%s]\n" % ind)
return
f.write("%s%s\n" % (ind, node))
-