Permalink
Browse files

cython.locals(...) decorator for pure python type declarations.

  • Loading branch information...
1 parent 5249c53 commit 7771e20441de2e691e5f72e022f38cfb4745b9d2 @robertwb robertwb committed Oct 1, 2008
@@ -713,6 +713,15 @@ def compile_time_value(self, denv):
def analyse_types(self, env):
self.entry = env.add_string_const(self.value)
+
+ def analyse_as_type(self, env):
+ from TreeFragment import TreeFragment
+ 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)
+ sizeof_node = declaration.root.stats[0].expr
+ sizeof_node.analyse_types(env)
+ if isinstance(sizeof_node, SizeofTypeNode):
+ return sizeof_node.arg_type
def coerce_to(self, dst_type, env):
if dst_type.is_int:
@@ -886,6 +895,8 @@ def analyse_as_module(self, env):
return None
def analyse_as_type(self, env):
+ if self.name in PyrexTypes.rank_to_type_name:
+ return PyrexTypes.simple_c_type(1, 0, self.name)
entry = self.entry
if not entry:
entry = env.lookup(self.name)
@@ -2767,6 +2778,9 @@ def generate_evaluation_code(self, code):
def generate_disposal_code(self, code):
self.key.generate_disposal_code(code)
self.value.generate_disposal_code(code)
+
+ def __iter__(self):
+ return iter([self.key, self.value])
class ClassNode(ExprNode):
View
@@ -1166,11 +1166,16 @@ class CFuncDefNode(FuncDefNode):
# overridable whether or not this is a cpdef function
child_attrs = ["base_type", "declarator", "body", "py_func"]
-
+
def unqualified_name(self):
return self.entry.name
def analyse_declarations(self, env):
+ if 'locals' in env.directives:
+ directive_locals = env.directives['locals']
+ else:
+ directive_locals = {}
+ self.directive_locals = directive_locals
base_type = self.base_type.analyse(env)
# The 2 here is because we need both function and argument names.
name_declarator, type = self.declarator.analyse(base_type, env, nonempty = 2 * (self.body is not None))
@@ -1442,11 +1447,27 @@ def __init__(self, pos, **kwds):
entry = None
def analyse_declarations(self, env):
+ if 'locals' in env.directives:
+ directive_locals = env.directives['locals']
+ else:
+ directive_locals = {}
+ self.directive_locals = directive_locals
for arg in self.args:
base_type = arg.base_type.analyse(env)
name_declarator, type = \
arg.declarator.analyse(base_type, env)
arg.name = name_declarator.name
+ if arg.name in directive_locals:
+ type_node = directive_locals[arg.name]
+ other_type = type_node.analyse_as_type(env)
+ if other_type is None:
+ error(type_node.pos, "Not a type")
+ elif (type is not PyrexTypes.py_object_type
+ and not type.same_as(other_type)):
+ error(arg.base_type.pos, "Signature does not agree with previous declaration")
+ error(type_node.pos, "Previous declaration here")
+ else:
+ type = other_type
if name_declarator.cname:
error(self.pos,
"Python function argument cannot have C name specification")
@@ -58,13 +58,15 @@
option_types = {
'boundscheck' : bool,
'nonecheck' : bool,
- 'embedsignature' : bool
+ 'embedsignature' : bool,
+ 'locals' : dict,
}
option_defaults = {
'boundscheck' : True,
'nonecheck' : False,
'embedsignature' : False,
+ 'locals' : {}
}
def parse_option_value(name, value):
@@ -308,6 +308,14 @@ def visit_FromCImportStatNode(self, node):
newimp.append((pos, name, as_name, kind))
node.imported_names = newimpo
return node
+
+ def visit_SingleAssignmentNode(self, node):
+ if (isinstance(node.rhs, ImportNode) and
+ node.rhs.module_name.value == u'cython'):
+ self.cython_module_names.add(node.lhs.name)
+ else:
+ self.visitchildren(node)
+ return node
def visit_Node(self, node):
self.visitchildren(node)
@@ -318,7 +326,7 @@ def try_to_parse_option(self, node):
# decorator), returns (optionname, value).
# Otherwise, returns None
optname = None
- if isinstance(node, SimpleCallNode):
+ if isinstance(node, CallNode):
if (isinstance(node.function, AttributeNode) and
isinstance(node.function.obj, NameNode) and
node.function.obj.name in self.cython_module_names):
@@ -330,12 +338,25 @@ def try_to_parse_option(self, node):
if optname:
optiontype = Options.option_types.get(optname)
if optiontype:
- args = node.args
+ if isinstance(node, SimpleCallNode):
+ 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 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,
'The %s option takes one compile-time boolean argument' % optname)
return (optname, args[0].value)
+ elif optiontype is dict:
+ if len(args) != 0:
+ raise PostParseError(dec.function.pos,
+ 'The %s option takes no prepositional arguments' % optname)
+ return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
else:
assert False
@@ -367,7 +388,7 @@ def visit_DefNode(self, node):
else:
realdecs.append(dec)
node.decorators = realdecs
-
+
if options:
optdict = {}
options.reverse() # Decorators coming first take precedence
@@ -499,12 +520,19 @@ def visit_FuncDefNode(self, node):
lenv = node.create_local_scope(self.env_stack[-1])
node.body.analyse_control_flow(lenv) # this will be totally refactored
node.declare_arguments(lenv)
+ for var, type_node in node.directive_locals.items():
+ if not lenv.lookup_here(var): # don't redeclare args
+ type = type_node.analyse_as_type(lenv)
+ if type:
+ lenv.declare_var(var, type, type_node.pos)
+ else:
+ error(type_node.pos, "Not a type")
node.body.analyse_declarations(lenv)
self.env_stack.append(lenv)
self.visitchildren(node)
self.env_stack.pop()
return node
-
+
# Some nodes are no longer needed after declaration
# analysis and can be dropped. The analysis was performed
# on these nodes in a seperate recursive process from the
@@ -1168,6 +1168,7 @@ def error_condition(self, result_code):
(1, 0, "int"): c_int_type,
(1, 1, "int"): c_long_type,
(1, 2, "int"): c_longlong_type,
+ (1, 0, "long"): c_long_type,
(1, 0, "Py_ssize_t"): c_py_ssize_t_type,
(1, 0, "float"): c_float_type,
(1, 0, "double"): c_double_type,
@@ -1216,6 +1217,19 @@ def c_ptr_type(base_type):
return c_char_ptr_type
else:
return CPtrType(base_type)
+
+def Node_to_type(node, env):
+ from ExprNodes import NameNode, AttributeNode, StringNode, error
+ if isinstance(node, StringNode):
+ node = NameNode(node.pos, name=node.value)
+ if isinstance(node, NameNode) and node.name in rank_to_type_name:
+ return simple_c_type(1, 0, node.name)
+ elif isinstance(node, (AttributeNode, NameNode)):
+ node.analyze_types(env)
+ if not node.entry.is_type:
+ pass
+ else:
+ error(node.pos, "Bad type")
def public_decl(base, dll_linkage):
if dll_linkage:
@@ -289,8 +289,8 @@ class PyrexScanner(Scanner):
resword_dict = build_resword_dict()
def __init__(self, file, filename, parent_scanner = None,
- scope = None, context = None, source_encoding=None, parse_comments=True):
- Scanner.__init__(self, get_lexicon(), file, filename)
+ scope = None, context = None, source_encoding=None, parse_comments=True, initial_pos=None):
+ Scanner.__init__(self, get_lexicon(), file, filename, initial_pos)
if parent_scanner:
self.context = parent_scanner.context
self.included_files = parent_scanner.included_files
@@ -29,7 +29,7 @@ def find_module(self, module_name, relative_to = None, pos = None, need_pxd = 1)
raise AssertionError("Not yet supporting any cimports/includes from string code snippets")
return ModuleScope(module_name, parent_module = None, context = self)
-def parse_from_strings(name, code, pxds={}, level=None):
+def parse_from_strings(name, code, pxds={}, level=None, initial_pos=None):
"""
Utility method to parse a (unicode) string of code. This is mostly
used for internal Cython compiler purposes (creating code snippets
@@ -47,7 +47,8 @@ def parse_from_strings(name, code, pxds={}, level=None):
encoding = "UTF-8"
module_name = name
- initial_pos = (name, 1, 0)
+ if initial_pos is None:
+ initial_pos = (name, 1, 0)
code_source = StringSourceDescriptor(name, code)
context = StringParseContext([], name)
@@ -56,7 +57,7 @@ def parse_from_strings(name, code, pxds={}, level=None):
buf = StringIO(code.encode(encoding))
scanner = PyrexScanner(buf, code_source, source_encoding = encoding,
- scope = scope, context = context)
+ scope = scope, context = context, initial_pos = initial_pos)
if level is None:
tree = Parsing.p_module(scanner, 0, module_name)
else:
@@ -181,16 +182,15 @@ def strip_common_indent(lines):
return lines
class TreeFragment(object):
- def __init__(self, code, name="(tree fragment)", pxds={}, temps=[], pipeline=[], level=None):
+ def __init__(self, code, name="(tree fragment)", pxds={}, temps=[], pipeline=[], level=None, initial_pos=None):
if isinstance(code, unicode):
def fmt(x): return u"\n".join(strip_common_indent(x.split(u"\n")))
fmt_code = fmt(code)
fmt_pxds = {}
for key, value in pxds.iteritems():
fmt_pxds[key] = fmt(value)
-
- mod = t = parse_from_strings(name, fmt_code, fmt_pxds, level=level)
+ mod = t = parse_from_strings(name, fmt_code, fmt_pxds, level=level, initial_pos=initial_pos)
if level is None:
t = t.body # Make sure a StatListNode is at the top
if not isinstance(t, StatListNode):
View
@@ -60,7 +60,7 @@ class Scanner:
queue = None # list of tokens to be returned
trace = 0
- def __init__(self, lexicon, stream, name = ''):
+ def __init__(self, lexicon, stream, name = '', initial_pos = None):
"""
Scanner(lexicon, stream, name = '')
@@ -84,6 +84,8 @@ def __init__(self, lexicon, stream, name = ''):
self.cur_line_start = 0
self.cur_char = BOL
self.input_state = 1
+ if initial_pos is not None:
+ self.cur_line, self.cur_line_start = initial_pos[1], -initial_pos[2]
def read(self):
"""
View
@@ -0,0 +1,16 @@
+def empty_decorator(x):
+ return x
+
+def locals(**arg_types):
+ return empty_decorator
+
+def cast(type, arg):
+ # can/should we emulate anything here?
+ return arg
+
+py_int = int
+py_long = long
+py_float = float
+
+# They just have to exist...
+int = long = char = bint = uint = ulong = longlong = ulonglong = Py_ssize_t = float = double = None
View
@@ -0,0 +1,2 @@
+# Void cython.* directives (for case insensitive operating systems).
+from Shadow import *
View
@@ -2,5 +2,11 @@
# Cython -- Main Program, generic
#
-from Cython.Compiler.Main import main
-main(command_line = 1)
+if __name__ == '__main__':
+
+ from Cython.Compiler.Main import main
+ main(command_line = 1)
+
+else:
+ # Void cython.* directives.
+ from Cython.Shadow import *

0 comments on commit 7771e20

Please sign in to comment.