-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add simple content lexer for external tag rules
- Loading branch information
1 parent
89cd39f
commit 4cb76d4
Showing
15 changed files
with
289 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TypeLang\PHPDoc\Tag; | ||
|
||
use TypeLang\Parser\Node\Stmt\TypeStatement; | ||
use TypeLang\Parser\ParserInterface as TypesParserInterface; | ||
use TypeLang\PHPDoc\Exception\InvalidTagException; | ||
use TypeLang\PHPDoc\Parser\Description\DescriptionParserInterface; | ||
use TypeLang\PHPDoc\Tag\Content\OptionalVariableNameApplicator; | ||
use TypeLang\PHPDoc\Tag\Content\TypeParserApplicator; | ||
use TypeLang\PHPDoc\Tag\Content\VariableNameApplicator; | ||
|
||
class Content implements \Stringable | ||
{ | ||
private readonly string $original; | ||
|
||
/** | ||
* @var int<0, max> | ||
* @psalm-readonly-allow-private-mutation | ||
*/ | ||
public int $offset = 0; | ||
|
||
public function __construct( | ||
public string $value, | ||
) { | ||
$this->original = $this->value; | ||
} | ||
|
||
/** | ||
* @param int<0, max> $offset | ||
*/ | ||
public function shift(int $offset, bool $ltrim = true): void | ||
{ | ||
if ($offset <= 0) { | ||
return; | ||
} | ||
|
||
$size = \strlen($this->value); | ||
$this->value = \substr($this->value, $offset); | ||
|
||
if ($ltrim) { | ||
$this->value = \ltrim($this->value); | ||
} | ||
|
||
/** @psalm-suppress InvalidPropertyAssignmentValue */ | ||
$this->offset += $size - \strlen($this->value); | ||
} | ||
|
||
public function getTagException(string $message, \Throwable $previous = null): InvalidTagException | ||
{ | ||
return new InvalidTagException( | ||
source: $this->original, | ||
offset: $this->offset, | ||
message: $message, | ||
previous: $previous, | ||
); | ||
} | ||
|
||
/** | ||
* @api | ||
* @param non-empty-string $tag | ||
*/ | ||
public function nextType(string $tag, TypesParserInterface $parser): TypeStatement | ||
{ | ||
return $this->apply(new TypeParserApplicator($tag, $parser)); | ||
} | ||
|
||
/** | ||
* @api | ||
* @param non-empty-string $tag | ||
* @return non-empty-string | ||
*/ | ||
public function nextVariable(string $tag): string | ||
{ | ||
return $this->apply(new VariableNameApplicator($tag)); | ||
} | ||
|
||
/** | ||
* @api | ||
* @return non-empty-string|null | ||
*/ | ||
public function nextOptionalVariable(): ?string | ||
{ | ||
return $this->apply(new OptionalVariableNameApplicator()); | ||
} | ||
|
||
/** | ||
* @template T of mixed | ||
* @param callable(Content):T $applicator | ||
* @return T | ||
*/ | ||
public function apply(callable $applicator): mixed | ||
{ | ||
return $applicator($this); | ||
} | ||
|
||
public function toDescription(DescriptionParserInterface $descriptions): DescriptionInterface | ||
{ | ||
return $descriptions->parse($this->value); | ||
} | ||
|
||
public function __toString(): string | ||
{ | ||
return $this->value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TypeLang\PHPDoc\Tag\Content; | ||
|
||
use TypeLang\PHPDoc\Tag\Content; | ||
|
||
/** | ||
* @template T of mixed | ||
*/ | ||
abstract class Applicator | ||
{ | ||
/** | ||
* @return T | ||
*/ | ||
abstract public function __invoke(Content $lexer): mixed; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TypeLang\PHPDoc\Tag\Content; | ||
|
||
use TypeLang\PHPDoc\Tag\Content; | ||
|
||
/** | ||
* @template-extends Applicator<non-empty-string|null> | ||
*/ | ||
final class OptionalVariableNameApplicator extends Applicator | ||
{ | ||
/** | ||
* @return non-empty-string|null | ||
*/ | ||
public function __invoke(Content $lexer): ?string | ||
{ | ||
if (!\str_starts_with($lexer->value, '$')) { | ||
return null; | ||
} | ||
|
||
\preg_match('/\$([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\b/u', $lexer->value, $matches); | ||
|
||
if (\count($matches) !== 2 || $matches[1] === '') { | ||
return null; | ||
} | ||
|
||
$lexer->shift(\strlen($matches[0])); | ||
|
||
return $matches[1]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TypeLang\PHPDoc\Tag\Content; | ||
|
||
use TypeLang\Parser\Exception\ParserExceptionInterface; | ||
use TypeLang\Parser\Node\Stmt\TypeStatement; | ||
use TypeLang\Parser\ParserInterface as TypesParserInterface; | ||
use TypeLang\PHPDoc\Exception\InvalidTagException; | ||
use TypeLang\PHPDoc\Tag\Content; | ||
|
||
/** | ||
* @template-extends Applicator<TypeStatement> | ||
*/ | ||
final class TypeParserApplicator extends Applicator | ||
{ | ||
/** | ||
* @param non-empty-string $tag | ||
*/ | ||
public function __construct( | ||
private readonly string $tag, | ||
private readonly TypesParserInterface $parser, | ||
) {} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @throws \Throwable | ||
* @throws InvalidTagException | ||
*/ | ||
public function __invoke(Content $lexer): TypeStatement | ||
{ | ||
try { | ||
/** @var TypeStatement $type */ | ||
$type = $this->parser->parse($lexer->value); | ||
} catch (ParserExceptionInterface $e) { | ||
/** @psalm-suppress InvalidArgument */ | ||
throw $lexer->getTagException( | ||
message: \sprintf('Tag @%s contains an incorrect type', $this->tag), | ||
previous: $e, | ||
); | ||
} | ||
|
||
/** | ||
* @psalm-suppress MixedArgument | ||
*/ | ||
$lexer->shift($this->parser->lastProcessedTokenOffset); | ||
|
||
return $type; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace TypeLang\PHPDoc\Tag\Content; | ||
|
||
use TypeLang\PHPDoc\Exception\InvalidTagException; | ||
use TypeLang\PHPDoc\Tag\Content; | ||
|
||
/** | ||
* @template-extends Applicator<non-empty-string> | ||
*/ | ||
final class VariableNameApplicator extends Applicator | ||
{ | ||
private readonly OptionalVariableNameApplicator $var; | ||
|
||
/** | ||
* @param non-empty-string $tag | ||
*/ | ||
public function __construct( | ||
private readonly string $tag, | ||
) { | ||
$this->var = new OptionalVariableNameApplicator(); | ||
} | ||
|
||
/** | ||
* @return non-empty-string | ||
* | ||
* @throws InvalidTagException | ||
*/ | ||
public function __invoke(Content $lexer): string | ||
{ | ||
return ($this->var)($lexer) | ||
?? throw $lexer->getTagException(\sprintf( | ||
'Tag @%s contains an incorrect variable name', | ||
$this->tag, | ||
)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.