docs: function (tpl) {
        var tree = []
        var openTag
        var tag
        var name
        var paramStr
        var node
        var closeTag
        var usedExtends = 0
  
        for (openTag = this.findTag('', tpl); openTag; openTag = this.findTag('', tpl)) {
          if (openTag.index) {
            tree = tree.concat(this.parseText(tpl.slice(0, openTag.index)))
          }
          tpl = tpl.slice((openTag.index + openTag[0].length))
          tag = openTag[1].match(/^\s*(\w+)(.*)$/)
          if (tag) {
            // Function?!
            name = tag[1]
            paramStr = (tag.length > 2) ? tag[2].replace(/^\s+|\s+$/g, '') : ''
            if (name in this.buildInFunctions) {
              var buildIn = this.buildInFunctions[name]
              var params = ('parseParams' in buildIn ? buildIn.parseParams.bind(this) : this.parseParams.bind(this))(paramStr)
              if (buildIn.type === 'block') {
                // Remove new line after block open tag (like in Smarty)
                tpl = tpl.replace(/^\n/, '')
                closeTag = this.findCloseTag('/' + name, name + ' +[^}]*', tpl)
                var functionTree = buildIn.parse.call(this, params, tpl.slice(0, closeTag.index))
                if (functionTree) {
                  // Some functions return false like {php} and {function}
                  tree = tree.concat(functionTree)
                }
                tpl = tpl.slice(closeTag.index + closeTag[0].length)
              } else {
                if (name === 'extends') {
                  // Anything before {extends} should be stripped.
                  tree.splice(0, tree.length)
                }
                tree = tree.concat(buildIn.parse.call(this, params))
                if (name === 'extends') {
                  usedExtends = tree.length
                }
              }
              tpl = tpl.replace(/^\n/, '')
            } else if (name in this.runTimePlugins) {
              // Possible it is function name. give it a priority before plugin.
              tree = tree.concat(this.parsePluginFunc(name, this.parseParams(paramStr)))
            } else if (name in this.plugins) {
              var plugin = this.plugins[name]
              if (plugin.type === 'block') {
                closeTag = this.findCloseTag('/' + name, name + ' +[^}]*', tpl)
                tree = tree.concat(this.parsePluginBlock(name, this.parseParams(paramStr), tpl.slice(0, closeTag.index)))
                tpl = tpl.slice(closeTag.index + closeTag[0].length)
              } else if (plugin.type === 'function') {
                tree = tree.concat(this.parsePluginFunc(name, this.parseParams(paramStr)))
              }
              if (name === 'append' || name === 'assign' || name === 'capture' || name === 'eval' || name === 'include') {
                tpl = tpl.replace(/^\n/, '')
              }
            } else {
              // Variable.
              node = this.buildInFunctions.expression.parse.call(this, openTag[1])
              tree.push(node)
            }
          } else {
            // Variable.
            node = this.buildInFunctions.expression.parse.call(this, openTag[1])
            if (node.expression.type === 'build-in' && node.expression.name === 'operator' && node.expression.op === '=') {
              tpl = tpl.replace(/^\n/, '')
            }
            tree.push(node)
          }
        }
        if (tpl) {
          tree = tree.concat(this.parseText(tpl))
        }
        tree.usedExtends = usedExtends
        return tree
      }