diff --git a/t/advanced/10lambda.t b/t/advanced/10lambda.t new file mode 100644 index 0000000..d81a68b --- /dev/null +++ b/t/advanced/10lambda.t @@ -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); +} diff --git a/t/advanced/10lambda_y_comb.t b/t/advanced/10lambda_y_comb.t new file mode 100644 index 0000000..ebd5b98 --- /dev/null +++ b/t/advanced/10lambda_y_comb.t @@ -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"); +} diff --git a/winxedst1.winxed b/winxedst1.winxed index 0e99f7b..952046e 100644 --- a/winxedst1.winxed +++ b/winxedst1.winxed @@ -654,7 +654,7 @@ class Tokenizer ':': getop }, '+': { '+': getop, '=': getop }, - '-': { '-': getop, '=': getop }, + '-': { '-': getop, '=': getop, '>': getop }, '*': { '=': getop }, '|': { '|': getop }, '&': { '&': getop }, @@ -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() @@ -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(), "'" ] ); } @@ -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(): @@ -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) @@ -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) @@ -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"); @@ -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 //*********************************************