diff --git a/lib/ast.js b/lib/ast.js index 06d251326..ab27e60de 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -1549,9 +1549,25 @@ exports.Call = Call = (function(superclass){ } return node.back = (args[index] = fun).body, node; }; - Call['let'] = function(args, body, generator){ - var params, res$, i$, len$, i, a, that, gotThis; - generator == null && (generator = false); + Call['let'] = function(args, body){ + var hasYield, hasAwait, params, res$, i$, len$, i, a, that, gotThis; + hasYield = false; + hasAwait = false; + body.traverseChildren(function(child){ + if (child instanceof Yield) { + switch (child.op) { + case 'yield': + case 'yieldfrom': + hasYield = true; + break; + case 'await': + hasAwait = true; + } + } + if (hasYield && hasAwait) { + return true; + } + }); res$ = []; for (i$ = 0, len$ = args.length; i$ < len$; ++i$) { i = i$; @@ -1568,7 +1584,12 @@ exports.Call = Call = (function(superclass){ } params = res$; gotThis || args.unshift(Literal('this')); - return this.block(Fun(params, body, null, null, null, generator), args, '.call'); + body = this.block(Fun(params, body, null, null, null, hasYield, hasAwait), args, '.call'); + if (hasYield || hasAwait) { + return Block(Yield(hasYield ? 'yieldfrom' : 'await', body)); + } else { + return body; + } }; return Call; }(Node)); @@ -3931,12 +3952,7 @@ exports.For = For = (function(superclass){ return ((this.kind || []).concat(this.index)).join(' '); }; For.prototype.addBody = function(body){ - var hasYield, ref$, that, assignments, x$, assigned, name, ref1$; - hasYield = !!body.traverseChildren(function(child){ - if (child instanceof Yield) { - return true; - } - }); + var ref$, that, assignments, x$, assigned, name, ref1$; if (this['let']) { if (ref$ = this.ref, delete this.ref, ref$) { this.item = Literal('..'); @@ -3958,14 +3974,11 @@ exports.For = For = (function(superclass){ } } return results$; - }()), assignments.concat([If((ref1$ = this.guard, delete this.guard, ref1$), Call['let'](assigned, body, hasYield))])) - : Call['let'](assignments, body, hasYield)); + }()), assignments.concat([If((ref1$ = this.guard, delete this.guard, ref1$), Call['let'](assigned, body))])) + : Call['let'](assignments, body)); } superclass.prototype.addBody.call(this, body); if (this['let']) { - if (hasYield) { - this.body = Block(Yield('yieldfrom', body)); - } delete this.index; delete this.item; } diff --git a/src/ast.ls b/src/ast.ls index b637b39c9..9bfa92502 100644 --- a/src/ast.ls +++ b/src/ast.ls @@ -1001,7 +1001,15 @@ class exports.Call extends Node ++index node <<< back: (args[index] = fun)body - @let = (args, body, generator = false) -> + @let = (args, body) -> + has-yield = false + has-await = false + body.traverse-children (child) -> + if child instanceof Yield + switch child.op + | \yield \yieldfrom => has-yield := true + | \await => has-await := true + return true if has-yield and has-await params = for a, i in args if a.op is \= and not a.logic and a.right args[i] = that @@ -1009,7 +1017,10 @@ class exports.Call extends Node a.left else Var a.var-name! || a.carp 'invalid "let" argument' gotThis or args.unshift Literal \this - @block Fun(params, body, null, null, null, generator), args, \.call + body = @block Fun(params, body, null, null, null, has-yield, has-await), args, \.call + if has-yield || has-await + Block Yield if has-yield then \yieldfrom else \await, body + else body #### List # An abstract node for a list of comma-separated items. @@ -2508,8 +2519,6 @@ class exports.For extends While show: -> ((@kind || []) ++ @index).join ' ' add-body: (body) -> - has-yield = !!body.traverse-children (child) -> - return true if child instanceof Yield if @let @item = Literal \.. if delete @ref @item = that if @item?rewrite-shorthand! @@ -2518,14 +2527,13 @@ class exports.For extends While ..push Assign that, Literal \item$$ if @item body = Block if @guard assigned = [Var name for assignments when ..assigns! for name in that] - assignments.concat [If delete @guard, Call.let assigned, body, has-yield] + assignments.concat [If delete @guard, Call.let assigned, body] else - Call.let assignments, body, has-yield + Call.let assignments, body super body if @let - @body := Block Yield \yieldfrom, body if has-yield delete @index delete @item this diff --git a/test/async.ls b/test/async.ls index 46e4c2d18..2059f3c1d 100644 --- a/test/async.ls +++ b/test/async.ls @@ -187,3 +187,35 @@ do -> # check that ->>* results in an error at compile time compile-throws "a generator can't be async" 1 '->>* 3' + +# [LiveScript#1019](https://github.com/gkz/LiveScript/issues/1019) +# in `let` blocks +do -> + x = ->> + let a = Promise.resolve 1 + await a + + y <- x!then! + eq y, 1 + +# [LiveScript#1021](https://github.com/gkz/LiveScript/issues/1021) +# in for..let loops +do -> + x = ->> + for let v in [Promise.resolve 1; Promise.resolve 2] + await v + + y <- x!then! + eq "#y" '1,2' + +# [LiveScript#1023](https://github.com/gkz/LiveScript/issues/1023) +# Loop guards (`when`, `case`, `|`) didn't work with `for..let` loops with `yield` in their bodies +do -> + x = (keys) ->> + pr = Promise~resolve + obj = {a: pr 1; b: pr 2; c: pr 3} + for own let k, v of obj when k in keys + await v + + y <- x(<[ a c ]>).then! + eq "#y", '1,3' diff --git a/test/generators.ls b/test/generators.ls index ebd77c074..81656fc55 100644 --- a/test/generators.ls +++ b/test/generators.ls @@ -62,7 +62,7 @@ first = ->* second = ->* yield from first! list = second! -for i to 3 +for i to 3 {value} = list.next! eq value, i @@ -173,3 +173,25 @@ eq 6 g8_result[2]() # splats should expand generators (https://github.com/gkz/LiveScript/issues/963) eq '0,1,2' "#{[...f!]}" + +# [LiveScript#1019](https://github.com/gkz/LiveScript/issues/1019) +# in `let` blocks +fn = ->* + let a = 1 + yield a +eq 1 fn!next!value + +# [LiveScript#1023](https://github.com/gkz/LiveScript/issues/1023) +# Loop guards (`when`, `case`, `|`) didn't work with `for..let` loops with `yield` in their bodies +fn = (remainder) ->* + obj = a: 1, b: 2, c: 3, d: 4 + for own let k, v of obj when v % 2 == remainder + yield v +gen = fn 0 +eq 2 gen.next!value +eq 4 gen.next!value +eq true gen.next!done +gen = fn 1 +eq 1 gen.next!value +eq 3 gen.next!value +eq true gen.next!done