From 34e517de09d86aeba694619f055f4cbcfabe7470 Mon Sep 17 00:00:00 2001 From: Gerald Lewis Date: Fri, 20 Jan 2012 17:23:50 -0500 Subject: [PATCH] Issue #2060 Disallow uppercase radix prefixes and exponential notation --- lib/coffee-script/lexer.js | 22 +++++++++++++--------- src/lexer.coffee | 18 +++++++++++------- test/numbers.coffee | 9 ++++++--- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/lib/coffee-script/lexer.js b/lib/coffee-script/lexer.js index e2503c9a8b..8c560c365c 100644 --- a/lib/coffee-script/lexer.js +++ b/lib/coffee-script/lexer.js @@ -109,19 +109,23 @@ }; Lexer.prototype.numberToken = function() { - var binaryLiteral, dec, lexedLength, match, nonStrictOctalLiteral, number, oct, octalLiteral; + var binaryLiteral, lexedLength, match, number, octalLiteral; if (!(match = NUMBER.exec(this.chunk))) return 0; number = match[0]; - lexedLength = number.length; - if (nonStrictOctalLiteral = /^0\d+/.test(number)) { - dec = /[89]/.test(number) ? "\"" + number + "\" " : ''; - oct = dec ? '' : "\"" + number + "\" "; - this.error("decimal literals " + dec + "must not be prefixed with '0'; octal literals " + oct + "must be prefixed with '0o'"); + if (/[E]/.test(number)) { + this.error("exponential notation must be indicated with a lowercase 'e'"); + } else if (/[BOX]/.test(number)) { + this.error("radix prefixes must be lowercase '" + number + "'"); + } else if (/^0[89]/.test(number)) { + this.error("decimal literals '" + number + "' must not be prefixed with '0'"); + } else if (/^0[0-7]/.test(number)) { + this.error("octal literals '" + number + "' must be prefixed with '0o'"); } - if (octalLiteral = /0o([0-7]+)/i.exec(number)) { + lexedLength = number.length; + if (octalLiteral = /0o([0-7]+)/.exec(number)) { number = (parseInt(octalLiteral[1], 8)).toString(); } - if (binaryLiteral = /0b([01]+)/i.exec(number)) { + if (binaryLiteral = /0b([01]+)/.exec(number)) { number = (parseInt(binaryLiteral[1], 2)).toString(); } this.token('NUMBER', number); @@ -645,7 +649,7 @@ IDENTIFIER = /^([$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)([^\n\S]*:(?!:))?/; - NUMBER = /^0x[\da-f]+|^0b[01]+|^0o[0-7]+|^\d*\.?\d+(?:e[+-]?\d+)?/i; + NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i; HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/; diff --git a/src/lexer.coffee b/src/lexer.coffee index 1f6650b531..3f62a5a7f6 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -133,14 +133,18 @@ exports.Lexer = class Lexer numberToken: -> return 0 unless match = NUMBER.exec @chunk number = match[0] + if /[E]/.test number + @error "exponential notation must be indicated with a lowercase 'e'" + else if /[BOX]/.test number + @error "radix prefixes must be lowercase '#{number}'" + else if /^0[89]/.test number + @error "decimal literals '#{number}' must not be prefixed with '0'" + else if /^0[0-7]/.test number + @error "octal literals '#{number}' must be prefixed with '0o'" lexedLength = number.length - if nonStrictOctalLiteral = /^0\d+/.test number - dec = if /[89]/.test number then "\"#{number}\" " else '' - oct = if dec then '' else "\"#{number}\" " - @error "decimal literals #{dec}must not be prefixed with '0'; octal literals #{oct}must be prefixed with '0o'" - if octalLiteral = /0o([0-7]+)/i.exec number + if octalLiteral = /0o([0-7]+)/.exec number number = (parseInt octalLiteral[1], 8).toString() - if binaryLiteral = /0b([01]+)/i.exec number + if binaryLiteral = /0b([01]+)/.exec number number = (parseInt binaryLiteral[1], 2).toString() @token 'NUMBER', number lexedLength @@ -596,9 +600,9 @@ IDENTIFIER = /// ^ /// NUMBER = /// - ^ 0x[\da-f]+ | # hex ^ 0b[01]+ | # binary ^ 0o[0-7]+ | # octal + ^ 0x[\da-f]+ | # hex ^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal ///i diff --git a/test/numbers.coffee b/test/numbers.coffee index f2797f71c1..2f1f7394f8 100644 --- a/test/numbers.coffee +++ b/test/numbers.coffee @@ -15,7 +15,6 @@ test "Parser recognises binary numbers", -> eq 4, 0b100 - eq 5, 0B101 # Decimal Integer Literals @@ -57,12 +56,16 @@ test '#1168: leading floating point suppresses newline', -> test "Python-style octal literal notation '0o777'", -> eq 511, 0o777 - eq 511, 0O777 eq 1, 0o1 - eq 1, 0O1 eq 1, 0o00001 eq parseInt('0777', 8), 0o777 eq '777', 0o777.toString 8 eq 4, 0o4.valueOf() eq Number::toString, 0o777['toString'] eq Number::toString, 0o777.toString + +test "#2060: Disallow uppercase radix prefixes and exponential notation", -> + for char in ['b', 'o', 'x', 'e'] + program = "0#{char}0" + doesNotThrow -> CoffeeScript.compile program, bare: yes + throws -> CoffeeScript.compile program.toUpperCase(), bare: yes