-
Notifications
You must be signed in to change notification settings - Fork 2k
Open
Description
Built-in tests are passing. But it wasn't tested extensively otherwise, so I'm not comfortable doing a PR yet.
The code could certainly be improved too. I'm gonna move on, so I don't know if I'll get back at it.
I'll just post a patch for anyone interested. (https://github.com/a1q0/coffeescript/tree/fix/function-literals-as-arguments)
From 9e8ab5cb2fc57e1b246c6c99b095090b6a1e8451 Mon Sep 17 00:00:00 2001
From: a1q0 <[email protected]>
Date: Sat, 10 May 2025 04:22:48 +0200
Subject: [PATCH] Fix function literals as arguments
---
src/rewriter.coffee | 32 ++++++++++++++++++++++++++++++--
1 file changed, 30 insertions(+), 2 deletions(-)
diff --git a/src/rewriter.coffee b/src/rewriter.coffee
index e8fbcbca..9412a25d 100644
--- a/src/rewriter.coffee
+++ b/src/rewriter.coffee
@@ -77,11 +77,39 @@ exports.Rewriter = class Rewriter
detectEnd: (i, condition, action, opts = {}) ->
{tokens} = this
levels = 0
+ inImplicitCall = false
while token = tokens[i]
return action.call this, token, i if levels is 0 and condition.call this, token, i
+
+ if opts.inSingleLineFunctionLiteral
+ backStack = []
+ j = i
+
+ # walk backwards til start of current expression ignoring generated tokens
+ while j >= 0 and (backStack.length or @tag(i) not in ['->', '=>'] and (@tag(i) not in EXPRESSION_START or @tokens[i].generated) and @tag(i) not in LINEBREAKS)
+
+ # set flag if inside of implicit call, aka IMPLICIT_FUNC token followed by IMPLICIT_CALL token at current expression level
+ if @tag(j) in IMPLICIT_FUNC and @tag(j + 1) in IMPLICIT_CALL and backStack.length == 0
+ inImplicitCall = true
+ break
+
+ backStack.push @tag(j) if @tag(j) in EXPRESSION_END
+ if @tag(j) in EXPRESSION_START and backStack.length
+ backStack.pop()
+
+ # don't go back further than current expression, only the return expression matters.
+ if backStack.length == 0
+ break
+
+ j -= 1
+
if token[0] in EXPRESSION_START
levels += 1
- else if token[0] in EXPRESSION_END
+ # the comma should act as EXPRESSION_END only when
+ # - expression levels is 0, we don't want to interfer with sub-expression.
+ # - it is not inside of an implicit call `foo(() -> implicitCall 1, 2)`
+ # - it is inside of a function literal `opts.inSingleLineFunctionLiteral`
+ else if token[0] in EXPRESSION_END or (opts.inSingleLineFunctionLiteral and (not inImplicitCall) and token[0] is ',' and levels is 0)
levels -= 1
if levels < 0
return if opts.returnOnNegativeLevel
@@ -728,7 +756,7 @@ exports.Rewriter = class Rewriter
if tag is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
i = closeElseTag tokens, i
tokens.splice i + 1, 0, indent
- @detectEnd i + 2, condition, action
+ @detectEnd i + 2, condition, action, inSingleLineFunctionLiteral: tag in ['->', '=>']
tokens.splice i, 1 if tag is 'THEN'
return 1
return 1
--
2.44.0.windows.1
Examples that only work with the patch:
foo(() -> {1, 2, 3}, 2)
// transpiled:
foo(function() {
return {1: 1, 2: 2, 3: 3};
}, 2);
foo(() -> implicitCall 1, 2)
// transpiled:
foo(function() {
return implicitCall(1, 2);
});
foo(() -> 1, 2, 3)
// transpiled:
foo(function() {
return 1;
}, 2, 3);
foo(() -> { 1, 2 })
// transpiled:
foo(function() {
return {1: 1, 2: 2};
});
foo(() -> implicitCall 1, 2; { 1, 2 })
// transpiled:
foo(function() {
implicitCall(1, 2);
return {1: 1, 2: 2};
});
Metadata
Metadata
Assignees
Labels
No labels