From 87a6a5a8cd91b03c99768ad4e8c1d299755a180d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 30 Jan 2013 16:38:31 +0100 Subject: [PATCH] Clean up readNumber, fix parsing of '2.+2' Issue #9 --- acorn.js | 30 +++++++++++++++--------------- index.html | 30 +++++++++++++++--------------- test/tests.js | 31 +++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 30 deletions(-) diff --git a/acorn.js b/acorn.js index b00096460b..a2f4aa1a91 100644 --- a/acorn.js +++ b/acorn.js @@ -536,9 +536,9 @@ // The `forceRegexp` parameter is used in the one case where the // `tokRegexpAllowed` trick does not work. See `parseStatement`. - function readToken_dot(code) { + function readToken_dot() { var next = input.charCodeAt(tokPos+1); - if (next >= 48 && next <= 57) return readNumber(String.fromCharCode(code)); + if (next >= 48 && next <= 57) return readNumber(true); ++tokPos; return finishToken(_dot); } @@ -600,7 +600,7 @@ // The interpretation of a dot depends on whether it is followed // by a digit. case 46: // '.' - return readToken_dot(code); + return readToken_dot(); // Punctuation tokens. case 40: ++tokPos; return finishToken(_parenL); @@ -621,7 +621,7 @@ // Anything else beginning with a digit is an integer, octal // number, or float. case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9 - return readNumber(String.fromCharCode(code)); + return readNumber(false); // Quotes produce strings. case 34: case 39: // '"', "'" @@ -746,18 +746,18 @@ // Read an integer, octal integer, or floating-point number. - function readNumber(ch) { - var start = tokPos, isFloat = ch === "."; - if (!isFloat && readInt(10) == null) raise(start, "Invalid number"); - if (isFloat || input.charAt(tokPos) === ".") { - var next = input.charAt(++tokPos); - if (next === "-" || next === "+") ++tokPos; - if (readInt(10) === null && ch === ".") raise(start, "Invalid number"); + function readNumber(startsWithDot) { + var start = tokPos, isFloat = false, octal = input.charCodeAt(tokPos) === 48; + if (!startsWithDot && readInt(10) === null) raise(start, "Invalid number"); + if (input.charCodeAt(tokPos) === 46) { + ++tokPos; + readInt(10); isFloat = true; } - if (/e/i.test(input.charAt(tokPos))) { - var next = input.charAt(++tokPos); - if (next === "-" || next === "+") ++tokPos; + var next = input.charCodeAt(tokPos); + if (next === 69 || next === 101) { // 'eE' + next = input.charCodeAt(++tokPos); + if (next === 43 || next === 45) ++tokPos; // '+-' if (readInt(10) === null) raise(start, "Invalid number") isFloat = true; } @@ -765,7 +765,7 @@ var str = input.slice(start, tokPos), val; if (isFloat) val = parseFloat(str); - else if (ch !== "0" || str.length === 1) val = parseInt(str, 10); + else if (!octal || str.length === 1) val = parseInt(str, 10); else if (/[89]/.test(str) || strict) raise(start, "Invalid number"); else val = parseInt(str, 8); return finishToken(_num, val); diff --git a/index.html b/index.html index 0a3e94590d..7b308ba7b8 100644 --- a/index.html +++ b/index.html @@ -341,9 +341,9 @@ into it.

All in the name of speed.

The forceRegexp parameter is used in the one case where the -tokRegexpAllowed trick does not work. See parseStatement.

  function readToken_dot(code) {
+tokRegexpAllowed trick does not work. See parseStatement.

  function readToken_dot() {
     var next = input.charCodeAt(tokPos+1);
-    if (next >= 48 && next <= 57) return readNumber(String.fromCharCode(code));
+    if (next >= 48 && next <= 57) return readNumber(true);
     ++tokPos;
     return finishToken(_dot);
   }
@@ -403,7 +403,7 @@ into it.

function getTokenFromCode(code) { switch(code) {

The interpretation of a dot depends on whether it is followed by a digit.

    case 46: // '.'
-      return readToken_dot(code);

Punctuation tokens.

    case 40: ++tokPos; return finishToken(_parenL);
+      return readToken_dot();

Punctuation tokens.

    case 40: ++tokPos; return finishToken(_parenL);
     case 41: ++tokPos; return finishToken(_parenR);
     case 59: ++tokPos; return finishToken(_semi);
     case 44: ++tokPos; return finishToken(_comma);
@@ -416,7 +416,7 @@ by a digit.

var next = input.charCodeAt(tokPos+1); if (next === 120 || next === 88) return readHexNumber();

Anything else beginning with a digit is an integer, octal number, or float.

    case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9
-      return readNumber(String.fromCharCode(code));

Quotes produce strings.

    case 34: case 39: // '"', "'"
+      return readNumber(false);

Quotes produce strings.

    case 34: case 39: // '"', "'"
       return readString(code);

Operators are parsed inline in tiny state machines. '=' (61) is often referred to. finishOp simply skips the amount of characters it is given as second argument, and returns a token @@ -517,18 +517,18 @@ will return null unless the integer has exactly len di if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number"); return finishToken(_num, val); }

Read an integer, octal integer, or floating-point number.

  
-  function readNumber(ch) {
-    var start = tokPos, isFloat = ch === ".";
-    if (!isFloat && readInt(10) == null) raise(start, "Invalid number");
-    if (isFloat || input.charAt(tokPos) === ".") {
-      var next = input.charAt(++tokPos);
-      if (next === "-" || next === "+") ++tokPos;
-      if (readInt(10) === null && ch === ".") raise(start, "Invalid number");
+  function readNumber(startsWithDot) {
+    var start = tokPos, isFloat = false, octal = input.charCodeAt(tokPos) === 48;
+    if (!startsWithDot && readInt(10) === null) raise(start, "Invalid number");
+    if (input.charCodeAt(tokPos) === 46) {
+      ++tokPos;
+      readInt(10);
       isFloat = true;
     }
-    if (/e/i.test(input.charAt(tokPos))) {
-      var next = input.charAt(++tokPos);
-      if (next === "-" || next === "+") ++tokPos;
+    var next = input.charCodeAt(tokPos);
+    if (next === 69 || next === 101) { // 'eE'
+      next = input.charCodeAt(++tokPos);
+      if (next === 43 || next === 45) ++tokPos; // '+-'
       if (readInt(10) === null) raise(start, "Invalid number")
       isFloat = true;
     }
@@ -536,7 +536,7 @@ will return null unless the integer has exactly len di
 
     var str = input.slice(start, tokPos), val;
     if (isFloat) val = parseFloat(str);
-    else if (ch !== "0" || str.length === 1) val = parseInt(str, 10);
+    else if (!octal || str.length === 1) val = parseInt(str, 10);
     else if (/[89]/.test(str) || strict) raise(start, "Invalid number");
     else val = parseInt(str, 8);
     return finishToken(_num, val);
diff --git a/test/tests.js b/test/tests.js
index 7e14ad7a33..f01f5d3c53 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -25987,6 +25987,37 @@ test("123..toString(10)", {
   ]
 });
 
+test("123.+2", {
+  type: "Program",
+  start: 0,
+  end: 6,
+  body: [
+    {
+      type: "ExpressionStatement",
+      start: 0,
+      end: 6,
+      expression: {
+        type: "BinaryExpression",
+        start: 0,
+        left: {
+          type: "Literal",
+          start: 0,
+          end: 4,
+          value: 123
+        },
+        operator: "+",
+        right: {
+          type: "Literal",
+          start: 5,
+          end: 6,
+          value: 2
+        },
+        end: 6
+      }
+    }
+  ]
+});
+
 test("a\u2028b", {
   type: "Program",
   start: 0,