text: var less, tree, charset;
  
  if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") {
      // Rhino
      // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88
      if (typeof(window) === 'undefined') {
          less = {}
      }
      else {
          less = window.less = {}
      }
      tree = less.tree = {};
      less.mode = 'rhino';
  } else if (typeof(window) === 'undefined') {
      // Node.js
      less = exports,
          tree = require('./tree');
      less.mode = 'node';
  } else {
      // Browser
      if (typeof(window.less) === 'undefined') {
          window.less = {}
      }
      less = window.less,
          tree = window.less.tree = {};
      less.mode = 'browser';
  }
  //
  // less.js - parser
  //
  //    A relatively straight-forward predictive parser.
  //    There is no tokenization/lexing stage, the input is parsed
  //    in one sweep.
  //
  //    To make the parser fast enough to run in the browser, several
  //    optimization had to be made:
  //
  //    - Matching and slicing on a huge input is often cause of slowdowns.
  //      The solution is to chunkify the input into smaller strings.
  //      The chunks are stored in the `chunks` var,
  //      `j` holds the current chunk index, and `current` holds
  //      the index of the current chunk in relation to `input`.
  //      This gives us an almost 4x speed-up.
  //
  //    - In many cases, we don't need to match individual tokens;
  //      for example, if a value doesn't hold any variables, operations
  //      or dynamic references, the parser can effectively 'skip' it,
  //      treating it as a literal.
  //      An example would be '1px solid #000' - which evaluates to itself,
  //      we don't need to know what the individual components are.
  //      The drawback, of course is that you don't get the benefits of
  //      syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
  //      and a smaller speed-up in the code-gen.
  //
  //
  //    Token matching is done with the `$` function, which either takes
  //    a terminal string or regexp, or a non-terminal function to call.
  //    It also takes care of moving all the indices forwards.
  //
  //
  less.Parser = function Parser(env) {
      var input,       // LeSS input string
          i,           // current index in `input`
          j,           // current chunk
          temp,        // temporarily holds a chunk's state, for backtracking
          memo,        // temporarily holds `i`, when backtracking
          furthest,    // furthest index the parser has gone to
          chunks,      // chunkified input
          current,     // index of current chunk, in `input`
          parser;
  
      var that = this;
  
      // Top parser on an import tree must be sure there is one "env"
      // which will then be passed arround by reference.
      var env = env || {};
      // env.contents and files must be passed arround with top env
      if (!env.contents) {
          env.contents = {};
      }
      env.rootpath = env.rootpath || '';       // env.rootpath must be initialized to '' if not provided
      if (!env.files) {
          env.files = {};
      }
  
      // This function is called after all files
      // have been imported through `@import`.
      var finish = function () {
      };
  
      var imports = this.imports = {
          paths: env.paths || [],  // Search paths, when importing
          queue: [],               // Files which haven't been imported yet
          files: env.files,        // Holds the imported parse trees
          contents: env.contents,  // Holds the imported file contents
          mime: env.mime,         // MIME type of .less files
          error: null,             // Error in parsing/evaluating an import
          push: function (path, callback) {
              var that = this;
              this.queue.push(path);
  
              //
              // Import a file asynchronously
              //
              less.Parser.importer(path, this.paths, function (e, root, fullPath) {
                  that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue
  
                  var imported = fullPath in that.files;
  
                  that.files[fullPath] = root;                        // Store the root
  
                  if (e && !that.error) {
                      that.error = e
                  }
  
                  callback(e, root, imported);
  
                  if (that.queue.length === 0) {
                      finish(that.error)
                  }       // Call `finish` if we're done importing
              }, env);
          }
      };
  
      function save() {
          temp = chunks[j], memo = i, current = i
      }
  
      function restore() {
          chunks[j] = temp, i = memo, current = i
      }
  
      function sync() {
          if (i > current) {
              chunks[j] = chunks[j].slice(i - current);
              current = i;
          }
      }
  
      function isWhitespace(c) {
          // Could change to \s?
          var code = c.charCodeAt(0);
          return code === 32 || code === 10 || code === 9;
      }
  
      //
      // Parse from a token, regexp or string, and move forward if match
      //
      function $(tok) {
          var match, args, length, index, k;
  
          //
          // Non-terminal
          //
          if (tok instanceof Function) {
              return tok.call(parser.parsers);
              //
              // Terminal
              //
              //     Either match a single character in the input,
              //     or match a regexp in the current chunk (chunk[j]).
              //
          } else if (typeof(tok) === 'string') {
              match = input.charAt(i) === tok ? tok : null;
              length = 1;
              sync();
          } else {
              sync();
  
              if (match = tok.exec(chunks[j])) {
                  length = match[0].length;
              } else {
                  return null;
              }
          }
  
          // The match is confirmed, add the match length to `i`,
          // and consume any extra white-space characters (' ' || '\n')
          // which come after that. The reason for this is that LeSS's
          // grammar is mostly white-space insensitive.
          //
          if (match) {
              skipWhitespace(length);
  
              if (typeof(match) === 'string') {
                  return match;
              } else {
                  return match.length === 1 ? match[0] : match;
              }
          }
      }
  
      function skipWhitespace(length) {
          var oldi = i, oldj = j,
              endIndex = i + chunks[j].length,
              mem = i += length;
  
          while (i < endIndex) {
              if (!isWhitespace(input.charAt(i))) {
                  break
              }
              i++;
          }
          chunks[j] = chunks[j].slice(length + (i - mem));
          current = i;
  
          if (chunks[j].length === 0 && j < chunks.length - 1) {
              j++
          }
  
          return oldi !== i || oldj !== j;
      }
  
      function expect(arg, msg) {
          var result = $(arg);
          if (!result) {
              error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'"
                      : "unexpected token"));
          } else {
              return result;
          }
      }
  
      function error(msg, type) {
          var e = new Error(msg);
          e.index = i;
          e.type = type || 'Syntax';
          throw e;
      }
  
      // Same as $(), but don't change the state of the parser,
      // just return the match.
      function peek(tok) {
          if (typeof(tok) === 'string') {
              return input.charAt(i) === tok;
          } else {
              if (tok.test(chunks[j])) {
                  return true;
              } else {
                  return false;
              }
          }
      }
  
      function getInput(e, env) {
          if (e.filename && env.filename && (e.filename !== env.filename)) {
              return parser.imports.contents[e.filename];
          } else {
              return input;
          }
      }
  
      function getLocation(index, input) {
          for (var n = index, column = -1;
              n >= 0 && input.charAt(n) !== '\n';
              n--) {
              column++
          }
  
          return {
              line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null,
              column: column
          };
      }
  
      function getFileName(e) {
          if (less.mode === 'browser' || less.mode === 'rhino') {
              return e.filename;
          } else {
              return require('path').resolve(e.filename);
          }
      }
  
      function getDebugInfo(index, inputStream, e) {
          return {
              lineNumber: getLocation(index, inputStream).line + 1,
              fileName: getFileName(e)
          };
      }
  
      function LessError(e, env) {
          var input = getInput(e, env),
              loc = getLocation(e.index, input),
              line = loc.line,
              col = loc.column,
              lines = input.split('\n');
  
          this.type = e.type || 'Syntax';
          this.message = e.message;
          this.filename = e.filename || env.filename;
          this.index = e.index;
          this.line = typeof(line) === 'number' ? line + 1 : null;
          this.callLine = e.call && (getLocation(e.call, input).line + 1);
          this.callExtract = lines[getLocation(e.call, input).line];
          this.stack = e.stack;
          this.column = col;
          this.extract = [
              lines[line - 1],
              lines[line],
              lines[line + 1]
          ];
      }
  
      this.env = env = env || {};
  
      // The optimization level dictates the thoroughness of the parser,
      // the lower the number, the less nodes it will create in the tree.
      // This could matter for debugging, or if you want to access
      // the individual nodes in the tree.
      this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
  
      this.env.filename = this.env.filename || null;
  
      //
      // The Parser
      //
      return parser = {
  
          imports: imports,
          //
          // Parse an input string into an abstract syntax tree,
          // call `callback` when done.
          //
          parse: function (str, callback) {
              var root, start, end, zone, line, lines, buff = [], c, error = null;
  
              i = j = current = furthest = 0;
              input = str.replace(/\r\n/g, '\n');
  
              // Remove potential UTF Byte Order Mark
              input = input.replace(/^\uFEFF/, '');
  
              // Split the input into chunks.
              chunks = (function (chunks) {
                  var j = 0,
                      skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,
                      comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,
                      string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,
                      level = 0,
                      match,
                      chunk = chunks[0],
                      inParam;
  
                  for (var i = 0, c, cc; i < input.length;) {
                      skip.lastIndex = i;
                      if (match = skip.exec(input)) {
                          if (match.index === i) {
                              i += match[0].length;
                              chunk.push(match[0]);
                          }
                      }
                      c = input.charAt(i);
                      comment.lastIndex = string.lastIndex = i;
  
                      if (match = string.exec(input)) {
                          if (match.index === i) {
                              i += match[0].length;
                              chunk.push(match[0]);
                              continue;
                          }
                      }
  
                      if (!inParam && c === '/') {
                          cc = input.charAt(i + 1);
                          if (cc === '/' || cc === '*') {
                              if (match = comment.exec(input)) {
                                  if (match.index === i) {
                                      i += match[0].length;
                                      chunk.push(match[0]);
                                      continue;
                                  }
                              }
                          }
                      }
  
                      switch (c) {
                          case '{':
                              if (!inParam) {
                                  level++;
                                  chunk.push(c);
                                  break
                              }
                          case '}':
                              if (!inParam) {
                                  level--;
                                  chunk.push(c);
                                  chunks[++j] = chunk = [];
                                  break
                              }
                          case '(':
                              if (!inParam) {
                                  inParam = true;
                                  chunk.push(c);
                                  break
                              }
                          case ')':
                              if (inParam) {
                                  inParam = false;
                                  chunk.push(c);
                                  break
                              }
                          default:
                              chunk.push(c);
                      }
  
                      i++;
                  }
                  if (level != 0) {
                      error = new (LessError)({
                          index: i - 1,
                          type: 'Parse',
                          message: (level > 0) ? "missing closing `}`" : "missing opening `{`",
                          filename: env.filename
                      }, env);
                  }
  
                  return chunks.map(function (c) {
                      return c.join('')
                  });
                  ;
              })([[]]);
  
              if (error) {
                  return callback(error, env);
              }
  
              // Start with the primary rule.
              // The whole syntax tree is held under a Ruleset node,
              // with the `root` property set to true, so no `{}` are
              // output. The callback is called when the input is parsed.
              try {
                  root = new (tree.Ruleset)([], $(this.parsers.primary));
                  root.root = true;
              } catch (e) {
                  return callback(new (LessError)(e, env));
              }
  
              root.toCSS = (function (evaluate) {
                  var line, lines, column;
  
                  return function (options, variables) {
                      var frames = [], importError;
  
                      options = options || {};
                      //
                      // Allows setting variables with a hash, so:
                      //
                      //   `{ color: new(tree.Color)('#f01') }` will become:
                      //
                      //   new(tree.Rule)('@color',
                      //     new(tree.Value)([
                      //       new(tree.Expression)([
                      //         new(tree.Color)('#f01')
                      //       ])
                      //     ])
                      //   )
                      //
                      if (typeof(variables) === 'object' && !Array.isArray(variables)) {
                          variables = Object.keys(variables).map(function (k) {
                              var value = variables[k];
  
                              if (!(value instanceof tree.Value)) {
                                  if (!(value instanceof tree.Expression)) {
                                      value = new (tree.Expression)([value]);
                                  }
                                  value = new (tree.Value)([value]);
                              }
                              return new (tree.Rule)('@' + k, value, false, 0);
                          });
                          frames = [new (tree.Ruleset)(null, variables)];
                      }
  
                      try {
                          var css = evaluate.call(this, {frames: frames})
                          .toCSS([], {compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers});
                      } catch (e) {
                          throw new (LessError)(e, env);
                      }
  
                      if ((importError = parser.imports.error)) { // Check if there was an error during importing
                          if (importError instanceof LessError) {
                              throw importError;
                          } else {
                              throw new (LessError)(importError, env);
                          }
                      }
  
                      if (options.yuicompress && less.mode === 'node') {
                          return require('ycssmin').cssmin(css);
                      } else if (options.compress) {
                          return css.replace(/(\s)+/g, "$1");
                      } else {
                          return css;
                      }
                  };
              })(root.eval);
  
              // If `i` is smaller than the `input.length - 1`,
              // it means the parser wasn't able to parse the whole
              // string, so we've got a parsing error.
              //
              // We try to extract a \n delimited string,
              // showing the line where the parse error occured.
              // We split it up into two parts (the part which parsed,
              // and the part which didn't), so we can color them differently.
              if (i < input.length - 1) {
                  i = furthest;
                  lines = input.split('\n');
                  line = (input.slice(0, i).match(/\n/g) || "").length + 1;
  
                  for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) {
                      column++
                  }
  
                  error = {
                      type: "Parse",
                      message: "Syntax Error on line " + line,
                      index: i,
                      filename: env.filename,
                      line: line,
                      column: column,
                      extract: [
                          lines[line - 2],
                          lines[line - 1],
                          lines[line]
                      ]
                  };
              }
  
              if (this.imports.queue.length > 0) {
                  finish = function (e) {
                      e = error || e;
                      if (e) {
                          callback(e);
                      } else {
                          callback(null, root);
                      }
                  };
              } else {
                  callback(error, root);
              }
          },
  
          //
          // Here in, the parsing rules/functions
          //
          // The basic structure of the syntax tree generated is as follows:
          //
          //   Ruleset ->  Rule -> Value -> Expression -> Entity
          //
          // Here's some LESS code:
          //
          //    .class {
          //      color: #fff;
          //      border: 1px solid #000;
          //      width: @w + 4px;
          //      > .child {...}
          //    }
          //
          // And here's what the parse tree might look like:
          //
          //     Ruleset (Selector '.class', [
          //         Rule ("color",  Value ([Expression [Color #fff]]))
          //         Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
          //         Rule ("width",  Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
          //         Ruleset (Selector [Element '>', '.child'], [...])
          //     ])
          //
          //  In general, most rules will try to parse a token with the `$()` function, and if the return
          //  value is truly, will return a new node, of the relevant type. Sometimes, we need to check
          //  first, before parsing, that's when we use `peek()`.
          //
          parsers: {
              //
              // The `primary` rule is the *entry* and *exit* point of the parser.
              // The rules here can appear at any level of the parse tree.
              //
              // The recursive nature of the grammar is an interplay between the `block`
              // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
              // as represented by this simplified grammar:
              //
              //     primary  →  (ruleset | rule)+
              //     ruleset  →  selector+ block
              //     block    →  '{' primary '}'
              //
              // Only at one point is the primary rule not called from the
              // block rule: at the root level.
              //
              primary: function () {
                  var node, root = [];
  
                  while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) ||
                      $(this.mixin.call) || $(this.comment) || $(this.directive))
                  || $(/^[\s\n]+/) || $(/^;+/)) {
                      node && root.push(node);
                  }
                  return root;
              },
  
              // We create a Comment node for CSS comments `/* */`,
              // but keep the LeSS comments `//` silent, by just skipping
              // over them.
              comment: function () {
                  var comment;
  
                  if (input.charAt(i) !== '/') {
                      return;
                  }
  
                  if (input.charAt(i + 1) === '/') {
                      return new (tree.Comment)($(/^\/\/.*/), true);
                  } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) {
                      return new (tree.Comment)(comment);
                  }
              },
  
              //
              // Entities are tokens which can be found inside an Expression
              //
              entities: {
                  //
                  // A string, which supports escaping " and '
                  //
                  //     "milky way" 'he\'s the one!'
                  //
                  quoted: function () {
                      var str, j = i, e;
  
                      if (input.charAt(j) === '~') {
                          j++, e = true
                      } // Escaped strings
                      if (input.charAt(j) !== '"' && input.charAt(j) !== "'") {
                          return;
                      }
  
                      e && $('~');
  
                      if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
                          return new (tree.Quoted)(str[0], str[1] || str[2], e);
                      }
                  },
  
                  //
                  // A catch-all word, such as:
                  //
                  //     black border-collapse
                  //
                  keyword: function () {
                      var k;
  
                      if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) {
                          if (tree.colors.hasOwnProperty(k)) {
                              // detect named color
                              return new (tree.Color)(tree.colors[k].slice(1));
                          } else {
                              return new (tree.Keyword)(k);
                          }
                      }
                  },
  
                  //
                  // A function call
                  //
                  //     rgb(255, 0, 255)
                  //
                  // We also try to catch IE's `alpha()`, but let the `alpha` parser
                  // deal with the details.
                  //
                  // The arguments are parsed with the `entities.arguments` parser.
                  //
                  call: function () {
                      var name, nameLC, args, alpha_ret, index = i;
  
                      if (!(name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) {
                          return;
                      }
  
                      name = name[1];
                      nameLC = name.toLowerCase();
  
                      if (nameLC === 'url') {
                          return null
                      }
                      else {
                          i += name.length
                      }
  
                      if (nameLC === 'alpha') {
                          alpha_ret = $(this.alpha);
                          if (typeof alpha_ret !== 'undefined') {
                              return alpha_ret;
                          }
                      }
  
                      $('('); // Parse the '(' and consume whitespace.
  
                      args = $(this.entities.arguments);
  
                      if (!$(')')) {
                          return;
                      }
  
                      if (name) {
                          return new (tree.Call)(name, args, index, env.filename)
                      }
                  },
                  arguments: function () {
                      var args = [], arg;
  
                      while (arg = $(this.entities.assignment) || $(this.expression)) {
                          args.push(arg);
                          if (!$(',')) {
                              break
                          }
                      }
                      return args;
                  },
                  literal: function () {
                      return $(this.entities.ratio) ||
                          $(this.entities.dimension) ||
                          $(this.entities.color) ||
                          $(this.entities.quoted) ||
                          $(this.entities.unicodeDescriptor);
                  },
  
                  // Assignments are argument entities for calls.
                  // They are present in ie filter properties as shown below.
                  //
                  //     filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
                  //
  
                  assignment: function () {
                      var key, value;
                      if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) {
                          return new (tree.Assignment)(key, value);
                      }
                  },
  
                  //
                  // Parse url() tokens
                  //
                  // We use a specific rule for urls, because they don't really behave like
                  // standard function calls. The difference is that the argument doesn't have
                  // to be enclosed within a string, so it can't be parsed as an Expression.
                  //
                  url: function () {
                      var value;
  
                      if (input.charAt(i) !== 'u' || !$(/^url\(/)) {
                          return;
                      }
                      value = $(this.entities.quoted) || $(this.entities.variable) ||
                          $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
  
                      expect(')');
  
                      return new (tree.URL)((value.value != null || value instanceof tree.Variable)
                          ? value : new (tree.Anonymous)(value), env.rootpath);
                  },
  
                  //
                  // A Variable entity, such as `@fink`, in
                  //
                  //     width: @fink + 2px
                  //
                  // We use a different parser for variable definitions,
                  // see `parsers.variable`.
                  //
                  variable: function () {
                      var name, index = i;
  
                      if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
                          return new (tree.Variable)(name, index, env.filename);
                      }
                  },
  
                  // A variable entity useing the protective {} e.g. @{var}
                  variableCurly: function () {
                      var name, curly, index = i;
  
                      if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) {
                          return new (tree.Variable)("@" + curly[1], index, env.filename);
                      }
                  },
  
                  //
                  // A Hexadecimal color
                  //
                  //     #4F3C2F
                  //
                  // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
                  //
                  color: function () {
                      var rgb;
  
                      if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
                          return new (tree.Color)(rgb[1]);
                      }
                  },
  
                  //
                  // A Dimension, that is, a number and a unit
                  //
                  //     0.5em 95%
                  //
                  dimension: function () {
                      var value, c = input.charCodeAt(i);
                      //Is the first char of the dimension 0-9, '.', '+' or '-'
                      if ((c > 57 || c < 43) || c === 47 || c == 44) {
                          return;
                      }
  
                      if (value = $(
                              /^([+-]?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/)) {
                          return new (tree.Dimension)(value[1], value[2]);
                      }
                  },
  
                  //
                  // A Ratio
                  //
                  //    16/9
                  //
                  ratio: function () {
                      var value, c = input.charCodeAt(i);
                      if (c > 57 || c < 48) {
                          return;
                      }
  
                      if (value = $(/^(\d+\/\d+)/)) {
                          return new (tree.Ratio)(value[1]);
                      }
                  },
  
                  //
                  // A unicode descriptor, as is used in unicode-range
                  //
                  // U+0??  or U+00A1-00A9
                  //
                  unicodeDescriptor: function () {
                      var ud;
  
                      if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) {
                          return new (tree.UnicodeDescriptor)(ud[0]);
                      }
                  },
  
                  //
                  // JavaScript code to be evaluated
                  //
                  //     `window.location.href`
                  //
                  javascript: function () {
                      var str, j = i, e;
  
                      if (input.charAt(j) === '~') {
                          j++, e = true
                      } // Escaped strings
                      if (input.charAt(j) !== '`') {
                          return
                      }
  
                      e && $('~');
  
                      if (str = $(/^`([^`]*)`/)) {
                          return new (tree.JavaScript)(str[1], i, e);
                      }
                  }
              },
  
              //
              // The variable part of a variable definition. Used in the `rule` parser
              //
              //     @fink:
              //
              variable: function () {
                  var name;
  
                  if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) {
                      return name[1]
                  }
              },
  
              //
              // A font size/line-height shorthand
              //
              //     small/12px
              //
              // We need to peek first, or we'll match on keywords and dimensions
              //
              shorthand: function () {
                  var a, b;
  
                  if (!peek(/^[@\w.%-]+\/[@\w.-]+/)) {
                      return;
                  }
  
                  save();
  
                  if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) {
                      return new (tree.Shorthand)(a, b);
                  }
  
                  restore();
              },
  
              //
              // Mixins
              //
              mixin: {
                  //
                  // A Mixin call, with an optional argument list
                  //
                  //     #mixins > .square(#fff);
                  //     .rounded(4px, black);
                  //     .button;
                  //
                  // The `while` loop is there because mixins can be
                  // namespaced, but we only support the child and descendant
                  // selector for now.
                  //
                  call: function () {
                      var elements = [], e, c, argsSemiColon = [], argsComma = [], args, delim, arg, nameLoop, expressions, isSemiColonSeperated, expressionContainsNamed, index = i, s = input.charAt(
                          i), name, value, important = false;
  
                      if (s !== '.' && s !== '#') {
                          return
                      }
  
                      save(); // stop us absorbing part of an invalid selector
  
                      while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) {
                          elements.push(new (tree.Element)(c, e, i));
                          c = $('>');
                      }
                      if ($('(')) {
                          expressions = [];
                          while (arg = $(this.expression)) {
                              nameLoop = null;
                              value = arg;
  
                              // Variable
                              if (arg.value.length == 1) {
                                  var val = arg.value[0];
                                  if (val instanceof tree.Variable) {
                                      if ($(':')) {
                                          if (expressions.length > 0) {
                                              if (isSemiColonSeperated) {
                                                  error("Cannot mix ; and , as delimiter types");
                                              }
                                              expressionContainsNamed = true;
                                          }
                                          value = expect(this.expression);
                                          nameLoop = (name = val.name);
                                      }
                                  }
                              }
  
                              expressions.push(value);
  
                              argsComma.push({name: nameLoop, value: value});
  
                              if ($(',')) {
                                  continue;
                              }
  
                              if ($(';') || isSemiColonSeperated) {
  
                                  if (expressionContainsNamed) {
                                      error("Cannot mix ; and , as delimiter types");
                                  }
  
                                  isSemiColonSeperated = true;
  
                                  if (expressions.length > 1) {
                                      value = new (tree.Value)(expressions);
                                  }
                                  argsSemiColon.push({name: name, value: value});
  
                                  name = null;
                                  expressions = [];
                                  expressionContainsNamed = false;
                              }
                          }
  
                          expect(')');
                      }
  
                      args = isSemiColonSeperated ? argsSemiColon : argsComma;
  
                      if ($(this.important)) {
                          important = true;
                      }
  
                      if (elements.length > 0 && ($(';') || peek('}'))) {
                          return new (tree.mixin.Call)(elements, args, index, env.filename, important);
                      }
  
                      restore();
                  },
  
                  //
                  // A Mixin definition, with a list of parameters
                  //
                  //     .rounded (@radius: 2px, @color) {
                  //        ...
                  //     }
                  //
                  // Until we have a finer grained state-machine, we have to
                  // do a look-ahead, to make sure we don't have a mixin call.
                  // See the `rule` function for more information.
                  //
                  // We start by matching `.rounded (`, and then proceed on to
                  // the argument list, which has optional default values.
                  // We store the parameters in `params`, with a `value` key,
                  // if there is a value, such as in the case of `@radius`.
                  //
                  // Once we've got our params list, and a closing `)`, we parse
                  // the `{...}` block.
                  //
                  definition: function () {
                      var name, params = [], match, ruleset, param, value, cond, variadic = false;
                      if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||
                          peek(/^[^{]*\}/)) {
                          return;
                      }
  
                      save();
  
                      if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) {
                          name = match[1];
  
                          do {
                              $(this.comment);
                              if (input.charAt(i) === '.' && $(/^\.{3}/)) {
                                  variadic = true;
                                  params.push({variadic: true});
                                  break;
                              } else if (param = $(this.entities.variable) || $(this.entities.literal)
                                      || $(this.entities.keyword)) {
                                  // Variable
                                  if (param instanceof tree.Variable) {
                                      if ($(':')) {
                                          value = expect(this.expression, 'expected expression');
                                          params.push({name: param.name, value: value});
                                      } else if ($(/^\.{3}/)) {
                                          params.push({name: param.name, variadic: true});
                                          variadic = true;
                                          break;
                                      } else {
                                          params.push({name: param.name});
                                      }
                                  } else {
                                      params.push({value: param});
                                  }
                              } else {
                                  break;
                              }
                          } while ($(',') || $(';'))
  
                          // .mixincall("@{a}"); 
                          // looks a bit like a mixin definition.. so we have to be nice and restore
                          if (!$(')')) {
                              furthest = i;
                              restore();
                          }
  
                          $(this.comment);
  
                          if ($(/^when/)) { // Guard
                              cond = expect(this.conditions, 'expected condition');
                          }
  
                          ruleset = $(this.block);
  
                          if (ruleset) {
                              return new (tree.mixin.Definition)(name, params, ruleset, cond, variadic);
                          } else {
                              restore();
                          }
                      }
                  }
              },
  
              //
              // Entities are the smallest recognized token,
              // and can be found inside a rule's value.
              //
              entity: function () {
                  return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) ||
                      $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) ||
                      $(this.comment);
              },
  
              //
              // A Rule terminator. Note that we use `peek()` to check for '}',
              // because the `block` rule will be expecting it, but we still need to make sure
              // it's there, if ';' was ommitted.
              //
              end: function () {
                  return $(';') || peek('}');
              },
  
              //
              // IE's alpha function
              //
              //     alpha(opacity=88)
              //
              alpha: function () {
                  var value;
  
                  if (!$(/^\(opacity=/i)) {
                      return;
                  }
                  if (value = $(/^\d+/) || $(this.entities.variable)) {
                      expect(')');
                      return new (tree.Alpha)(value);
                  }
              },
  
              //
              // A Selector Element
              //
              //     div
              //     + h1
              //     #socks
              //     input[type="text"]
              //
              // Elements are the building blocks for Selectors,
              // they are made out of a `Combinator` (see combinator rule),
              // and an element name, such as a tag a class, or `*`.
              //
              element: function () {
                  var e, t, c, v;
  
                  c = $(this.combinator);
  
                  e = $(/^(?:\d+\.\d+|\d+)%/) || $(
                          /^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
                      $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(
                          this.entities.variableCurly);
  
                  if (!e) {
                      if ($('(')) {
                          if ((v = ($(this.entities.variableCurly) ||
                              $(this.entities.variable) ||
                              $(this.selector))) &&
                              $(')')) {
                              e = new (tree.Paren)(v);
                          }
                      }
                  }
  
                  if (e) {
                      return new (tree.Element)(c, e, i)
                  }
              },
  
              //
              // Combinators combine elements together, in a Selector.
              //
              // Because our parser isn't white-space sensitive, special care
              // has to be taken, when parsing the descendant combinator, ` `,
              // as it's an empty space. We have to check the previous character
              // in the input, to see if it's a ` ` character. More info on how
              // we deal with this in *combinator.js*.
              //
              combinator: function () {
                  var match, c = input.charAt(i);
  
                  if (c === '>' || c === '+' || c === '~' || c === '|') {
                      i++;
                      while (input.charAt(i).match(/\s/)) {
                          i++
                      }
                      return new (tree.Combinator)(c);
                  } else if (input.charAt(i - 1).match(/\s/)) {
                      return new (tree.Combinator)(" ");
                  } else {
                      return new (tree.Combinator)(null);
                  }
              },
  
              //
              // A CSS Selector
              //
              //     .class > div + h1
              //     li a:hover
              //
              // Selectors are made out of one or more Elements, see above.
              //
              selector: function () {
                  var sel, e, elements = [], c, match;
  
                  // depreciated, will be removed soon
                  if ($('(')) {
                      sel = $(this.entity);
                      if (!$(')')) {
                          return null;
                      }
                      return new (tree.Selector)([new (tree.Element)('', sel, i)]);
                  }
  
                  while (e = $(this.element)) {
                      c = input.charAt(i);
                      elements.push(e)
                      if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {
                          break
                      }
                  }
  
                  if (elements.length > 0) {
                      return new (tree.Selector)(elements)
                  }
              },
              attribute: function () {
                  var attr = '', key, val, op;
  
                  if (!$('[')) {
                      return;
                  }
  
                  if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) {
                      if ((op = $(/^[|~*$^]?=/)) &&
                          (val = $(this.entities.quoted) || $(/^[\w-]+/))) {
                          attr = [key, op, val.toCSS ? val.toCSS() : val].join('');
                      } else {
                          attr = key
                      }
                  }
  
                  if (!$(']')) {
                      return;
                  }
  
                  if (attr) {
                      return "[" + attr + "]"
                  }
              },
  
              //
              // The `block` rule is used by `ruleset` and `mixin.definition`.
              // It's a wrapper around the `primary` rule, with added `{}`.
              //
              block: function () {
                  var content;
                  if ($('{') && (content = $(this.primary)) && $('}')) {
                      return content;
                  }
              },
  
              //
              // div, .class, body > p {...}
              //
              ruleset: function () {
                  var selectors = [], s, rules, match, debugInfo;
  
                  save();
  
                  if (env.dumpLineNumbers) {
                      debugInfo = getDebugInfo(i, input, env);
                  }
  
                  while (s = $(this.selector)) {
                      selectors.push(s);
                      $(this.comment);
                      if (!$(',')) {
                          break
                      }
                      $(this.comment);
                  }
  
                  if (selectors.length > 0 && (rules = $(this.block))) {
                      var ruleset = new (tree.Ruleset)(selectors, rules, env.strictImports);
                      if (env.dumpLineNumbers) {
                          ruleset.debugInfo = debugInfo;
                      }
                      return ruleset;
                  } else {
                      // Backtrack
                      furthest = i;
                      restore();
                  }
              },
              rule: function () {
                  var name, value, c = input.charAt(i), important, match;
                  save();
  
                  if (c === '.' || c === '#' || c === '&') {
                      return
                  }
  
                  if (name = $(this.variable) || $(this.property)) {
                      if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) {
                          i += match[0].length - 1;
                          value = new (tree.Anonymous)(match[1]);
                      } else if (name === "font") {
                          value = $(this.font);
                      } else {
                          value = $(this.value);
                      }
                      important = $(this.important);
  
                      if (value && $(this.end)) {
                          return new (tree.Rule)(name, value, important, memo);
                      } else {
                          furthest = i;
                          restore();
                      }
                  }
              },
  
              //
              // An @import directive
              //
              //     @import "lib";
              //
              // Depending on our environemnt, importing is done differently:
              // In the browser, it's an XHR request, in Node, it would be a
              // file-system operation. The function used for importing is
              // stored in `import`, which we pass to the Import constructor.
              //
              "import": function () {
                  var path, features, index = i;
  
                  save();
  
                  var dir = $(/^@import(?:-(once))?\s+/);
  
                  if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) {
                      features = $(this.mediaFeatures);
                      if ($(';')) {
                          return new (tree.Import)(path, imports, features, (dir[1] === 'once'), index, env.rootpath);
                      }
                  }
  
                  restore();
              },
  
              mediaFeature: function () {
                  var e, p, nodes = [];
  
                  do {
                      if (e = $(this.entities.keyword)) {
                          nodes.push(e);
                      } else if ($('(')) {
                          p = $(this.property);
                          e = $(this.entity);
                          if ($(')')) {
                              if (p && e) {
                                  nodes.push(new (tree.Paren)(new (tree.Rule)(p, e, null, i, true)));
                              } else if (e) {
                                  nodes.push(new (tree.Paren)(e));
                              } else {
                                  return null;
                              }
                          } else {
                              return null
                          }
                      }
                  } while (e);
  
                  if (nodes.length > 0) {
                      return new (tree.Expression)(nodes);
                  }
              },
  
              mediaFeatures: function () {
                  var e, features = [];
  
                  do {
                      if (e = $(this.mediaFeature)) {
                          features.push(e);
                          if (!$(',')) {
                              break
                          }
                      } else if (e = $(this.entities.variable)) {
                          features.push(e);
                          if (!$(',')) {
                              break
                          }
                      }
                  } while (e);
  
                  return features.length > 0 ? features : null;
              },
  
              media: function () {
                  var features, rules, media, debugInfo;
  
                  if (env.dumpLineNumbers) {
                      debugInfo = getDebugInfo(i, input, env);
                  }
  
                  if ($(/^@media/)) {
                      features = $(this.mediaFeatures);
  
                      if (rules = $(this.block)) {
                          media = new (tree.Media)(rules, features);
                          if (env.dumpLineNumbers) {
                              media.debugInfo = debugInfo;
                          }
                          return media;
                      }
                  }
              },
  
              //
              // A CSS Directive
              //
              //     @charset "utf-8";
              //
              directive: function () {
                  var name, value, rules, identifier, e, nodes, nonVendorSpecificName,
                      hasBlock, hasIdentifier, hasExpression;
  
                  if (input.charAt(i) !== '@') {
                      return;
                  }
  
                  if (value = $(this['import']) || $(this.media)) {
                      return value;
                  }
  
                  save();
  
                  name = $(/^@[a-z-]+/);
  
                  if (!name) {
                      return;
                  }
  
                  nonVendorSpecificName = name;
                  if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
                      nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
                  }
  
                  switch (nonVendorSpecificName) {
                      case "@font-face":
                          hasBlock = true;
                          break;
                      case "@viewport":
                      case "@top-left":
                      case "@top-left-corner":
                      case "@top-center":
                      case "@top-right":
                      case "@top-right-corner":
                      case "@bottom-left":
                      case "@bottom-left-corner":
                      case "@bottom-center":
                      case "@bottom-right":
                      case "@bottom-right-corner":
                      case "@left-top":
                      case "@left-middle":
                      case "@left-bottom":
                      case "@right-top":
                      case "@right-middle":
                      case "@right-bottom":
                          hasBlock = true;
                          break;
                      case "@page":
                      case "@document":
                      case "@supports":
                      case "@keyframes":
                          hasBlock = true;
                          hasIdentifier = true;
                          break;
                      case "@namespace":
                          hasExpression = true;
                          break;
                  }
  
                  if (hasIdentifier) {
                      name += " " + ($(/^[^{]+/) || '').trim();
                  }
  
                  if (hasBlock) {
                      if (rules = $(this.block)) {
                          return new (tree.Directive)(name, rules);
                      }
                  } else {
                      if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) {
                          var directive = new (tree.Directive)(name, value);
                          if (env.dumpLineNumbers) {
                              directive.debugInfo = getDebugInfo(i, input, env);
                          }
                          return directive;
                      }
                  }
  
                  restore();
              },
              font: function () {
                  var value = [], expression = [], weight, shorthand, font, e;
  
                  while (e = $(this.shorthand) || $(this.entity)) {
                      expression.push(e);
                  }
                  value.push(new (tree.Expression)(expression));
  
                  if ($(',')) {
                      while (e = $(this.expression)) {
                          value.push(e);
                          if (!$(',')) {
                              break
                          }
                      }
                  }
                  return new (tree.Value)(value);
              },
  
              //
              // A Value is a comma-delimited list of Expressions
              //
              //     font-family: Baskerville, Georgia, serif;
              //
              // In a Rule, a Value represents everything after the `:`,
              // and before the `;`.
              //
              value: function () {
                  var e, expressions = [], important;
  
                  while (e = $(this.expression)) {
                      expressions.push(e);
                      if (!$(',')) {
                          break
                      }
                  }
  
                  if (expressions.length > 0) {
                      return new (tree.Value)(expressions);
                  }
              },
              important: function () {
                  if (input.charAt(i) === '!') {
                      return $(/^! *important/);
                  }
              },
              sub: function () {
                  var e;
  
                  if ($('(') && (e = $(this.expression)) && $(')')) {
                      return e;
                  }
              },
              multiplication: function () {
                  var m, a, op, operation;
                  if (m = $(this.operand)) {
                      while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) {
                          operation = new (tree.Operation)(op, [operation || m, a]);
                      }
                      return operation || m;
                  }
              },
              addition: function () {
                  var m, a, op, operation;
                  if (m = $(this.multiplication)) {
                      while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) &&
                      (a = $(this.multiplication))) {
                          operation = new (tree.Operation)(op, [operation || m, a]);
                      }
                      return operation || m;
                  }
              },
              conditions: function () {
                  var a, b, index = i, condition;
  
                  if (a = $(this.condition)) {
                      while ($(',') && (b = $(this.condition))) {
                          condition = new (tree.Condition)('or', condition || a, b, index);
                      }
                      return condition || a;
                  }
              },
              condition: function () {
                  var a, b, c, op, index = i, negate = false;
  
                  if ($(/^not/)) {
                      negate = true
                  }
                  expect('(');
                  if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) {
                      if (op = $(/^(?:>=|=<|[<=>])/)) {
                          if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) {
                              c = new (tree.Condition)(op, a, b, index, negate);
                          } else {
                              error('expected expression');
                          }
                      } else {
                          c = new (tree.Condition)('=', a, new (tree.Keyword)('true'), index, negate);
                      }
                      expect(')');
                      return $(/^and/) ? new (tree.Condition)('and', c, $(this.condition)) : c;
                  }
              },
  
              //
              // An operand is anything that can be part of an operation,
              // such as a Color, or a Variable
              //
              operand: function () {
                  var negate, p = input.charAt(i + 1);
  
                  if (input.charAt(i) === '-' && (p === '@' || p === '(')) {
                      negate = $('-')
                  }
                  var o = $(this.sub) || $(this.entities.dimension) ||
                      $(this.entities.color) || $(this.entities.variable) ||
                      $(this.entities.call);
                  return negate ? new (tree.Operation)('*', [new (tree.Dimension)(-1), o])
                      : o;
              },
  
              //
              // Expressions either represent mathematical operations,
              // or white-space delimited Entities.
              //
              //     1px solid black
              //     @var * 2
              //
              expression: function () {
                  var e, delim, entities = [], d;
  
                  while (e = $(this.addition) || $(this.entity)) {
                      entities.push(e);
                  }
                  if (entities.length > 0) {
                      return new (tree.Expression)(entities);
                  }
              },
              property: function () {
                  var name;
  
                  if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) {
                      return name[1];
                  }
              }
          }
      };
  };
  
  if (less.mode === 'browser' || less.mode === 'rhino') {
      //
      // Used by `@import` directives
      //
      less.Parser.importer = function (path, paths, callback, env) {
          if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) {
              path = paths[0] + path;
          }
          // We pass `true` as 3rd argument, to force the reload of the import.
          // This is so we can get the syntax tree as opposed to just the CSS output,
          // as we need this to evaluate the current stylesheet.
          loadStyleSheet({
                  href: path,
                  title: path,
                  type: env.mime,
                  contents: env.contents,
                  files: env.files,
                  rootpath: env.rootpath,
                  entryPath: env.entryPath,
                  relativeUrls: env.relativeUrls
              },
              function (e, root, data, sheet, _, path) {
                  if (e && typeof(env.errback) === "function") {
                      env.errback.call(null, path, paths, callback, env);
                  } else {
                      callback.call(null, e, root, path);
                  }
              }, true);
      };
  }
  
  