From f53a5a5cd5318fe3bd5df45413cd44311d9237f9 Mon Sep 17 00:00:00 2001 From: Saif Eddin Gmati <29315886+azjezz@users.noreply.github.com> Date: Sun, 15 Jan 2023 17:37:00 +0100 Subject: [PATCH] feat(type): add signed integer types (#394) --- CHANGELOG.md | 18 +++++++ docs/component/type.md | 4 ++ src/Psl/Internal/Loader.php | 8 ++++ src/Psl/Math/constants.php | 2 +- src/Psl/Type/Internal/I16Type.php | 77 ++++++++++++++++++++++++++++++ src/Psl/Type/Internal/I32Type.php | 78 +++++++++++++++++++++++++++++++ src/Psl/Type/Internal/I64Type.php | 70 +++++++++++++++++++++++++++ src/Psl/Type/Internal/I8Type.php | 77 ++++++++++++++++++++++++++++++ src/Psl/Type/Internal/VecType.php | 2 +- src/Psl/Type/i16.php | 15 ++++++ src/Psl/Type/i32.php | 15 ++++++ src/Psl/Type/i64.php | 15 ++++++ src/Psl/Type/i8.php | 15 ++++++ tests/unit/Type/I16TypeTest.php | 67 ++++++++++++++++++++++++++ tests/unit/Type/I32TypeTest.php | 67 ++++++++++++++++++++++++++ tests/unit/Type/I64TypeTest.php | 67 ++++++++++++++++++++++++++ tests/unit/Type/I8TypeTest.php | 64 +++++++++++++++++++++++++ 17 files changed, 659 insertions(+), 2 deletions(-) create mode 100644 src/Psl/Type/Internal/I16Type.php create mode 100644 src/Psl/Type/Internal/I32Type.php create mode 100644 src/Psl/Type/Internal/I64Type.php create mode 100644 src/Psl/Type/Internal/I8Type.php create mode 100644 src/Psl/Type/i16.php create mode 100644 src/Psl/Type/i32.php create mode 100644 src/Psl/Type/i64.php create mode 100644 src/Psl/Type/i8.php create mode 100644 tests/unit/Type/I16TypeTest.php create mode 100644 tests/unit/Type/I32TypeTest.php create mode 100644 tests/unit/Type/I64TypeTest.php create mode 100644 tests/unit/Type/I8TypeTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 85b906d9..df736ca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 3.0.0 + +### features + +* introduced a new `Psl\Range` component - by @azjezz +* introduced a new `Psl\Str\range` function - by @azjezz +* introduced a new `Psl\Str\Byte\range` function - by @azjezz +* introduced a new `Psl\Str\Grapheme\range` function - by @azjezz +* introduced a new `Psl\Type\uint` function - by @azjezz +* introduced a new `Psl\Type\i8` function - by @azjezz +* introduced a new `Psl\Type\i16` function - by @azjezz +* introduced a new `Psl\Type\i32` function - by @azjezz +* introduced a new `Psl\Type\i64` function - by @azjezz + +### deprecations + +* deprecated `Psl\Type\positive_int` function, use `Psl\Type\uint` instead - by @azjezz + ## 2.1.0 ### features diff --git a/docs/component/type.md b/docs/component/type.md index 638caa1f..557fd769 100644 --- a/docs/component/type.md +++ b/docs/component/type.md @@ -17,6 +17,10 @@ - [bool](./../../src/Psl/Type/bool.php#L10) - [dict](./../../src/Psl/Type/dict.php#L16) - [float](./../../src/Psl/Type/float.php#L10) +- [i16](./../../src/Psl/Type/i16.php#L12) +- [i32](./../../src/Psl/Type/i32.php#L12) +- [i64](./../../src/Psl/Type/i64.php#L12) +- [i8](./../../src/Psl/Type/i8.php#L12) - [instance_of](./../../src/Psl/Type/instance_of.php#L14) - [int](./../../src/Psl/Type/int.php#L10) - [intersection](./../../src/Psl/Type/intersection.php#L18) diff --git a/src/Psl/Internal/Loader.php b/src/Psl/Internal/Loader.php index 262c030c..424421d4 100644 --- a/src/Psl/Internal/Loader.php +++ b/src/Psl/Internal/Loader.php @@ -331,6 +331,10 @@ final class Loader 'Psl\\Type\\scalar' => 'Psl/Type/scalar.php', 'Psl\\Type\\shape' => 'Psl/Type/shape.php', 'Psl\\Type\\uint' => 'Psl/Type/uint.php', + 'Psl\\Type\\i8' => 'Psl/Type/i8.php', + 'Psl\\Type\\i16' => 'Psl/Type/i16.php', + 'Psl\\Type\\i32' => 'Psl/Type/i32.php', + 'Psl\\Type\\i64' => 'Psl/Type/i64.php', 'Psl\\Type\\union' => 'Psl/Type/union.php', 'Psl\\Type\\vec' => 'Psl/Type/vec.php', 'Psl\\Type\\dict' => 'Psl/Type/dict.php', @@ -638,6 +642,10 @@ final class Loader 'Psl\\Type\\Internal\\NonEmptyStringType' => 'Psl/Type/Internal/NonEmptyStringType.php', 'Psl\\Type\\Internal\\NonEmptyVecType' => 'Psl/Type/Internal/NonEmptyVecType.php', 'Psl\\Type\\Internal\\UIntType' => 'Psl/Type/Internal/UIntType.php', + 'Psl\\Type\\Internal\\I8Type' => 'Psl/Type/Internal/I8Type.php', + 'Psl\\Type\\Internal\\I16Type' => 'Psl/Type/Internal/I16Type.php', + 'Psl\\Type\\Internal\\I32Type' => 'Psl/Type/Internal/I32Type.php', + 'Psl\\Type\\Internal\\I64Type' => 'Psl/Type/Internal/I64Type.php', 'Psl\\Type\\Internal\\UnionType' => 'Psl/Type/Internal/UnionType.php', 'Psl\\Type\\Internal\\VecType' => 'Psl/Type/Internal/VecType.php', 'Psl\\Type\\Internal\\DictType' => 'Psl/Type/Internal/DictType.php', diff --git a/src/Psl/Math/constants.php b/src/Psl/Math/constants.php index 18ddb704..4c49bd41 100644 --- a/src/Psl/Math/constants.php +++ b/src/Psl/Math/constants.php @@ -96,7 +96,7 @@ * * @var int */ -const INT8_MAX = 128; +const INT8_MAX = 127; /** * The minimum integer value representable in a 8-bit binary-coded decimal. diff --git a/src/Psl/Type/Internal/I16Type.php b/src/Psl/Type/Internal/I16Type.php new file mode 100644 index 00000000..bf49e56b --- /dev/null +++ b/src/Psl/Type/Internal/I16Type.php @@ -0,0 +1,77 @@ + + * + * @extends Type\Type> + * + * @internal + */ +final class I16Type extends Type\Type +{ + /** + * @ara-assert-if-true i16 $value + * + * @psalm-assert-if-true int<-32768, 32767> $value + */ + public function matches(mixed $value): bool + { + return is_int($value) && $value >= Math\INT16_MIN && $value <= MATH\INT16_MAX; + } + + /** + * @throws CoercionException + * + * @ara-return i16 + * + * @return int<-32768, 32767> + */ + public function coerce(mixed $value): int + { + $integer = Type\int() + ->withTrace($this->getTrace()->withFrame($this->toString())) + ->coerce($value); + + if ($integer >= Math\INT16_MIN && $integer <= MATH\INT16_MAX) { + return $integer; + } + + throw CoercionException::withValue($value, $this->toString(), $this->getTrace()); + } + + /** + * @ara-assert i16 $value + * + * @psalm-assert int<-32768, 32767> $value + * + * @throws AssertException + * + * @ara-return i16 + * + * @return int<-32768, 32767> + */ + public function assert(mixed $value): int + { + if (is_int($value) && $value >= Math\INT16_MIN && $value <= MATH\INT16_MAX) { + return $value; + } + + throw AssertException::withValue($value, $this->toString(), $this->getTrace()); + } + + public function toString(): string + { + return 'i16'; + } +} diff --git a/src/Psl/Type/Internal/I32Type.php b/src/Psl/Type/Internal/I32Type.php new file mode 100644 index 00000000..c0e14623 --- /dev/null +++ b/src/Psl/Type/Internal/I32Type.php @@ -0,0 +1,78 @@ + + * + * @extends Type\Type> + * + * @internal + */ +final class I32Type extends Type\Type +{ + /** + * @ara-assert-if-true i32 $value + * + * @psalm-assert-if-true int<-2147483648, 2147483647> $value + */ + public function matches(mixed $value): bool + { + return is_int($value) && $value >= Math\INT32_MIN && $value <= MATH\INT32_MAX; + } + + /** + * @throws CoercionException + * + * @ara-return i32 + * + * @return int<-2147483648, 2147483647> + */ + public function coerce(mixed $value): int + { + $integer = Type\int() + ->withTrace($this->getTrace()->withFrame($this->toString())) + ->coerce($value); + + if ($integer >= Math\INT32_MIN && $integer <= MATH\INT32_MAX) { + return $integer; + } + + throw CoercionException::withValue($value, $this->toString(), $this->getTrace()); + } + + /** + * @ara-assert i32 $value + * + * @psalm-assert int<-2147483648, 2147483647> $value + * + * @throws AssertException + * + * @ara-return i32 + * + * @return int<-2147483648, 2147483647> + */ + public function assert(mixed $value): int + { + if (is_int($value) && $value >= Math\INT32_MIN && $value <= MATH\INT32_MAX) { + return $value; + } + + throw AssertException::withValue($value, $this->toString(), $this->getTrace()); + } + + public function toString(): string + { + return 'i32'; + } +} diff --git a/src/Psl/Type/Internal/I64Type.php b/src/Psl/Type/Internal/I64Type.php new file mode 100644 index 00000000..54b41454 --- /dev/null +++ b/src/Psl/Type/Internal/I64Type.php @@ -0,0 +1,70 @@ + + * + * @extends Type\Type + * + * @internal + */ +final class I64Type extends Type\Type +{ + /** + * @ara-assert-if-true i64 $value + * + * @psalm-assert-if-true int $value + */ + public function matches(mixed $value): bool + { + return is_int($value); + } + + /** + * @throws CoercionException + * + * @ara-return i64 + * + * @return int + */ + public function coerce(mixed $value): int + { + return Type\int() + ->withTrace($this->getTrace()->withFrame($this->toString())) + ->coerce($value); + } + + /** + * @ara-assert i64 $value + * + * @psalm-assert int $value + * + * @throws AssertException + * + * @ara-return i64 + * + * @return int + */ + public function assert(mixed $value): int + { + if (is_int($value)) { + return $value; + } + + throw AssertException::withValue($value, $this->toString(), $this->getTrace()); + } + + public function toString(): string + { + return 'i64'; + } +} diff --git a/src/Psl/Type/Internal/I8Type.php b/src/Psl/Type/Internal/I8Type.php new file mode 100644 index 00000000..6e1f3e9e --- /dev/null +++ b/src/Psl/Type/Internal/I8Type.php @@ -0,0 +1,77 @@ + + * + * @extends Type\Type> + * + * @internal + */ +final class I8Type extends Type\Type +{ + /** + * @ara-assert-if-true i8 $value + * + * @psalm-assert-if-true int<-128, 127> $value + */ + public function matches(mixed $value): bool + { + return is_int($value) && $value >= Math\INT8_MIN && $value <= Math\INT8_MAX; + } + + /** + * @throws CoercionException + * + * @ara-return i8 + * + * @return int<-128, 127> + */ + public function coerce(mixed $value): int + { + $integer = Type\int() + ->withTrace($this->getTrace()->withFrame($this->toString())) + ->coerce($value); + + if ($integer >= Math\INT8_MIN && $integer <= Math\INT8_MAX) { + return $integer; + } + + throw CoercionException::withValue($value, $this->toString(), $this->getTrace()); + } + + /** + * @ara-assert i8 $value + * + * @psalm-assert int<-128, 127> $value + * + * @throws AssertException + * + * @ara-return i8 + * + * @return int<-128, 127> + */ + public function assert(mixed $value): int + { + if (is_int($value) && $value >= Math\INT8_MIN && $value <= Math\INT8_MAX) { + return $value; + } + + throw AssertException::withValue($value, $this->toString(), $this->getTrace()); + } + + public function toString(): string + { + return 'i8'; + } +} diff --git a/src/Psl/Type/Internal/VecType.php b/src/Psl/Type/Internal/VecType.php index 3b2085f3..00cde252 100644 --- a/src/Psl/Type/Internal/VecType.php +++ b/src/Psl/Type/Internal/VecType.php @@ -61,7 +61,7 @@ public function coerce(mixed $value): iterable /** @var Type\Type $value_type */ $value_type = $this->value_type->withTrace( $this->getTrace() - ->withFrame('vec<' . $this->value_type->toString() . '>') + ->withFrame($this->toString()) ); /** diff --git a/src/Psl/Type/i16.php b/src/Psl/Type/i16.php new file mode 100644 index 00000000..6a879e02 --- /dev/null +++ b/src/Psl/Type/i16.php @@ -0,0 +1,15 @@ + + * + * @return TypeInterface> + */ +function i16(): TypeInterface +{ + return new Internal\I16Type(); +} diff --git a/src/Psl/Type/i32.php b/src/Psl/Type/i32.php new file mode 100644 index 00000000..bb8d050e --- /dev/null +++ b/src/Psl/Type/i32.php @@ -0,0 +1,15 @@ + + * + * @return TypeInterface> + */ +function i32(): TypeInterface +{ + return new Internal\I32Type(); +} diff --git a/src/Psl/Type/i64.php b/src/Psl/Type/i64.php new file mode 100644 index 00000000..aff40727 --- /dev/null +++ b/src/Psl/Type/i64.php @@ -0,0 +1,15 @@ + + * + * @return TypeInterface + */ +function i64(): TypeInterface +{ + return new Internal\I64Type(); +} diff --git a/src/Psl/Type/i8.php b/src/Psl/Type/i8.php new file mode 100644 index 00000000..1603e580 --- /dev/null +++ b/src/Psl/Type/i8.php @@ -0,0 +1,15 @@ + + * + * @return TypeInterface> + */ +function i8(): TypeInterface +{ + return new Internal\I8Type(); +} diff --git a/tests/unit/Type/I16TypeTest.php b/tests/unit/Type/I16TypeTest.php new file mode 100644 index 00000000..11f9b4b9 --- /dev/null +++ b/tests/unit/Type/I16TypeTest.php @@ -0,0 +1,67 @@ +stringable('123'), 123]; + yield ['7', 7]; + yield ['07', 7]; + yield ['007', 7]; + yield ['-107', -107]; + yield ['000', 0]; + yield [1.0, 1]; + yield [$this->stringable((string) Math\INT16_MIN), Math\INT16_MIN]; + yield [$this->stringable((string) Math\INT16_MAX), Math\INT16_MAX]; + yield [$this->stringable('-321'), -321]; + yield ['-321', -321]; + yield [-321, -321]; + } + + public function getInvalidCoercions(): iterable + { + yield [1.23]; + yield ['1.23']; + yield ['1e123']; + yield ['']; + yield [[]]; + yield [[123]]; + yield [null]; + yield [false]; + yield [$this->stringable('1.23')]; + yield [$this->stringable('-007')]; + yield ['-007']; + yield ['9223372036854775808']; + yield [$this->stringable('9223372036854775808')]; + yield ['-9223372036854775809']; + yield [$this->stringable('-9223372036854775809')]; + yield ['0xFF']; + yield ['']; + yield [$this->stringable((string) Math\INT32_MIN)]; + yield [$this->stringable((string) Math\INT32_MAX)]; + yield [$this->stringable((string) Math\INT64_MAX)]; + yield [(string) Math\INT64_MAX]; + yield [Math\INT64_MAX]; + } + + public function getToStringExamples(): iterable + { + yield [$this->getType(), 'i16']; + } +} diff --git a/tests/unit/Type/I32TypeTest.php b/tests/unit/Type/I32TypeTest.php new file mode 100644 index 00000000..09769225 --- /dev/null +++ b/tests/unit/Type/I32TypeTest.php @@ -0,0 +1,67 @@ +stringable('123'), 123]; + yield ['7', 7]; + yield ['07', 7]; + yield ['007', 7]; + yield ['-107', -107]; + yield ['000', 0]; + yield [1.0, 1]; + yield [$this->stringable((string) Math\INT16_MIN), Math\INT16_MIN]; + yield [$this->stringable((string) Math\INT16_MAX), Math\INT16_MAX]; + yield [$this->stringable('-321'), -321]; + yield ['-321', -321]; + yield [-321, -321]; + yield [$this->stringable((string) Math\INT32_MIN), Math\INT32_MIN]; + yield [$this->stringable((string) Math\INT32_MAX), Math\INT32_MAX]; + } + + public function getInvalidCoercions(): iterable + { + yield [1.23]; + yield ['1.23']; + yield ['1e123']; + yield ['']; + yield [[]]; + yield [[123]]; + yield [null]; + yield [false]; + yield [$this->stringable('1.23')]; + yield [$this->stringable('-007')]; + yield ['-007']; + yield ['9223372036854775808']; + yield [$this->stringable('9223372036854775808')]; + yield ['-9223372036854775809']; + yield [$this->stringable('-9223372036854775809')]; + yield ['0xFF']; + yield ['']; + yield [$this->stringable((string) Math\INT64_MAX)]; + yield [(string) Math\INT64_MAX]; + yield [Math\INT64_MAX]; + } + + public function getToStringExamples(): iterable + { + yield [$this->getType(), 'i32']; + } +} diff --git a/tests/unit/Type/I64TypeTest.php b/tests/unit/Type/I64TypeTest.php new file mode 100644 index 00000000..88a5b1d8 --- /dev/null +++ b/tests/unit/Type/I64TypeTest.php @@ -0,0 +1,67 @@ +stringable('123'), 123]; + yield ['7', 7]; + yield ['07', 7]; + yield ['007', 7]; + yield ['-107', -107]; + yield ['000', 0]; + yield [1.0, 1]; + yield [$this->stringable((string) Math\INT16_MIN), Math\INT16_MIN]; + yield [$this->stringable((string) Math\INT16_MAX), Math\INT16_MAX]; + yield [$this->stringable('-321'), -321]; + yield ['-321', -321]; + yield [-321, -321]; + yield [$this->stringable((string) Math\INT32_MIN), Math\INT32_MIN]; + yield [$this->stringable((string) Math\INT32_MAX), Math\INT32_MAX]; + yield [$this->stringable((string) Math\INT64_MAX), Math\INT64_MAX]; + yield [(string) Math\INT64_MAX, Math\INT64_MAX]; + yield [Math\INT64_MAX, Math\INT64_MAX]; + } + + public function getInvalidCoercions(): iterable + { + yield [1.23]; + yield ['1.23']; + yield ['1e123']; + yield ['']; + yield [[]]; + yield [[123]]; + yield [null]; + yield [false]; + yield [$this->stringable('1.23')]; + yield [$this->stringable('-007')]; + yield ['-007']; + yield ['9223372036854775808']; + yield [$this->stringable('9223372036854775808')]; + yield ['-9223372036854775809']; + yield [$this->stringable('-9223372036854775809')]; + yield ['0xFF']; + yield ['']; + } + + public function getToStringExamples(): iterable + { + yield [$this->getType(), 'i64']; + } +} diff --git a/tests/unit/Type/I8TypeTest.php b/tests/unit/Type/I8TypeTest.php new file mode 100644 index 00000000..36a43acc --- /dev/null +++ b/tests/unit/Type/I8TypeTest.php @@ -0,0 +1,64 @@ +stringable('123'), 123]; + yield ['7', 7]; + yield ['07', 7]; + yield ['007', 7]; + yield ['-107', -107]; + yield ['000', 0]; + yield [1.0, 1]; + } + + public function getInvalidCoercions(): iterable + { + yield [1.23]; + yield ['1.23']; + yield ['1e123']; + yield ['']; + yield [[]]; + yield [[123]]; + yield [null]; + yield [false]; + yield [$this->stringable('1.23')]; + yield [$this->stringable('-007')]; + yield ['-007']; + yield ['9223372036854775808']; + yield [$this->stringable('9223372036854775808')]; + yield ['-9223372036854775809']; + yield [$this->stringable('-9223372036854775809')]; + yield ['0xFF']; + yield ['']; + yield [$this->stringable('-321')]; + yield ['-321']; + yield [-321]; + yield [$this->stringable((string) Math\INT16_MAX)]; + yield [$this->stringable((string) Math\INT64_MAX)]; + yield [(string) Math\INT64_MAX]; + yield [Math\INT64_MAX]; + } + + public function getToStringExamples(): iterable + { + yield [$this->getType(), 'i8']; + } +}