Closed rbuckton closed 3 years ago
I think the latter example is pretty compelling; i don't find any value in supporting it for module namespace objects (but obv it wouldn't make any sense to treat one differently than the other).
How could we structure such a grammar such that a.b::c.d()
is not ambiguous between
((a.b)::(c.d))()
(this issue’s goal) versus
(a.b)::((c.d)())
(useless) and
((a.b)::c).d)()
(the current behavior)?
The new
operator does something similar somehow. It'll keep grabbing stuff to the right of the new
until it reaches function-call syntax, or something of lower precedence than property access (.
).
Would it be possible to employ a similar trick? The ::
token will keep grabbing stuff to its right, until it reaches call syntax or something with precedence lower than property access?
We would just need to also add special handling when ::
and new
is found together in the same space, like with obj::new f()
and new obj::f()
. Both are pretty useless, so I would be ok making them into syntax errrors. But, I'm also fine with them still having runtime semantics, e.g. the second one be interpreted as "first bind obj
to f
, then new
f (causing the bound obj to immediately be chucked - so, it's useless to do this, but it doesn't necessarily have to be a syntax error)".
I think it might be possible, yeah. And I think that new a::b
and new a::b()
would probably both be SyntaxErrors.
Expression | Current grouping | Desired grouping |
---|---|---|
a.b::c.d |
(a.b::c).d |
(a.b)::(c.d) |
a.b::c.d() |
((a.b::c).d)() |
((a.b)::(c.d))() |
a.b()::c.d |
(((a.b)())::c).d |
((a.b)())::(c.d) |
new a.b |
new (a.b) |
new (a.b) |
new a.b()() |
(new (a.b)())() |
(new (a.b)())() |
new a.b().c |
(new (a.b)()).c |
(new (a.b)()).c |
new a.b::c |
new ((a.b)::c) |
SyntaxError? |
new a.b::c() |
new ((a.b)::c)() |
SyntaxError? |
new a.b()::c |
(new (a.b)())::c |
(new (a.b)())::c |
a?.b::c.d |
((a?.b)::c).d |
(a?.b)::(c.d) |
a.b::c?.d |
(a?.b)::c)?.d |
((a.b)::c)?.d |
I’ll think about this more.
If we do end up figuring out how to make a.b::c.d()
unambiguously group as ((a.b)::(c.d))()
, then I think that the RHS chain-of-property-identifiers expression is similar enough to decorators’ syntax that they should share a production, named something like SimpleMemberExpression.
I think the following grammar unambiguously fulfills the table in the preceding comment.
I will file a pull request later.
The grammar in my previous comment would parse a.b::c?.d
as ((a.b)::c)?.d
.
I don’t like this. a.b::c.d
, the version without the ?
, groups very differently into (a.b)::(c.d)
. Having the grouping silently change so much simply by adding ?
is probably a footgun.
So I think a.b::c?.d
should be a SyntaxError without parentheses.
(Of course, a.b::c()?.d
would still be valid and would still group into (((a.b)::c)())?.d
.)
This change should be doable by removing the OptionalExpression : BindThisExpression OptionalChain
production from the previous comment’s grammar, which would prohibit optional chains from immediately following bind-this
expressions without parentheses.
This SyntaxError probably wouldn’t affect everyday usage anyway. Developers probably wouldn’t try to put an optional chain in the bind-this
expression’s RHS, since a.b::(null)
would throw a TypeError anyway. And developers probably wouldn’t try to put an optional chain immediately after a bind-this
expression, since a bind-this
expression will always create a bound function and will never create a nullish value.
The current explainer expects the RHS of
::
to be either:This works well for existing variables like this:
But seems to require excess ceremony when used with module namespace imports:
It would be nice to support a dotted name, especially since there are some methods built into the JS core API that intentionally support tear-offs (i.e., Array.prototype.*):