Grammar notation: [Yield] and [Await] parameters

I’ve just started looking into parameterised productions and I’m wondering if I’m expanding/interpreting them correctly.

Here is the first production for FunctionDeclaration:

FD_[Yield, Await, Default] :

function BI_[?Yield, ?Await] ( FP_[~Yield, ~Await] ) { FB_[~Yield, ~Await] }

(NOTE: I've abbreviated the non-terminals.)

I expanded this as follows:

FD :

function BI (FP) { FB }

FD_Yield :

function BI_Yield (FP) { FB }

FD_Await : 
function BI_Await (FP) { FB }

FD_Yield_Await : 
function BI_Yield_Await (FP) { FB }


  1. Am I reading/expanding the productions correctly?

I read FunctionBody[~Yield, ~Await] as meaning "neither 'yield' nor 'await' are reserved words for instances of this FunctionBody".

  1. Why were the [~Yield] and [~Await] prefixes removed from the RHS of BindingIdentifier?

They were there in 2017, but have been omitted since. Initially, I thought that it was due to the newer specs using Early Errors to restrict the use of "await" and "yield" instead (see error #4 and #5), but the 2017 spec had these errors too.

  1. How might we arrive at a production like FD_Yield : function BI_Yield (FP) { FB } during a parse?

My understanding is that the _Yield suffix gets appended to the FunctionDeclaration non-terminal if it appears on the RHS of a GeneratorDeclaration (because GeneratorBody : FunctionBody_Yield ==>+ FunctionDeclaration_Yield).

So the FunctionDeclaration_Yield production is applied when we encounter a function declaration inside a generator. The BindingIndentifier_Yield non-terminal on the RHS ensures that it is a syntax error if this function declaration is named "yield" (see early error for BindingIdentifier_Yield):

function* gen() {
  function yield() {}; 

} // SyntaxError

(As an aside, it is legal for that function to contain a function named "yield" since the FunctionBody non-terminal on the RHS does not inherit the _Yield suffix, so the below is allowed:)

function* gen() {
  function foo() {
    function yield() {};

} // no error

Yes. That said, I would not encourage thinking about the expansions; just think about the presence or absence of the flags directly.

I've written more about the interpretation of the flags here.

git blame is helpful here. It will point you to this PR, which you can read to see that it was so that ASI would not apply between the let and the await 0 in

await 0;

in an async function.

Your understanding looks right to me.

1 Like

Very well-written, thank you.

Oh right, I had seen this note:


yield and await are permitted as BindingIdentifier in the grammar, and prohibited with static semantics below, to prohibit automatic semicolon insertion in cases such as

await 0;

though it went over my head. I had some intuition for ASI just from practical experience with the language but hadn't seen how it was specified in the spec until now. Never again will I take semicolons for granted lol

Thank you for your help as always @bakkot.