docs: function(mixin){
      var ast = [];
      var self = this;
      var name = 'pug_mixins[';
      var args = mixin.args || '';
      var block = mixin.block;
      var attrs = mixin.attrs;
      var attrsBlocks = this.attributeBlocks(mixin.attributeBlocks);
      var pp = this.pp;
      var dynamic = mixin.name[0]==='#';
      var key = mixin.name;
      if (dynamic) this.dynamicMixins = true;
      name += (dynamic ? mixin.name.substr(2,mixin.name.length-3):'"'+mixin.name+'"')+']';
      var mixinName = dynamic ? (mixin.astName || this.parseExpr(mixin.name.substr(2,mixin.name.length-3))): t.stringLiteral(mixin.name);
      this.mixins[key] = this.mixins[key] || {used: false, instances: []};
  
      // mixin invocation
      if (mixin.call) {
        this.mixins[key].used = true;
        if (pp) {
          ast.push(t.expressionStatement(t.callExpression(
            t.memberExpression(t.identifier('pug_indent'), t.identifier('push')),
            [t.stringLiteral(Array(this.indents + 1).join(pp))]
          )))
        }
        if (block || attrs.length || attrsBlocks.length) {
  
          var astArgs = []
          ast.push(
            t.expressionStatement(this.wrapCallExpression(t.callExpression(
              t.memberExpression(
                t.memberExpression(t.identifier('pug_mixins'), mixinName, true),
                t.identifier("call")
              ),
              astArgs
            )))
          );
  
          var astObj, astKey;
          if (block || attrsBlocks.length || attrs.length) {
            astKey = [];
            astObj = t.objectExpression(astKey);
            astArgs.push(astObj);
          }
  
          if (block) {
            var astFunc = [];
          
            // Render block with no indents, dynamically added when rendered
            this.parentIndents++;
            var _indents = this.indents;
            this.indents = 0;
            push.apply(astFunc, this.visit(mixin.block, mixin));
            this.indents = _indents;
            this.parentIndents--;
  
            astKey.push(t.objectProperty(
              t.identifier('block'),
              t.functionExpression(
                null,
                [],
                t.blockStatement(astFunc),
                this.useGenerators
              )
            ));
   
          }
  
          if (attrsBlocks.length) {
            if (attrs.length) {
              var val = this.attrs(attrs);
              attrsBlocks.unshift(val);
            }
            if (attrsBlocks.length > 1) {
              astKey.push(t.objectProperty(
                t.identifier('attributes'),
                t.callExpression(
                  this.runtime('merge', true),
                  attrsBlocks.map(function(b) { return self.parseExpr(b) })
                )
              ));
            } else {
              astKey.push(t.objectProperty(
                t.identifier('attributes'),
                this.parseExpr(attrsBlocks[0])
              ));
            }
          } else if (attrs.length) {
            var val = this.attrs(attrs);
            astKey.push(t.objectProperty(
              t.identifier('attributes'),
              this.parseExpr(val)
            ));
          }
  
          if (args) { 
            args = args ? args.split(',') : [];
            Array.prototype.push.apply(astArgs, mixin.astArgs || this.parseArgs(args) )
          }
  
        } else {
          var astArgs = mixin.astArgs || this.parseArgs(args);
          ast.push(t.expressionStatement(this.wrapCallExpression(t.callExpression(
            t.memberExpression(t.identifier('pug_mixins'), mixinName, true),
            astArgs
          ))));
        }
        if (pp) {
          ast.push(t.expressionStatement(t.callExpression(
            t.memberExpression(t.identifier('pug_indent'), t.identifier('pop')),
            []
          )))
   
        }
      }
      // mixin definition
      else {
        args = args ? args.split(',') : [];
        var rest;
        if (args.length && /^\.\.\./.test(args[args.length - 1].trim())) {
          rest = args.pop().trim().replace(/^\.\.\./, '');
        }
        var astArgs = args.map(function(arg) { return t.identifier(arg.trim())})
        // we need use pug_interp here for v8: https://code.google.com/p/v8/issues/detail?id=4165
        // once fixed, use this: this.buf.push(name + ' = function(' + args.join(',') + '){');
        var astMixin = [];
  
        astMixin.push(
          t.variableDeclaration('var', [
            t.variableDeclarator(
              t.identifier('block'),
              t.logicalExpression('&&', t.thisExpression(), t.memberExpression(t.thisExpression(), t.identifier('block')))
            ),
            t.variableDeclarator(
              t.identifier('attributes'),
              t.logicalExpression('||',
                t.logicalExpression('&&', t.thisExpression(), t.memberExpression(t.thisExpression(), t.identifier('attributes'))),
                t.objectExpression([])
              )
            )
          ])
        )
  
        if (rest) {
          astMixin.push(
            t.variableDeclaration('var', [
              t.variableDeclarator(
                t.identifier(rest),
                t.arrayExpression([])
              )
            ])
          )
          astMixin.push(
            t.forStatement(
              t.assignmentExpression('=', t.identifier('pug_interp'), t.numericLiteral(args.length)),
              t.binaryExpression('<', t.identifier('pug_interp'), t.memberExpression(t.identifier('arguments'), t.identifier('length'))),
              t.updateExpression('++', t.identifier('pug_interp'), false),
              t.expressionStatement(
                t.callExpression(
                  t.memberExpression(t.identifier(rest), t.identifier('push')),
                  [t.memberExpression(t.identifier('arguments'), t.identifier('pug_interp'), true)]
                )
              )
            )
          )
        }
  
        this.parentIndents++;
        push.apply(astMixin, this.visit(block, mixin));
        this.parentIndents--;
  
  
        var mixinStmt = 
          t.expressionStatement(
            t.assignmentExpression(
              '=',
              t.memberExpression(t.identifier('pug_mixins'), mixinName, true),
              t.assignmentExpression(
                '=',
                t.identifier('pug_interp'),
                t.functionExpression(
                  null,
                  astArgs,
                  t.blockStatement(astMixin),
                  this.useGenerators
                )
              )
            )
          );
        ast.push(mixinStmt);
   
  
  
        this.mixins[key].instances.push({stmt: mixinStmt});
      }
      return ast;
    }