Why does CallExpression production include Cover-CallExpression-And-AsyncArrowHead?

I wonder what is the point of this production:

CallExpression[Yield, Await] :
  CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await]
  ...

Why can't it be simply:

CallExpression[Yield, Await] :
  MemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await]
  ...

Here's the supplemental syntax whose purpose I don't understand:

When processing an instance of the production
CallExpression : CoverCallExpressionAndAsyncArrowHead
the interpretation of CoverCallExpressionAndAsyncArrowHead is refined using the following grammar:

CallMemberExpression[Yield, Await] :
  MemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await]

The CallMemberExpression production that's supposed to refine the cover, is exactly how CoverCallExpressionAndAsyncArrowHead is defined in full.

2 Likes

CoverCallExpressionAndAsyncArrowHead is used in two productions, CallExpression (as you've seen) and also AsyncArrowFunction. If we were to replace both occurrences with MemberExpression[...] Arguments[...], that would be fine for CallExpression, but not for AsyncArrowFunction.

That's not what I asked.

Well, if we replaced only one of the occurrences, then we'd get roughly the same parsing conflict that the Cover grammar is there to avoid.

For the supplemental syntax to avoid some ambiguity, it must restrict the sequence of tokens matched by CoverCallExpressionAndAsyncArrowHead. I don't see how CallMemberExpression can do that, when it's identical to CoverCallExpressionAndAsyncArrowHead.

No, that's not the case. You could have several supplemental syntaxes, none of which restrict the sequence of tokens matched by the cover symbol, and there wouldn't be an ambiguity.

Are you thinking of an ambiguity between different supplemental syntaxes, or between a supplemental syntax and the syntax of the cover symbol? Neither kind of ambiguity can happen, because no two of those syntaxes can be involved in the same parse.

The supplemental syntax is a tool to avoid ambiguity. Speaking about ambiguity between supplemental syntax and something else doesn't make much sense, there should be none.

I was referring to this paragraph from 5.1.4 The Syntactic Grammar:

In certain cases, in order to avoid ambiguities, the syntactic grammar uses generalized productions that permit token sequences that do not form a valid ECMAScript Script or Module. (...)

In such cases a more restrictive supplemental grammar is provided that further restricts the acceptable token sequences. Typically, an early error rule will then define an error condition if "P is not covering an N", where P is a Parse Node (an instance of the generalized production) and N is a nonterminal from the supplemental grammar. Here, the sequence of tokens originally matched by P is parsed again using N as the goal symbol. (...)

An error occurs if the sequence of tokens cannot be parsed as a single instance of N, with no tokens left over. (...)

Substituting:

  • P = CoverCallExpressionAndAsyncArrowHead
  • N = CallMemberExpression

Every sequence of tokens matched by P can also be parsed as a single instance of N, because they're the same grammar.

(Not just that, but also to avoid other parsing problems like the need for large/unbounded lookahead to resolve conflicts.)

Should be, sure, but you seem to be concerned about an ambiguity that isn't being avoided, so I was trying to figure out what you had in mind.

Yeah, it says that, but it doesn't have to be more restrictive. The supplemental grammar's restrictiveness isn't what causes the ambiguity (or parsing problems) to be avoided.

Problems are avoided by introducing a covering symbol at two spots in the grammar that would otherwise come into conflict during parsing.

Yup, and that's okay.

1 Like

Thank you, this is the important link I was missing.

Just to make sure I understand that correctly... The covering symbol here doesn't avoid any ambiguity, it allows parsing with only a single "pending" non-terminal. The next token that comes after CoverCallExpressionAndAsyncArrowHead tells you whether it was AsyncArrowHead or CallMemberExpression (and then you apply the supplemental syntax).

Hypothetically, if I replaced both occurrences of CoverCallExpressionAndAsyncArrowHead with their respective supplemental syntax, I would need to hold two "pending" non-terminals (MemberExpression and Arguments) before reaching the '=>' (or finding it's not there). Is that correct?

1 Like

That sounds pretty close to correct. But the problem isn't so much that MemberExpression and Arguments are two non-terminals. In fact, note that the actual supplemental syntax there is CallMemberExpression, which is only one non-terminal.

I'd put it more like this: there's a context that could equally well be the start of a CallExpression or AsyncErrorFunction, and (in the absence of CoverCallEtc):

  • CallExpression is expecting CallMemberExpression, whereas
  • AsyncArrowFunction is expecting AsyncArrowHead,

and attempting to handle those two possibilities will quickly lead to conflicts (i.e., points where the two possibilities will 'tell' the parser to do something different). And yes, those two possibilities can be distinguished by a following => (or lack thereof), but that's arbitrarily far away, so unless your parser has unbounded lookahead, it's stuck.

(That's assuming it's a deterministic parser. A non-deterministic parser wouldn't care about any of the above.)

1 Like