* This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **/ class CompositeMatcher { protected $matchers = array(); private $wasConstructed = false; public function add($matcher) { $this->matchers[] = $matcher; } public function matches($string, &$value) { if(!$this->wasConstructed) { $this->build(); $this->wasConstructed = true; } return $this->match($string, $value); } protected function build() {} } class ParallelMatcher extends CompositeMatcher { protected function match($string, &$value) { $maxLength = false; $result = null; foreach($this->matchers as $matcher) { $length = $matcher->matches($string, $subvalue); if($maxLength === false || $length > $maxLength) { $maxLength = $length; $result = $subvalue; } } $value = $this->process($result); return $maxLength; } protected function process($value) { return $value; } } class SerialMatcher extends CompositeMatcher { protected function match($string, &$value) { $results = array(); $total_length = 0; foreach($this->matchers as $matcher) { if(($length = $matcher->matches($string, $result)) === false) return false; $total_length += $length; $results[] = $result; $string = substr($string, $length); } $value = $this->process($results); return $total_length; } protected function process($results) { return implode('', $results); } } class SimpleSerialMatcher extends SerialMatcher { private $return_part_index; public function __construct($return_part_index = 0) { $this->return_part_index = $return_part_index; } public function process($parts) { return $parts[$this->return_part_index]; } } class RegexMatcher { private $regex; public function __construct($regex) { $this->regex = $regex; } public function matches($string, &$value) { if(preg_match("/^{$this->regex}/", $string, $matches)) { $value = $this->process($matches); return strlen($matches[0]); } $value = false; return false; } protected function process($matches) { return $matches[0]; } } class AnnotationsMatcher { public function matches($string, &$annotations) { $annotations = array(); $annotation_matcher = new AnnotationMatcher; while(true) { if(preg_match('/\s(?=@)/', $string, $matches, PREG_OFFSET_CAPTURE)) { $offset = $matches[0][1] + 1; $string = substr($string, $offset); } else { return; // no more annotations } if(($length = $annotation_matcher->matches($string, $data)) !== false) { $string = substr($string, $length); list($name, $params) = $data; $annotations[$name][] = $params; } } } } class AnnotationMatcher extends SerialMatcher { protected function build() { $this->add(new RegexMatcher('@')); $this->add(new RegexMatcher('[A-Z][a-zA-Z0-9_]+')); $this->add(new AnnotationParametersMatcher); } protected function process($results) { return array($results[1], $results[2]); } } class ConstantMatcher extends RegexMatcher { private $constant; public function __construct($regex, $constant) { parent::__construct($regex); $this->constant = $constant; } protected function process($matches) { return $this->constant; } } class AnnotationParametersMatcher extends ParallelMatcher { protected function build() { $this->add(new ConstantMatcher('', array())); $this->add(new ConstantMatcher('\(\)', array())); $params_matcher = new SimpleSerialMatcher(1); $params_matcher->add(new RegexMatcher('\(\s*')); $params_matcher->add(new AnnotationValuesMatcher); $params_matcher->add(new RegexMatcher('\s*\)')); $this->add($params_matcher); } } class AnnotationValuesMatcher extends ParallelMatcher { protected function build() { $this->add(new AnnotationTopValueMatcher); $this->add(new AnnotationHashMatcher); } } class AnnotationTopValueMatcher extends AnnotationValueMatcher { protected function process($value) { return array('value' => $value); } } class AnnotationValueMatcher extends ParallelMatcher { protected function build() { $this->add(new ConstantMatcher('true', true)); $this->add(new ConstantMatcher('false', false)); $this->add(new ConstantMatcher('TRUE', true)); $this->add(new ConstantMatcher('FALSE', false)); $this->add(new AnnotationStringMatcher); $this->add(new AnnotationNumberMatcher); $this->add(new AnnotationArrayMatcher); } } class AnnotationKeyMatcher extends ParallelMatcher { protected function build() { $this->add(new RegexMatcher('[a-zA-Z][a-zA-Z0-9_]*')); $this->add(new AnnotationStringMatcher); $this->add(new AnnotationIntegerMatcher); } } class AnnotationPairMatcher extends SerialMatcher { protected function build() { $this->add(new AnnotationKeyMatcher); $this->add(new RegexMatcher('\s*=\s*')); $this->add(new AnnotationValueMatcher); } protected function process($parts) { return array($parts[0] => $parts[2]); } } class AnnotationHashMatcher extends ParallelMatcher { protected function build() { $this->add(new AnnotationPairMatcher); $this->add(new AnnotationMorePairsMatcher); } } class AnnotationMorePairsMatcher extends SerialMatcher { protected function build() { $this->add(new AnnotationPairMatcher); $this->add(new RegexMatcher('\s*,\s*')); $this->add(new AnnotationHashMatcher); } protected function match($string, &$value) { $result = parent::match($string, $value); return $result; } public function process($parts) { return array_merge($parts[0], $parts[2]); } } class AnnotationArrayMatcher extends ParallelMatcher { protected function build() { $this->add(new ConstantMatcher('{}', array())); $values_matcher = new SimpleSerialMatcher(1); $values_matcher->add(new RegexMatcher('\s*{\s*')); $values_matcher->add(new AnnotationArrayValuesMatcher); $values_matcher->add(new RegexMatcher('\s*}\s*')); $this->add($values_matcher); } } class AnnotationArrayValuesMatcher extends ParallelMatcher { protected function build() { $this->add(new AnnotationArrayValueMatcher); $this->add(new AnnotationMoreValuesMatcher); } } class AnnotationMoreValuesMatcher extends SimpleSerialMatcher { protected function build() { $this->add(new AnnotationArrayValueMatcher); $this->add(new RegexMatcher('\s*,\s*')); $this->add(new AnnotationArrayValuesMatcher); } protected function match($string, &$value) { $result = parent::match($string, $value); return $result; } public function process($parts) { return array_merge($parts[0], $parts[2]); } } class AnnotationArrayValueMatcher extends ParallelMatcher { protected function build() { $this->add(new AnnotationValueInArrayMatcher); $this->add(new AnnotationPairMatcher); } } class AnnotationValueInArrayMatcher extends AnnotationValueMatcher { public function process($value) { return array($value); } } class AnnotationStringMatcher extends ParallelMatcher { protected function build() { $this->add(new AnnotationSingleQuotedStringMatcher); $this->add(new AnnotationDoubleQuotedStringMatcher); } } class AnnotationNumberMatcher extends RegexMatcher { public function __construct() { parent::__construct("-?[0-9]*\.?[0-9]*"); } protected function process($matches) { $isFloat = strpos($matches[0], '.') !== false; return $isFloat ? (float) $matches[0] : (int) $matches[0]; } } class AnnotationIntegerMatcher extends RegexMatcher { public function __construct() { parent::__construct("-?[0-9]*"); } protected function process($matches) { return (int) $matches[0]; } } class AnnotationSingleQuotedStringMatcher extends RegexMatcher { public function __construct() { parent::__construct("'([^']*)'"); } protected function process($matches) { return $matches[1]; } } class AnnotationDoubleQuotedStringMatcher extends RegexMatcher { public function __construct() { parent::__construct('"([^"]*)"'); } protected function process($matches) { return $matches[1]; } } ?>