src/optimizer/transforms/combine-repeating-patterns-transform.js
function combineRepeatingPatternLeft(alternative, child, index) {
  const {node} = alternative;

  const nbPossibleLengths = Math.ceil(index / 2);
  let i = 0;

  while (i < nbPossibleLengths) {
    const startIndex = index - 2 * i - 1;
    let right, left;

    if (i === 0) {
      right = child;
      left = alternative.getChild(startIndex);
    } else {
      right = NodePath.getForNode({
        type: 'Alternative',
        expressions: [...node.expressions.slice(index - i, index), child.node]
      });

      left = NodePath.getForNode({
        type: 'Alternative',
        expressions: [...node.expressions.slice(startIndex, index - i)]
      });
    }

    if (right.hasEqualSource(left)) {
      for (let j = 0; j < 2 * i + 1; j++) {
        alternative.getChild(startIndex).remove();
      }

      child.replace({
        type: 'Repetition',
        expression: i === 0 ? right.node : {
          type: 'Group',
          capturing: false,
          expression: right.node
        },
        quantifier: {
          type: 'Quantifier',
          kind: 'Range',
          from: 2,
          to: 2,
          greedy: true
        }
      });
      return startIndex;
    }

    i++;
  }

  return index;
}
function combineRepeatingPatternLeft(alternative, child, index) {
  const {node} = alternative;

  const nbPossibleLengths = Math.ceil(index / 2);
  let i = 0;

  while (i < nbPossibleLengths) {
    const startIndex = index - 2 * i - 1;
    let right, left;

    if (i === 0) {
      right = child;
      left = alternative.getChild(startIndex);
    } else {
      right = NodePath.getForNode({
        type: 'Alternative',
        expressions: [...node.expressions.slice(index - i, index), child.node]
      });

      left = NodePath.getForNode({
        type: 'Alternative',
        expressions: [...node.expressions.slice(startIndex, index - i)]
      });
    }

    if (right.hasEqualSource(left)) {
      for (let j = 0; j < 2 * i + 1; j++) {
        alternative.getChild(startIndex).remove();
      }

      child.replace({
        type: 'Repetition',
        expression: i === 0 ? right.node : {
          type: 'Group',
          capturing: false,
          expression: right.node
        },
        quantifier: {
          type: 'Quantifier',
          kind: 'Range',
          from: 2,
          to: 2,
          greedy: true
        }
      });
      return startIndex;
    }

    i++;
  }

  return index;
}
function combineRepeatingPatternLeft ( alternative , child , index ) { const { node } = alternative ; const nbPossibleLengths = Math . ceil ( index / 2 ) ; let i = 0 ; while ( i < nbPossibleLengths ) { const startIndex = index - 2 * i - 1 ; let right , left ; if ( i === 0 ) { right = child ; left = alternative . getChild ( startIndex ) ; } else { right = NodePath . getForNode ( { type : 'Alternative' , expressions : [ ... node . expressions . slice ( index - i , index ) , child . node ] } ) ; left = NodePath . getForNode ( { type : 'Alternative' , expressions : [ ... node . expressions . slice ( startIndex , index - i ) ] } ) ; } if ( right . hasEqualSource ( left ) ) { for ( let j = 0 ; j < 2 * i + 1 ; j ++ ) { alternative . getChild ( startIndex ) . remove ( ) ; } child . replace ( { type : 'Repetition' , expression : i === 0 ? right . node : { type : 'Group' , capturing : false , expression : right . node } , quantifier : { type : 'Quantifier' , kind : 'Range' , from : 2 , to : 2 , greedy : true } } ) ; return startIndex ; } i ++ ; } return index ; }
abcabc -> (?:abc){2}
abcabc - > ( ? : abc ) { 2 }