Why is 019 not a JavaScript syntax error? Or why is 019 > 020
Asked Answered
M

1

78

If I type 019 > 020 in the JavaScript console (tested in both Chrome and Firefox), I get the answer true.

This is due to 020 being interpreted as an OctalIntegerLiteral (equals 16) whereas 019 is apparently being interpreted as DecimalLiteral (and equals 19). As 19 is greater than 16, 019 > 020 is true.

What puzzles me is why 019 is interpreted as a DecimalLiteral in first place. Which production is it? DecimalIntegerLiteral does not allow 019:

DecimalIntegerLiteral ::
    0
    NonZeroDigit DecimalDigits_opt

OctalIntegerLiteral also does not allow 019 (as 9 is not an octal digit):

OctalIntegerLiteral ::
    0 OctalDigit
    OctalIntegerLiteral OctalDigit

OctalDigit :: one of
    0 1 2 3 4 5 6 7

So from what I see in the specification, 019 should actually be rejected, I don't see why it is interpreted as a decimal integer.

I guess there's some kind of compatibility rule in place here but I have failed to find a formal definition. Could please anyone help me with this?

(Why I need this: I'm developing a JavaScript/ECMAScript parser for Java with JavaCC and have to pay a special attention to the specification - and deviations thereof.)

Manchu answered 24/1, 2015 at 15:15 Comment(14)
Could it be the left argument gets treated as a string, and (therefore) so does the right argument -- after being converted to a decimal?Untrue
@Untrue No, it's not inside any quotes so not a string.Manchu
JavaScript is pretty forgiving. Maybe it just converts the 019 by trimming the excess zeros.Fungiform
But you don't get any error... hm. What is 019+1?Untrue
019 + 0 == 19 and 020 + 0 == 17 so it's ignoring the leading zero if it contains non Octal digits.Langouste
@MathewFoscarini This is what I'm saying. The question is why of if is it formally correct.Manchu
Fun fact: 'use strict'; 019 → SyntaxError: octal literals and octal escape sequences are deprecatedOtisotitis
@Manchu this is a specific parsing issue. You're looking for the specifications of how JavaScript will interrupt a literal as Octal. Not sure where that would be documented.Langouste
@Mathew: surely 020 + 0 ≠≠ 17 :)Untrue
@René Interesting. 019 is actually not an octal literal as far as I can tell.Manchu
@lexicore, also interesting: parseInt("020", 8) = 16, while parseInt("019", 8) = 1. mdn - parseIntFungiform
@Mouser: not the same thing, parseInt just stops at the first invalid character.Untrue
@FelixKling Because that's how the production is formulated. Either 0 or NonZeroDigit DecimalDigits_opt. If 0 is followed by anything, it's not DecimalIntegerLiteral anymore.Manchu
Yeah, sorry, I didn't read it correctly. FYI, 019 throws an error in esprima.Summerlin
V
52

From what I could find, it seems that some implementations of JavaScript just don't follow the spec on that point.

From the MDN site:

Note that decimal literals can start with a zero (0) followed by another decimal digit, but If the next digit after the leading 0 is smaller than 8, the number gets parsed as an octal number. This won't throw in JavaScript, see bug 957513. See also the page about parseInt().

This still doesn't explain why 019 == 19, given that the next digit after the leading 0 is 1 and the whole number should therefore be parsed as octal. But the referenced bug does seem related to your case. Its description says:

The following JavaScript program should throw an error:

08

As per the spec, DecimalIntegerLiteral can never be 0 directly followed by another decimal digit, although Chrome/Opera, PrestOpera, and Firefox do support it.

The bug is closed as WONTFIX

However, 019 would be a valid decimal literal, with value equal to 19, according to the draft of the next edition:

https://people.mozilla.org/~jorendorff/es6-draft.html#sec-additional-syntax-numeric-literals

(I've marked the relevant rules)

The syntax and semantics of 11.8.3 is extended as follows except that 
this extension is not allowed for strict mode code:

[...]

DecimalIntegerLiteral ::
    0
    NonZeroDigit DecimalDigits_opt
    NonOctalDecimalIntegerLiteral                         // (1)

NonOctalDecimalIntegerLiteral ::
    0 NonOctalDigit
    LegacyOctalLikeDecimalIntegerLiteral NonOctalDigit    // (2)
    NonOctalDecimalIntegerLiteral DecimalDigit

LegacyOctalLikeDecimalIntegerLiteral ::
    0 OctalDigit                                          // (3)
    LegacyOctalLikeDecimalIntegerLiteral OctalDigit

So 01 is a LegacyOctalLikeDecimalIntegerLiteral (3) . Then 019 is a NonOctalDecimalIntegerLiteral (2) which in turn is a DecimalIntegerLiteral (1).

Vendible answered 24/1, 2015 at 16:30 Comment(5)
This is the correct answer. Chrome seems to have a similar problem. Evaluating 019 with Spidermonkey yields 1: warning: 09 is not a legal ECMA-262 octal constant: 019. It seems like it was backed out because existing (important) sites would break.Summerlin
Not sure why you'd expect 019 to be an octal literal just because the second digit is a 1? Surely anything that contains a 9 can't be octal…Lewiss
@Lewiss I guess you should ask the guys at MDN about that, as I couldn't find any point in the ECMAScript spec that justifies this interpretation.Vendible
Looking at V8 parsing scanner, it indeed assumes that if a number starts with 0 but has a digit 8 or 9, then the number is decimal (there is no comment explaining why though) github.com/v8/v8/blob/97757e2d8c5b706f1f642340a424b38e20022a2c/…Delacourt
@VitaliiFedorenko Very impressed how you digged it out.Manchu

© 2022 - 2024 — McMap. All rights reserved.