Skip to content

Commit

Permalink
Detect when from in a for loop declaration is an identifier (#4393)
Browse files Browse the repository at this point in the history
* Try to detect when `from` in a `for` loop declaration is an identifier, not a keyword

* Handle destructured arrays

* from as a destructured, aliased object variable name in a for loop declaration
  • Loading branch information
GeoffreyBooth committed Dec 6, 2016
1 parent 3ea0481 commit 88f2bf9
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 4 deletions.
21 changes: 19 additions & 2 deletions lib/coffee-script/lexer.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion src/lexer.coffee
Expand Up @@ -165,7 +165,8 @@ exports.Lexer = class Lexer
if @value() is '!'
poppedToken = @tokens.pop()
id = '!' + id
else if tag is 'IDENTIFIER' and @seenFor and id is 'from'
else if tag is 'IDENTIFIER' and @seenFor and id is 'from' and
isForFrom(prev)
tag = 'FORFROM'
@seenFor = no

Expand Down Expand Up @@ -824,6 +825,27 @@ isUnassignable = (name, displayName = name) -> switch

exports.isUnassignable = isUnassignable

# `from` isn’t a CoffeeScript keyword, but it behaves like one in `import` and
# `export` statements (handled above) and in the declaration line of a `for`
# loop. Try to detect when `from` is a variable identifier and when it is this
# “sometimes” keyword.
isForFrom = (prev) ->
if prev[0] is 'IDENTIFIER'
# `for i from from`, `for from from iterable`
if prev[1] is 'from'
prev[1][0] = 'IDENTIFIER'
yes
# `for i from iterable`
yes
# `for from…`
else if prev[0] is 'FOR'
no
# `for {from}…`, `for [from]…`, `for {a, from}…`, `for {a: from}…`
else if prev[1] in ['{', '[', ',', ':']
no
else
yes

# Constants
# ---------

Expand Down
57 changes: 56 additions & 1 deletion test/generators.coffee
Expand Up @@ -277,7 +277,6 @@ test "for-from loops over generators", ->
ok array3.length is 0 or array3.join(',') is '70,20'
arrayEq array4, []


test "for-from comprehensions over generators", ->
gen = ->
yield from [30, 41, 51, 60]
Expand All @@ -288,3 +287,59 @@ test "for-from comprehensions over generators", ->

ok array1.join(' ') is '41 51'
ok array2.length is 0

test "from as an iterable variable name in a for loop declaration", ->
from = [1, 2, 3]
out = []
for i from from
out.push i
arrayEq from, out

test "from as an iterator variable name in a for loop declaration", ->
a = [1, 2, 3]
b = []
for from from a
b.push from
arrayEq a, b

test "from as a destructured object variable name in a for loop declaration", ->
a = [
from: 1
to: 2
,
from: 3
to: 4
]
b = []
for {from, to} in a
b.push from
arrayEq b, [1, 3]

c = []
for {to, from} in a
c.push from
arrayEq c, [1, 3]

test "from as a destructured, aliased object variable name in a for loop declaration", ->
a = [
b: 1
c: 2
,
b: 3
c: 4
]
out = []

for {b: from} in a
out.push from
arrayEq out, [1, 3]

test "from as a destructured array variable name in a for loop declaration", ->
a = [
[1, 2]
[3, 4]
]
b = []
for [from, to] from a
b.push from
arrayEq b, [1, 3]

0 comments on commit 88f2bf9

Please sign in to comment.