-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a for .. from ..
loop for generators, see #3832
#4306
Conversation
I like this. 👍 |
@@ -2178,6 +2180,9 @@ exports.For = class For extends While | |||
if @object | |||
forPartFragments = [@makeCode("#{kvar} in #{svar}")] | |||
guardPart = "\n#{idt1}if (!#{utility 'hasProp', o}.call(#{svar}, #{kvar})) continue;" if @own | |||
if @from | |||
forPartFragments = [@makeCode("#{kvar} of #{svar}")] | |||
guardPart = "\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean the guardPart? Yeah, I'll delete that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
I'm actually starting to understand how |
I like this too. @lydell should we merge it? |
I get this error when running the tests:
|
@atg I'd like to merge this too. Could you please look into the failing test whenever you get the time? :) |
I took a look at this, it seems like there's a bug in V8 with resuming iterators in a for-of loop (it failed in both Chrome and Node), it works fine on SpiderMonkey though. |
Oops, didn't see the updates. I'll see if I can get this up to date. |
@lydell OK the branch is conflict-free now. The test is ok insofar as CoffeeScript is generating the right code. But the code doesn't run as expected. As @Jamesernator said, the test works in Firefox but not in v8. So I tried in Safari, which agrees with v8 that the test is wrong. I dug into the spec, and it's difficult to understand, but from what I can determine my test & spidermonkey are wrong and v8/nitro are doing the right thing. This is the relevant section. You can see that all paths lead to |
@atg so does the test pass (in Node 6.9.1 or 7.0.0, with |
I'll change the test to accept either the firefox or v8/nitro behaviour as a pass. |
|
@@ -123,3 +123,10 @@ test "#3001: `own` shouldn't be allowed in a `for`-`in` loop", -> | |||
|
|||
test "#2994: single-line `if` requires `then`", -> | |||
cantCompile "if b else x" | |||
|
|||
test "indexes are not supported in for-from loops", -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put these tests in error_messages.coffee
.
# Different JS engines have different opinions on the value of array3: | ||
# https://github.com/jashkenas/coffeescript/pull/4306#issuecomment-257066877 | ||
# As a temporary measure, either result is accepted. | ||
ok array3.length == 0 or array3.join(',') == '70,20' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please always use is
instead of ==
.
@error "reserved word '#{id}'", length: id.length | ||
|
||
if tag is 'IDENTIFIER' | ||
if @seenFor and id is 'from' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be refactored so that we don't branch this if
block again? In other words, don't indent the following 15ish lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure. You can copy the tag is 'IDENTIFIER'
into each of the ifs, but then the else if
condition becomes comically long (see below).
I do agree that probably the if at the bottom of the big if (if id in RESERVED
) should broken out and returned to the main body of the method.
if tag is 'IDENTIFIER' and @seenFor and id is 'from'
tag = 'FORFROM'
@seenFor = no
else if tag is 'IDENTIFIER' and (id in JS_KEYWORDS or id in COFFEE_KEYWORDS) and
not (@exportSpecifierList and id in COFFEE_KEYWORDS)
tag = id.toUpperCase()
if tag is 'WHEN' and @tag() in LINE_BREAK
tag = 'LEADING_WHEN'
else if tag is 'FOR'
@seenFor = yes
else if tag is 'UNLESS'
tag = 'IF'
else if tag is 'IMPORT'
@seenImport = yes
else if tag is 'EXPORT'
@seenExport = yes
else if tag in UNARY
tag = 'UNARY'
else if tag in RELATION
if tag isnt 'INSTANCEOF' and @seenFor
tag = 'FOR' + tag
@seenFor = no
else
tag = 'RELATION'
if @value() is '!'
poppedToken = @tokens.pop()
id = '!' + id
if tag is 'IDENTIFIER' and id in RESERVED
@error "reserved word '#{id}'", length: id.length
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, do it or don't. I got places to be. |
@@ -123,3 +123,6 @@ test "#3001: `own` shouldn't be allowed in a `for`-`in` loop", -> | |||
|
|||
test "#2994: single-line `if` requires `then`", -> | |||
cantCompile "if b else x" | |||
|
|||
test "own is not supported in for-from loops", -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also go in error_messages.coffee
.
* Added support for for-from loop, see #3832 * for-from: remove extra newline and add support for ranges * for-from: tidy up the lexer * for-from: add support for patterns * for-from: fix bad alignment * for-from: add two more tests * for-from: fix test "for-from loops over generators" See explanation here: #4306 (comment) * for-from: delete leftover console.log * Refactor the big `if` block in the lexer to be as minimal a change from `master` as we can get away with * Cleanup to make more idiomatic, remove trailing whitespace, minor performance improvements * for-from: move code from one file to another * for-from: clean up whitespace * for-from: lexer bikeshedding * Move "own is not supported in for-from loops" test into error_messages.coffee; improve error message so that "own" is underlined * Revert unnecessary changes, to minimize the lines of code modified by this PR
Closed per #4355 |
This implements a
for x from g
loop as argued for in this comment.Currently CoffeeScript supports generators using
yield
andyield from
, but it doesn't support a way to use these generators likefor (x of g)
in ES6.CoffeeScript already has
for in
(for Arrays) andfor of
(for Objects), however users expect both to generate es5-compatible code. It's impossible to modify either of these syntaxes to have all three of es5 compatibility, efficiency and support for generators/iterables.That means the choice is either a compile-time flag or a new loop syntax. A compile-time flag is likely to create subtle bugs: if the code is expected to be compiled in es6 mode but is actually compiled in es5 mode, then a
for in
loop over an iterator will be a normal for loopfor (var i = 0; i < iterator.length; i++)
. Iterators are objects, and.length
on an iterator is usually 0. The loop will therefore silently fail to run, with no error message.That leaves a new loop syntax.
The best argument for introducing a new for loop syntax for generators is that they are fundamentally different from either arrays or objects. Iterating over a generator mutates that generator, the next time you iterate over it, it will be empty. Generators cannot be iterated over in reverse order (because they are not indexed). Generators can be infinite.
So aside from being the only option, a new syntax has the benefit of making it clear from the code which loops are meant for Arrays, and which loops are meant for generators (or any iterable).
As for the specific syntax,
for x from y
is a nice complement toyield from y
.