-
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
Fix #3921 & #2342: inline function without parentheses used in condition, inline 'else' #4838
Conversation
Why wouldn’t extending the grammar be an option? When I look at Expression: [
o 'Value'
o 'Code'
o 'Operation'
o 'Assign'
o 'If'
o 'Try'
o 'While'
o 'For'
o 'Switch'
o 'Class'
o 'Throw'
o 'Yield'
] Several of these don’t make much sense to allow inside an IfBlock: [
o 'IF SimpleConditional Block', -> new If $2, $3, type: $1
o 'IfBlock ELSE IF SimpleConditional Block', -> $1.addElse LOC(3,5) new If $4, $5, type: $3
o 'IF ( ComplexConditional ) Block', -> new If $3, $4, type: $1
o 'IfBlock ELSE IF ( ComplexConditional ) Block', -> $1.addElse LOC(3,7) new If $5, $7, type: $3
] And then we’d copy |
Fixing this in the grammar would be much better yeah. WRT other if bugs, see satyr/coco#127 as it has quite a bit of discussion |
OK, I bite the bullet, discarded the As said before, the problem with the inline functions being used in conditional and control flow ( For example: f ->
a
### Tokens
IDENTIFIER -> INDENT IDENTIFIER OUTDENT
### And the final output from the rewriter is f (->
a
)
### Tokens
IDENTIFIER CALL_START -> INDENT IDENTIFIER OUTDENT CALL_END
### Grammar breaks this expression into
And, in this example if f ( ->
a
b
)
Essentially, the problem is misinterpreted inline function ( The initial idea for solving this, was adjusting The former would mean adding more condition rules when scanning tokens, the latter added just another unnecessary loop over the tokens. By looking closer at grammar it's clear that the main reason if do -> a then b
if do -> 'wrong' then do -> 'right'
c if a = f -> b
for a in do -> b
a
while do (a = 1) -> f a
c
while b = f -> a when a isnt 3
foo b
c = switch b = f -> a
when 2 then "two"
when 1 then "one"
else "none"
c = switch
when do -> a
'ok'
when do -> b
'ko'
Besides control flow, the same issue also appears when used in a = [do -> b...5]
a = arr[do -> b...5]
arr = [1, 2, 3, do -> a, 5, 6] Note that examples above works in current master when inline function is the last element: a = [1...do -> b]
a = arr[1...do -> b]
arr = [1, 2, 3, do -> a] Besides new grammar rules, I also changed existing This PR is still WIP as there are still some cases (especially |
@GeoffreyBooth |
Would I be right saying this could potentially fix |
@vendethiel which ticket exactly? In your example I just did a quick test locally (not committed yet) and a(function() {
return b;
}, c); and this case f(a(function() {
return b;
}, c)); Is this expected? |
This ticket: satyr/coco#127 I'd say the compilation is correct. It's "unexpected", because we currently parse it as The question I have then is: did this change the meaning of these cases: if true then if false
true Does this code now compile? switch
when a then if b then c else d |
I know, it's a bit broader than what the ticket attempts to do, but it's one big can of worms.. |
Yeah, I was looking at those also and I think this PR could also cover such cases. |
test/control_flow.coffee
Outdated
@@ -485,6 +485,293 @@ test "#4267: lots of for-loops in the same scope", -> | |||
""" | |||
ok CoffeeScript.eval(code) | |||
|
|||
# Test for issue #2342: Lexer: Inline `else` binds to wrong `if`/`switch` | |||
test "issue 2343: if / then / if / then / else", -> |
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 is very minor, but elsewhere in the tests when we reference an issue we do it as test "#2343: ...
.
The last commit includes the fix for #2342. # if a then if b then c else d
if (a) {
if (b) {
c;
} else {
d;
}
} # switch e
# when f then if g then h else i
switch (e) {
case f:
if (g) {
h;
} else {
i;
}
} I've tried to fix this in |
The more I learn about this codebase, the more I feel like Re |
binding to closest in cases where you want another possibility could be using a semicolon: |
I think the behavior proposed here (binding to closest |
Binding
Count me in. I'm working on improved |
i assume you flipped |
Okay, but is this a breaking change? |
@Inve1951 yes, |
Yes, pretty much everyone agrees on this. Do note, tho, that CSR has some minor rewriting (notably regexes).
It's not ambiguous, but i'm afraid this PR changing its meaning (tho for the best) is a huge breaking change. |
I’m okay with a breaking change that fixes a bug, like when we changed chaining in 1.12.7 to go from the start of a line rather than the last function in the line. But when we make such a change, we need to be sure that the prior behavior is a bug, and clearly seems to be such. I think this qualifies; what do you all think? I would disagree that this is a “huge” breaking change; to me, relying on multiple |
Survey of the crowd (including @jashkenas, @lydell, @connec, others not already on this thread): A bunch of bugfix PRs have been merged in recently, some of them substantial changes, so I’m thinking the next release should be 2.2.0. That should give people enough of a hint that it’s not just a small patch. In addition, this PR is technically a breaking change: if no then if yes then alert 1 else alert 2
switch 1
when 1 then if yes then alert 1 else alert 2
As you can see in the output, in the first example the In the second example, the So the question is, what do we think of this change?
I’m inclined for option 2, releasing 2.2.1 as just this PR. This change feels to me a lot like when we fixed chaining between 1.12.6 and 1.12.7; technically it was a breaking change, but it corrected a bug. Though at least in that case the intended behavior was in the docs; this is changing undocumented behavior. On the other hand, 2.x is a lot less settled than 1.12.6 was, so changing undocumented behavior at this point should bother a lot fewer people. |
The last commit adds support for nested // if a then if b then c else d else e
if (a) {
if (b) {
c;
} else {
d;
}
} else {
e;
}
// if a then if b then if c then d else e else f
if (a) {
if (b) {
if (c) {
d;
} else {
e;
}
} else {
f;
}
}
// if a then if b then if c then d else if e then f else if g then h else i else j else k
if (a) {
if (b) {
if (c) {
d;
} else if (e) {
f;
} else if (g) {
h;
} else {
i;
}
} else {
j;
}
} else {
k;
} |
I’m thinking of just merging this in and releasing a big 2.2.0. Any objections? The minor (as opposed to batch) version bump should tell people that there might be meaningful changes in the release, as there very well could be for some people. @zdenko This looks good to me. Anything else you have planned for this PR, or is it ready? And any other PRs you’re working on that should be slipped into 2.2.0? |
@GeoffreyBooth I think it's ready and can be merged. I'm still working on improved |
Great, thanks for all your hard work! |
Fixes #3921 and #2342.
Compile fails when inline function is used in condition (control flow):
if
,unless
,while
,until
,switch
.Example cases:
[1], [2], [3], [4]
The main cause of the issue is incorrect positions of
INDENT
andOUTDENT
tag pair inserted inRewriter::normalizeLines
.Example:
The second
OUTDENT
should be placed betweena
andINDENT
.Besides that,
@tagPostfixConditionals
also convertsIF
intoPOST_IF
tag in such cases.At first, I’ve tried to adjust the conditions in
@normalizeLines
to correct theINDENT
/OUTDENT
position. I managed to get it work for cases where newline follows inline function, but I couldn’t solve cases with inline condition (if -> a then
) which are too ambiguous.So, at the end, I decided to add new method
@addParensToConditions
which wraps the function in parentheses.The better solution would be controlling the output in
grammar
andnodes
, but currently I don’t see it as an option, sinceExpression
used ingrammar
is too broad, which means that rules forIf
,While
andSwitch
nodes would have to be split into more detailed rules, e.g. forValue
,Code
,Assign
, …