1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

Detect when from in a for loop declaration is an identifier (#4393)

* 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
This commit is contained in:
Geoffrey Booth 2016-12-06 12:29:02 -08:00 committed by GitHub
parent 3ea0481bbe
commit 88f2bf9fa5
3 changed files with 98 additions and 4 deletions

View file

@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.12.0
(function() {
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isUnassignable, key, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError,
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, HERE_JSTOKEN, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isForFrom, isUnassignable, key, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
slice = [].slice;
@ -131,7 +131,7 @@
}
}
}
} else if (tag === 'IDENTIFIER' && this.seenFor && id === 'from') {
} else if (tag === 'IDENTIFIER' && this.seenFor && id === 'from' && isForFrom(prev)) {
tag = 'FORFROM';
this.seenFor = false;
}
@ -963,6 +963,23 @@
exports.isUnassignable = isUnassignable;
isForFrom = function(prev) {
var ref2;
if (prev[0] === 'IDENTIFIER') {
if (prev[1] === 'from') {
prev[1][0] = 'IDENTIFIER';
true;
}
return true;
} else if (prev[0] === 'FOR') {
return false;
} else if ((ref2 = prev[1]) === '{' || ref2 === '[' || ref2 === ',' || ref2 === ':') {
return false;
} else {
return true;
}
};
JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'yield', 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'super', 'import', 'export', 'default'];
COFFEE_KEYWORDS = ['undefined', 'Infinity', 'NaN', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when'];

View file

@ -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
@ -824,6 +825,27 @@ isUnassignable = (name, displayName = name) -> switch
exports.isUnassignable = isUnassignable
# `from` isnt 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
# ---------

View file

@ -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]
@ -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]