Skip to content

Commit

Permalink
Merge pull request #11 from longitude-one/lexer-3-feature
Browse files Browse the repository at this point in the history
Lexer 3 feature
  • Loading branch information
Alexandre-T authored Mar 20, 2024
2 parents ff20faf + 0470aee commit fe717bf
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 77 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"license": "MIT",
"require": {
"php": "^8.1",
"doctrine/lexer": "^2.1"
"doctrine/lexer": "^2.1|^3.0"
},
"require-dev": {
"phpunit/phpunit": "~10.0"
Expand Down
15 changes: 3 additions & 12 deletions lib/LongitudeOne/Geo/String/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/**
* Tokenize geographic coordinates.
*
* @extends AbstractLexer<int, int|float|string>
* @extends AbstractLexer<int, int|string>
*/
class Lexer extends AbstractLexer
{
Expand All @@ -35,19 +35,10 @@ class Lexer extends AbstractLexer
public const T_PLUS = 8;
public const T_QUOTE = 13;

/**
* @param string|null $input
*/
public function __construct($input = null)
public function __construct(?string $input = null)
{
if (!is_null($input) && !is_string($input)) {
trigger_error(sprintf(
'Passing a non-string "%s" value in LongitudeOne\Geo\String\Lexer::__construct() is deprecated',
$input), E_USER_DEPRECATED);
}

if (null !== $input) {
$this->setInput((string) $input);
$this->setInput($input);
}
}

Expand Down
48 changes: 18 additions & 30 deletions lib/LongitudeOne/Geo/String/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,11 @@ class Parser
* Constructor.
*
* Setup up instance properties
*
* @param string|float|int|null $input
*/
public function __construct($input = null)
public function __construct(string|int|null $input = null)
{
$this->lexer = new Lexer();

if (is_float($input)) {
trigger_error('Since longitudeone/geo-parser 2.1: Passing a float to LongitudeOne\Geo\String\Parser::__construct() is deprecated. Use a string instead.', E_USER_DEPRECATED);
}

if (null !== $input) {
$this->input = (string) $input;
}
Expand All @@ -68,14 +62,10 @@ public function __construct($input = null)
/**
* Parse input string.
*
* @param int|float|string|null $input
* @return float|int|array<int|float>
*/
public function parse($input = null): float|int|array
public function parse(string|int|null $input = null): float|int|array
{
if (is_float($input)) {
trigger_error('Since longitudeone/geo-parser 2.1: Passing a float to LongitudeOne\Geo\String\Parser::parse() is deprecated. Use a string instead.', E_USER_DEPRECATED);
}

if (null !== $input) {
$this->input = (string) $input;
}
Expand Down Expand Up @@ -202,6 +192,7 @@ private function degrees(): float|int
}

// If degrees isn't a float, it must be an integer
/** @var int $degrees */
$degrees = $this->number();

// If a symbol does not follow integer, this value is complete
Expand All @@ -218,7 +209,7 @@ private function degrees(): float|int
}

// Add minutes to value
$degrees += $this->minutes();
$degrees += (float) $this->minutes();

// Return value
return $degrees;
Expand All @@ -227,20 +218,19 @@ private function degrees(): float|int
/**
* Match token and return value.
*/
private function match(int $token): string|float|int
private function match(int $token): string|int
{
// If the next token isn't type specified throw error
if (!$this->lexer->isNextToken($token)) {
throw $this->syntaxError($this->lexer->getLiteral($token));
throw $this->syntaxError((string) $this->lexer->getLiteral($token));
}

// Move lexer to the next token
$this->lexer->moveNext();

/** @var Token<int, string|int|float> $nextToken nextToken cannot be null, because of the above test. */
/** @var Token<int, string|int> $nextToken nextToken cannot be null, because of the above test. */
$nextToken = $this->lexer->token;

// FIXME We currently return string|int|float and this is not good. We should return only string|int.
return $nextToken->value;
}

Expand All @@ -249,7 +239,7 @@ private function match(int $token): string|float|int
*
* @throws RangeException
*/
private function minutes(): float|int
private function minutes(): string|int
{
// If using colon or minutes is an integer parse value
if (Lexer::T_COLON === $this->nextSymbol || $this->lexer->isNextToken(Lexer::T_INTEGER)) {
Expand All @@ -266,22 +256,18 @@ private function minutes(): float|int

// If using colon and one doesn't follow value is done
if (Lexer::T_COLON === $this->nextSymbol && !$this->lexer->isNextToken(Lexer::T_COLON)) {
return $minutes;
return (string) $minutes;
}

// Match minutes symbol
$this->symbol();

// Add seconds to value
$minutes += $this->seconds();

// Return value
return $minutes;
// Add seconds to value, then return the result.
return (string) ((float) $minutes + (float) $this->seconds());
}

// If minutes is a float there will be no seconds
if ($this->lexer->isNextToken(Lexer::T_FLOAT)) {
/** @var float $minutes */
$minutes = $this->match(Lexer::T_FLOAT);

// Throw exception if minutes are greater than 60
Expand All @@ -290,7 +276,7 @@ private function minutes(): float|int
}

// Get fractional minutes
$minutes /= 60;
$minutes = (string) ((float) $minutes / 60);

// Match minutes symbol
$this->symbol();
Expand All @@ -308,7 +294,7 @@ private function minutes(): float|int
*
* @throws UnexpectedValueException
*/
private function number(): int|float
private function number(): int|string
{
// If the next token is a float match, then return it
if ($this->lexer->isNextToken(Lexer::T_FLOAT)) {
Expand All @@ -327,6 +313,8 @@ private function number(): int|float
/**
* Match and return single value or pair.
*
* @return float|int|array<int|float>
*
* @throws UnexpectedValueException
*/
private function point(): float|int|array
Expand Down Expand Up @@ -372,7 +360,7 @@ private function rangeError(string $type, int $high, ?int $low = null): RangeExc
*
* @throws RangeException
*/
private function seconds(): int|float
private function seconds(): int|string
{
// Seconds value can be an integer or float
if ($this->lexer->isNextTokenAny([Lexer::T_INTEGER, Lexer::T_FLOAT])) {
Expand All @@ -384,7 +372,7 @@ private function seconds(): int|float
}

// Get fractional seconds
$seconds /= 3600;
$seconds = (string) ((float) $seconds / 3600);

// Match seconds symbol if requirement not colon
if (Lexer::T_COLON !== $this->nextSymbol) {
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
displayDetailsOnTestsThatTriggerDeprecations="true"
colors="true" bootstrap="./vendor/autoload.php" cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="Tests">
Expand Down
31 changes: 3 additions & 28 deletions quality/php-stan/phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,41 +1,16 @@
parameters:
ignoreErrors:
-
message: "#^Result of && is always false\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Lexer.php

-
message: "#^Type float\\|int\\|string in generic type Doctrine\\\\Common\\\\Lexer\\\\AbstractLexer\\<int, float\\|int\\|string\\> in PHPDoc tag @extends is not subtype of template type V of int\\|string of class Doctrine\\\\Common\\\\Lexer\\\\AbstractLexer\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Lexer.php

-
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:number\\(\\) should return float\\|int but returns float\\|int\\|string\\.$#"
count: 2
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:point\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Parameter \\#1 \\$expected of method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:syntaxError\\(\\) expects string, int\\|string given\\.$#"
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:point\\(\\) never returns array\\<float\\|int\\> so it can be removed from the return type\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Type float\\|int\\|string in generic type Doctrine\\\\Common\\\\Lexer\\\\Token\\<int, float\\|int\\|string\\> in PHPDoc tag @var for variable \\$nextToken is not subtype of template type V of int\\|string of class Doctrine\\\\Common\\\\Lexer\\\\Token\\.$#"
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Tests\\\\LexerTest\\:\\:dataProvider\\(\\) return type with generic class Doctrine\\\\Common\\\\Lexer\\\\Token does not specify its types\\: T, V$#"
count: 1
path: ../../tests/LongitudeOne/Geo/String/Tests/LexerTest.php
path: ../../tests/LongitudeOne/Geo/String/Tests/LexerTest.php
1 change: 1 addition & 0 deletions tests/LongitudeOne/Geo/String/Tests/LexerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
class LexerTest extends TestCase
{
/**
* @see https://stackoverflow.com/questions/78159317/how-to-typehint-this-generator
* return Generator<array{string, Token<int, int|string>[]}>.
*
* @return \Generator<array{string, Token[]}>
Expand Down
12 changes: 6 additions & 6 deletions tests/LongitudeOne/Geo/String/Tests/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ public static function dataSourceGood(): \Generator
yield ['40° 26\' N, 79° 58\' W', [40.4333333333333333, -79.96666666666666669]];
yield ['40.4738° N, 79.553° W', [40.4738, -79.553]];
yield ['40.4738° S, 79.553° W', [-40.4738, -79.553]];
yield ['40° 26.222\' N 79° 58.52\' E', [40.43703333333333, 79.97533333333334]];
yield ['40°26.222\'N 79°58.52\'E', [40.43703333333333, 79.97533333333334]];
yield ['40°26.222\' 79°58.52\'', [40.43703333333333, 79.97533333333334]];
yield ['40° 26.222\' N 79° 58.52\' E', [40.43703333333333, 79.97533333333332]];
yield ['40°26.222\'N 79°58.52\'E', [40.43703333333333, 79.97533333333332]];
yield ['40°26.222\' 79°58.52\'', [40.43703333333333, 79.97533333333332]];
yield ['40.222° -79.5852°', [40.222, -79.5852]];
yield ['40.222°, -79.5852°', [40.222, -79.5852]];
yield ['44°58\'53.9"N 93°19\'25.8"W', [44.98163888888888888, -93.3238333333333334]];
yield ['44°58\'53.9"N, 93°19\'25.8"W', [44.98163888888888888, -93.3238333333333334]];
yield ['44°58\'53.9"N 93°19\'25.7"W', [44.98163888888888888, -93.32380555555557]];
yield ['44°58\'53.9"N, 93°19\'25.7"W', [44.98163888888888888, -93.32380555555557]];
yield ['79:56:55W 40:26:46N', [-79.94861111111111, 40.44611111111111]];
yield ['79:56:55 W, 40:26:46 N', [-79.94861111111111, 40.44611111111111]];
yield ['79°56′55″W, 40°26′46″N', [-79.94861111111111, 40.44611111111111]];
Expand All @@ -135,7 +135,7 @@ public function testBadValues(string $input, string $exception, string $message)
}

/**
* @param int|float|float[]|int[] $expected
* @param int|float|array<int|float> $expected
*
* @dataProvider dataSourceGood
*/
Expand Down

0 comments on commit fe717bf

Please sign in to comment.