whole_func_string: function parse_tag (str, parsers) {
    if (typeof str !== 'string' || str[0] !== '@') { return null }
  
    var data = parsers.reduce(function (state, parser) {
      var result
  
      try {
        result = parser(state.source, merge({}, state.data))
      } catch (err) {
        state.data.errors = (state.data.errors || [])
          .concat(parser.name + ': ' + err.message)
      }
  
      if (result) {
        state.source = state.source.slice(result.source.length)
        state.data = merge(state.data, result.data)
      }
  
      return state
    }, {
      source: str,
      data: {}
    }).data
  
    data.optional = !!data.optional
    data.type = data.type === undefined ? '' : data.type
    data.name = data.name === undefined ? '' : data.name
    data.description = data.description === undefined ? '' : data.description
  
    return data
  }
func_code_string: function parse_tag (str, parsers) {
    if (typeof str !== 'string' || str[0] !== '@') { return null }
  
    var data = parsers.reduce(function (state, parser) {
      var result
  
      try {
        result = parser(state.source, merge({}, state.data))
      } catch (err) {
        state.data.errors = (state.data.errors || [])
          .concat(parser.name + ': ' + err.message)
      }
  
      if (result) {
        state.source = state.source.slice(result.source.length)
        state.data = merge(state.data, result.data)
      }
  
      return state
    }, {
      source: str,
      data: {}
    }).data
  
    data.optional = !!data.optional
    data.type = data.type === undefined ? '' : data.type
    data.name = data.name === undefined ? '' : data.name
    data.description = data.description === undefined ? '' : data.description
  
    return data
  }
func_documentation_string: /* ------- parsing ------- 
  Parses "@tag {type} name description"
  @param {string} str Raw doc string
  @param {Array[function]} parsers Array of parsers to be applied to the source
  @returns {object} parsed tag node