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;
  };
  