EN_REPLACEMENT, (m) => '(100% - ' + m + ')')
        : 100 - Number.parseFloat(num)
    })
    return this.restoreTokens(state)
  },
  flipLength (value) {
    return config.useCalc ? `calc(100% - ${value})` : value
  },
  save (what, who, replacement, restorer, exclude) {
    const state = {
      value: who,
      store: [],
      replacement,
      restorer
    }
    state.value = state.value.replace(what, (c) => {
      if (exclude && exclude.test(c)) {
        return c
      }

      state.store.push(c)
      return state.replacement
    })
    return state
  },
  restore (state) {
    let index = 0
    const result = state.value.replace(state.restorer, () => state.store[index++])
    state.store.length = 0
    return result
  },
  saveComments (value) {
    return this.save(REGEX_COMMENT, value, CHAR_COMMENT_REPLACEMENT, REGEX_COMMENT_REPLACEMENT)
  },
  restoreComments (state) {
    return this.restore(state)
  },
  saveTokens (value, excludeCalc) {
    return excludeCalc === true
      ? this.save(REGEX_TOKENS_WITH_NAME, value, CHAR_TOKEN_REPLACEMENT, REGEX_TOKEN_REPLACEMENT, REGEX_CALC)
      : this.save(REGEX_TOKENS, value, CHAR_TOKEN_REPLACEMENT, REGEX_TOKEN_REPLACEMENT)
  },
  restoreTokens (state) {
    return this.restore(state)
  },
  guard (what, who) {
    const state = {
      value: who,
      store: [],
      offset: tokenId++,
      token: CHAR_TOKEN_START + tokenId
    }

    while (what.test(state.value)) {
      state.value = state.value.replace(what, (m) => {
        state.store.push(m)
        return `${state.token}:${state.store.length}${CHAR_TOKEN_END}`
      })
    }

    return state
  },
  unguard (state, callback) {
    const detokenizer = new RegExp('(\\w*?)' + state.token + ':(\\d+)' + CHAR_TOKEN_END, 'i')
    while (detokenizer.test(state.value)) {
      state.value = state.value.replace(detokenizer, (match, name, index) => {
        const value = state.store[index - 1]
        return typeof callback === 'function'
          ? name + callback(value, name)
          : name + value
      })
    }

    return state.value
  },
  guardHexColors (value) {
    return this.guard(REGEX_HEX_COLOR, value)
  },
  unguardHexColors (state, callback) {
    return this.unguard(state, callback)
  },
  guardFunctions (value) {
    return this.guard(REGEX_FUNCTION, value)
  },
  unguardFunctions (state, callback) {
    return this.unguard(state, callback)
  },
  trimDirective (value) {
    return value.replace(REGEX_DIRECTIVE, '')
  },
  regexCache: {},
  regexDirective (name) {
    // /(?:\/\*(?:!)?rtl:ignore(?::)?)([^]*?)(?:\*\/)/img
    this.regexCache[name] = this.regexCache[name] || new RegExp('(?:\\/\\*\\s*(?:!)?\\s*rtl:' + (name ? escapeRegExp(name) + '(?::)?' : '') + ')([^]*?)(?:\\*\\/)', 'img')
    return this.regexCache[name]
  },
  regex (what, options) {
    let expression = ''

    for (const exp of what) {
      switch (exp) {
        case 'percent':
          expression += `|(${PATTERN_NUMBER}%)`
          break
        case 'length':
          expression += `|(${PATTERN_NUMBER})(?:ex|ch|r?em|vh|vw|vmin|vmax|px|mm|cm|in|pt|pc)?`
          break
        case 'number':
          expression += `|(${PATTERN_NUMBER})`
          break
        case 'position':
          expression += '|(left|center|right|top|bottom)'
          break
        case 'calc':
          expression += `|(calc${PATTERN_TOKEN})`
          break
        case 'func':
          expression += `|(\\w+${PATTERN_TOKEN})`
          break
      }
    }

    return new RegExp(expression.slice(1), options)
  },
  isLastOfType (node) {
    let isLast = true
    let next = node.next()

    while (next) {
      if (next.type === node.type) {
        isLast = false
        break
      }

      next = next.next()
    }

    return isLast
  },
  /**
   * Simple breakable each returning false if the callback returns false
   * otherwise it returns true
   */
  each (array, callback) {
    return !array.some((element) => callback(element) === false)
  }
}

module.exports.configure = function (configuration) {
  config = configuration
  return