All files / src/core/nfa simulate.ts

100% Statements 80/80
95.65% Branches 44/46
100% Functions 3/3
100% Lines 74/74
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157  15x 15x 15x   1530x       1530x 1530x             1530x 1530x 100408x 100408x 100408x 1444x 1444x 1444x   98964x 109291x 109291x 109291x 107177x 8032x 8032x     99145x         86x   15x   109291x 109291x 109291x 109291x 9840x 1485x 1356x 1356x 1356x     9840x   99451x 14567x 14567x 4480x 4480x 1295x     3185x   4480x   4480x         109291x 15458x     93833x     109291x 109291x 107177x 93833x 79351x               14482x   93833x                 93833x     13344x   13344x 5312x 5312x 5312x                   5312x       8032x           1444x 1444x   1444x 42141x 37410x         37410x 4670x 4670x   37410x   42141x 42141x   1444x 1444x        
import isProblem from "../../utils/isProblem";
import isStr from "../../preds/isStr";
 
export default function simulate( nfa, rawInput, walkFn, walkOpts ) {
 
  var r: any = {
    matched: false,
    chain: null,
  };
 
  const inputType = typeof rawInput;
 
  var initial = {
    state: 0,
    offset: 0,
    input: [ rawInput ],
    groupCount: 0,
    arrayed: false };
  var frontier = [ initial ];
  while ( frontier.length > 0 ) {
    var current = frontier.shift();
    const { offset: currentOffset, input } = current;
    if ( current.state === nfa.finalState && currentOffset === input.length ) {
      r.matched = true;
      r.chain = _getChain( nfa, current, inputType );
      return r;
    }
    for ( var nextStateStr in nfa.transitions[ current.state ] ) {
      var nextState = parseInt( nextStateStr );
 
      var m = _getNextMove( nfa, nextState, current, walkFn, walkOpts );
      if ( m ) {
        if ( m.isProblem ) {
          let { name, problem, position } = m;
          r.lastProblem = { name, problem, position };
        } else {
          frontier.push( m );
        }
      }
    }
  }
  return r;
}
 
function _getNextMove( nfa, nextState, current, walkFn, walkOpts ) {
  var { input, offset: currentOffset, groupCount, arrayed } = current;
  var observed = input[ currentOffset ];
  var transition = nfa.transitions[ current.state ][ nextState ];
 
  if ( transition.group === 'in' ) {
    if ( groupCount === 0 ) {
      if ( Array.isArray( input[ 0 ] ) || isStr( input [ 0 ] ) ) {
        input = input[ 0 ];
        currentOffset = 0;
        arrayed = true;
      }
    }
    groupCount += 1;
 
  } else if ( transition.group === 'out' ) {
    groupCouEnt -= 1;
    if ( groupCount === 0 ) {
      if ( arrayed ) {
        if ( currentOffset === input.length ) {
          currentOffset = 1;
        } else {
          currentOffset = 0;
        }
        input = [ input ];
      }
      arrayed = false;
    }
  }
 
  var nextOffset;
  var move;
  if ( !transition.isEpsilon ) {
    nextOffset = currentOffset + 1;
  } else {
    nextOffset = currentOffset;
  }
 
  var validateResult,
    next;
 
  var name = transition.name || current.move && current.move.name;
  if ( nextOffset <= input.length ) {
    if ( transition.isEpsilon ) {
      if ( transition.dir ) {
        move = {
          dir: transition.dir,
          name: transition.name,
          op: transition.op,
          group: transition.group
        };
      } else {
        move = null;
      }
      next = {
        input, groupCount, arrayed,
        state: nextState,
        offset: nextOffset,
        move: move,
        clause: transition,
        prev: current,
        isEpsilon: true,
      };
      return next;
    } else {
      validateResult = walkFn( transition, observed, walkOpts );
              //E validateResult = walkFn(transition, observed, walkOpts);
      if ( !isProblem( validateResult ) ) {
        if ( currentOffset < input.length ) {
          move = { dir: 'clause' };
          next = {
            input, groupCount, arrayed,
            state: nextState,
            offset: nextOffset,
            move: move,
            prev: current,
            isEpsilon: false,
            clause: transition,
            guide: validateResult,
          };
          return next;
        }
      } else {
        return { isProblem: true, problem: validateResult, name: name, position: currentOffset };
      }
    }
  }
}
 
function _getChain( nfa, finalState, inputType ) {
  var chain: any = [];
  var curr = finalState;
  var prev;
  while ( curr ) {
    if ( !prev || ( curr.state !== prev.state ) && curr.move ) {
      var o: any = {
        isEpsilon: curr.isEpsilon,
        move: curr.move,
        state: curr.state,
      };
      if ( !curr.isEpsilon ) {
        o.guide = curr.guide;
        o.clause = curr.clause;
      }
      chain.unshift( o );
    }
    prev = curr;
    curr = curr.prev;
  }
  chain.inputType = inputType;
  return chain;
}