From 5493329b2eed7e151f4a323c108caad2253b08bb Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Sat, 22 Mar 2025 14:52:35 +0100 Subject: Refactor parser for current account statement --- app/Data/Iban.hs | 10 +++++----- app/Data/Res.hs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 app/Data/Res.hs (limited to 'app/Data') diff --git a/app/Data/Iban.hs b/app/Data/Iban.hs index 45343ec..412577a 100644 --- a/app/Data/Iban.hs +++ b/app/Data/Iban.hs @@ -4,6 +4,8 @@ import Control.Applicative ((<|>)) import Data.Attoparsec.Text as AP import Data.Char ( digitToInt, + isAscii, + isDigit, ord, toUpper, ) @@ -24,7 +26,7 @@ validateIban = AP.parseOnly $ do then if valid countryCode checkDigits chars then return () - else fail $ "IBAN checksum does not match (" <> countryCode <> checkDigits <> chars <> ")" + else fail $ "IBAN checksum does not match (" ++ countryCode ++ checkDigits ++ chars ++ ")" else fail "IBAN has more than 34 characters" where letterToInt c = ord (toUpper c) - ord 'A' + 10 @@ -32,10 +34,8 @@ validateIban = AP.parseOnly $ do foldl' ( \acc -> \case d - | '0' <= d && d <= '9' -> acc * 10 + toInteger (digitToInt d) - | 'A' <= d && d <= 'Z' - || 'a' <= d && d <= 'z' -> - acc * 100 + toInteger (letterToInt d) + | isDigit d -> acc * 10 + toInteger (digitToInt d) + | isAscii d -> acc * 100 + toInteger (letterToInt d) | otherwise -> error "unreachable" ) 0 diff --git a/app/Data/Res.hs b/app/Data/Res.hs new file mode 100644 index 0000000..e8c4ca4 --- /dev/null +++ b/app/Data/Res.hs @@ -0,0 +1,31 @@ +module Data.Res where + +import Control.Applicative +import Data.String (IsString (fromString)) + +data Res e r = Ok r | Err e + +instance Functor (Res e) where + fmap f (Ok v) = Ok (f v) + fmap _ (Err e) = Err e + +instance Applicative (Res e) where + pure = Ok + (Ok f) <*> (Ok v) = Ok (f v) + (Err e) <*> _ = Err e + _ <*> (Err e) = Err e + +instance Monad (Res e) where + (Ok v) >>= f = f v + (Err e) >>= _ = Err e + +instance IsString e => MonadFail (Res e) where + fail = Err . fromString + +instance IsString e => Alternative (Res e) where + empty = fail "mzero" + m1@(Ok _) <|> _ = m1 + (Err _) <|> m2 = m2 + +liftEither :: Either e r -> Res e r +liftEither = either Err Ok -- cgit v1.2.3