text: var openParentheses = "(".charCodeAt(0);
  var closeParentheses = ")".charCodeAt(0);
  var singleQuote = "'".charCodeAt(0);
  var doubleQuote = '"'.charCodeAt(0);
  var backslash = "\\".charCodeAt(0);
  var slash = "/".charCodeAt(0);
  var comma = ",".charCodeAt(0);
  var colon = ":".charCodeAt(0);
  var star = "*".charCodeAt(0);
  
  module.exports = function(input) {
    var tokens = [];
    var value = input;
  
    var next, quote, prev, token, escape, escapePos, whitespacePos;
    var pos = 0;
    var code = value.charCodeAt(pos);
    var max = value.length;
    var stack = [{ nodes: tokens }];
    var balanced = 0;
    var parent;
  
    var name = "";
    var before = "";
    var after = "";
  
    while (pos < max) {
      // Whitespaces
      if (code <= 32) {
        next = pos;
        do {
          next += 1;
          code = value.charCodeAt(next);
        } while (code <= 32);
        token = value.slice(pos, next);
  
        prev = tokens[tokens.length - 1];
        if (code === closeParentheses && balanced) {
          after = token;
        } else if (prev && prev.type === "div") {
          prev.after = token;
        } else if (
          code === comma ||
          code === colon ||
          (code === slash && value.charCodeAt(next + 1) !== star)
        ) {
          before = token;
        } else {
          tokens.push({
            type: "space",
            sourceIndex: pos,
            value: token
          });
        }
  
        pos = next;
  
        // Quotes
      } else if (code === singleQuote || code === doubleQuote) {
        next = pos;
        quote = code === singleQuote ? "'" : '"';
        token = {
          type: "string",
          sourceIndex: pos,
          quote: quote
        };
        do {
          escape = false;
          next = value.indexOf(quote, next + 1);
          if (~next) {
            escapePos = next;
            while (value.charCodeAt(escapePos - 1) === backslash) {
              escapePos -= 1;
              escape = !escape;
            }
          } else {
            value += quote;
            next = value.length - 1;
            token.unclosed = true;
          }
        } while (escape);
        token.value = value.slice(pos + 1, next);
  
        tokens.push(token);
        pos = next + 1;
        code = value.charCodeAt(pos);
  
        // Comments
      } else if (code === slash && value.charCodeAt(pos + 1) === star) {
        token = {
          type: "comment",
          sourceIndex: pos
        };
  
        next = value.indexOf("*/", pos);
        if (next === -1) {
          token.unclosed = true;
          next = value.length;
        }
  
        token.value = value.slice(pos + 2, next);
        tokens.push(token);
  
        pos = next + 2;
        code = value.charCodeAt(pos);
  
        // Dividers
      } else if (code === slash || code === comma || code === colon) {
        token = value[pos];
  
        tokens.push({
          type: "div",
          sourceIndex: pos - before.length,
          value: token,
          before: before,
          after: ""
        });
        before = "";
  
        pos += 1;
        code = value.charCodeAt(pos);
  
        // Open parentheses
      } else if (openParentheses === code) {
        // Whitespaces after open parentheses
        next = pos;
        do {
          next += 1;
          code = value.charCodeAt(next);
        } while (code <= 32);
        token = {
          type: "function",
          sourceIndex: pos - name.length,
          value: name,
          before: value.slice(pos + 1, next)
        };
        pos = next;
  
        if (name === "url" && code !== singleQuote && code !== doubleQuote) {
          next -= 1;
          do {
            escape = false;
            next = value.indexOf(")", next + 1);
            if (~next) {
              escapePos = next;
              while (value.charCodeAt(escapePos - 1) === backslash) {
                escapePos -= 1;
                escape = !escape;
              }
            } else {
              value += ")";
              next = value.length - 1;
              token.unclosed = true;
            }
          } while (escape);
          // Whitespaces before closed
          whitespacePos = next;
          do {
            whitespacePos -= 1;
            code = value.charCodeAt(whitespacePos);
          } while (code <= 32);
          if (pos !== whitespacePos + 1) {
            token.nodes = [
              {
                type: "word",
                sourceIndex: pos,
                value: value.slice(pos, whitespacePos + 1)
              }
            ];
          } else {
            token.nodes = [];
          }
          if (token.unclosed && whitespacePos + 1 !== next) {
            token.after = "";
            token.nodes.push({
              type: "space",
              sourceIndex: whitespacePos + 1,
              value: value.slice(whitespacePos + 1, next)
            });
          } else {
            token.after = value.slice(whitespacePos + 1, next);
          }
          pos = next + 1;
          code = value.charCodeAt(pos);
          tokens.push(token);
        } else {
          balanced += 1;
          token.after = "";
          tokens.push(token);
          stack.push(token);
          tokens = token.nodes = [];
          parent = token;
        }
        name = "";
  
        // Close parentheses
      } else if (closeParentheses === code && balanced) {
        pos += 1;
        code = value.charCodeAt(pos);
  
        parent.after = after;
        after = "";
        balanced -= 1;
        stack.pop();
        parent = stack[balanced];
        tokens = parent.nodes;
  
        // Words
      } else {
        next = pos;
        do {
          if (code === backslash) {
            next += 1;
          }
          next += 1;
          code = value.charCodeAt(next);
        } while (
          next < max &&
          !(
            code <= 32 ||
            code === singleQuote ||
            code === doubleQuote ||
            code === comma ||
            code === colon ||
            code === slash ||
            code === openParentheses ||
            (code === closeParentheses && balanced)
          )
        );
        token = value.slice(pos, next);
  
        if (openParentheses === code) {
          name = token;
        } else {
          tokens.push({
            type: "word",
            sourceIndex: pos,
            value: token
          });
        }
  
        pos = next;
      }
    }
  
    for (pos = stack.length - 1; pos; pos -= 1) {
      stack[pos].unclosed = true;
    }
  
    return stack[0].nodes;
  };
  