Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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]