code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
-class PyCFunctionNode(AtomicExprNode):
+class PyCFunctionNode(ExprNode):
# Helper class used in the implementation of Python
# class definitions. Constructs a PyCFunction object
# from a PyMethodDef struct.
#
# pymethdef_cname string PyMethodDef structure
# self_object ExprNode or None
-
+
+ subexprs = []
self_object = None
def analyse_types(self, env):
gil_message = "Constructing Python function"
- def generate_result_code(self, code):
+ def self_result_code(self):
if self.self_object is None:
self_result = "NULL"
else:
self_result = self.self_object.py_result()
+ return self_result
+
+ def generate_result_code(self, code):
code.putln(
"%s = PyCFunction_New(&%s, %s); %s" % (
self.result(),
self.pymethdef_cname,
- self_result,
+ self.self_result_code(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
+class InnerFunctionNode(PyCFunctionNode):
+ # Special PyCFunctionNode that depends on a closure class
+ #
+ def self_result_code(self):
+ return "((PyObject*)%s)" % Naming.cur_scope_cname
+
+class LambdaNode(InnerFunctionNode):
+ # Lambda expression node (only used as a function reference)
+ #
+ # args [CArgDeclNode] formal arguments
+ # star_arg PyArgDeclNode or None * argument
+ # starstar_arg PyArgDeclNode or None ** argument
+ # lambda_name string a module-globally unique lambda name
+ # result_expr ExprNode
+ # def_node DefNode the underlying function 'def' node
+
+ child_attrs = ['def_node']
+
+ def_node = None
+ name = StringEncoding.EncodedString('<lambda>')
+
+ def analyse_declarations(self, env):
+ #self.def_node.needs_closure = self.needs_closure
+ self.def_node.analyse_declarations(env)
+ self.pymethdef_cname = self.def_node.entry.pymethdef_cname
+ env.add_lambda_def(self.def_node)
+
#-------------------------------------------------------------------
#
# Unary operator nodes
code.globalstate.insert_global_var_declarations_into(code)
self.generate_cached_builtins_decls(env, code)
+ # generate lambda function definitions
+ for node in env.lambda_defs:
+ node.generate_function_definitions(env, code)
+ # generate normal function definitions
self.body.generate_function_definitions(env, code)
code.mark_pos(None)
self.generate_typeobj_definitions(env, code)
convert_func_prefix = pyrex_prefix + "convert_"
closure_scope_prefix = pyrex_prefix + "scope_"
closure_class_prefix = pyrex_prefix + "scope_struct_"
+lambda_func_prefix = pyrex_prefix + "lambda_"
args_cname = pyrex_prefix + "args"
pykwdlist_cname = pyrex_prefix + "pyargnames"
lenv.mangle_closure_cnames(outer_scope_cname)
# Generate closure function definitions
self.body.generate_function_definitions(lenv, code)
+ # generate lambda function definitions
+ for node in lenv.lambda_defs:
+ node.generate_function_definitions(lenv, code)
is_getbuffer_slot = (self.entry.name == "__getbuffer__" and
self.entry.scope.is_c_class_scope)
self.generate_cached_builtins_decls(lenv, code)
# ----- Function header
code.putln("")
+ with_pymethdef = env.is_py_class_scope or env.is_closure_scope
if self.py_func:
self.py_func.generate_function_header(code,
- with_pymethdef = env.is_py_class_scope or env.is_closure_scope,
+ with_pymethdef = with_pymethdef,
proto_only=True)
self.generate_function_header(code,
- with_pymethdef = env.is_py_class_scope or env.is_closure_scope)
+ with_pymethdef = with_pymethdef)
# ----- Local variable declarations
if lenv.is_closure_scope:
code.put(lenv.scope_class.type.declaration_code(Naming.cur_scope_cname))
# A Python function definition.
#
# name string the Python name of the function
+ # lambda_name string the internal name of a lambda 'function'
# decorators [DecoratorNode] list of decorators
# args [CArgDeclNode] formal arguments
# star_arg PyArgDeclNode or None * argument
child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators"]
+ lambda_name = None
assmt = None
num_kwonly_args = 0
num_required_kw_args = 0
if arg.not_none and not arg.type.is_extension_type:
error(self.pos,
"Only extension type arguments can have 'not None'")
- self.declare_pyfunction(env)
+ if self.name == '<lambda>':
+ self.declare_lambda_function(env)
+ else:
+ self.declare_pyfunction(env)
self.analyse_signature(env)
self.return_type = self.entry.signature.return_type()
def declare_pyfunction(self, env):
#print "DefNode.declare_pyfunction:", self.name, "in", env ###
name = self.name
- entry = env.lookup_here(self.name)
+ entry = env.lookup_here(name)
if entry and entry.type.is_cfunction and not self.is_wrapper:
warning(self.pos, "Overriding cdef method with def method.", 5)
- entry = env.declare_pyfunction(self.name, self.pos)
+ entry = env.declare_pyfunction(name, self.pos)
self.entry = entry
prefix = env.scope_prefix
entry.func_cname = \
else:
entry.doc = None
+ def declare_lambda_function(self, env):
+ name = self.name
+ prefix = env.scope_prefix
+ func_cname = \
+ Naming.lambda_func_prefix + u'funcdef' + prefix + self.lambda_name
+ entry = env.declare_lambda_function(func_cname, self.pos)
+ entry.pymethdef_cname = \
+ Naming.lambda_func_prefix + u'methdef' + prefix + self.lambda_name
+ entry.qualified_name = env.qualify_name(self.lambda_name)
+ entry.doc = None
+ self.entry = entry
+
def declare_arguments(self, env):
for arg in self.args:
if not arg.name:
function = ExprNodes.PyCFunctionNode(self.pos,
pymethdef_cname = self.entry.pymethdef_cname))
elif env.is_closure_scope:
- self_object = ExprNodes.TempNode(self.pos, env.scope_class.type, env)
- self_object.temp_cname = "((PyObject*)%s)" % Naming.cur_scope_cname
- rhs = ExprNodes.PyCFunctionNode(self.pos,
- self_object = self_object,
- pymethdef_cname = self.entry.pymethdef_cname)
+ rhs = ExprNodes.InnerFunctionNode(
+ self.pos, pymethdef_cname = self.entry.pymethdef_cname)
self.assmt = SingleAssignmentNode(self.pos,
lhs = ExprNodes.NameNode(self.pos, name = self.name),
rhs = rhs)
for child_result in children.itervalues():
if type(child_result) is list:
for child in child_result:
- if child.constant_result is not_a_constant:
+ if getattr(child, 'constant_result', not_a_constant) is not_a_constant:
return
- elif child_result.constant_result is not_a_constant:
+ elif getattr(child_result, 'constant_result', not_a_constant) is not_a_constant:
return
# now try to calculate the real constant value
def visit_ModuleNode(self, node):
self.scope_type = 'module'
self.scope_node = node
+ self.lambda_counter = 1
self.visitchildren(node)
return node
def visit_CStructOrUnionDefNode(self, node):
return self.visit_scope(node, 'struct')
+ def visit_LambdaNode(self, node):
+ # unpack a lambda expression into the corresponding DefNode
+ if self.scope_type != 'function':
+ error(node.pos,
+ "lambda functions are currently only supported in functions")
+ lambda_id = self.lambda_counter
+ self.lambda_counter += 1
+ node.lambda_name = EncodedString(u'lambda%d' % lambda_id)
+
+ body = Nodes.ReturnStatNode(
+ node.result_expr.pos, value = node.result_expr)
+ node.def_node = Nodes.DefNode(
+ node.pos, name=node.name, lambda_name=node.lambda_name,
+ args=node.args, star_arg=node.star_arg,
+ starstar_arg=node.starstar_arg,
+ body=body)
+ self.visitchildren(node)
+ return node
+
# cdef variables
def handle_bufferdefaults(self, decl):
if not isinstance(decl.default, DictNode):
self.visitchildren(node)
self.seen_vars_stack.pop()
return node
-
+
+ def visit_LambdaNode(self, node):
+ node.analyse_declarations(self.env_stack[-1])
+ self.visitchildren(node)
+ return node
+
def visit_FuncDefNode(self, node):
self.seen_vars_stack.append(set())
lenv = node.create_local_scope(self.env_stack[-1])
node.needs_closure = self.needs_closure
self.needs_closure = True
return node
-
+
+ def visit_LambdaNode(self, node):
+ self.needs_closure = False
+ self.visitchildren(node)
+ node.needs_closure = self.needs_closure
+ self.needs_closure = True
+ return node
+
def visit_ClassDefNode(self, node):
self.visitchildren(node)
self.needs_closure = True
#expression: or_test [if or_test else test] | lambda_form
+# actually:
+#test: or_test ['if' or_test 'else' test] | lambdef
+
def p_simple_expr(s):
+ if s.sy == 'lambda':
+ return p_lambdef(s)
pos = s.position()
expr = p_or_test(s)
if s.sy == 'if':
return ExprNodes.CondExprNode(pos, test=test, true_val=expr, false_val=other)
else:
return expr
-
-#test: or_test | lambda_form
-
+
+#lambdef: 'lambda' [varargslist] ':' test
+
+def p_lambdef(s, allow_conditional=True):
+ # s.sy == 'lambda'
+ pos = s.position()
+ s.next()
+ if s.sy == ':':
+ args = []
+ star_arg = starstar_arg = None
+ else:
+ args, star_arg, starstar_arg = p_varargslist(s, terminator=':')
+ s.expect(':')
+ if allow_conditional:
+ expr = p_test(s)
+ else:
+ expr = p_test_nocond(s)
+ return ExprNodes.LambdaNode(
+ pos, args = args,
+ star_arg = star_arg, starstar_arg = starstar_arg,
+ result_expr = expr)
+
+#lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
+
+def p_lambdef_nocond(s):
+ return p_lambdef(s, allow_conditional=False)
+
+#test: or_test | lambdef
+
def p_test(s):
return p_or_test(s)
+#test_nocond: or_test | lambdef_nocond
+
+def p_test_nocond(s):
+ if s.sy == 'lambda':
+ return p_lambdef_nocond(s)
+ else:
+ return p_or_test(s)
+
#or_test: and_test ('or' and_test)*
def p_or_test(s):
return kind, value
# list_display ::= "[" [listmaker] "]"
-# listmaker ::= expression ( list_for | ( "," expression )* [","] )
-# list_iter ::= list_for | list_if
-# list_for ::= "for" expression_list "in" testlist [list_iter]
-# list_if ::= "if" test [list_iter]
+# listmaker ::= expression ( comp_for | ( "," expression )* [","] )
+# 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 == '['
target = ExprNodes.ListNode(pos, args = [])
append = ExprNodes.ComprehensionAppendNode(
pos, expr=expr, target=ExprNodes.CloneNode(target))
- loop = p_list_for(s, Nodes.ExprStatNode(append.pos, expr=append))
+ loop = p_comp_for(s, Nodes.ExprStatNode(append.pos, expr=append))
s.expect(']')
return ExprNodes.ComprehensionNode(
pos, loop=loop, append=append, target=target)
s.expect(']')
return ExprNodes.ListNode(pos, args = exprs)
-def p_list_iter(s, body):
+def p_comp_iter(s, body):
if s.sy == 'for':
- return p_list_for(s, body)
+ return p_comp_for(s, body)
elif s.sy == 'if':
- return p_list_if(s, body)
+ return p_comp_if(s, body)
else:
# insert the 'append' operation into the loop
return body
-def p_list_for(s, body):
+def p_comp_for(s, body):
# s.sy == 'for'
pos = s.position()
s.next()
kw = p_for_bounds(s)
kw['else_clause'] = None
- kw['body'] = p_list_iter(s, body)
+ kw['body'] = p_comp_iter(s, body)
return Nodes.ForStatNode(pos, **kw)
-def p_list_if(s, body):
+def p_comp_if(s, body):
# s.sy == 'if'
pos = s.position()
s.next()
- test = p_test(s)
+ test = p_test_nocond(s)
return Nodes.IfStatNode(pos,
if_clauses = [Nodes.IfClauseNode(pos, condition = test,
- body = p_list_iter(s, body))],
+ body = p_comp_iter(s, body))],
else_clause = None )
#dictmaker: test ':' test (',' test ':' test)* [',']
target = ExprNodes.SetNode(pos, args=[])
append = ExprNodes.ComprehensionAppendNode(
item.pos, expr=item, target=ExprNodes.CloneNode(target))
- loop = p_list_for(s, Nodes.ExprStatNode(append.pos, expr=append))
+ loop = p_comp_for(s, Nodes.ExprStatNode(append.pos, expr=append))
s.expect('}')
return ExprNodes.ComprehensionNode(
pos, loop=loop, append=append, target=target)
append = ExprNodes.DictComprehensionAppendNode(
item.pos, key_expr=key, value_expr=value,
target=ExprNodes.CloneNode(target))
- loop = p_list_for(s, Nodes.ExprStatNode(append.pos, expr=append))
+ loop = p_comp_for(s, Nodes.ExprStatNode(append.pos, expr=append))
s.expect('}')
return ExprNodes.ComprehensionNode(
pos, loop=loop, append=append, target=target)
pos = s.position()
s.next()
name = EncodedString( p_ident(s) )
- #args = []
s.expect('(');
+ args, star_arg, starstar_arg = p_varargslist(s, terminator=')')
+ s.expect(')')
+ if p_nogil(s):
+ error(s.pos, "Python function cannot be declared nogil")
+ 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, decorators = decorators)
+
+def p_varargslist(s, terminator=')'):
args = p_c_arg_list(s, in_pyfunc = 1, nonempty_declarators = 1)
star_arg = None
starstar_arg = None
s.next()
args.extend(p_c_arg_list(s, in_pyfunc = 1,
nonempty_declarators = 1, kw_only = 1))
- elif s.sy != ')':
+ elif s.sy != terminator:
s.error("Syntax error in Python function argument list")
if s.sy == '**':
s.next()
starstar_arg = p_py_arg_decl(s)
- s.expect(')')
- if p_nogil(s):
- error(s.pos, "Python function cannot be declared nogil")
- 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, decorators = decorators)
+ return (args, star_arg, starstar_arg)
def p_py_arg_decl(s):
pos = s.position()
self.obj_to_entry = {}
self.pystring_entries = []
self.buffer_entries = []
+ self.lambda_defs = []
self.control_flow = ControlFlow.LinearControlFlow()
def start_branching(self, pos):
entry.signature = pyfunction_signature
self.pyfunc_entries.append(entry)
return entry
-
+
+ def declare_lambda_function(self, func_cname, pos):
+ # Add an entry for an anonymous Python function.
+ entry = self.declare_var(None, py_object_type, pos,
+ cname=func_cname, visibility='private')
+ entry.name = EncodedString(func_cname)
+ entry.func_cname = func_cname
+ entry.signature = pyfunction_signature
+ self.pyfunc_entries.append(entry)
+ return entry
+
+ def add_lambda_def(self, def_node):
+ self.lambda_defs.append(def_node)
+
def register_pyfunction(self, entry):
self.pyfunc_entries.append(entry)
--- /dev/null
+__doc__ = u"""
+#>>> py_identity = lambda x:x
+#>>> py_identity(1) == cy_identity(1)
+#True
+
+>>> idcall = make_identity()
+>>> idcall(1)
+1
+>>> idcall(2)
+2
+
+>>> make_const0(1)()
+1
+
+>>> make_const1(1)(2)
+1
+
+>>> make_const1(1)(2)
+1
+
+>>> make_const_calc0()()
+11
+>>> make_const_calc1()(2)
+11
+>>> make_const_calc1_xy(8)(2)
+27
+"""
+
+#cy_identity = lambda x:x
+
+def make_identity():
+ return lambda x:x
+
+def make_const0(x):
+ return lambda :x
+
+def make_const1(x):
+ return lambda _:x
+
+
+def make_const_calc0():
+ return lambda : 1*2*3+5
+
+def make_const_calc1():
+ return lambda _: 1*2*3+5
+
+def make_const_calc1_xy(x):
+ return lambda y: x*y+(1*2*3+5)