Distinguish a block from an object initializer

This is more of a theoretical question than a practical one. It's about disassembling some code, limited by curly braces.

Here are two examples of object initializers :

f({}); ({a:3}) 

Here are two examples of blocks :

 ;{} {a:3;} 

In practice, it seems that {...} blocks the block if an expression is required for the use-case code.

But I have never seen such a rule explicit or obvious in the ECMAScript specification, and I'm not even sure if this is true.

Is there any vague ambiguous reference? The correct rule if this is not so?

+6
source share
1 answer

Is there any vague reference?

This is similar to specification spread.

Short answer:

It depends on the context in which the constructor appears (which is why it is throughout the specification). Probably the most specific place it addressed is §12.4 , which states that the ExpressionStatement expression (the expression used where approval is required) cannot begin with { .

Long answer:

The key that the parser expects when it encounters { : if it expects an operator, then it knows that { starts a block. But if he expects expression, then he knows that { starts the initializer of the object. Look at the assignment:

 doThis(); // This line is just for context x = {a: 3}; 

At the beginning of the second line above, the parser is awaiting approval. But then he sees x = and knows that he is doing the job; at that moment, seeing = , the parser expects expression. The application is not valid. Therefore, he knows that { starts the initializer of the object, not the block.

On the contrary:

 doThis(); // This line is just for context {a: 3}; 

The second line above is the block containing the labeled statement. (Very strange looking, we'll get back to that.) The parser knows that since the parser expects an expression, not an expression, at the beginning of this line.

There are many other places where the parser expects to see expressions rather than statements. For example, after : in the property initializer:

 obj = { prop: {a: 3} }; 

... or inside arguments when executing a function call:

 foo({a: 3}); 

... either after the unary operator or immediately after opening ( etc.) In the specification you can find out what the parser will expect by what it says in the grammar, for parser analysis, for example this syntax diagram from §12.5 defining if :

  IfStatement:

if (Expression) Statement else Statement
if (Expression) Statement

This tells us that when processing the if in () parser expects an expression, but after the if () bit it expects an assertion.

So far so good, but JavaScript allows (almost) any expression where approval is allowed. This is true, for example:

 doThis(); // This line is just for context flag && doThat(); 

The second line above is a binary logical expression of the operator, but autonomous. The parser was awaiting a statement when it came across this. Thus, the && expression is what the specification invokes the ExpressionStatement expression. ExpressionStatement is defined in §12.4 .

So this leaves us with some ambiguity: if the parser is waiting for approval and sees { how does it know that it does not start with an object initializer expression that acts as an ExpressionStatement expression?

Answer: By fiat. :-) §12.4 talks about this when defining an ExpressionStatement expression:

NOTE. ExpressionStatement cannot begin with an opening curly brace, because this can make it ambiguous with a block.

So there is no ambiguity, not because of the subtle syntax trick, but only because the specification says so :-)

(If you had a reason really, really wanting to use the object initializer expression as an expression, you can do this, just put it in () .)

+7
source

All Articles