Skip to content

Commit

Permalink
Allow using enums as a token type
Browse files Browse the repository at this point in the history
Enums allow to avoid having to come up with values to assign for each
name (unless you use a backed enum), and will provide more type safety.
  • Loading branch information
greg0ire committed Nov 28, 2022
1 parent 3086f6d commit e7d0960
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 4 deletions.
18 changes: 15 additions & 3 deletions lib/Doctrine/Common/Lexer/AbstractLexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
namespace Doctrine\Common\Lexer;

use ReflectionClass;
use UnitEnum;

use function assert;
use function get_class;
use function implode;
use function is_int;
use function is_string;
use function preg_split;
use function sprintf;
use function substr;
Expand All @@ -18,7 +23,7 @@
/**
* Base class for writing simple lexers, i.e. for creating small DSLs.
*
* @template T of string|int
* @template T of UnitEnum|string|int
*/
abstract class AbstractLexer
{
Expand Down Expand Up @@ -275,13 +280,18 @@ protected function scan($input)
/**
* Gets the literal for a given token.
*
* @param int|string $token
* @param T $token
*
* @return int|string
*/
public function getLiteral($token)
{
$className = static::class;
if ($token instanceof UnitEnum) {
$className = get_class($token);
} else {
$className = static::class;
}

$reflClass = new ReflectionClass($className);
$constants = $reflClass->getConstants();

Expand All @@ -291,6 +301,8 @@ public function getLiteral($token)
}
}

assert(is_string($token) || is_int($token));

return $token;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/Doctrine/Common/Lexer/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
use Countable;
use Doctrine\Deprecations\Deprecation;
use ReturnTypeWillChange;
use UnitEnum;

use function in_array;

/**
* @template T of string|int
* @template T of UnitEnum|string|int
* @implements ArrayAccess<string,mixed>
*/
final class Token implements ArrayAccess, Countable
Expand Down
9 changes: 9 additions & 0 deletions tests/Doctrine/Common/Lexer/AbstractLexerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@ public function testGetLiteral(): void
$this->assertSame('fake_token', $this->concreteLexer->getLiteral('fake_token'));
}

public function testGetLiteralWithEnumLexer(): void
{
$enumLexer = new EnumLexer();
$this->assertSame(
'Doctrine\Tests\Common\Lexer\TokenType::OPERATOR',
$enumLexer->getLiteral(TokenType::OPERATOR)
);
}

public function testIsA(): void
{
$this->assertTrue($this->concreteLexer->isA(11, 'int'));
Expand Down
58 changes: 58 additions & 0 deletions tests/Doctrine/Common/Lexer/EnumLexer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Common\Lexer;

use Doctrine\Common\Lexer\AbstractLexer;

use function in_array;
use function is_numeric;
use function is_string;

/** @extends AbstractLexer<TokenType> */
class EnumLexer extends AbstractLexer
{
/**
* {@inheritDoc}
*/
protected function getCatchablePatterns()
{
return [
'=|<|>',
'[a-z]+',
'\d+',
];
}

/**
* {@inheritDoc}
*/
protected function getNonCatchablePatterns()
{
return [
'\s+',
'(.)',
];
}

/**
* {@inheritDoc}
*/
protected function getType(&$value)
{
if (is_numeric($value)) {
$value = (int) $value;

return TokenType::INT;
}

if (in_array($value, ['=', '<', '>'])) {
return TokenType::OPERATOR;
}

if (is_string($value)) {
return TokenType::STRING;
}
}
}
12 changes: 12 additions & 0 deletions tests/Doctrine/Common/Lexer/TokenType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Common\Lexer;

enum TokenType
{
case INT;
case OPERATOR;
case STRING;
}

0 comments on commit e7d0960

Please sign in to comment.