diff --git a/docs/utilities.md b/docs/utilities.md index d0c2f0b..e3ca639 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -53,6 +53,28 @@ assert(null === $parser("123")); assert(null === $parser("")); ``` +## `hex` + +This parses a sequence of hexadecimal digits converting them to an `int`. + +#### OOP and FP + +```php +$parser = p::hex(); + +assert([0x1a] === $parser("1a")->parsed); +assert([0xf] === $parser("F")->parsed); +assert([0] === $parser("0")->parsed); + +//doesn't include an end condition or prefixes +assert([0] === $parser("0xa")->parsed); +assert("xa" === $parser("0xa")->unparsed); + +assert(null === $parser("")->parsed); +//doesn't handle signs +assert(null === $parser("-123")->parsed); +``` + ## `int` This parses a sequence of decimal digits converting them to an `int`. diff --git a/src/Functions.php b/src/Functions.php index c224ea1..36718b8 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -10,7 +10,9 @@ final class Functions const and = self::class . "::and"; const drop = self::class . "::drop"; const end = self::class . "::end"; + const float = self::class . "::float"; const fold = self::class . "::fold"; + const hex = self::class . "::hex"; const int = self::class . "::int"; const lit = self::class . "::lit"; const map = self::class . "::map"; @@ -170,6 +172,39 @@ function (array $a, $s) use ($f): array { }; } + /** + * @return callable(string):?r + */ + public static function hex(): callable + { + /** + * @var array> + */ + $decLits = array_map(self::lit, range("0", "9")); + /** + * @var array> + */ + $hexLits = array_merge( + array_map(self::lit, range("a", "f")), + array_map(self::lit, range("A", "F")) + ); + + $dec = self::map('intval', self::or(...$decLits)); + $hex = self::map(function (string $v): int { + return 9 + (0xf & ord($v)); + }, self::or(...$hexLits)); + + $digits = self::or($dec, $hex); + + $hexSequence = self::and($digits, self::repeat($digits)); + + $hexVal = function (int $d, int $a): array { + return [$a * 0x10 + $d]; + }; + + return self::fold($hexVal, [0], $hexSequence); + } + /** * @return callable(string):?r */ diff --git a/src/Oop.php b/src/Oop.php index c886b02..b73440a 100644 --- a/src/Oop.php +++ b/src/Oop.php @@ -9,6 +9,7 @@ final class Oop { //convenience constants for passing functions to functions const float = self::class . "::float"; + const hex = self::class . "::hex"; const int = self::class . "::int"; const lit = self::class . "::lit"; const pop = self::class . "::pop"; @@ -68,6 +69,11 @@ public function fold(callable $f, array $start = []): self return new self(p::fold($f, $start, $this->parser)); } + public static function hex(): self + { + return new self(p::hex()); + } + public static function int(): self { return new self(p::int()); diff --git a/test/Unit/FunctionsTest.php b/test/Unit/FunctionsTest.php index 6d8c595..5d8426d 100644 --- a/test/Unit/FunctionsTest.php +++ b/test/Unit/FunctionsTest.php @@ -133,6 +133,27 @@ public function test_fold( $this->assertEquals($expected, $actual); } + public function hex_provider(): array + { + return [ + ["1a", r::make("", [0x1a])], + ["F", r::make("", [0xf])], + ["0", r::make("", [0])], + ["0xa", r::make("xa", [0])], + ["", null], + ["-123", null], + ]; + } + /** + * @dataProvider hex_provider + */ + public function test_hex(string $input, ?r $expected): void + { + $p = p::hex(); + + self::assertEquals($expected, $p($input)); + } + public function int_provider(): array { return [ diff --git a/test/Unit/OopTest.php b/test/Unit/OopTest.php index a430d9a..f858440 100644 --- a/test/Unit/OopTest.php +++ b/test/Unit/OopTest.php @@ -126,6 +126,27 @@ public function testFold( $this->assertEquals($expected, $actual); } + public function hexProvider(): array + { + return [ + ["1a", r::make("", [0x1a])], + ["F", r::make("", [0xf])], + ["0", r::make("", [0])], + ["0xa", r::make("xa", [0])], + ["", null], + ["-123", null], + ]; + } + /** + * @dataProvider hexProvider + */ + public function testHex(string $input, ?r $expected): void + { + $p = p::hex(); + + self::assertEquals($expected, $p($input)); + } + public function intProvider(): array { return [