_sampler = $sampler; return; } /** * Visit an element. * * @param \Hoa\Visitor\Element $element Element to visit. * @param mixed &$handle Handle (reference). * @param mixed $eldnah Handle (not reference). * @return mixed * @throws \Hoa\Regex\Exception */ public function visit( Visitor\Element $element, &$handle = null, $eldnah = null ) { switch ($element->getId()) { case '#expression': case '#capturing': case '#noncapturing': case '#namedcapturing': return $element->getChild(0)->accept($this, $handle, $eldnah); case '#alternation': case '#class': return $element->getChild($this->_sampler->getInteger( 0, $element->getChildrenNumber() - 1 ))->accept($this, $handle, $eldnah); case '#concatenation': $out = null; foreach ($element->getChildren() as $child) { $out .= $child->accept($this, $handle, $eldnah); } return $out; case '#quantification': $out = null; $xy = $element->getChild(1)->getValueValue(); $x = 0; $y = 0; switch ($element->getChild(1)->getValueToken()) { case 'zero_or_one': $y = 1; break; case 'zero_or_more': $y = mt_rand(5, 8); // why not? break; case 'one_or_more': $x = 1; $y = mt_rand(5, 8); // why not? break; case 'exactly_n': $x = $y = (int) substr($xy, 1, -1); break; case 'n_to_m': $xy = explode(',', substr($xy, 1, -1)); $x = (int) trim($xy[0]); $y = (int) trim($xy[1]); break; case 'n_or_more': $xy = explode(',', substr($xy, 1, -1)); $x = (int) trim($xy[0]); $y = mt_rand($x + 5, $x + 8); // why not? break; } for ( $i = 0, $max = $this->_sampler->getInteger($x, $y); $i < $max; ++$i ) { $out .= $element->getChild(0)->accept( $this, $handle, $eldnah ); } return $out; case '#negativeclass': $c = []; foreach ($element->getChildren() as $child) { $c[Ustring::toCode( $child->accept($this, $handle, $eldnah) )] = true; } do { // all printable ASCII. $i = $this->_sampler->getInteger(32, 126); } while (isset($c[$i])); return Ustring::fromCode($i); case '#range': $out = null; $left = $element->getChild(0)->accept($this, $handle, $eldnah); $right = $element->getChild(1)->accept($this, $handle, $eldnah); return Ustring::fromCode( $this->_sampler->getInteger( Ustring::toCode($left), Ustring::toCode($right) ) ); case 'token': $value = $element->getValueValue(); switch ($element->getValueToken()) { case 'character': $value = ltrim($value, '\\'); switch ($value) { case 'a': return "\a"; case 'e': return "\e"; case 'f': return "\f"; case 'n': return "\n"; case 'r': return "\r"; case 't': return "\t"; default: return Ustring::fromCode( intval( substr($value, 1) ) ); } break; case 'dynamic_character': $value = ltrim($value, '\\'); switch ($value[0]) { case 'x': $value = trim($value, 'x{}'); return Ustring::fromCode( hexdec($value) ); default: return Ustring::fromCode(octdec($value)); } break; case 'character_type': $value = ltrim($value, '\\'); if ('s' === $value) { $value = $this->_sampler->getInteger(0, 1) ? 'h' : 'v'; } switch ($value) { case 'C': return $this->_sampler->getInteger(0, 127); case 'd': return $this->_sampler->getInteger(0, 9); case 'h': $h = [ Ustring::fromCode(0x0009), Ustring::fromCode(0x0020), Ustring::fromCode(0x00a0) ]; return $h[$this->_sampler->getInteger(0, count($h) -1)]; case 'v': $v = [ Ustring::fromCode(0x000a), Ustring::fromCode(0x000b), Ustring::fromCode(0x000c), Ustring::fromCode(0x000d) ]; return $v[$this->_sampler->getInteger(0, count($v) -1)]; case 'w': $w = array_merge( range(0x41, 0x5a), range(0x61, 0x7a), [0x5f] ); return Ustring::fromCode($w[$this->_sampler->getInteger(0, count($w) - 1)]); default: return '?'; } break; case 'literal': if ('.' === $value) { $w = array_merge( range(0x41, 0x5a), range(0x61, 0x7a), [0x5f] ); return Ustring::fromCode($w[$this->_sampler->getInteger(0, count($w) - 1)]); } return str_replace( '\\\\', '\\', preg_replace( '#\\\(?!\\\)#', '', $value ) ); } break; case '#internal_options': break; default: throw new Regex\Exception( 'Unsupported node: %s.', 0, $element->getId() ); } return; } }