Skip to content
44 changes: 44 additions & 0 deletions t/advanced/10lambda.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#! winxed

// Tests for lambda -> syntax

using extern Test.More plan, is;

function main[main]()
{
plan(13);

// Defining basic lambdas, assinging to variables
var f1 = -> { return 4; };
is(f1(), 4);

var f2 = -> 4;
is(f2(), 4);

// Lambdas with arguments
var f3 = ->(int x) { return x + 1; };
is(f3(4), 5);

var f4 = ->(int x) x + 1;
is(f4(4), 5);

// Inline definition and call
is((-> { return 5; })(), 5);
is((-> 5)(), 5);
is((->(int x) { return x + 1; })(4), 5);
is((->(int x) x + 1)(4), 5);

// Lambdas returning lambdas
var f5 = -> { return -> { return 5; }; };
is(f5()(), 5);

var f6 = -> -> 5;
is(f6()(), 5);

is((-> -> 5)()(), 5);

// Lambdas as arguments to functions and lambdas
var f7 = ->(f7_inner) { return f7_inner(); };
is(f7(-> { return 5; }), 5);
is(f7(-> 5), 5);
}
47 changes: 47 additions & 0 deletions t/advanced/10lambda_y_comb.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#! winxed

// Tests for closures and lambdas, using Y-combinators as an example

using extern Test.More plan, is;

// "normal" Y-combinator using function syntax
function Y(outer)
{
return (function(func) {
return func(func);
})(function(func) {
return outer(function(arg) {
return func(func)(arg);
});
});
}

// Y-combinator written with lambdas
function Y_lambda(var outer) {
return (->(func) func(func))(->(func) outer(->(arg) func(func)(arg)));
}

// Naive recursive implementation of a factorial
function factorial_recursive(int n)
{
return n == 0 ? 1 : n * int(factorial_recursive(n - 1));
}

function main()
{
plan(5);
is(factorial_recursive(6), 720, "recursive factorial gives correct results");

var factorial_y = function(var func) {
return function(int n) {
return n == 0 ? 1 : n * int(func(n - 1));
};
};
var lambda_fact = ->(func) ->(int n) n == 0 ? 1 : n * int(func(n - 1));

is(Y(factorial_y)(6), 720, "Normal Y(factorial) works");
is(Y_lambda(factorial_y)(6), 720, "Y_lambda(factorial) works");

is(Y(lambda_fact)(6), 720, "Y(lambda_fact) works");
is(Y_lambda(lambda_fact)(6), 720, "Y_lambda(lambda_fact) works");
}
88 changes: 80 additions & 8 deletions winxedst1.winxed
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ class Tokenizer
':': getop
},
'+': { '+': getop, '=': getop },
'-': { '-': getop, '=': getop },
'-': { '-': getop, '=': getop, '>': getop },
'*': { '=': getop },
'|': { '|': getop },
'&': { '&': getop },
Expand Down Expand Up @@ -2867,10 +2867,14 @@ class FunctionExpr : Expr
function FunctionExpr(tk, owner, start)
{
self.Expr(owner, start);
var t = tk.get();
if (!t.isop('('))
Expected('anonymous function', t);
self.fn = new LocalFunctionStatement(start, tk, owner);
if (start.isop('->')) {
self.fn = new LambdaFunctionStatement(start, tk, owner);
} else {
var t = tk.get();
if (!t.isop('('))
Expected('anonymous function', t);
self.fn = new LocalFunctionStatement(start, tk, owner);
}
}
function checkresult() { return REGvar; }
function optimize()
Expand Down Expand Up @@ -5401,7 +5405,7 @@ class CallExpr : Expr
call = sym.emit_get(e, self);
break;
case sym instanceof Builtin:
InternalError("Builtin unexpeted here", self);
InternalError("Builtin unexpected here", self.start);
default:
call = join("", [ "'", funref.getName(), "'" ] );
}
Expand Down Expand Up @@ -6505,6 +6509,8 @@ function parseExpr_0(tk, owner)
return parseNew(tk, owner, t);
case t.iskeyword('function'):
return new FunctionExpr(tk, owner, t);
case t.isop('->'):
return new FunctionExpr(tk, owner, t);
case t.iskeyword('class'):
return new OpClassExpr(tk, owner, t);
case t.isidentifier():
Expand Down Expand Up @@ -6920,7 +6926,7 @@ class ReturnYieldStatement : Statement
{
var values = self.values;
if (values != null)
values = values.optimize();
self.values = values.optimize();
return self;
}
function emit(e)
Expand Down Expand Up @@ -6961,6 +6967,39 @@ class ReturnStatement : ReturnYieldStatement
}
}

class SimpleReturnStatement : ReturnStatement
{
function SimpleReturnStatement(start, tk, owner)
{
self.Statement(start, owner);
self.values = parseExpr(tk, owner);
}

function emit(e)
{
// Experimental tailcall optimization
var value = self.values;
if (value.cantailcall()) {
self.annotate(e);
return value.emit(e, '.tailcall');
}

string reg;
if (value.isnull()) {
string pnull = self.owner.tempreg(REGvar);
e.emitnull(pnull);
reg = pnull;
}
else
reg = value.emit_get(e);

self.annotate(e);
self.emitret(e);
e.print(reg);
e.say(')');
}
}

class YieldStatement : ReturnYieldStatement
{
function YieldStatement(start, tk, owner)
Expand Down Expand Up @@ -8830,7 +8869,10 @@ class FunctionBase : BlockStatement
else {
e.comment('Body');
body.emit(e);
e.annotate(body.getend());
int can_getend = 0;
${ can can_getend, body, "getend" };
if (can_getend)
e.annotate(body.getend());
}
e.say("\n.end # ", name, "\n");

Expand Down Expand Up @@ -8993,6 +9035,36 @@ class LocalFunctionStatement : FunctionBase
}
}

//*********************************************
// LambdaFunctionStatement
//*********************************************

class LambdaFunctionStatement : LocalFunctionStatement
{
function LambdaFunctionStatement(start, tk, owner)
{
self.FunctionBase(start, owner);
self.outer = owner.getouter();
self.outer.makesubid();
var subid = self.makesubid();
self.name = subid;
var t = tk.get();
if (t.isop('(')) {
self.parse_parameters(tk);
t = tk.get();
} else
self.params = [];

if (t.isop('{'))
self.body = new CompoundStatement(t, tk, self);
else {
tk.unget(t);
self.body = new SimpleReturnStatement(t, tk, self);
}
owner.addlocalfunction(self);
}
}

//*********************************************
// MethodStatement
//*********************************************
Expand Down