1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.regexp.node.Node; 
    18  import org.as2lib.regexp.node.TreeInfo;
    19  
    20  /**
    21   * {@code Loop} handles the repetition count for a greedy Curly. The 
    22   * matchInit is called from the Prolog to save the index of where the group
    23   * beginning is stored. A zero length group check occurs in the
    24   * normal match but is skipped in the matchInit.
    25   * 
    26   * @author Igor Sadovskiy
    27   */
    28   
    29  class org.as2lib.regexp.node.Loop extends Node {
    30  	
    31      private var body:Node;
    32      private var countIndex:Number; // local count index in matcher locals
    33      private var beginIndex:Number; // group begining index
    34      private var cmin, cmax:Number;
    35      
    36      public function Loop(countIndex:Number, beginIndex:Number) {
    37          this.countIndex = countIndex;
    38          this.beginIndex = beginIndex;
    39      }
    40      
    41      public function match(matcher:Object, i:Number, seq:String):Boolean {
    42          // Avoid infinite loop in zero-length case.
    43          if (i > matcher.locals[beginIndex]) {
    44              var count:Number = matcher.locals[countIndex];
    45  
    46              // This block is for before we reach the minimum
    47              // iterations required for the loop to match
    48              if (count < cmin) {
    49                  matcher.locals[countIndex] = count + 1;
    50                  var b:Boolean = body.match(matcher, i, seq);
    51                  // If match failed we must backtrack, so
    52                  // the loop count should NOT be incremented
    53                  if (!b)
    54                      matcher.locals[countIndex] = count;
    55                  // Return success or failure since we are under
    56                  // minimum
    57                  return b;
    58              }
    59              // This block is for after we have the minimum
    60              // iterations required for the loop to match
    61              if (count < cmax) {
    62                  matcher.locals[countIndex] = count + 1;
    63                  var b:Boolean = body.match(matcher, i, seq);
    64                  // If match failed we must backtrack, so
    65                  // the loop count should NOT be incremented
    66                  if (!b)
    67                      matcher.locals[countIndex] = count;
    68                  else
    69                      return true;
    70              }
    71          }
    72          return next.match(matcher, i, seq);
    73      }
    74      
    75      public function matchInit(matcher:Object, i:Number, seq:String):Boolean {
    76          var save:Number = matcher.locals[countIndex];
    77          var ret:Boolean = false;
    78          if (0 < cmin) {
    79              matcher.locals[countIndex] = 1;
    80              ret = body.match(matcher, i, seq);
    81          } else if (0 < cmax) {
    82              matcher.locals[countIndex] = 1;
    83              ret = body.match(matcher, i, seq);
    84              if (ret == false)
    85                  ret = next.match(matcher, i, seq);
    86          } else {
    87              ret = next.match(matcher, i, seq);
    88          }
    89          matcher.locals[countIndex] = save;
    90          return ret;
    91      }
    92      
    93      public function study(info:TreeInfo):Boolean {
    94          info.maxValid = false;
    95          info.deterministic = false;
    96          return false;
    97      }
    98      
    99      public function getCmin(Void):Number {
   100      	return cmin;	
   101      }
   102  
   103      public function setCmin(cmin:Number):Void {
   104      	this.cmin = cmin;
   105      }
   106      
   107      public function getCmax(Void):Number {
   108      	return cmax;	
   109      }
   110  
   111      public function setCmax(cmax:Number):Void {
   112      	this.cmax = cmax;
   113      }
   114      
   115      public function getBody(Void):Node {
   116      	return body;
   117      }
   118  
   119      public function setBody(body:Node):Void {
   120      	this.body = body;
   121      }
   122      
   123  }
   124  
   125