1<?php 2 3/** 4 * Hoa 5 * 6 * 7 * @license 8 * 9 * New BSD License 10 * 11 * Copyright © 2007-2017, Hoa community. All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions are met: 15 * * Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * * Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * * Neither the name of the Hoa nor the names of its contributors may be 21 * used to endorse or promote products derived from this software without 22 * specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37namespace Hoa\Regex\Visitor; 38 39use Hoa\Math; 40use Hoa\Regex; 41use Hoa\Ustring; 42use Hoa\Visitor; 43 44/** 45 * Class \Hoa\Regex\Visitor\Isotropic. 46 * 47 * Isotropic walk on the AST to generate a data. 48 * 49 * @copyright Copyright © 2007-2017 Hoa community 50 * @license New BSD License 51 */ 52class Isotropic implements Visitor\Visit 53{ 54 /** 55 * Numeric-sampler. 56 * 57 * @var \Hoa\Math\Sampler 58 */ 59 protected $_sampler = null; 60 61 62 63 /** 64 * Initialize numeric-sampler. 65 * 66 * @param \Hoa\Math\Sampler $sampler Numeric-sampler. 67 */ 68 public function __construct(Math\Sampler $sampler) 69 { 70 $this->_sampler = $sampler; 71 72 return; 73 } 74 75 /** 76 * Visit an element. 77 * 78 * @param \Hoa\Visitor\Element $element Element to visit. 79 * @param mixed &$handle Handle (reference). 80 * @param mixed $eldnah Handle (not reference). 81 * @return mixed 82 * @throws \Hoa\Regex\Exception 83 */ 84 public function visit( 85 Visitor\Element $element, 86 &$handle = null, 87 $eldnah = null 88 ) { 89 switch ($element->getId()) { 90 case '#expression': 91 case '#capturing': 92 case '#noncapturing': 93 case '#namedcapturing': 94 return $element->getChild(0)->accept($this, $handle, $eldnah); 95 96 case '#alternation': 97 case '#class': 98 return $element->getChild($this->_sampler->getInteger( 99 0, 100 $element->getChildrenNumber() - 1 101 ))->accept($this, $handle, $eldnah); 102 103 case '#concatenation': 104 $out = null; 105 106 foreach ($element->getChildren() as $child) { 107 $out .= $child->accept($this, $handle, $eldnah); 108 } 109 110 return $out; 111 112 case '#quantification': 113 $out = null; 114 $xy = $element->getChild(1)->getValueValue(); 115 $x = 0; 116 $y = 0; 117 118 switch ($element->getChild(1)->getValueToken()) { 119 case 'zero_or_one': 120 $y = 1; 121 122 break; 123 124 case 'zero_or_more': 125 $y = mt_rand(5, 8); // why not? 126 127 break; 128 129 case 'one_or_more': 130 $x = 1; 131 $y = mt_rand(5, 8); // why not? 132 133 break; 134 135 case 'exactly_n': 136 $x = $y = (int) substr($xy, 1, -1); 137 138 break; 139 140 case 'n_to_m': 141 $xy = explode(',', substr($xy, 1, -1)); 142 $x = (int) trim($xy[0]); 143 $y = (int) trim($xy[1]); 144 145 break; 146 147 case 'n_or_more': 148 $xy = explode(',', substr($xy, 1, -1)); 149 $x = (int) trim($xy[0]); 150 $y = mt_rand($x + 5, $x + 8); // why not? 151 152 break; 153 } 154 155 for ( 156 $i = 0, $max = $this->_sampler->getInteger($x, $y); 157 $i < $max; 158 ++$i 159 ) { 160 $out .= $element->getChild(0)->accept( 161 $this, 162 $handle, 163 $eldnah 164 ); 165 } 166 167 return $out; 168 169 case '#negativeclass': 170 $c = []; 171 172 foreach ($element->getChildren() as $child) { 173 $c[Ustring::toCode( 174 $child->accept($this, $handle, $eldnah) 175 )] = true; 176 } 177 178 do { 179 // all printable ASCII. 180 $i = $this->_sampler->getInteger(32, 126); 181 } while (isset($c[$i])); 182 183 return Ustring::fromCode($i); 184 185 case '#range': 186 $out = null; 187 $left = $element->getChild(0)->accept($this, $handle, $eldnah); 188 $right = $element->getChild(1)->accept($this, $handle, $eldnah); 189 190 return 191 Ustring::fromCode( 192 $this->_sampler->getInteger( 193 Ustring::toCode($left), 194 Ustring::toCode($right) 195 ) 196 ); 197 198 case 'token': 199 $value = $element->getValueValue(); 200 201 switch ($element->getValueToken()) { 202 case 'character': 203 $value = ltrim($value, '\\'); 204 205 switch ($value) { 206 case 'a': 207 return "\a"; 208 209 case 'e': 210 return "\e"; 211 212 case 'f': 213 return "\f"; 214 215 case 'n': 216 return "\n"; 217 218 case 'r': 219 return "\r"; 220 221 case 't': 222 return "\t"; 223 224 default: 225 return 226 Ustring::fromCode( 227 intval( 228 substr($value, 1) 229 ) 230 ); 231 } 232 233 break; 234 235 case 'dynamic_character': 236 $value = ltrim($value, '\\'); 237 238 switch ($value[0]) { 239 case 'x': 240 $value = trim($value, 'x{}'); 241 242 return Ustring::fromCode( 243 hexdec($value) 244 ); 245 246 default: 247 return Ustring::fromCode(octdec($value)); 248 } 249 250 break; 251 252 case 'character_type': 253 $value = ltrim($value, '\\'); 254 255 if ('s' === $value) { 256 $value = $this->_sampler->getInteger(0, 1) ? 'h' : 'v'; 257 } 258 259 switch ($value) { 260 case 'C': 261 return $this->_sampler->getInteger(0, 127); 262 263 case 'd': 264 return $this->_sampler->getInteger(0, 9); 265 266 case 'h': 267 $h = [ 268 Ustring::fromCode(0x0009), 269 Ustring::fromCode(0x0020), 270 Ustring::fromCode(0x00a0) 271 ]; 272 273 return $h[$this->_sampler->getInteger(0, count($h) -1)]; 274 275 case 'v': 276 $v = [ 277 Ustring::fromCode(0x000a), 278 Ustring::fromCode(0x000b), 279 Ustring::fromCode(0x000c), 280 Ustring::fromCode(0x000d) 281 ]; 282 283 return $v[$this->_sampler->getInteger(0, count($v) -1)]; 284 285 case 'w': 286 $w = array_merge( 287 range(0x41, 0x5a), 288 range(0x61, 0x7a), 289 [0x5f] 290 ); 291 292 return Ustring::fromCode($w[$this->_sampler->getInteger(0, count($w) - 1)]); 293 294 default: 295 return '?'; 296 } 297 298 break; 299 300 case 'literal': 301 if ('.' === $value) { 302 $w = array_merge( 303 range(0x41, 0x5a), 304 range(0x61, 0x7a), 305 [0x5f] 306 ); 307 308 return Ustring::fromCode($w[$this->_sampler->getInteger(0, count($w) - 1)]); 309 } 310 311 return 312 str_replace( 313 '\\\\', 314 '\\', 315 preg_replace( 316 '#\\\(?!\\\)#', 317 '', 318 $value 319 ) 320 ); 321 } 322 323 break; 324 325 case '#internal_options': 326 break; 327 328 default: 329 throw new Regex\Exception( 330 'Unsupported node: %s.', 331 0, 332 $element->getId() 333 ); 334 } 335 336 return; 337 } 338} 339