<commit_before>/*
  Regularity.js: Regular expressions for humans
  Author: <NAME>
  License: MIT
  Url: https://github.com/oinak/regularity
*/

// souvenirs from Crockford-land:
Function.prototype.method = function(name, func){
  this.prototype[name] = func;
  return this;
};

Function.method('bind', function(that){
  var method = this,
      slice = Array.prototype.slice,
      args = slice.apply(arguments, [1]);
  return function(){
    return method.apply(that,
      args.concat(slice.apply(arguments, [0])));
  };
});

var Regularity = function() {

  /* PRIVATE state and auxiliary data*/
  var _result     = '',
    _flags      = '',
    _escapedChars = [ '*', '.', '?', '^', '+', '$', '|', '(', ')', '[', ']', '{', '}' ],
    _patterns     = {
      'digit'       : '[0-9]',
      'lowercase'   : '[a-z]',
      'uppercase'   : '[A-Z]',
      'letter'      : '[A-Za-z]',
      'alphanumeric': '[A-Za-z0-9]',
      'whitespace'  : '\\s',
      'space'       : ' ',
      'tab'         : '\t'
    };

  /* PRIVATE Helpers */

  //Javascript, the good parts: Douglas Crockford
  var isArray = function (value) {
    return value &&
      typeof value === 'object' &&
      typeof value.length === 'number' &&
      typeof value.splice === 'function' &&
      !(value.propertyIsEnumerable('length'));
  };

  var write = function(str, args){
    //console.log('write(str:'+str+' args:'+JSON.stringify(args)+')');
    var toAdd = ( args === null ? str : str.replace("%s", args ) );
    _result = _result + toAdd;
    return this;
  }.bind(this);

  // Translate/escape characters etc and return regex-ready string
  function interpret(){
    //console.log('  interpret(arguments: '+JSON.stringify(arguments)+')');
    //console.log('  interpret(flat:'+JSON.stringify(flat)+') <flat.length:'+flat.length+'>');
    switch (arguments.length) {
      case 2:
        return numberedConstraint(arguments[0],arguments[1]);
      case 1:
        return patternedConstraint(arguments[0]);
      default:
        var argumentError = new Error('interpret: wrong number of arguments, ' + arguments.length + ' for 1,2');
        throw(argumentError);
    }
  }

  // Ex: (2, 'x') or (3, 'digits')
  function numberedConstraint(count, type){
    var replaced = '',
        pattern = patternedConstraint(type);
    //console.log('    numberedConstraint('+count+', '+type+')');
    replaced = '%s{%c}'.replace('%s', pattern).replace('%c', count);
    return replaced;
  }

  // Ex: ('aa') or ('$')
  function patternedConstraint(pattern){
    //console.log('  patternedConstraint('+JSON.stringify(pattern)+')');
    if (pattern === null){
      var patternError = new Error('Unrecognized pattern');
      throw( patternError );
    }
    return translate( escapeStr(pattern));
  }

  // Escape special regex characters in a string
  // Ex:
  //   escapeStr("one.two")
  //   # => "one\.two"
  function escapeStr(pattern){
    var escaped = '';
    for (var i in pattern) {
      if ( _escapedChars.indexOf( pattern[i] ) !== -1 ){
        escaped += '\\' + pattern[i];
      } else {
        escaped += pattern[i];
      }
    }
    return escaped;
  }

  // Translate an identifier such as 'digits' to [0-9], etc
  // Returns the original identifier if no character class found
  function translate(pattern){
    return _patterns[singularize(pattern)] || pattern;
  }

  // Remove a trailing 's', if there is one
  function singularize(str){
    if ( typeof(str) === 'string' && str[str.length-1] == 's' ) {
      return str.slice(0,str.length-1);
    } else {
      return str;
    }
  }

  function empty(){
    return (_result === '');
  }

  function flatten(list){
    var args = Array.prototype.slice.call(list);
    //console.log('flatten: args: ' + JSON.stringify(args) + ' -> ' + JSON.stringify( [].concat.apply([], args)[0] ) );
    return [].concat(args);
  }

  var _enableFlag = function(flagName) {
    if (_flags.indexOf(flagName) !== -1) {
      throw new Error('can\'t enable the "' + flagName + '"" flag more than once!');
    }

    _flags += flagName;
  };

/* PUBLIC OBJECT API: */

  // The line must start with the specified pattern
  this.startWith = function() {
    if ( empty() ) {
      return write('^%s', interpret(arguments));
    } else {
      throw new Error('start_with called multiple times');
    }
  };

  // Append a pattern to the end (Also aliased to then)
  this.append = function(){
    return write(interpret(arguments));
  };

  // The line must end with the specified pattern
  this.endWith = function(){
    //console.log('  endWith => arguments:'+JSON.stringify(arguments)+' flat:'+JSON.stringify(flat)+'');
    return write('%s$', interpret(arguments));
  };

  // Zero or one of the specified pattern
  this.maybe = function(){
    return write('%s?', interpret(arguments));
  };

  // Specify an alternation, e.g. one_of(['a', 'b', 'c'])
  this.oneOf = function(){
    var flat = flatten(arguments);
        values = flat.map(function(c){
          return escapeStr(c); // missing interpret?
        }).join('|');
    return write('[%s]', values);
  };

  //: Specify a bounded repetition, e.g. between([2,4], 'digits')
  this.between = function(range, pattern){
    var values = [];
    if (isArray(range) &&
        range.length === 2 &&
        ( typeof(range[0]) === 'number' || typeof(range[1]) === 'number' ) ){
      values = [interpret(pattern), range[0] || '0', range[1] || '0'];
      replaced = '%s{%f,%t}'.replace('%s', values[0]).replace('%f', values[1]).replace('%t', values[2]);
      return write(replaced);
    } else {
      throw new Error("must provide an array of 2 elements, one of them must be an integer");
    }
  };

  // Specify that the pattern or identifer should appear zero or many times
  this.zeroOrMore = function(){
    return write('%s*', interpret(arguments));
  };<commit_msg>Remove another usage of 'flatten' and rewrite a 'map' more succintly.<commit_after>/*
  Regularity.js: Regular expressions for humans
  Author: <NAME>
  License: MIT
  Url: https://github.com/oinak/regularity
*/

// souvenirs from Crockford-land:
Function.prototype.method = function(name, func){
  this.prototype[name] = func;
  return this;
};

Function.method('bind', function(that){
  var method = this,
      slice = Array.prototype.slice,
      args = slice.apply(arguments, [1]);
  return function(){
    return method.apply(that,
      args.concat(slice.apply(arguments, [0])));
  };
});

var Regularity = function() {

  /* PRIVATE state and auxiliary data*/
  var _result     = '',
    _flags      = '',
    _escapedChars = [ '*', '.', '?', '^', '+', '$', '|', '(', ')', '[', ']', '{', '}' ],
    _patterns     = {
      'digit'       : '[0-9]',
      'lowercase'   : '[a-z]',
      'uppercase'   : '[A-Z]',
      'letter'      : '[A-Za-z]',
      'alphanumeric': '[A-Za-z0-9]',
      'whitespace'  : '\\s',
      'space'       : ' ',
      'tab'         : '\t'
    };

  /* PRIVATE Helpers */

  //Javascript, the good parts: Douglas Crockford
  var isArray = function (value) {
    return value &&
      typeof value === 'object' &&
      typeof value.length === 'number' &&
      typeof value.splice === 'function' &&
      !(value.propertyIsEnumerable('length'));
  };

  var write = function(str, args){
    //console.log('write(str:'+str+' args:'+JSON.stringify(args)+')');
    var toAdd = ( args === null ? str : str.replace("%s", args ) );
    _result = _result + toAdd;
    return this;
  }.bind(this);

  // Translate/escape characters etc and return regex-ready string
  function interpret(){
    //console.log('  interpret(arguments: '+JSON.stringify(arguments)+')');
    //console.log('  interpret(flat:'+JSON.stringify(flat)+') <flat.length:'+flat.length+'>');
    switch (arguments.length) {
      case 2:
        return numberedConstraint(arguments[0],arguments[1]);
      case 1:
        return patternedConstraint(arguments[0]);
      default:
        var argumentError = new Error('interpret: wrong number of arguments, ' + arguments.length + ' for 1,2');
        throw(argumentError);
    }
  }

  // Ex: (2, 'x') or (3, 'digits')
  function numberedConstraint(count, type){
    var replaced = '',
        pattern = patternedConstraint(type);
    //console.log('    numberedConstraint('+count+', '+type+')');
    replaced = '%s{%c}'.replace('%s', pattern).replace('%c', count);
    return replaced;
  }

  // Ex: ('aa') or ('$')
  function patternedConstraint(pattern){
    //console.log('  patternedConstraint('+JSON.stringify(pattern)+')');
    if (pattern === null){
      var patternError = new Error('Unrecognized pattern');
      throw( patternError );
    }
    return translate( escapeStr(pattern));
  }

  // Escape special regex characters in a string
  // Ex:
  //   escapeStr("one.two")
  //   # => "one\.two"
  function escapeStr(pattern){
    var escaped = '';
    for (var i in pattern) {
      if ( _escapedChars.indexOf( pattern[i] ) !== -1 ){
        escaped += '\\' + pattern[i];
      } else {
        escaped += pattern[i];
      }
    }
    return escaped;
  }

  // Translate an identifier such as 'digits' to [0-9], etc
  // Returns the original identifier if no character class found
  function translate(pattern){
    return _patterns[singularize(pattern)] || pattern;
  }

  // Remove a trailing 's', if there is one
  function singularize(str){
    if ( typeof(str) === 'string' && str[str.length-1] == 's' ) {
      return str.slice(0,str.length-1);
    } else {
      return str;
    }
  }

  function empty(){
    return (_result === '');
  }

  function flatten(list){
    var args = Array.prototype.slice.call(list);
    //console.log('flatten: args: ' + JSON.stringify(args) + ' -> ' + JSON.stringify( [].concat.apply([], args)[0] ) );
    return [].concat(args);
  }

  var _enableFlag = function(flagName) {
    if (_flags.indexOf(flagName) !== -1) {
      throw new Error('can\'t enable the "' + flagName + '"" flag more than once!');
    }

    _flags += flagName;
  };

/* PUBLIC OBJECT API: */

  // The line must start with the specified pattern
  this.startWith = function() {
    if ( empty() ) {
      return write('^%s', interpret(arguments));
    } else {
      throw new Error('start_with called multiple times');
    }
  };

  // Append a pattern to the end (Also aliased to then)
  this.append = function(){
    return write(interpret(arguments));
  };

  // The line must end with the specified pattern
  this.endWith = function(){
    //console.log('  endWith => arguments:'+JSON.stringify(arguments)+' flat:'+JSON.stringify(flat)+'');
    return write('%s$', interpret(arguments));
  };

  // Zero or one of the specified pattern
  this.maybe = function(){
    return write('%s?', interpret(arguments));
  };

  // Specify an alternation, e.g. one_of(['a', 'b', 'c'])
  this.oneOf = function(){
    var choices = Array.prototype.slice.call(arguments)
      .map(escapeStr) // missing interpret?
      .join('|');

    return write('[%s]', choices);
  };

  //: Specify a bounded repetition, e.g. between([2,4], 'digits')
  this.between = function(range, pattern){
    var values = [];
    if (isArray(range) &&
        range.length === 2 &&
        ( typeof(range[0]) === 'number' || typeof(range[1]) === 'number' ) ){
      values = [interpret(pattern), range[0] || '0', range[1] || '0'];
      replaced = '%s{%f,%t}'.replace('%s', values[0]).replace('%f', values[1]).replace('%t', values[2]);
      return write(replaced);
    } else {
      throw new Error("must provide an array of 2 elements, one of them must be an integer");
    }
  };

  // Specify that the pattern or identifer should appear zero or many times
  this.zeroOrMore = function(){
    return write('%s*', interpret(arguments));
  };
{'fraction_non_alphanumeric': 0.18504641062760793, 'fraction_numerical': 0.006131312271140253, 'mean_word_length': 2.8657011191573405, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2923918', 'n_tokens_mistral': 3775, 'n_tokens_neox': 3670, 'n_words': 915}