<commit_before>const vm = require('vm')

module.exports = class Guards {
  constructor () {
    this.guardRegex = /[^=<>!']=[^=]/

    this.templateRegex = /[^|]\|[^|]/

    this.optionsVM = {
      displayErrors: true,
      filename: 'guards'
    }
  }

  getStack () {
    const origin = Error.prepareStackTrace
    const error = new Error()

    Error.prepareStackTrace = (_, stack) => stack

    Error.captureStackTrace(error, this.getStack)

    const stack = error.stack

    Error.prepareStackTrace = origin

    // V8 stack traces.
    return stack
  }

  equal (constants) {
    return template => {
      const guards = this.parse(template.raw[0])
      const lineOffset = this.getStack()[1].getLineNumber()
      const firstTruthyGuard = (
        guards.map(
          g => this.runInVM(g.eval, constants, lineOffset)
        )
      ).findIndex(a => a === true)

      if (firstTruthyGuard === -1) this.error('No truthy guard found!')

      // First truthy guard is returned, like in Haskell.
      return guards[firstTruthyGuard].result
    }
  }

  error (e) {
    console.error(e)

    process.exit(1)
  }

  parse (template) {
    // Inline guards need filtering.
    return template
      .split(this.templateRegex)
      .filter(g => g.trim() !== '')
      .map((g, i) => {
        // Remove break line and extract the guard.
        const parts = g.trim().split(this.guardRegex)

        return {
          eval: parts[0],
          result: JSON.parse(`${parts[1].trim().replace(/'/g, '"')}`)
        }
      })
  }

  runInVM (code, sandbox, lineOffset) {
    const options = Object.assign({}, this.optionsVM, { lineOffset: lineOffset })

    return vm.runInNewContext(code, sandbox, options)
  }
}
<commit_msg>Add better error report for non-exhaustive patterns :hammer:.<commit_after>const vm = require('vm')

module.exports = class Guards {
  constructor () {
    this.guardRegex = /[^=<>!']=[^=]/

    this.templateRegex = /[^|]\|[^|]/

    this.optionsVM = {
      displayErrors: true,
      filename: 'guards'
    }
  }

  getStack () {
    const origin = Error.prepareStackTrace
    const error = new Error()

    Error.prepareStackTrace = (_, stack) => stack

    Error.captureStackTrace(error, this.getStack)

    const stack = error.stack

    Error.prepareStackTrace = origin

    // V8 stack traces.
    return stack
  }

  equal (constants) {
    return template => {
      const guards = this.parse(template.raw[0])
      const lineOffset = this.getStack()[1].getLineNumber()
      const firstTruthyGuard = (
        guards.map(
          g => this.runInVM(g.eval, constants, lineOffset)
        )
      ).findIndex(a => a === true)

      if (firstTruthyGuard === -1) this.error(`Non-exhaustive patterns in guards at line ${lineOffset}!`)

      // First truthy guard is returned, like in Haskell.
      return guards[firstTruthyGuard].result
    }
  }

  error (e) {
    console.error(e)

    process.exit(1)
  }

  parse (template) {
    // Inline guards need filtering.
    return template
      .split(this.templateRegex)
      .filter(g => g.trim() !== '')
      .map((g, i) => {
        // Remove break line and extract the guard.
        const parts = g.trim().split(this.guardRegex)

        return {
          eval: parts[0],
          result: JSON.parse(`${parts[1].trim().replace(/'/g, '"')}`)
        }
      })
  }

  runInVM (code, sandbox, lineOffset) {
    const options = Object.assign({}, this.optionsVM, { lineOffset: lineOffset })

    return vm.runInNewContext(code, sandbox, options)
  }
}

{'fraction_non_alphanumeric': 0.14704225352112676, 'fraction_numerical': 0.003943661971830986, 'mean_word_length': 2.4013409961685825, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1280870', 'n_tokens_mistral': 1142, 'n_tokens_neox': 1093, 'n_words': 250}