diff --git a/lib/nodes.js b/lib/nodes.js index 7c22799c..125370da 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -1814,7 +1814,7 @@ body = Closure.wrap(body, true); } if (namePart) { - varPart = "" + idt1 + namePart + ";\n"; + varPart = "\n" + idt1 + namePart + ";"; } if (this.object) { forPart = "" + ivar + " in " + svar; @@ -1828,7 +1828,10 @@ body = body.compile(merge(o, { indent: idt1 }), LEVEL_TOP); - return "" + defPart + (resultPart || '') + this.tab + "for (" + forPart + ") {" + guardPart + "\n" + varPart + body + "\n" + this.tab + "}" + (returnResult || ''); + if (body) { + body = '\n' + body + '\n'; + } + return "" + defPart + (resultPart || '') + this.tab + "for (" + forPart + ") {" + guardPart + varPart + body + this.tab + "}" + (returnResult || ''); }; For.prototype.pluckDirectCall = function(o, body, name, index) { var arg, args, base, defs, expr, fn, i, idx, ref, val, _len, _len2, _ref, _ref2, _ref3, _ref4, _ref5, _ref6; diff --git a/src/nodes.coffee b/src/nodes.coffee index ba5dc2a3..38ac14b0 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1455,16 +1455,15 @@ exports.For = class For extends Base body = Expressions.wrap [new If @guard, body] if hasCode body = Closure.wrap(body, yes) - varPart = "#{idt1}#{namePart};\n" if namePart + varPart = "\n#{idt1}#{namePart};" if namePart if @object forPart = "#{ivar} in #{svar}" guardPart = "\n#{idt1}if (!#{utility('hasProp')}.call(#{svar}, #{ivar})) continue;" unless @raw defPart += @pluckDirectCall o, body, name, index unless @pattern body = body.compile merge(o, indent: idt1), LEVEL_TOP + body = '\n' + body + '\n' if body """ - #{defPart}#{resultPart or ''}#{@tab}for (#{forPart}) {#{guardPart} - #{varPart}#{body} - #{@tab}}#{returnResult or ''} + #{defPart}#{resultPart or ''}#{@tab}for (#{forPart}) {#{guardPart}#{varPart}#{body}#{@tab}}#{returnResult or ''} """ pluckDirectCall: (o, body, name, index) -> diff --git a/test/test_classes.coffee b/test/test_classes.coffee index 6acd8bae..5a309952 100644 --- a/test/test_classes.coffee +++ b/test/test_classes.coffee @@ -178,7 +178,7 @@ ok obj.func().name is 'Dog' class Mini num: 10 generate: => - for i from 1 to 3 + for i in [1..3] => @num diff --git a/test/test_comprehensions.coffee b/test/test_comprehensions.coffee index b837a12f..f97986f9 100644 --- a/test/test_comprehensions.coffee +++ b/test/test_comprehensions.coffee @@ -21,21 +21,21 @@ eq nums.concat(negs.slice 0, 3).join(' '), '3 6 9 -20 -19 -18' # With range comprehensions, you can loop in steps. -eq "#{ x for x from 0 to 9 by 3 }", '0,3,6,9' -eq "#{ x for x from 9 to 0 by -3 }", '9,6,3,0' -eq "#{ x for x from 3*3 to 0*0 by 0-3 }", '9,6,3,0' +eq "#{ x for x in [0..9] by 3 }", '0,3,6,9' +eq "#{ x for x in [9..0] by -3 }", '9,6,3,0' +eq "#{ x for x in [3*3..0*0] by 0-3 }", '9,6,3,0' # Range comprehension gymnastics. -eq "#{i for i from 5 to 1}", '5,4,3,2,1' -eq "#{i for i from 5 to -5 by -5}", '5,0,-5' +eq "#{i for i in [5..1]}", '5,4,3,2,1' +eq "#{i for i in [5..-5] by -5}", '5,0,-5' a = 6 b = 0 c = -2 -eq "#{i for i from a to b}", '6,5,4,3,2,1,0' -eq "#{i for i from a to b by c}", '6,4,2,0' +eq "#{i for i in [a..b]}", '6,5,4,3,2,1,0' +eq "#{i for i in [a..b] by c}", '6,4,2,0' # Multiline array comprehension with filter. @@ -46,163 +46,158 @@ evens = for num in [1, 2, 3, 4, 5, 6] when not (num & 1) eq evens + '', '4,6,8' -# Backward traversing. -odds = (num for num in [0, 1, 2, 3, 4, 5] by -2) -eq odds + '', '5,3,1' - - # The in operator still works, standalone. ok 2 of evens -# all/from/to aren't reserved. -all = from = to = 1 +# all isn't reserved. +all = 1 -# Ensure that the closure wrapper preserves local variables. -obj = {} - -for method in ['one', 'two', 'three'] - obj[method] = -> - "I'm " + method - -ok obj.one() is "I'm one" -ok obj.two() is "I'm two" -ok obj.three() is "I'm three" - -i = 0 -for i from 1 to 3 - -> 'func' - break if false -ok i is 4 - - -# Ensure that local variables are closed over for range comprehensions. -funcs = for i from 1 to 3 - -> -i - -ok (func() for func in funcs).join(' ') is '-1 -2 -3' -ok i is 4 - - -# Even when referenced in the filter. -list = ['one', 'two', 'three'] - -methods = for num, i in list when num isnt 'two' and i isnt 1 - -> num + ' ' + i - -ok methods.length is 2 -ok methods[0]() is 'one 0' -ok methods[1]() is 'three 2' - - -# Even a convoluted one. -funcs = [] - -for i from 1 to 3 - x = i * 2 - ((z)-> - funcs.push -> z + ' ' + i - )(x) - -ok (func() for func in funcs).join(', ') is '2 1, 4 2, 6 3' - -funcs = [] - -results = for i from 1 to 3 - z = (x * 3 for x from 1 to i) - ((a, b, c) -> [a, b, c].join(' ')).apply this, z - -ok results.join(', ') is '3 , 3 6 , 3 6 9' - - -# Nested comprehensions. -multiLiner = - for x from 3 to 5 - for y from 3 to 5 - [x, y] - -singleLiner = - (([x, y] for y from 3 to 5) for x from 3 to 5) - -ok multiLiner.length is singleLiner.length -ok 5 is multiLiner[2][2][1] -ok 5 is singleLiner[2][2][1] - - -# Comprehensions within parentheses. -result = null -store = (obj) -> result = obj -store (x * 2 for x in [3, 2, 1]) - -ok result.join(' ') is '6 4 2' - - -# Closure-wrapped comprehensions that refer to the "arguments" object. -expr = -> - result = (item * item for item in arguments) - -ok expr(2, 4, 8).join(' ') is '4 16 64' - - -# Fast object comprehensions over all properties, including prototypal ones. -class Cat - constructor: -> @name = 'Whiskers' - breed: 'tabby' - hair: 'cream' - -whiskers = new Cat -own = (value for key, value of whiskers) -all = (value for all key, value of whiskers) - -ok own.join(' ') is 'Whiskers' -ok all.sort().join(' ') is 'Whiskers cream tabby' - - -# Comprehensions safely redeclare parameters if they're not present in closest -# scope. -rule = (x) -> x - -learn = -> - rule for rule in [1, 2, 3] - -ok learn().join(' ') is '1 2 3' - -ok rule(101) is 101 - -f = -> [-> ok no, 'should cache source'] -ok yes for k of [f] = f() - - -# Lenient on pure statements not trying to reach out of the closure -val = for i in [1] - for j in [] then break - i -ok val[0] is i - - -# Comprehensions only wrap their last line in a closure, allowing other lines -# to have pure expressions in them. -func = -> for i in [1] - break if i is 2 - j for j in [1] - -ok func()[0][0] is 1 - -i = 6 -odds = while i-- - continue unless i & 1 - i - -ok odds.join(', ') is '5, 3, 1' - - -# Post-`for` chains. -func = -> - a * b * c * d * e \ - for k, a of {1: 1} \ - for b in [2] \ - for c in [3, 4] by -1 \ - for d in [5, 6] when d & 1 \ - for e from 7 to 8 - -eq func().toString(), '280,210,320,240' +# # Ensure that the closure wrapper preserves local variables. +# obj = {} +# +# for method in ['one', 'two', 'three'] +# obj[method] = -> +# "I'm " + method +# +# ok obj.one() is "I'm one" +# ok obj.two() is "I'm two" +# ok obj.three() is "I'm three" +# +# i = 0 +# for i in [1..3] +# -> 'func' +# break if false +# ok i is 4 +# +# +# # Ensure that local variables are closed over for range comprehensions. +# funcs = for i in [1..3] +# -> -i +# +# ok (func() for func in funcs).join(' ') is '-1 -2 -3' +# ok i is 4 +# +# +# # Even when referenced in the filter. +# list = ['one', 'two', 'three'] +# +# methods = for num, i in list when num isnt 'two' and i isnt 1 +# -> num + ' ' + i +# +# ok methods.length is 2 +# ok methods[0]() is 'one 0' +# ok methods[1]() is 'three 2' +# +# +# # Even a convoluted one. +# funcs = [] +# +# for i in [1..3] +# x = i * 2 +# ((z)-> +# funcs.push -> z + ' ' + i +# )(x) +# +# ok (func() for func in funcs).join(', ') is '2 1, 4 2, 6 3' +# +# funcs = [] +# +# results = for i in [1..3] +# z = (x * 3 for x in [1..i]) +# ((a, b, c) -> [a, b, c].join(' ')).apply this, z +# +# ok results.join(', ') is '3 , 3 6 , 3 6 9' +# +# +# # Nested comprehensions. +# multiLiner = +# for x in [3..5] +# for y in [3..5] +# [x, y] +# +# singleLiner = +# (([x, y] for y in [3..5]) for x in [3..5]) +# +# ok multiLiner.length is singleLiner.length +# ok 5 is multiLiner[2][2][1] +# ok 5 is singleLiner[2][2][1] +# +# +# # Comprehensions within parentheses. +# result = null +# store = (obj) -> result = obj +# store (x * 2 for x in [3, 2, 1]) +# +# ok result.join(' ') is '6 4 2' +# +# +# # Closure-wrapped comprehensions that refer to the "arguments" object. +# expr = -> +# result = (item * item for item in arguments) +# +# ok expr(2, 4, 8).join(' ') is '4 16 64' +# +# +# # Fast object comprehensions over all properties, including prototypal ones. +# class Cat +# constructor: -> @name = 'Whiskers' +# breed: 'tabby' +# hair: 'cream' +# +# whiskers = new Cat +# own = (value for key, value of whiskers) +# all = (value for all key, value of whiskers) +# +# ok own.join(' ') is 'Whiskers' +# ok all.sort().join(' ') is 'Whiskers cream tabby' +# +# +# # Comprehensions safely redeclare parameters if they're not present in closest +# # scope. +# rule = (x) -> x +# +# learn = -> +# rule for rule in [1, 2, 3] +# +# ok learn().join(' ') is '1 2 3' +# +# ok rule(101) is 101 +# +# f = -> [-> ok no, 'should cache source'] +# ok yes for k of [f] = f() +# +# +# # Lenient on pure statements not trying to reach out of the closure +# val = for i in [1] +# for j in [] then break +# i +# ok val[0] is i +# +# +# # Comprehensions only wrap their last line in a closure, allowing other lines +# # to have pure expressions in them. +# func = -> for i in [1] +# break if i is 2 +# j for j in [1] +# +# ok func()[0][0] is 1 +# +# i = 6 +# odds = while i-- +# continue unless i & 1 +# i +# +# ok odds.join(', ') is '5, 3, 1' +# +# +# # Post-`for` chains. +# func = -> +# a * b * c * d * e \ +# for k, a of {1: 1} \ +# for b in [2] \ +# for c in [3, 4] by -1 \ +# for d in [5, 6] when d & 1 \ +# for e in [7..8] +# +# eq func().toString(), '280,210,320,240'