var sugardoll = exports;

var vm = require('vm'),
    uglify = require('uglify-js');

sugardoll.plugins = require('./sugardoll/plugins');

function generateFromTokens(tokens) {
  function tok() {
    if (tokens.length == 0) return {type: 'eof'};
    return tokens.shift();
  };
  tok.context = function() {
    return {};
  };

  return uglify.parser.parse(tok);
};

function collectTokens(code, tokens) {
  var tok = uglify.parser.tokenizer(code),
      token;

  do {
    tokens.push(token = tok());
  } while (token.type !== 'eof');
};

sugardoll.tokenizer = function tokenizer(code, plugins) {
  var index = -1,
      tokens = [];

  plugins || (plugins = []);

  collectTokens(code, tokens);

  function nextToken() {
    var token = tokens[++index];

    if (token.type === 'name') {
      // Caught invoke
      var plugin;
      if (plugin = plugins[token.value]) {
        var _index = index;

        var collected = plugin.collect(nextToken);

        var newIndex = index;
        index = _index;

        if (collected !== false) {
          // Token name should be collected too
          if (collected === true) {
            collected = newIndex - index;
          }
          collected++;

          // Result is replacement for the matched code
          var trailing = tokens.slice(index + collected),
              collected = tokens.slice(index + 1, index + collected);

          tokens = tokens.slice(0, index);

          if (plugin.prepare) {
            collected = plugin.prepare(collected);
          }

          var ast = generateFromTokens(collected),
              compiled = uglify.uglify.gen_code(ast);

          // Recursive parsing
          code = sugardoll.parse(compiled, plugins);

          var code = plugin.generate.call({
                generate: uglify.uglify.gen_code
              }, ast);

          if (Array.isArray(code)) {
            // code is AST
            code = uglify.uglify.gen_code(code);
          }

          // Collect tokens from result
          collectTokens(code, tokens);

          // Remove eof token
          tokens.pop();

          tokens = tokens.concat(trailing);

          // Update existing token
          token = tokens[index];
        }
      }

    }

    return token;
  };

  nextToken.context = function() {
    return {};
  };

  return nextToken;
};

sugardoll.parse = function(code, plugins) {
  return uglify.parser.parse(sugardoll.tokenizer(code, plugins));
}

sugardoll.compile = function(code, plugins, options) {
  var ast = sugardoll.parse(code, plugins) 

  return uglify.uglify.gen_code(ast, options);
};

sugardoll.run = function(code, plugins, options) {
  code = sugardoll.compile(code, plugins, options);

  return vm.runInNewContext(code);
};

@@ -170,16 +170,62 @@
 kens) %7B%0A
+  // Clone tokens%0A  tokens = tokens.slice(0);%0A
   functi
@@ -647,26 +647,8 @@
 var 
-index = -1,%0A      
 toke
@@ -726,93 +726,233 @@
 %0A  f
-unction nextToken() %7B%0A    var token = tokens%5B++index%5D;%0A%0A    if (token.type === 'name'
+or (var index = tokens.length - 1; index %3E= 0; index--) %7B%0A    var token = tokens%5Bindex%5D,%0A        prev = tokens%5Bindex - 1%5D %7C%7C %7B%7D;%0A%0A    if (token.type === 'name' &&%0A        (prev.type !== 'keyword' %7C%7C prev.value !== 'function')
 ) %7B%0A
@@ -1047,97 +1047,178 @@
     
-var _index = 
+function collector() %7B%0A          return tokens%5B++collector.
 index
+%5D
 ;%0A
-%0A
+   
      
-   var collected = plugin.collect(nextToken);%0A%0A        var newI
+%7D;%0A        collector.context = function() %7B%0A          return %7B%7D;%0A        %7D;%0A        collector.i
 ndex
@@ -1227,24 +1227,25 @@
  index;%0A
+%0A
         
 index = 
@@ -1240,22 +1240,49 @@
     
-index = _index
+var collected = plugin.collect(collector)
 ;%0A%0A 
@@ -1427,12 +1427,19 @@
 d = 
-newI
+collector.i
 ndex
@@ -1850,33 +1850,32 @@
 okens(collected)
-,
 %0A              c
@@ -1877,148 +1877,8 @@
     
-compiled = uglify.uglify.gen_code(ast);%0A%0A          // Recursive parsing%0A          code = sugardoll.parse(compiled, plugins);%0A%0A          var 
 code
@@ -2281,27 +2281,24 @@
 iling);%0A
-%0A  
         
 // Updat
@@ -2293,91 +2293,55 @@
     
-// Update existing token%0A          token = tokens%5Bindex%5D;%0A        %7D%0A      %7D%0A%0A    %7D%0A
+%7D%0A      %7D%0A%0A    %7D%0A  %7D;%0A%0A  function nextToken() %7B
 %0A   
@@ -2353,24 +2353,32 @@
 rn token
+s.shift()
 ;%0A  %7D;%0A
-%0A
   nextTo
@@ -2495,22 +2495,145 @@
 ns) %7B%0A  
-return
+// Wrap code in function, so %60return%60 outside function won't%0A  // throw error%0A  code = 'function _() %7B' + code + '%7D';%0A  var ast =
  uglify.
@@ -2682,16 +2682,53 @@
 gins));%0A
+  return %5B'toplevel', ast%5B1%5D%5B0%5D%5B3%5D%5D;%0A
 %7D%0A%0Asugar

change direction of parsing