From 714c05750703bc9c88a545e9654d9bb531bad87d Mon Sep 17 00:00:00 2001
From: Ken Rockot <ken@kenrockot.com>
Date: Mon, 11 Jun 2012 06:40:07 +0000
Subject: [PATCH] [scheme] implement correct parsing of all numeric formats
 defined in R5RS

---
 mode/scheme/scheme.js | 66 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 59 insertions(+), 7 deletions(-)

diff --git a/mode/scheme/scheme.js b/mode/scheme/scheme.js
index 8872edafe..f981b235b 100644
--- a/mode/scheme/scheme.js
+++ b/mode/scheme/scheme.js
@@ -29,6 +29,30 @@ CodeMirror.defineMode("scheme", function (config, mode) {
         state.indentStack = state.indentStack.prev;
     }
 
+    var binaryMatcher = new RegExp(/^(?:[-+]i|[-+][01]+#*(?:\/[01]+#*)?i|[-+]?[01]+#*(?:\/[01]+#*)?@[-+]?[01]+#*(?:\/[01]+#*)?|[-+]?[01]+#*(?:\/[01]+#*)?[-+](?:[01]+#*(?:\/[01]+#*)?)?i|[-+]?[01]+#*(?:\/[01]+#*)?)(?=[()\s;"]|$)/i);
+    var octalMatcher = new RegExp(/^(?:[-+]i|[-+][0-7]+#*(?:\/[0-7]+#*)?i|[-+]?[0-7]+#*(?:\/[0-7]+#*)?@[-+]?[0-7]+#*(?:\/[0-7]+#*)?|[-+]?[0-7]+#*(?:\/[0-7]+#*)?[-+](?:[0-7]+#*(?:\/[0-7]+#*)?)?i|[-+]?[0-7]+#*(?:\/[0-7]+#*)?)(?=[()\s;"]|$)/i);
+    var hexMatcher = new RegExp(/^(?:[-+]i|[-+][\da-f]+#*(?:\/[\da-f]+#*)?i|[-+]?[\da-f]+#*(?:\/[\da-f]+#*)?@[-+]?[\da-f]+#*(?:\/[\da-f]+#*)?|[-+]?[\da-f]+#*(?:\/[\da-f]+#*)?[-+](?:[\da-f]+#*(?:\/[\da-f]+#*)?)?i|[-+]?[\da-f]+#*(?:\/[\da-f]+#*)?)(?=[()\s;"]|$)/i);
+    var decimalMatcher = new RegExp(/^(?:[-+]i|[-+](?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)i|[-+]?(?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)@[-+]?(?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)|[-+]?(?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)[-+](?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*)?i|(?:(?:(?:\d+#+\.?#*|\d+\.\d*#*|\.\d+#*|\d+)(?:[esfdl][-+]?\d+)?)|\d+#*\/\d+#*))(?=[()\s;"]|$)/i);
+
+    function isBinaryNumber (stream) {
+        return stream.match(binaryMatcher);
+    }
+
+    function isOctalNumber (stream) {
+        return stream.match(octalMatcher);
+    }
+
+    function isDecimalNumber (stream, backup) {
+        if (backup === true) {
+            stream.backUp(1);
+        }
+        return stream.match(decimalMatcher);
+    }
+
+    function isHexNumber (stream) {
+        return stream.match(hexMatcher);
+    }
+
     return {
         startState: function () {
             return {
@@ -97,22 +121,50 @@ CodeMirror.defineMode("scheme", function (config, mode) {
                     } else if (ch == "'") {
                         returnType = ATOM;
                     } else if (ch == '#') {
-                        if (stream.eat("|")) {					// Multi-line comment
+                        if (stream.eat("|")) {                    // Multi-line comment
                             state.mode = "comment"; // toggle to comment mode
                             returnType = COMMENT;
-                        } else if (stream.eat(/[tf]/)) {			// #t/#f (atom)
+                        } else if (stream.eat(/[tf]/i)) {            // #t/#f (atom)
                             returnType = ATOM;
-                        } else if (stream.eat(';')) {				// S-Expr comment
+                        } else if (stream.eat(';')) {                // S-Expr comment
                             state.mode = "s-expr-comment";
                             returnType = COMMENT;
+                        } else {
+                            var numTest = null, hasExactness = false, hasRadix = true;
+                            if (stream.eat(/[ei]/i)) {
+                                hasExactness = true;
+                            } else {
+                                stream.backUp(1);       // must be radix specifier
+                            }
+                            if (stream.match(/^#b/i)) {
+                                numTest = isBinaryNumber;
+                            } else if (stream.match(/^#o/i)) {
+                                numTest = isOctalNumber;
+                            } else if (stream.match(/^#x/i)) {
+                                numTest = isHexNumber;
+                            } else if (stream.match(/^#d/i)) {
+                                numTest = isDecimalNumber;
+                            } else if (stream.match(/^[-+0-9.]/, false)) {
+                                hasRadix = false;
+                                numTest = isDecimalNumber;
+                            // re-consume the intial # if all matches failed
+                            } else if (!hasExactness) {
+                                stream.eat('#');
+                            }
+                            if (numTest != null) {
+                                if (hasRadix && !hasExactness) {
+                                    // consume optional exactness after radix
+                                    stream.match(/^#[ei]/i);
+                                }
+                                if (numTest(stream))
+                                    returnType = NUMBER;
+                            }
                         }
-
+                    } else if (/^[-+0-9.]/.test(ch) && isDecimalNumber(stream, true)) { // match non-prefixed number, must be decimal
+                        returnType = NUMBER;
                     } else if (ch == ";") { // comment
                         stream.skipToEnd(); // rest of the line is a comment
                         returnType = COMMENT;
-                    } else if (/\d/.test(ch) && stream.match(/$|^\d*(?:\/\d+|(?:\.\d+)?(?:[eE][+\-]?\d+)?)\b/) ||
-                               ch == "-" && stream.match(/^\d+(?:\/\d+|(?:\.\d+)?(?:[eE][+\-]?\d+)?)\b/)) {
-                        returnType = NUMBER;
                     } else if (ch == "(" || ch == "[") {
                         var keyWord = ''; var indentTemp = stream.column();
                         /**
-- 
GitLab