What's a valid left-hand-side expression in JavaScript grammar?
Asked Answered
S

2

17

Okay, we all know what the valid left-hand-side expressions are. Kind of.*

But, looking at the definition from the ECMA-Script standard, I'm very confused:

LeftHandSideExpression :
    NewExpression
    CallExpression

Is that just an error in the definition, or am I getting something wrong here? I mean, doesn't that actually mean that

new Object = 1; // NewExpression AssignmentOperator PrimaryExpression
function () { return foo; }() = 1;// CallExpression AssignmentOperator PrimaryExpression

are supposed to be valid assignment expressions?


* From my humble understanding, this would make much more sense:

LeftHandSideExpression :
    Identifier
    MemberExpression [ Expression ]
    MemberExpression . IdentifierName
    CallExpression [ Expression ]
    CallExpression . IdentifierName

Stain answered 14/9, 2010 at 14:32 Comment(3)
Your second example will work if you wrap the function call in brackets: (function () { return window; }()).x = 1Wolgast
@Daniel: Right, I updated the exampleStain
@Stain Your (humble understanding) definition of LeftHandSideExpression is missing the grouping operator ( LeftHandSideExpression ).Amabil
R
14

To concisely answer your question, everything beneath the LeftHandSideExpression production is a valid LeftHandSideExpression.


I think the question you are really asking is:

What is a valid LeftHandSideExpression and also assignable?

The answer to that is anything that resolves to a Reference which is a well defined concept in the specification. In your example

new Object = 1;

The new Object is a valid LeftHandSideExpression but it does not resolve to a Reference.

(new Object).x = 1;

The left hand side is a MemberExpression . IdentifierName which according to the spec the final step is:

Return a value of type Reference ...


If you consider it 2 separate properties it makes a lot more sense.

  1. Is it a valid LeftHandSideExpression?
  2. Is it a valid reference?

Property 1 is determined in the syntactical analysis phase and property 2 is determined in the semantic analysis phase. Check out 8.7.2 PutValue (V, W) for more details.

Here is a full explanation in the specification itself:

8.7 The Reference Specification Type

The Reference type is used to explain the behaviour of such operators as delete, typeof, and the assignment operators. For example, the left-hand operand of an assignment is expected to produce a reference. The behaviour of assignment could, instead, be explained entirely in terms of a case analysis on the syntactic form of the left-hand operand of an assignment operator, but for one difficulty: function calls are permitted to return references. This possibility is admitted purely for the sake of host objects. No built-in ECMAScript function defined by this specification returns a reference and there is no provision for a user-defined function to return a reference. (Another reason not to use a syntactic case analysis is that it would be lengthy and awkward, affecting many parts of the specification.)


After taking a look at your suggestion I believe it would throw off certain valid expressions (Note: I don't condone this.)

function OuterObj() {
    this.Name = "Outer";
    this.InnerObj = function() {
        this.Name = "Inner";
    }
}

var obj; (obj = new new OuterObj().InnerObj).Name = "Assigned";

This is a case where NewExpression is important

Rhetorical answered 14/9, 2010 at 14:46 Comment(3)
True. I always thought that phase 2 is carried out at run-time while phase 1 occurs at parse-time. Thus, if (false) { new Object = 1; } should not be a syntax error, and does ideed work in Safari. A look at the Firefox source code led me to this: Firefox treats this one as a special case. (Fun fact: error code is called JSMSG_BAD_LEFTSIDE_OF_ASS)Stain
@Pumbaa80 - I think doing it this way really makes sense in a dynamic language.Rhetorical
Why the specification setted the production ClassHeritage as "extends" LeftHandSideExpression? shouldn't it be BindingIdentifier? I found this awfully complicated. Imagine a class definition like class X extends (new Y), or class X extends (function() {})! Who in the world would need such funcionality? In fact the firefox even acused the first case as invalid.Intercut
M
2

This is an alternative JavaScript grammar which only will match valid LeftHandSideExpressions, that is, LeftHandSideExpressions that are actually assignable.

NewExpression :
    PrimaryExpression
    new NewExpressionQualifier Arguments
    new NewExpressionQualifier

NewExpressionQualifier :
    NewExpressionQualifier Qualifier
    NewExpression

CallExpression :
    NewExpression
    CallExpressionQualifier Arguments

CallExpressionQualifier :
    CallExpression
    CallExpressionQualifier Qualifier

LeftHandSideExpression :
    LeftHandSideExpression Qualifier
    CallExpression Qualifier
    Identifier
    ( LeftHandSideExpression )
    ( Expression , LeftHandSideExpression )

Qualifier :
    . IdentifierName
    [ Expression ]

Each Arguments for which the choice of associated new or call expression is ambiguous shall be associated with the nearest possible new expression that would otherwise have no corresponding Arguments. I think this is one of the reasons why there is both a NewExpression and a MemberExpression nonterminal in the JavaScript grammar.

Milieu answered 14/9, 2017 at 15:18 Comment(2)
From your definition of LeftHandSideExpression I conclude that (a = 1, b) would be an assignable LeftHandSideExpression. However, using it in an assignment (a = 1, b) = 2 results in Uncaught ReferenceError: Invalid left-hand side in assignment.Amabil
The comma operator is not a left hand side expression, since it does not return a reference (4. Return GetValue(rref)). However, it may contain a simple assignment expression as right operand. a = 1, b = 2 may be grouped as (a = 1) , (b = 2), but (a = 1, b) = 2 would result in GetValue(b) = 2.Amabil

© 2022 - 2024 — McMap. All rights reserved.