Merge branch 'master' of ssh://git.planet-lab.org/git/plstackapi into observer3.0
[plstackapi.git] / planetstack / core / xoslib / static / js / xsh / tokens.js
diff --git a/planetstack/core/xoslib/static/js/xsh/tokens.js b/planetstack/core/xoslib/static/js/xsh/tokens.js
new file mode 100644 (file)
index 0000000..49c246e
--- /dev/null
@@ -0,0 +1,268 @@
+// tokens.js
+// 2009-05-17
+
+// (c) 2006 Douglas Crockford
+
+// Produce an array of simple token objects from a string.
+// A simple token object contains these members:
+//      type: 'name', 'string', 'number', 'operator'
+//      value: string or number value of the token
+//      from: index of first character of the token
+//      to: index of the last character + 1
+
+// Comments of the // type are ignored.
+
+// Operators are by default single characters. Multicharacter
+// operators can be made by supplying a string of prefix and
+// suffix characters.
+// characters. For example,
+//      '<>+-&', '=>&:'
+// will match any of these:
+//      <=  >>  >>>  <>  >=  +: -: &: &&: &&
+
+
+
+String.prototype.tokens = function (prefix, suffix) {
+    var c;                      // The current character.
+    var from;                   // The index of the start of the token.
+    var i = 0;                  // The index of the current character.
+    var length = this.length;
+    var n;                      // The number value.
+    var q;                      // The quote character.
+    var str;                    // The string value.
+
+    var result = [];            // An array to hold the results.
+
+    var make = function (type, value) {
+
+// Make a token object.
+
+        return {
+            type: type,
+            value: value,
+            from: from,
+            to: i
+        };
+    };
+
+// Begin tokenization. If the source string is empty, return nothing.
+
+    if (!this) {
+        return;
+    }
+
+// If prefix and suffix strings are not provided, supply defaults.
+
+    if (typeof prefix !== 'string') {
+        prefix = '<>+-&';
+    }
+    if (typeof suffix !== 'string') {
+        suffix = '=>&:';
+    }
+
+
+// Loop through this text, one character at a time.
+
+    c = this.charAt(i);
+    while (c) {
+        from = i;
+
+// Ignore whitespace.
+
+        if (c <= ' ') {
+            i += 1;
+            c = this.charAt(i);
+
+// name.
+
+        } else if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
+            str = c;
+            i += 1;
+            for (;;) {
+                c = this.charAt(i);
+                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+                        (c >= '0' && c <= '9') || c === '_') {
+                    str += c;
+                    i += 1;
+                } else {
+                    break;
+                }
+            }
+            result.push(make('name', str));
+
+// number.
+
+// A number cannot start with a decimal point. It must start with a digit,
+// possibly '0'.
+
+        } else if (c >= '0' && c <= '9') {
+            str = c;
+            i += 1;
+
+// Look for more digits.
+
+            for (;;) {
+                c = this.charAt(i);
+                if (c < '0' || c > '9') {
+                    break;
+                }
+                i += 1;
+                str += c;
+            }
+
+// Look for a decimal fraction part.
+
+            if (c === '.') {
+                i += 1;
+                str += c;
+                for (;;) {
+                    c = this.charAt(i);
+                    if (c < '0' || c > '9') {
+                        break;
+                    }
+                    i += 1;
+                    str += c;
+                }
+            }
+
+// Look for an exponent part.
+
+            if (c === 'e' || c === 'E') {
+                i += 1;
+                str += c;
+                c = this.charAt(i);
+                if (c === '-' || c === '+') {
+                    i += 1;
+                    str += c;
+                    c = this.charAt(i);
+                }
+                if (c < '0' || c > '9') {
+                    make('number', str).error("Bad exponent");
+                }
+                do {
+                    i += 1;
+                    str += c;
+                    c = this.charAt(i);
+                } while (c >= '0' && c <= '9');
+            }
+
+// Make sure the next character is not a letter.
+
+            if (c >= 'a' && c <= 'z') {
+                str += c;
+                i += 1;
+                make('number', str).error("Bad number");
+            }
+
+// Convert the string value to a number. If it is finite, then it is a good
+// token.
+
+            n = +str;
+            if (isFinite(n)) {
+                result.push(make('number', n));
+            } else {
+                make('number', str).error("Bad number");
+            }
+
+// string
+
+        } else if (c === '\'' || c === '"') {
+            str = '';
+            q = c;
+            i += 1;
+            for (;;) {
+                c = this.charAt(i);
+                if (c < ' ') {
+                    make('string', str).error(c === '\n' || c === '\r' || c === '' ?
+                        "Unterminated string." :
+                        "Control character in string.", make('', str));
+                }
+
+// Look for the closing quote.
+
+                if (c === q) {
+                    break;
+                }
+
+// Look for escapement.
+
+                if (c === '\\') {
+                    i += 1;
+                    if (i >= length) {
+                        make('string', str).error("Unterminated string");
+                    }
+                    c = this.charAt(i);
+                    switch (c) {
+                    case 'b':
+                        c = '\b';
+                        break;
+                    case 'f':
+                        c = '\f';
+                        break;
+                    case 'n':
+                        c = '\n';
+                        break;
+                    case 'r':
+                        c = '\r';
+                        break;
+                    case 't':
+                        c = '\t';
+                        break;
+                    case 'u':
+                        if (i >= length) {
+                            make('string', str).error("Unterminated string");
+                        }
+                        c = parseInt(this.substr(i + 1, 4), 16);
+                        if (!isFinite(c) || c < 0) {
+                            make('string', str).error("Unterminated string");
+                        }
+                        c = String.fromCharCode(c);
+                        i += 4;
+                        break;
+                    }
+                }
+                str += c;
+                i += 1;
+            }
+            i += 1;
+            result.push(make('string', str));
+            c = this.charAt(i);
+
+// comment.
+
+        } else if (c === '/' && this.charAt(i + 1) === '/') {
+            i += 1;
+            for (;;) {
+                c = this.charAt(i);
+                if (c === '\n' || c === '\r' || c === '') {
+                    break;
+                }
+                i += 1;
+            }
+
+// combining
+
+        } else if (prefix.indexOf(c) >= 0) {
+            str = c;
+            i += 1;
+            while (i < length) {
+                c = this.charAt(i);
+                if (suffix.indexOf(c) < 0) {
+                    break;
+                }
+                str += c;
+                i += 1;
+            }
+            result.push(make('operator', str));
+
+// single-character operator
+
+        } else {
+            i += 1;
+            result.push(make('operator', c));
+            c = this.charAt(i);
+        }
+    }
+    return result;
+};
+