From f2f99f96ab48467d034bb482df6397ff39ce44d2 Mon Sep 17 00:00:00 2001 From: Whiteknight Date: Thu, 4 Aug 2011 13:36:21 -0400 Subject: [PATCH 1/7] Quick implementation of a streamlined perl6ish lambda syntax for winxed --- winxedst1.winxed | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/winxedst1.winxed b/winxedst1.winxed index ada3c23..dc9e07c 100644 --- a/winxedst1.winxed +++ b/winxedst1.winxed @@ -654,7 +654,7 @@ class Tokenizer ':': getop }, '+': { '+': getop, '=': getop }, - '-': { '-': getop, '=': getop }, + '-': { '-': getop, '=': getop, '>': getop }, '*': { '=': getop }, '|': { '|': getop }, '&': { '&': getop }, @@ -2808,10 +2808,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() @@ -6441,6 +6445,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(): @@ -8927,6 +8933,31 @@ 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 = []; + RequireOp('{', t); + self.body = new CompoundStatement(t, tk, self); + owner.addlocalfunction(self); + } +} + //********************************************* // MethodStatement //********************************************* From 8275222506b9efe8b08afdde23cf880a56c993b0 Mon Sep 17 00:00:00 2001 From: Whiteknight Date: Fri, 5 Aug 2011 13:01:29 -0400 Subject: [PATCH 2/7] Enable an even shorter lambda syntax. If curly brackets are ommitted we parse the body as an expression and we implicitly return the result --- winxedst1.winxed | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/winxedst1.winxed b/winxedst1.winxed index dc9e07c..cac05ea 100644 --- a/winxedst1.winxed +++ b/winxedst1.winxed @@ -8770,7 +8770,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"); @@ -8952,8 +8955,15 @@ class LambdaFunctionStatement : LocalFunctionStatement t = tk.get(); } else self.params = []; - RequireOp('{', t); - self.body = new CompoundStatement(t, tk, self); + + if (t.isop('{')) + self.body = new CompoundStatement(t, tk, self); + else { + tk.unget(t); + self.body = new ReturnStatement(t, tk, self); + var term = new TokenOp(t.file, t.line, ';'); + tk.unget(term); + } owner.addlocalfunction(self); } } From b59c06752bd3cc2e261b14aa372f42ce8d0e3793 Mon Sep 17 00:00:00 2001 From: Whiteknight Date: Fri, 5 Aug 2011 14:04:19 -0400 Subject: [PATCH 3/7] Add in a SimpleReturnStatement which is like ReturnStatement but parses only a simple, single return value. Use this to cleanup the new lambda syntax with implicit return, and make it work more like a normal expression: --- winxedst1.winxed | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/winxedst1.winxed b/winxedst1.winxed index cac05ea..e5ecc68 100644 --- a/winxedst1.winxed +++ b/winxedst1.winxed @@ -6903,6 +6903,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) @@ -8960,9 +8993,7 @@ class LambdaFunctionStatement : LocalFunctionStatement self.body = new CompoundStatement(t, tk, self); else { tk.unget(t); - self.body = new ReturnStatement(t, tk, self); - var term = new TokenOp(t.file, t.line, ';'); - tk.unget(term); + self.body = new SimpleReturnStatement(t, tk, self); } owner.addlocalfunction(self); } From 7284bc1a096cf54af989e9da7971a91c206c8ae4 Mon Sep 17 00:00:00 2001 From: Whiteknight Date: Wed, 10 Aug 2011 21:43:08 -0400 Subject: [PATCH 4/7] Add in some tests showing Y combinators written in Winxed and recursive factorial functions. Both parts are written in 'classic' syntax and new syntax --- t/advanced/10lambda_y_comb.t | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 t/advanced/10lambda_y_comb.t 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"); +} From 2f589616988fdc9626b71797af079a972698f0a4 Mon Sep 17 00:00:00 2001 From: Whiteknight Date: Fri, 12 Aug 2011 20:03:46 -0400 Subject: [PATCH 5/7] fix small spelling mistake --- winxedst1.winxed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winxedst1.winxed b/winxedst1.winxed index e5ecc68..1ed4ff6 100644 --- a/winxedst1.winxed +++ b/winxedst1.winxed @@ -5342,7 +5342,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); default: call = join("", [ "'", funref.getName(), "'" ] ); } From 4a9acd6d59d7b10e05c33ec551a15041494169b2 Mon Sep 17 00:00:00 2001 From: Whiteknight Date: Fri, 12 Aug 2011 20:47:15 -0400 Subject: [PATCH 6/7] ReturnYieldStatement was ignoring the results of optimization. doing a 'tailcall' into a builtin would fail parsing. Fix that, and now lambdas work for all cases I've tested --- winxedst1.winxed | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winxedst1.winxed b/winxedst1.winxed index 1ed4ff6..d53fc38 100644 --- a/winxedst1.winxed +++ b/winxedst1.winxed @@ -5342,7 +5342,7 @@ class CallExpr : Expr call = sym.emit_get(e, self); break; case sym instanceof Builtin: - InternalError("Builtin unexpected here", self); + InternalError("Builtin unexpected here", self.start); default: call = join("", [ "'", funref.getName(), "'" ] ); } @@ -6862,7 +6862,7 @@ class ReturnYieldStatement : Statement { var values = self.values; if (values != null) - values = values.optimize(); + self.values = values.optimize(); return self; } function emit(e) From e346d7c4c8bc66f193fd477d6a30b4d581b11c38 Mon Sep 17 00:00:00 2001 From: Whiteknight Date: Fri, 12 Aug 2011 21:42:00 -0400 Subject: [PATCH 7/7] add in a test file with various usage patterns and syntaxes for lambdas --- t/advanced/10lambda.t | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 t/advanced/10lambda.t 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); +}