whole_func_string: function (untilTokens) {
                  var i, e, done, value, 
                      tok = untilTokens || ';',
                      index = parserInput.i, result = [];
  
                  function testCurrentChar() {
                      var char = parserInput.currentChar();
                      if (typeof tok === 'string') {
                          return char === tok;
                      } else {
                          return tok.test(char);
                      }
                  }
                  if (testCurrentChar()) {
                      return;
                  }
                  value = [];
                  do {
                      e = this.comment();
                      if (e) {
                          value.push(e);
                          continue;
                      }
                      e = this.entity();
                      if (e) {
                          value.push(e);
                      }
                  } while (e);
  
                  done = testCurrentChar();
                  
                  if (value.length > 0) {
                      value = new(tree.Expression)(value);
                      if (done) {
                          return value;
                      }
                      else {
                          result.push(value);
                      }
                      // Preserve space before $parseUntil as it will not
                      if (parserInput.prevChar() === ' ') {
                          result.push(new tree.Anonymous(' ', index));
                      }
                  }
                  parserInput.save();
                  
                  value = parserInput.$parseUntil(tok);
  
                  if (value) {
                      if (typeof value === 'string') {
                          error('Expected \'' + value + '\'', 'Parse');
                      }
                      if (value.length === 1 && value[0] === ' ') {
                          parserInput.forget();
                          return new tree.Anonymous('', index);
                      }
                      var item;
                      for (i = 0; i < value.length; i++) {
                          item = value[i];
                          if (Array.isArray(item)) {
                              // Treat actual quotes as normal quoted values
                              result.push(new tree.Quoted(item[0], item[1], true, index, fileInfo));
                          }
                          else {
                              if (i === value.length - 1) {
                                  item = item.trim();
                              }
                              // Treat like quoted values, but replace vars like unquoted expressions
                              var quote = new tree.Quoted('\'', item, true, index, fileInfo);
                              quote.variableRegex = /@([\w-]+)/g;
                              quote.propRegex = /\$([\w-]+)/g;
                              result.push(quote);
                          }
                      }
                      parserInput.forget();
                      return new tree.Expression(result, true);
                  }
                  parserInput.restore();
              }
func_code_string: function (untilTokens) {
                  var i, e, done, value, 
                      tok = untilTokens || ';',
                      index = parserInput.i, result = [];
  
                  function testCurrentChar() {
                      var char = parserInput.currentChar();
                      if (typeof tok === 'string') {
                          return char === tok;
                      } else {
                          return tok.test(char);
                      }
                  }
                  if (testCurrentChar()) {
                      return;
                  }
                  value = [];
                  do {
                      e = this.comment();
                      if (e) {
                          value.push(e);
                          continue;
                      }
                      e = this.entity();
                      if (e) {
                          value.push(e);
                      }
                  } while (e);
  
                  done = testCurrentChar();
                  
                  if (value.length > 0) {
                      value = new(tree.Expression)(value);
                      if (done) {
                          return value;
                      }
                      else {
                          result.push(value);
                      }
                      // Preserve space before $parseUntil as it will not
                      if (parserInput.prevChar() === ' ') {
                          result.push(new tree.Anonymous(' ', index));
                      }
                  }
                  parserInput.save();
                  
                  value = parserInput.$parseUntil(tok);
  
                  if (value) {
                      if (typeof value === 'string') {
                          error('Expected \'' + value + '\'', 'Parse');
                      }
                      if (value.length === 1 && value[0] === ' ') {
                          parserInput.forget();
                          return new tree.Anonymous('', index);
                      }
                      var item;
                      for (i = 0; i < value.length; i++) {
                          item = value[i];
                          if (Array.isArray(item)) {
                              // Treat actual quotes as normal quoted values
                              result.push(new tree.Quoted(item[0], item[1], true, index, fileInfo));
                          }
                          else {
                              if (i === value.length - 1) {
                                  item = item.trim();
                              }
                              // Treat like quoted values, but replace vars like unquoted expressions
                              var quote = new tree.Quoted('\'', item, true, index, fileInfo);
                              quote.variableRegex = /@([\w-]+)/g;
                              quote.propRegex = /\$([\w-]+)/g;
                              result.push(quote);
                          }
                      }
                      parserInput.forget();
                      return new tree.Expression(result, true);
                  }
                  parserInput.restore();
              }
func_documentation_string: Used for custom properties, at-rules, and variables (as fallback)
  Parses almost anything inside of {} [] () "" blocks
  until it reaches outer-most tokens.
  
  First, it will try to parse comments and entities to reach
  the end. This is mostly like the Expression parser except no
  math is allowed.