Permalink
Browse files

[DocParser] added support for constants

  • Loading branch information...
1 parent fc26d10 commit 03949c31cf9cea931fd8ac318c10e339db470ca7 @FabioBatSilva committed Feb 5, 2012
@@ -53,6 +53,22 @@ public static function semanticalError($message)
}
/**
+ * Creates a new AnnotationException describing a constant semantical error.
+ *
+ * @since 2.3
+ * @param string $identifier
+ * @param string $context
+ * @return AnnotationException
+ */
+ public static function semanticalErrorConstants($identifier, $context = null)
+ {
+ return self::semanticalError(sprintf(
+ "Couldn't find constant %s%s", $identifier,
+ $context ? ", $context." : "."
+ ));
+ }
+
+ /**
* Creates a new AnnotationException describing an error which occurred during
* the creation of the annotation.
*
@@ -33,11 +33,12 @@
final class DocLexer extends Lexer
{
const T_NONE = 1;
- const T_IDENTIFIER = 2;
- const T_INTEGER = 3;
- const T_STRING = 4;
- const T_FLOAT = 5;
+ const T_INTEGER = 2;
+ const T_STRING = 3;
+ const T_FLOAT = 4;
+ // All tokens that are also identifiers should be >= 100
+ const T_IDENTIFIER = 100;
const T_AT = 101;
const T_CLOSE_CURLY_BRACES = 102;
const T_CLOSE_PARENTHESIS = 103;
@@ -57,7 +58,7 @@
protected function getCatchablePatterns()
{
return array(
- '[a-z_][a-z0-9_:]*',
+ '[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}',
'(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
'"(?:[^"]|"")*"',
);
@@ -127,7 +128,7 @@ protected function getType(&$value)
return self::T_COLON;
default:
- if (ctype_alpha($value[0]) || $value[0] === '_') {
+ if (ctype_alpha($value[0]) || $value[0] === '_' || $value[0] === '\\') {
return self::T_IDENTIFIER;
}
@@ -537,20 +537,7 @@ private function Annotation()
$this->match(DocLexer::T_AT);
// check if we have an annotation
- if ($this->lexer->isNextTokenAny(self::$classIdentifiers)) {
- $this->lexer->moveNext();
- $name = $this->lexer->token['value'];
- } else if ($this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) {
- $name = '';
- } else {
- $this->syntaxError('namespace separator or identifier');
- }
-
- while ($this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value']) && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) {
- $this->match(DocLexer::T_NAMESPACE_SEPARATOR);
- $this->matchAny(self::$classIdentifiers);
- $name .= '\\'.$this->lexer->token['value'];
- }
+ $name = $this->Identifier();
// only process names which are not fully qualified, yet
// fully qualified names must start with a \
@@ -747,6 +734,82 @@ private function Values()
}
/**
+ * Constant ::= integer | string | float | boolean
+ *
+ * @return mixed
+ */
+ private function Constant()
+ {
+ $identifier = $this->Identifier();
+
+ if (!defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) {
+
+ list($className, $const) = explode('::', $identifier);
+ $alias = (false === $pos = strpos($className, '\\'))? $className : substr($className, 0, $pos);
+
+ $found = false;
+ switch (true) {
+ case !empty ($this->namespace):
+ foreach ($this->namespaces as $ns) {
+ if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
+ $className = $ns.'\\'.$className;
+ $found = true;
+ break;
+ }
+ }
+ break;
+
+ case isset($this->imports[$loweredAlias = strtolower($alias)]):
+ $found = true;
+ if (false !== $pos) {
+ $className = $this->imports[$loweredAlias].substr($className, $pos);
+ } else {
+ $className = $this->imports[$loweredAlias];
+ }
+ break;
+
+ default:
+ if(isset($this->imports['__NAMESPACE__'])) {
+ $ns = $this->imports['__NAMESPACE__'];
+ if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
+ $className = $ns.'\\'.$className;
+ $found = true;
+ }
+ }
+ break;
+ }
+
+ if ($found) {
+ $identifier = $className . '::' . $const;
+ }
+ }
+
+ if (!defined($identifier)) {
+ throw AnnotationException::semanticalErrorConstants($identifier, $this->context);
+ }
+
+ return constant($identifier);
+ }
+
+ /**
+ * Identifier ::= string
+ *
+ * @return string
+ */
+ private function Identifier()
+ {
+ // check if we have an annotation
+ if ($this->lexer->isNextTokenAny(self::$classIdentifiers)) {
+ $this->lexer->moveNext();
+ $className = $this->lexer->token['value'];
+ } else {
+ $this->syntaxError('namespace separator or identifier');
+ }
+
+ return $className;
+ }
+
+ /**
* Value ::= PlainValue | FieldAssignment
*
* @return mixed
@@ -777,6 +840,10 @@ private function PlainValue()
return $this->Annotation();
}
+ if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
+ return $this->Constant();
+ }
+
switch ($this->lexer->lookahead['type']) {
case DocLexer::T_STRING:
$this->match(DocLexer::T_STRING);
@@ -838,7 +905,7 @@ private function Arrayx()
$this->match(DocLexer::T_OPEN_CURLY_BRACES);
$values[] = $this->ArrayEntry();
-
+
while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
$this->match(DocLexer::T_COMMA);
@@ -867,8 +934,8 @@ private function Arrayx()
/**
* ArrayEntry ::= Value | KeyValuePair
- * KeyValuePair ::= Key ("=" | ":") PlainValue
- * Key ::= string | integer
+ * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant
+ * Key ::= string | integer | Constant
*
* @return array
*/
@@ -878,9 +945,14 @@ private function ArrayEntry()
if (DocLexer::T_EQUALS === $peek['type']
|| DocLexer::T_COLON === $peek['type']) {
- $this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING));
- $key = $this->lexer->token['value'];
+ if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
+ $key = $this->Constant();
+ } else {
+ $this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING));
+ $key = $this->lexer->token['value'];
+ }
+
$this->matchAny(array(DocLexer::T_EQUALS, DocLexer::T_COLON));
return array($key, $this->PlainValue());
@@ -24,4 +24,72 @@ public function testMarkerAnnotation()
$this->assertFalse($lexer->moveNext());
}
+
+ public function testScannerTokenizesDocBlockWhitConstants()
+ {
+ $lexer = new DocLexer();
+ $docblock = '@AnnotationWithConstants(PHP_EOL, ClassWithConstants::SOME_VALUE, \Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_VALUE)';
+
+ $tokens = array (
+ array(
+ 'value' => '@',
+ 'position' => 0,
+ 'type' => DocLexer::T_AT,
+ ),
+ array(
+ 'value' => 'AnnotationWithConstants',
+ 'position' => 1,
+ 'type' => DocLexer::T_IDENTIFIER,
+ ),
+ array(
+ 'value' => '(',
+ 'position' => 24,
+ 'type' => DocLexer::T_OPEN_PARENTHESIS,
+ ),
+ array(
+ 'value' => 'PHP_EOL',
+ 'position' => 25,
+ 'type' => DocLexer::T_IDENTIFIER,
+ ),
+ array(
+ 'value' => ',',
+ 'position' => 32,
+ 'type' => DocLexer::T_COMMA,
+ ),
+ array(
+ 'value' => 'ClassWithConstants::SOME_VALUE',
+ 'position' => 34,
+ 'type' => DocLexer::T_IDENTIFIER,
+ ),
+ array(
+ 'value' => ',',
+ 'position' => 64,
+ 'type' => DocLexer::T_COMMA,
+ ),
+ array(
+ 'value' => '\\Doctrine\\Tests\\Common\\Annotations\\Fixtures\\IntefaceWithConstants::SOME_VALUE',
+ 'position' => 66,
+ 'type' => DocLexer::T_IDENTIFIER,
+ ),
+ array(
+ 'value' => ')',
+ 'position' => 143,
+ 'type' => DocLexer::T_CLOSE_PARENTHESIS,
+ )
+
+ );
+
+ $lexer->setInput($docblock);
+
+ foreach ($tokens as $expected) {
+ $lexer->moveNext();
+ $lookahead = $lexer->lookahead;
+ $this->assertEquals($expected['value'], $lookahead['value']);
+ $this->assertEquals($expected['type'], $lookahead['type']);
+ $this->assertEquals($expected['position'], $lookahead['position']);
+ }
+
+ $this->assertFalse($lexer->moveNext());
+ }
+
}
Oops, something went wrong.

0 comments on commit 03949c3

Please sign in to comment.