use v6.c; use Math::Units::Defs; #use Grammar::Tracer; class Math::Units::Parser { has %.defUnits; has $.parser; grammar UnitParserGrammar { regex TOP { \s* } regex fac { <[+-]>? \d+ [ '.' \d+ ]? } regex units { [ '/' ]? } regex num { [ \s* ]+ } regex den { } regex expr { ? [ [ '^' || '**' ] $ = (\d+) ]? } token mag { [ T || G || M || k || h || da || d || c || m || u || ยต || n || dn || p || f ] ','? } proto regex unit { * } } submethod BUILD { $!parser = UnitParserGrammar.new; } method addUnit(Str $unit) { return if %.defUnits{$unit}.defined; $.parser.^add_multi_method("unit:sym<$unit>", my regex { $unit }); $.parser.^compose; %.defUnits{$unit} = 1 } method parse($s) { my $m = $.parser.parse($s); my ($mag, $unitParts) = self!handleUnitData($m); ( fac => $m.Num, mag => $mag, units => $m.Str, unitParts => $unitParts.list ); } method parseUnits(Str $u) { my $m = $.parser.subparse($u, :rule('units')); die "Could not parse units" unless $.defined && $m ~~ Match; self!handleUnitData($m) } method !handleUnitData(Match $m) { my $mag = 1; my $unitParts = []; for $m -> $ne { my $pow = $ne.defined ?? $ne.Str.Int !! 1; $unitParts.push: [ $ne.Str, $pow; ]; $mag *= Magnitude.enums{$ne.Str} if $ne.defined; } if $m.defined { $unitParts.push: [ $m.Str, ($m.defined ?? $m.Str.Int !! 1) * -1 ]; $mag /= Magnitude.enms{$m} if $.defined; } $mag, $unitParts; } }