Commit b016353f authored by Robert Bradshaw's avatar Robert Bradshaw

More pure cython mode stuff.

parent 3bcf8140
...@@ -16,5 +16,11 @@ class CythonScope(ModuleScope): ...@@ -16,5 +16,11 @@ class CythonScope(ModuleScope):
defining = 1, defining = 1,
cname='<error>') cname='<error>')
def lookup_type(self, name):
# This function should go away when types are all first-level objects.
type = parse_basic_type(name)
if type:
return type
def create_cython_scope(context): def create_cython_scope(context):
return CythonScope(context) return CythonScope(context)
...@@ -564,6 +564,9 @@ class ExprNode(Node): ...@@ -564,6 +564,9 @@ class ExprNode(Node):
# reference, or temporary. # reference, or temporary.
return self.result_in_temp() return self.result_in_temp()
def as_cython_attribute(self):
return None
class AtomicExprNode(ExprNode): class AtomicExprNode(ExprNode):
# Abstract base class for expression nodes which have # Abstract base class for expression nodes which have
...@@ -715,6 +718,9 @@ class StringNode(ConstNode): ...@@ -715,6 +718,9 @@ class StringNode(ConstNode):
self.entry = env.add_string_const(self.value) self.entry = env.add_string_const(self.value)
def analyse_as_type(self, env): def analyse_as_type(self, env):
type = PyrexTypes.parse_basic_type(self.value)
if type is not None:
return type
from TreeFragment import TreeFragment from TreeFragment import TreeFragment
pos = (self.pos[0], self.pos[1], self.pos[2]-7) pos = (self.pos[0], self.pos[1], self.pos[2]-7)
declaration = TreeFragment(u"sizeof(%s)" % self.value, name=pos[0].filename, initial_pos=pos) declaration = TreeFragment(u"sizeof(%s)" % self.value, name=pos[0].filename, initial_pos=pos)
...@@ -849,7 +855,8 @@ class NameNode(AtomicExprNode): ...@@ -849,7 +855,8 @@ class NameNode(AtomicExprNode):
# entry Entry Symbol table entry # entry Entry Symbol table entry
# interned_cname string # interned_cname string
is_name = 1 is_name = True
is_cython_module = False
skip_assignment_decref = False skip_assignment_decref = False
entry = None entry = None
...@@ -895,8 +902,9 @@ class NameNode(AtomicExprNode): ...@@ -895,8 +902,9 @@ class NameNode(AtomicExprNode):
return None return None
def analyse_as_type(self, env): def analyse_as_type(self, env):
if self.name in PyrexTypes.rank_to_type_name: type = PyrexTypes.parse_basic_type(self.name)
return PyrexTypes.simple_c_type(1, 0, self.name) if type:
return type
entry = self.entry entry = self.entry
if not entry: if not entry:
entry = env.lookup(self.name) entry = env.lookup(self.name)
...@@ -1809,6 +1817,21 @@ class SimpleCallNode(CallNode): ...@@ -1809,6 +1817,21 @@ class SimpleCallNode(CallNode):
except Exception, e: except Exception, e:
self.compile_time_value_error(e) self.compile_time_value_error(e)
def analyse_as_type(self, env):
attr = self.function.as_cython_attribute()
if attr == 'pointer':
if len(self.args) != 1:
error(self.args.pos, "only one type allowed.")
else:
type = self.args[0].analyse_as_type(env)
if not type:
error(self.args[0].pos, "Unknown type")
else:
return PyrexTypes.CPtrType(type)
def explicit_args_kwds(self):
return self.args, None
def analyse_types(self, env): def analyse_types(self, env):
function = self.function function = self.function
function.is_called = 1 function.is_called = 1
...@@ -2038,6 +2061,12 @@ class GeneralCallNode(CallNode): ...@@ -2038,6 +2061,12 @@ class GeneralCallNode(CallNode):
except Exception, e: except Exception, e:
self.compile_time_value_error(e) self.compile_time_value_error(e)
def explicit_args_kwds(self):
if self.starstar_arg or not isinstance(self.positional_args, TupleNode):
raise PostParseError(self.pos,
'Compile-time keyword arguments must be explicit.')
return self.positional_args.args, self.keyword_args
def analyse_types(self, env): def analyse_types(self, env):
self.function.analyse_types(env) self.function.analyse_types(env)
self.positional_args.analyse_types(env) self.positional_args.analyse_types(env)
...@@ -2140,6 +2169,10 @@ class AttributeNode(ExprNode): ...@@ -2140,6 +2169,10 @@ class AttributeNode(ExprNode):
is_called = 0 is_called = 0
needs_none_check = True needs_none_check = True
def as_cython_attribute(self):
if isinstance(self.obj, NameNode) and self.obj.is_cython_module:
return self.attribute
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
# If coercing to a generic pyobject and this is a cpdef function # If coercing to a generic pyobject and this is a cpdef function
# we can create the corresponding attribute # we can create the corresponding attribute
...@@ -2217,9 +2250,7 @@ class AttributeNode(ExprNode): ...@@ -2217,9 +2250,7 @@ class AttributeNode(ExprNode):
def analyse_as_type(self, env): def analyse_as_type(self, env):
module_scope = self.obj.analyse_as_module(env) module_scope = self.obj.analyse_as_module(env)
if module_scope: if module_scope:
entry = module_scope.lookup_here(self.attribute) return module_scope.lookup_type(self.attribute)
if entry and entry.is_type:
return entry.type
return None return None
def analyse_as_extension_type(self, env): def analyse_as_extension_type(self, env):
......
...@@ -2597,8 +2597,67 @@ class SingleAssignmentNode(AssignmentNode): ...@@ -2597,8 +2597,67 @@ class SingleAssignmentNode(AssignmentNode):
child_attrs = ["lhs", "rhs"] child_attrs = ["lhs", "rhs"]
first = False first = False
declaration_only = False
def analyse_declarations(self, env): def analyse_declarations(self, env):
import ExprNodes
# handle declarations of the form x = cython.foo()
if isinstance(self.rhs, ExprNodes.CallNode):
func_name = self.rhs.function.as_cython_attribute()
if func_name:
args, kwds = self.rhs.explicit_args_kwds()
if func_name in ['declare', 'typedef']:
self.declaration_only = True
if len(args) != 1 or kwds is not None:
error(rhs.pos, "Can only declare one type at a time.")
return
type = args[0].analyse_as_type(env)
if type is None:
error(args[0].pos, "Unknown type")
return
lhs = self.lhs
if func_name == 'declare':
if isinstance(lhs, ExprNodes.NameNode):
vars = [(lhs.name, lhs.pos)]
elif isinstance(lhs, ExprNodes.TupleNode):
vars = [(var.name, var.pos) for var in lhs.args]
else:
error(lhs.pos, "Invalid declaration")
return
for var, pos in vars:
env.declare_var(var, type, pos, is_cdef = True)
else:
if not isinstance(lhs, ExprNodes.NameNode):
error(lhs.pos, "Invalid declaration.")
env.declare_typedef(lhs.name, type, self.pos, 'private')
elif func_name in ['struct', 'union']:
self.declaration_only = True
if len(args) > 0 or kwds is None:
error(rhs.pos, "Struct or union members must be given by name.")
return
members = []
for member, type_node in kwds.key_value_pairs:
type = type_node.analyse_as_type(env)
if type is None:
error(type_node.pos, "Unknown type")
else:
members.append((member.value, type, member.pos))
if len(members) < len(kwds.key_value_pairs):
return
if not isinstance(self.lhs, ExprNodes.NameNode):
error(self.lhs.pos, "Invalid declaration.")
name = self.lhs.name
scope = StructOrUnionScope(name)
env.declare_struct_or_union(name, func_name, scope, False, self.rhs.pos)
for member, type, pos in members:
scope.declare_var(member, type, pos)
if self.declaration_only:
return
else:
self.lhs.analyse_target_declaration(env) self.lhs.analyse_target_declaration(env)
def analyse_types(self, env, use_temp = 0): def analyse_types(self, env, use_temp = 0):
......
...@@ -313,11 +313,19 @@ class InterpretCompilerDirectives(CythonTransform): ...@@ -313,11 +313,19 @@ class InterpretCompilerDirectives(CythonTransform):
def visit_SingleAssignmentNode(self, node): def visit_SingleAssignmentNode(self, node):
if (isinstance(node.rhs, ImportNode) and if (isinstance(node.rhs, ImportNode) and
node.rhs.module_name.value == u'cython'): node.rhs.module_name.value == u'cython'):
self.cython_module_names.add(node.lhs.name) node = CImportStatNode(node.pos,
module_name = u'cython',
as_name = node.lhs.name)
self.visit_CImportStatNode(node)
else: else:
self.visitchildren(node) self.visitchildren(node)
return node return node
def visit_NameNode(self, node):
if node.name in self.cython_module_names:
node.is_cython_module = True
return node
def visit_Node(self, node): def visit_Node(self, node):
self.visitchildren(node) self.visitchildren(node)
return node return node
...@@ -339,15 +347,7 @@ class InterpretCompilerDirectives(CythonTransform): ...@@ -339,15 +347,7 @@ class InterpretCompilerDirectives(CythonTransform):
if optname: if optname:
optiontype = Options.option_types.get(optname) optiontype = Options.option_types.get(optname)
if optiontype: if optiontype:
if isinstance(node, SimpleCallNode): args, kwds = node.explicit_args_kwds()
args = node.args
kwds = None
else:
if node.starstar_arg or not isinstance(node.positional_args, TupleNode):
raise PostParseError(dec.function.pos,
'Compile-time keyword arguments must be explicit.' % optname)
args = node.positional_args.args
kwds = node.keyword_args
if optiontype is bool: if optiontype is bool:
if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode): if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
raise PostParseError(dec.function.pos, raise PostParseError(dec.function.pos,
...@@ -686,24 +686,20 @@ class EnvTransform(CythonTransform): ...@@ -686,24 +686,20 @@ class EnvTransform(CythonTransform):
class TransformBuiltinMethods(EnvTransform): class TransformBuiltinMethods(EnvTransform):
def cython_attribute(self, node): def visit_SingleAssignmentNode(self, node):
if (isinstance(node, AttributeNode) and if node.declaration_only:
isinstance(node.obj, NameNode) and return None
node.obj.name in self.cython_module_names): else:
return node.attribute
def visit_ModuleNode(self, node):
self.cython_module_names = node.cython_module_names
self.visitchildren(node) self.visitchildren(node)
return node return node
def visit_AttributeNode(self, node): def visit_AttributeNode(self, node):
attribute = self.cython_attribute(node) attribute = node.as_cython_attribute()
if attribute: if attribute:
if attribute == u'compiled': if attribute == u'compiled':
node = BoolNode(node.pos, value=True) node = BoolNode(node.pos, value=True)
else: else:
error(node.function.pos, u"'%s' not a valid cython attribute" % function) error(node.pos, u"'%s' not a valid cython attribute" % attribute)
return node return node
def visit_SimpleCallNode(self, node): def visit_SimpleCallNode(self, node):
...@@ -719,7 +715,7 @@ class TransformBuiltinMethods(EnvTransform): ...@@ -719,7 +715,7 @@ class TransformBuiltinMethods(EnvTransform):
return ExprNodes.DictNode(pos, key_value_pairs=items) return ExprNodes.DictNode(pos, key_value_pairs=items)
# cython.foo # cython.foo
function = self.cython_attribute(node.function) function = node.function.as_cython_attribute()
if function: if function:
if function == u'cast': if function == u'cast':
if len(node.args) != 2: if len(node.args) != 2:
...@@ -739,6 +735,11 @@ class TransformBuiltinMethods(EnvTransform): ...@@ -739,6 +735,11 @@ class TransformBuiltinMethods(EnvTransform):
node = SizeofTypeNode(node.function.pos, arg_type=type) node = SizeofTypeNode(node.function.pos, arg_type=type)
else: else:
node = SizeofVarNode(node.function.pos, operand=node.args[0]) node = SizeofVarNode(node.function.pos, operand=node.args[0])
elif function == 'address':
if len(node.args) != 1:
error(node.function.pos, u"sizeof takes exactly one argument" % function)
else:
node = AmpersandNode(node.function.pos, operand=node.args[0])
else: else:
error(node.function.pos, u"'%s' not a valid cython language construct" % function) error(node.function.pos, u"'%s' not a valid cython language construct" % function)
......
...@@ -1182,6 +1182,7 @@ modifiers_and_name_to_type = { ...@@ -1182,6 +1182,7 @@ modifiers_and_name_to_type = {
(2, 0, "Py_ssize_t"): c_py_ssize_t_type, (2, 0, "Py_ssize_t"): c_py_ssize_t_type,
(1, 0, "long"): c_long_type, (1, 0, "long"): c_long_type,
(1, 0, "longlong"): c_longlong_type,
(1, 0, "bint"): c_bint_type, (1, 0, "bint"): c_bint_type,
} }
...@@ -1206,6 +1207,21 @@ def simple_c_type(signed, longness, name): ...@@ -1206,6 +1207,21 @@ def simple_c_type(signed, longness, name):
# Returns None if arguments don't make sense. # Returns None if arguments don't make sense.
return modifiers_and_name_to_type.get((signed, longness, name)) return modifiers_and_name_to_type.get((signed, longness, name))
def parse_basic_type(name):
base = None
if name.startswith('p_'):
base = parse_basic_type(name[2:])
elif name.startswith('p'):
base = parse_basic_type(name[1:])
elif name.endswith('*'):
base = parse_basic_type(name[:-1])
if base:
return CPtrType(base)
elif name.startswith('u'):
return simple_c_type(0, 0, name[1:])
else:
return simple_c_type(1, 0, name)
def c_array_type(base_type, size): def c_array_type(base_type, size):
# Construct a C array type. # Construct a C array type.
if base_type is c_char_type: if base_type is c_char_type:
......
...@@ -503,6 +503,11 @@ class Scope: ...@@ -503,6 +503,11 @@ class Scope:
entry = self.declare_var(name, py_object_type, None) entry = self.declare_var(name, py_object_type, None)
return entry return entry
def lookup_type(self, name):
entry = self.lookup(name)
if entry and entry.is_type:
return entry.type
def add_string_const(self, value, identifier = False): def add_string_const(self, value, identifier = False):
# Add an entry for a string constant. # Add an entry for a string constant.
if identifier: if identifier:
......
...@@ -6,17 +6,157 @@ def empty_decorator(x): ...@@ -6,17 +6,157 @@ def empty_decorator(x):
def locals(**arg_types): def locals(**arg_types):
return empty_decorator return empty_decorator
# Emulated language constructs
def cast(type, arg): def cast(type, arg):
# can/should we emulate anything here? if callable(type):
return type(arg)
else:
return arg return arg
def sizeof(arg): def sizeof(arg):
# can/should we emulate anything here?
return 1 return 1
def address(arg):
return pointer(type(arg))([arg])
def declare(type):
if callable(type):
return type()
else:
return None
# Emulated types
class CythonType(object):
def _pointer(self, n=1):
for i in range(n):
self = pointer(self)
return self
def __getitem__(self, ix):
return array(self, ix)
class PointerType(CythonType):
def __init__(self, value=None):
if isinstance(value, ArrayType):
self._items = [cast(self._basetype, a) for a in value._items]
elif isinstance(value, list):
self._items = [cast(self._basetype, a) for a in value]
elif value is None:
self._items = []
else:
raise ValueError
def __getitem__(self, ix):
if ix < 0:
raise IndexError, "negative indexing not allowed in C"
return self._items[ix]
def __setitem__(self, ix, value):
if ix < 0:
raise IndexError, "negative indexing not allowed in C"
self._items[ix] = cast(self._basetype, value)
class ArrayType(PointerType):
def __init__(self):
self._items = [None] * self._n
class StructType(CythonType):
def __init__(self, **data):
for key, value in data.items():
setattr(self, key, value)
def __setattr__(self, key, value):
if key in self._members:
self.__dict__[key] = cast(self._members[key], value)
else:
raise AttributeError, "Struct has no member '%s'" % key
class UnionType(CythonType):
def __init__(self, **data):
if len(data) > 0:
raise AttributeError, "Union can only store one field at a time."
for key, value in data.items():
setattr(self, key, value)
def __setattr__(self, key, value):
if key in '__dict__':
CythonType.__setattr__(self, key, value)
elif key in self._members:
self.__dict__ = {key: cast(self._members[key], value)}
else:
raise AttributeError, "Union has no member '%s'" % key
def pointer(basetype):
class PointerInstance(PointerType):
_basetype = basetype
return PointerInstance
def array(basetype, n):
class ArrayInstance(ArrayType):
_basetype = basetype
_n = n
return ArrayInstance
def struct(**members):
class StructInstance(StructType):
_members = members
for key in members.keys():
setattr(StructInstance, key, None)
return StructInstance
def union(**members):
class UnionInstance(UnionType):
_members = members
for key in members.keys():
setattr(UnionInstance, key, None)
return UnionInstance
class typedef(CythonType):
def __init__(self, type):
self._basetype = type
def __call__(self, value=None):
if value is not None:
value = cast(self._basetype, value)
return value
py_int = int py_int = int
py_long = long py_long = long
py_float = float py_float = float
# They just have to exist...
int = long = char = bint = uint = ulong = longlong = ulonglong = Py_ssize_t = float = double = None # Predefined types
int_types = ['char', 'short', 'int', 'long', 'longlong', 'Py_ssize_t']
float_types = ['double', 'float']
other_types = ['bint', 'Py_ssize_t', 'void']
gs = globals()
for name in int_types:
gs[name] = typedef(py_int)
gs['u'+name] = typedef(py_int)
double = float = typedef(py_float)
bint = typedef(bool)
void = typedef(int)
for t in int_types + float_types + other_types:
for i in range(1, 4):
gs["%s_%s" % ('p'*i, t)] = globals()[t]._pointer(i)
void = typedef(None)
NULL = None
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment