diff --git a/src/Parser.php b/src/Parser.php index 62cbb8b..9e9f998 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -13,35 +13,41 @@ public function __construct(protected Registry $registry) public function parse(string $input): Unit { + if ($this->registry->has($input)) { + return $this->registry->get($input);; + } + $tokens = $this->tokenize($input); $parts = []; foreach ($tokens as $i => $token) { $prevToken = $tokens[$i - 1] ?? null; - if ($token['type'] === 'unit') { - $unit = $this->registry->get($token['value']); + if ($token['type'] !== 'unit') { + continue; + } - if ($unit === null) { - throw new InvalidUnitException("Unknown unit: {$token['value']}"); - } + $unit = $this->registry->get($token['value']); - $powerSign = $prevToken && $prevToken['type'] === 'operator' && $prevToken['value'] === '/' - ? -1 - : 1; - - $parts[] = array_map(function (UnitPart|FactorUnitPart $part) use ($token, $powerSign) { - if ($part instanceof FactorUnitPart) { - return new FactorUnitPart($part->getRatio()); - } - - return new UnitPart( - $part->getRatio(), - $part->getDimension(), - $part->getPower() * $token['power'] * $powerSign, - ); - }, $unit->getParts()); + if ($unit === null) { + throw new InvalidUnitException("Unknown unit: {$token['value']}"); } + $powerSign = $prevToken && $prevToken['type'] === 'operator' && $prevToken['value'] === '/' + ? -1 + : 1; + $power = $token['power'] * $powerSign; + + $parts[] = array_map(function (UnitPart|FactorUnitPart $part) use ($power) { + if ($part instanceof FactorUnitPart) { + return new FactorUnitPart($part->getRatio()); + } + + return new UnitPart( + $part->getRatio(), + $part->getDimension(), + $part->getPower() * $power, + ); + }, $unit->getParts()); } return new Unit(...array_merge([], ...$parts)); @@ -59,7 +65,6 @@ protected function tokenize(string $input): array PREG_SET_ORDER ); - foreach ($matches as $match) { if ($match['unit']) { $tokens[] = [ diff --git a/tests/ParserTest.php b/tests/ParserTest.php index ad31e1e..47a0f21 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -175,4 +175,77 @@ public function test_throws_on_unknown_token() $this->parser->parse('kilometer / yeet'); } -} + + public function test_parses_dash_separated_units() + { + $result = $this->parser->parse('gram-liter'); + [$gram, $liter] = $result->getParts(); + + $this->assertCount(2, $result->getParts()); + + $this->assertEquals(Dimension::MASS, $gram->getDimension()); + $this->assertEquals(0.001, $gram->getRatio()); + $this->assertEquals(1, $gram->getPower()); + + $this->assertEquals(Dimension::LENGTH, $liter->getDimension()); + $this->assertEquals(0.1, $liter->getRatio()); + $this->assertEquals(3, $liter->getPower()); + } + + public function test_parses_dash_separated_unit_with_operator() + { + $result = $this->parser->parse('gram-liter/hour'); + [$gram, $liter, $hour] = $result->getParts(); + + $this->assertCount(3, $result->getParts()); + + $this->assertEquals(Dimension::MASS, $gram->getDimension()); + $this->assertEquals(0.001, $gram->getRatio()); + $this->assertEquals(1, $gram->getPower()); + + $this->assertEquals(Dimension::LENGTH, $liter->getDimension()); + $this->assertEquals(0.1, $liter->getRatio()); + $this->assertEquals(3, $liter->getPower()); + + $this->assertEquals(Dimension::TIME, $hour->getDimension()); + $this->assertEquals(3600, $hour->getRatio()); + $this->assertEquals(-1, $hour->getPower()); + } + + public function test_parses_dash_joined_single_unit_directly_if_registered() + { + $wattHour = $this->registry->get('watt-hour'); + $result = $this->parser->parse('watt-hour'); + [$wattMass, $wattLength, $wattTime, $hour] = $result->getParts(); + + $this->assertCount(4, $result->getParts()); + $this->assertEquals($wattMass, $wattHour->getPart(0)); + $this->assertEquals($wattLength, $wattHour->getPart(1)); + $this->assertEquals($wattTime, $wattHour->getPart(2)); + $this->assertEquals($hour, $wattHour->getPart(3)); + } + + public function test_parses_dash_joined_single_unit_with_operator() + { + $wattHour = $this->registry->get('watt-hour'); + $result = $this->parser->parse('watt-hour/kg'); + [$wattMass, $wattLength, $wattTime, $hour, $kilo, $gram] = $result->getParts(); + + $this->assertCount(6, $result->getParts()); + + // Watt-hour + $this->assertEquals($wattMass, $wattHour->getPart(0)); + $this->assertEquals($wattLength, $wattHour->getPart(1)); + $this->assertEquals($wattTime, $wattHour->getPart(2)); + $this->assertEquals($hour, $wattHour->getPart(3)); + + // Kilogram + $this->assertNull($kilo->getDimension()); + $this->assertEquals(1000, $kilo->getRatio()); + $this->assertEquals(1, $kilo->getPower()); + + $this->assertEquals(Dimension::MASS, $gram->getDimension()); + $this->assertEquals(0.001, $gram->getRatio()); + $this->assertEquals(-1, $gram->getPower()); + } +} \ No newline at end of file