Search code examples
javascriptsyntaxlanguage-design

Why is JavaScript UpdateExpression syntax so defined?


According to Ecma-262, prefix increment and decrement operators are defined as:

UpdateExpression :
    LeftHandSideExpression ++
    LeftHandSideExpression ‐‐
    ++ UnaryExpression
    ‐‐ UnaryExpression

This is surprising, both that the postfix and prefix operators are defined in the same rule, and that it's ++ UnaryExpression when surely a unary expression in general is not actually a valid argument for the update operators.

It would seem cleaner and more logical to define the prefix increment and decrement operators in UnaryExpression itself, along with the other prefix operators, and other C syntax languages do this, e.g. the C standard section 6.5.3 defines unary-expression to include the prefix update operators along with the others.

Why does JavaScript define these operators the way it does?


Solution

  • Why does JavaScript define these operators the way it does?

    It's mostly historical, as so many other oddities in the spec.

    It would seem cleaner and more logical to define the prefix increment and decrement operators in UnaryExpression itself, along with the other prefix operators, and other C syntax languages do this

    In fact, that's how it was defined for many years, in ES5.1 and ES6. It's simple, and has one syntactic production per precedence level (PostfixExpression being the next-tighter one), and was probably directly inherited from the C or Java grammar when JS was conceived.

    Surely a unary expression in general is not actually a valid argument for the update operators

    In C, -- -- x is actually valid, so that's why the prefix increment and decrement operator expressions contain another UnaryExpression. Of course, it would throw an exception when trying to evaluate the expression, but parsing it would work fine in ES5.

    ES6 then added the concept of early errors, and increment and decrement operations now required a valid assignment target expression upon parsing. This was not encoded in the syntactical productions though.

    It is surprising that the postfix and prefix operators are defined in the same rule

    This definition is new in ES7. It changed with this commit that introduced the exponentiation operator, where the PostfixExpression was renamed to UpdateExpression and the prefix operators were moved into the rule. Some change like that was necessary because the ** operator allows an update expression, but not a UnaryExpression as its first argument (because unary minus, as in -x ** 3, was deemed too ambiguous).