packages feed

roman-numerals 0.1.0.1 → 0.3

raw patch · 3 files changed

+286/−228 lines, 3 filesdep +bytestringdep +monads-fddep −mtldep ~base-unicode-symbolsPVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring, monads-fd

Dependencies removed: mtl

Dependency ranges changed: base-unicode-symbols

API changes (from Hackage documentation)

- Text.RomanNumerals: NC :: n -> Maybe String -> [(String, n)] -> NumeralConfig n
- Text.RomanNumerals: convertFrom :: (Ord n, Num n, MonadError String m) => NumeralConfig n -> String -> m n
- Text.RomanNumerals: convertTo :: (Ord n, Num n, MonadError String m) => NumeralConfig n -> n -> m String
- Text.RomanNumerals: data (Ord n, Num n) => NumeralConfig n
- Text.RomanNumerals: fromRoman :: (Ord n, Num n, MonadError String m) => String -> m n
- Text.RomanNumerals: modernRoman :: (Ord n, Num n) => NumeralConfig n
- Text.RomanNumerals: ncMax :: NumeralConfig n -> n
- Text.RomanNumerals: ncTable :: NumeralConfig n -> [(String, n)]
- Text.RomanNumerals: ncZero :: NumeralConfig n -> Maybe String
- Text.RomanNumerals: simpleRoman :: (Ord n, Num n) => NumeralConfig n
- Text.RomanNumerals: toRoman :: (Ord n, Num n, MonadError String m) => n -> m String
- Text.RomanNumerals: unsafeConvertFrom :: (Ord n, Num n) => NumeralConfig n -> String -> n
- Text.RomanNumerals: unsafeConvertTo :: (Ord n, Num n) => NumeralConfig n -> n -> String
- Text.RomanNumerals: unsafeFromRoman :: (Ord n, Num n) => String -> n
- Text.RomanNumerals: unsafeToRoman :: (Ord n, Num n) => n -> String
+ Text.Numeral.Roman: NC :: n -> Maybe s -> [(s, n)] -> NumeralConfig s n
+ Text.Numeral.Roman: class StripPrefix s
+ Text.Numeral.Roman: convertFrom :: (Monoid s, StripPrefix s, Eq s, Ord n, Num n, MonadError String m) => NumeralConfig s n -> s -> m n
+ Text.Numeral.Roman: convertTo :: (Monoid s, Ord n, Num n, MonadError String m) => NumeralConfig s n -> n -> m s
+ Text.Numeral.Roman: data NumeralConfig s n
+ Text.Numeral.Roman: fromRoman :: (IsString s, Monoid s, StripPrefix s, Eq s, Ord n, Num n, MonadError String m) => s -> m n
+ Text.Numeral.Roman: instance (Eq α) => StripPrefix [α]
+ Text.Numeral.Roman: instance StripPrefix ByteString
+ Text.Numeral.Roman: modernRoman :: (IsString s, Ord n, Num n) => NumeralConfig s n
+ Text.Numeral.Roman: ncMax :: NumeralConfig s n -> n
+ Text.Numeral.Roman: ncTable :: NumeralConfig s n -> [(s, n)]
+ Text.Numeral.Roman: ncZero :: NumeralConfig s n -> Maybe s
+ Text.Numeral.Roman: null :: (StripPrefix s) => s -> Bool
+ Text.Numeral.Roman: simpleRoman :: (IsString s, Ord n, Num n) => NumeralConfig s n
+ Text.Numeral.Roman: stripPrefix :: (StripPrefix s) => s -> s -> Maybe s
+ Text.Numeral.Roman: toRoman :: (IsString s, Monoid s, Ord n, Num n, MonadError String m) => n -> m s
+ Text.Numeral.Roman: unsafeConvertFrom :: (Monoid s, StripPrefix s, Eq s, Ord n, Num n) => NumeralConfig s n -> s -> n
+ Text.Numeral.Roman: unsafeConvertTo :: (Monoid s, Ord n, Num n) => NumeralConfig s n -> n -> s
+ Text.Numeral.Roman: unsafeFromRoman :: (IsString s, Monoid s, StripPrefix s, Eq s, Ord n, Num n) => s -> n
+ Text.Numeral.Roman: unsafeToRoman :: (IsString s, Monoid s, Ord n, Num n) => n -> s

Files

+ Text/Numeral/Roman.hs view
@@ -0,0 +1,277 @@+{-# LANGUAGE FlexibleContexts+           , NoImplicitPrelude+           , OverloadedStrings+           , UnicodeSyntax+  #-}++{-| Parsing and pretty printing of Roman numerals.++This module provides functions for parsing and pretty printing Roman+numerals. Because the notation of Roman numerals has varied through+the centuries this package allows for some customisation using a+configuration that is passed to the conversion functions. Exceptions+are dealt with by wrapping the results of conversions in the error+monad.++Example:++@+  \> 'toRoman' 1729 &#x2237; Either String String+  Right \"MDCCXXIX\"+@++@+  \> 'fromRoman' \"MDCCXXIX\" &#x2237; Either String Integer+  Right 1729+@++@+  \> 'convertTo' 'simpleRoman' 1729 &#x2237; Either String String+  Right \"MDCCXXVIIII\"+@++@+  \> 'fromRoman' \"Bla\" &#x2237; Either String Integer+  Left \"Roman.convertFrom: can't parse\"+@++-}+module Text.Numeral.Roman+  ( -- * Types+    NumeralConfig(..)++    -- * Standard configurations+  , modernRoman+  , simpleRoman++    -- * Pretty printing+  , convertTo+  , unsafeConvertTo+  , toRoman+  , unsafeToRoman++    -- * Parsing+  , StripPrefix(..)++  , convertFrom+  , fromRoman+  , unsafeConvertFrom+  , unsafeFromRoman+  ) where+++-------------------------------------------------------------------------------+-- Imports+-------------------------------------------------------------------------------++-- base+import Data.Bool     ( Bool(False, True), otherwise )+import Data.Char     ( String )+import Data.Either   ( either )+import Data.Eq       ( Eq, (==) )+import Data.Function ( ($), id, const )+import Data.Maybe    ( Maybe(Nothing, Just), maybe )+import Data.Monoid   ( Monoid )+import Data.Ord      ( Ord, (>), (<) )+import Data.String   ( IsString, fromString )+import qualified Data.List as L ( null, stripPrefix )+import Control.Monad ( (>>=), (>>), return, fail, liftM2 )+import Prelude       ( Num, (+), (-), Integer, fromInteger, error )+import Text.Show     ( show )++-- base-unicode-symbols+import Data.Eq.Unicode       ( (≡) )+import Data.Function.Unicode ( (∘) )+import Data.Ord.Unicode      ( (≥) )+import Data.Monoid.Unicode   ( (∅), (⊕) )++-- bytestring+import qualified Data.ByteString as BS++-- monads-fd+import Control.Monad.Error ( MonadError, throwError )+++-------------------------------------------------------------------------------+-- Types+-------------------------------------------------------------------------------++-- |A configuration with which the 'convertTo' and 'convertFrom'+--  functions can be parameterized.+data NumeralConfig s n = NC +    { -- |The largest value that can be represented using this+      -- configuration.+      ncMax ∷ n+      -- |Symbol to represent the value 0. The Romans did not have a+      -- symbol for zero. If set to Nothing a 'convertFrom' 0 will+      -- throw an error.+    , ncZero ∷ Maybe s+      -- |A table of symbols and their numerical values. The table+      -- must be ordered in descending order of the value of the+      -- symbols. If any symbol is the empty string then 'convertFrom'+      -- will be &#x22a5;. If any symbol in this table is associated+      -- with the value 0 both the 'convertTo' and 'convertFrom'+      -- function will be &#x22a5;.+    , ncTable ∷ [(s, n)]+    }++-------------------------------------------------------------------------------+-- Default tables+-------------------------------------------------------------------------------++-- |Configuration for Roman numerals as they are commonly used+-- today. The value 0 is represented by the empty string. It can be+-- interpreted as not writing down a number. This configuration is+-- limited to the range [1..3999]. Larger numbers can be represented+-- using Roman numerals but you will need notations that are hard or+-- impossible to express using strings.+modernRoman ∷ (IsString s, Ord n, Num n) ⇒ NumeralConfig s n+modernRoman = NC { ncMax      = 3999+                 , ncZero     = Just ""+                 , ncTable    = [ ("M",  1000)+                                , ("CM",  900)+                                , ("D",   500)+                                , ("CD",  400)+                                , ("C",   100)+                                , ("XC",   90)+                                , ("L",    50)+                                , ("XL",   40)+                                , ("X",    10)+                                , ("IX",    9)+                                , ("V",     5)+                                , ("IV",    4)+                                , ("I",     1)+                                ]+                 }++-- |Configuration for Roman numerals that do not use the rule that a+-- lower rank symbol can be placed before a higher rank symbol to+-- denote the difference between them. Thus a numeral like \"IV\" will+-- not be accepted or generated by this configuration.+simpleRoman ∷ (IsString s, Ord n, Num n) ⇒ NumeralConfig s n+simpleRoman = NC { ncMax      = 3999+                 , ncZero     = Just ""+                 , ncTable    = [ ("M", 1000)+                                , ("D",  500)+                                , ("C",  100)+                                , ("L",   50)+                                , ("X",   10)+                                , ("V",    5)+                                , ("I",    1)+                                ]+                 }++-------------------------------------------------------------------------------+-- Pretty printing+-------------------------------------------------------------------------------++-- |Converts a number to a Roman numeral according to the given+-- configuration. Numbers which are out of bounds will cause+-- exceptions to be thrown. An exception will also be raised if no+-- representation is possible with the given configuration. If the+-- value of any symbol in the configuration is equal to 0 or a symbol+-- is the empty string this function is undefined.+convertTo ∷ (Monoid s, Ord n, Num n, MonadError String m)+          ⇒ NumeralConfig s n → n → m s+convertTo nc n +    | n < 0     = errMsg $ "can't represent negative numbers"+    | n > maxN  = errMsg $ "too large (max = " ⊕ (show maxN) ⊕ ")"+    | n ≡ 0     = maybe (errMsg "no symbol for zero")+                        return+                        (ncZero nc)+    | otherwise = go n $ ncTable nc+    where maxN = ncMax nc++          go 0 _  = return (∅)+          go _ [] = errMsg "out of symbols"+          go i tab@(~(sym, val) : ts)+              | n ≥ val   = liftM2 (⊕) (return sym) $ go (i - val) tab+              | otherwise = go i ts++          errMsg = throwError ∘ ("Roman.convertTo: " ⊕)++-- |Like 'convertTo', but exceptions are promoted to errors.+unsafeConvertTo ∷ (Monoid s, Ord n, Num n) ⇒ NumeralConfig s n → n → s+unsafeConvertTo nc = either error id ∘ convertTo nc++-- |Converts a number to a modern Roman numeral. See 'convertTo' for+--  possible exceptions.+toRoman ∷ (IsString s, Monoid s, Ord n, Num n, MonadError String m)+        ⇒ n → m s+toRoman = convertTo modernRoman++-- |Like 'toRoman', but exceptions are promoted to errors.+unsafeToRoman ∷ (IsString s, Monoid s, Ord n, Num n) ⇒ n  → s+unsafeToRoman = unsafeConvertTo modernRoman++-------------------------------------------------------------------------------+-- Parsing+-------------------------------------------------------------------------------++-- |Class used to overload the input of the parsing functions.+class StripPrefix s where+    null        ∷ s → Bool+    stripPrefix ∷ s → s → Maybe s++instance Eq α ⇒ StripPrefix [α] where+    null        = L.null+    stripPrefix = L.stripPrefix++instance StripPrefix BS.ByteString where+    null = BS.null+    stripPrefix p s = let (h, t) = BS.breakSubstring p s+                      in if null h+                         then Just $ BS.drop (BS.length p) t+                         else Nothing++-- |Parses a string as a Roman numeral according to the given+-- configuration. An exception will be raised if the input is not a+-- valid numeral.+convertFrom ∷ (Monoid s, StripPrefix s, Eq s, Ord n, Num n, MonadError String m)+            ⇒ NumeralConfig s n → s → m n+convertFrom nc s | maybe False (≡ s) (ncZero nc) = return 0+                 | otherwise = do n  ← (go 0 (ncTable nc) s)+                                  s' ← convertTo nc n+                                  if s ≡ s'+                                    then return n+                                    else errMsg "invalid Roman numeral"+    where go n _  x | null x = return n+          go _ [] _          = errMsg "can't parse"+          go n tab@((sym, val) : ts) x = maybe (go n ts x)+                                               (go (n + val) tab)+                                               $ stripPrefix sym x+          errMsg = throwError ∘ ("Roman.convertFrom: " ⊕)++-- |Like 'convertFrom', but exceptions are promoted to errors.+unsafeConvertFrom ∷ (Monoid s, StripPrefix s, Eq s, Ord n, Num n)+                  ⇒ NumeralConfig s n → s → n+unsafeConvertFrom nc = either error id ∘ convertFrom nc++-- |Parses a string as a modern Roman numeral. See 'convertFrom' for+-- possible exceptions.+fromRoman ∷ ( IsString s, Monoid s, StripPrefix s, Eq s+            , Ord n, Num n, MonadError String m+            ) ⇒ s → m n+fromRoman = convertFrom modernRoman++-- |Like 'fromRoman', but exceptions are promoted to errors.+unsafeFromRoman ∷ ( IsString s, Monoid s, StripPrefix s, Eq s+                  , Ord n, Num n+                  ) ⇒ s → n+unsafeFromRoman = unsafeConvertFrom modernRoman++-------------------------------------------------------------------------------+-- Properties+-------------------------------------------------------------------------------++prop_printParseIsId ∷ NumeralConfig String Integer → String → Bool+prop_printParseIsId nc xs = either (const True) id+                                   $ do n   ← convertFrom nc xs+                                        xs' ← convertTo   nc n+                                        return $ xs' ≡ xs++prop_parsePrintIsId ∷ NumeralConfig String Integer → Integer → Bool+prop_parsePrintIsId nc n = either (const True) id+                                  $ do xs ← convertTo   nc n+                                       n' ← convertFrom nc xs+                                       return $ n' ≡ n
− Text/RomanNumerals.hs
@@ -1,221 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE UnicodeSyntax #-}--{-| Parsing and pretty printing of Roman numerals.--  This module provides functions for parsing and pretty printing Roman-  numerals. Because the notation of Roman numerals has varied through-  the centuries this package allows for some customisation using a-  configuration that is passed to the conversion functions. Exceptions-  are dealt with by wrapping the results of conversions in the error-  monad.--}-module Text.RomanNumerals-  ( -- * Types-    NumeralConfig(..)-    -- * Sample configurations-  , modernRoman-  , simpleRoman-    -- * Pretty printing-  , convertTo-  , unsafeConvertTo-  , toRoman-  , unsafeToRoman-    -- * Parsing-  , convertFrom-  , fromRoman-  , unsafeConvertFrom-  , unsafeFromRoman-  ) where------------------------------------------------------------------------------------- Imports------------------------------------------------------------------------------------ base-import Control.Monad         ( (>>=), (>>), fail, liftM2, return )-import Data.Bool             ( Bool(False, True), otherwise )-import Data.Char             ( String )-import Data.Either           ( either )-import Data.Eq               ( (==) {- needed for desugaring -} )-import Data.Function         ( ($), const, id )-import Data.List             ( stripPrefix, take )-import Data.Maybe            ( Maybe(Just), maybe )-import Data.Ord              ( Ord, (<), (>) )-import Prelude               ( Num, (-), (+), error, fromInteger )-import Text.Show             ( show )---- base-unicode-symbols-import Data.Eq.Unicode       ( (≡) )-import Data.Function.Unicode ( (∘) )-import Data.Monoid.Unicode   ( (⊕) )-import Data.Ord.Unicode      ( (≥) )---- mtl-import Control.Monad.Error   ( MonadError, throwError )------------------------------------------------------------------------------------ Types------------------------------------------------------------------------------------ |A configuration with which the 'convertTo' and 'convertFrom'---  functions can be parameterized.-data (Ord n, Num n) ⇒-     NumeralConfig n = NC { -- |The largest value that can be-                            --  represented using this configuration.-                            ncMax ∷ n-                            -- |Symbol to represent the value 0. The-                            --  Romans did not have a symbol for-                            --  zero. If set to Nothing a-                            --  'convertFrom' 0 will throw an error.-                          , ncZero ∷ Maybe String-                            -- |A table of symbols and their numerical-                            --  values. The table must be ordered with-                            --  the largest symbols appearing-                            --  first. If any symbol is the empty-                            --  string then 'convertFrom' will be-                            --  undefined. If any symbol in this table-                            --  is associated with the value 0 both-                            --  the convertTo and 'convertFrom'-                            --  function will be undefined.-                          , ncTable ∷ [(String, n)]-                          }------------------------------------------------------------------------------------ Default tables------------------------------------------------------------------------------------ |Configuration for Roman numerals as they are commenly used---  today. The value 0 is represented by the empty string. It can be---  interpreted as not writing down a number. This configuration is---  limited to the range [1..3999]. Larger numbers can be represented---  using Roman numerals but you will need notations that are hard or---  impossible to express using strings.-modernRoman ∷ (Ord n, Num n) ⇒ NumeralConfig n-modernRoman = NC { ncMax      = 3999-                 , ncZero     = Just ""-                 , ncTable    = [ ("M",  1000)-                                , ("CM",  900)-                                , ("D",   500)-                                , ("CD",  400)-                                , ("C",   100)-                                , ("XC",   90)-                                , ("L",    50)-                                , ("XL",   40)-                                , ("X",    10)-                                , ("IX",    9)-                                , ("V",     5)-                                , ("IV",    4)-                                , ("I",     1)-                                ]-                 }---- |Configuration for Roman numerals that do not use the rule that a---  lower rank symbol can be placed before a higher rank symbol to---  denote the difference between them. Thus a numeral like "IV" will---  not be accepted or generated by this configuration.-simpleRoman ∷ (Ord n, Num n) ⇒ NumeralConfig n-simpleRoman = NC { ncMax      = 3999-                 , ncZero     = Just ""-                 , ncTable    = [ ("M", 1000)-                                , ("D",  500)-                                , ("C",  100)-                                , ("L",   50)-                                , ("X",   10)-                                , ("V",    5)-                                , ("I",    1)-                                ]-                 }------------------------------------------------------------------------------------- Pretty printing------------------------------------------------------------------------------------- |Converts a number to a Roman numeral according to the given---  configuration. Numbers which are out of bounds will cause---  exceptions to be thrown. An exception will also be raised if no---  representation is possible with the given configuration. If the---  value of any symbol in the configuration is equal to 0 or a symbol---  is the empty string this function is undefined.-convertTo ∷ (Ord n, Num n, MonadError String m) -          ⇒ NumeralConfig n → n → m String-convertTo nc n | n < 0    = throwError "Roman.convertTo: can't represent negative numbers"-               | n > maxN = throwError $ "Roman.convertTo: too large (max = " ⊕ (show maxN) ⊕ ")"-               | n ≡ 0    = maybe (throwError "Roman.convertTo: no symbol for zero")-                                  return-                                  $ ncZero nc-               | otherwise = go n $ ncTable nc-    where maxN = ncMax nc-          go 0 _  = return ""-          go _ [] = throwError "Roman.convertTo: out of symbols"-          go n tab@(~(sym, val) : ts) | n ≥ val   = liftM2 (⊕) (return sym) $ go (n - val) tab-                                      | otherwise = go n ts---- |Like 'convertTo', but exceptions are promoted to errors.-unsafeConvertTo ∷ (Ord n, Num n) ⇒ NumeralConfig n → n → String-unsafeConvertTo nc = either error id ∘ convertTo nc---- |Converts a number to a modern Roman numeral. See 'convertTo' for---  possible exceptions.-toRoman ∷ (Ord n, Num n, MonadError String m) ⇒ n → m String-toRoman = convertTo modernRoman---- |Like 'toRoman', but exceptions are promoted to errors.-unsafeToRoman ∷ (Ord n, Num n) ⇒ n → String-unsafeToRoman = unsafeConvertTo modernRoman------------------------------------------------------------------------------------ Parsing------------------------------------------------------------------------------------ convertFrom nc xs ≡ undefined when any symbol in the NumeralConfig--- nc is associated with the value 0.---- |Parses a string as a Roman numeral according to the given---  configuration. An exception will be raised if the input is not a---  valid numeral.-convertFrom ∷ (Ord n, Num n, MonadError String m) ⇒ NumeralConfig n → String → m n-convertFrom nc xs | maybe False (≡ xs) (ncZero nc) = return 0-                  | otherwise = do n   ← (go 0 (ncTable nc) xs)-                                   xs' ← convertTo nc n-                                   if xs ≡ xs'-                                     then return n-                                     else throwError "Roman.convertFrom: invalid Roman numeral"-    where go n _  []  = return n-          go _ [] xs  = throwError $ "Roman.convertFrom: can't parse: '" ⊕ (take 5 xs) ⊕ "'"-          go n tab@((sym, val) : ts) xs = maybe (go n ts xs)-                                                (go (n + val) tab)-                                                $ stripPrefix sym xs---- |Like 'convertFrom', but exceptions are promoted to errors.-unsafeConvertFrom ∷ (Ord n, Num n) ⇒ NumeralConfig n → String → n-unsafeConvertFrom nc = either error id ∘ convertFrom nc---- |Parses a string as a modern Roman numeral. See 'convertFrom' for---  possible exceptions.-fromRoman ∷ (Ord n, Num n, MonadError String m) ⇒ String → m n-fromRoman = convertFrom modernRoman---- |Like 'fromRoman', but exceptions are promoted to errors.-unsafeFromRoman ∷ (Ord n, Num n) ⇒ String → n-unsafeFromRoman = unsafeConvertFrom modernRoman------------------------------------------------------------------------------------ Properties----------------------------------------------------------------------------------printParseIsId ∷ (Ord n, Num n) ⇒ NumeralConfig n → String → Bool-printParseIsId nc xs = either (const True) id-                         $ do n   ← convertFrom nc xs-                              xs' ← convertTo   nc n-                              return $ xs' ≡ xs--parsePrintIsId ∷ (Ord n, Num n) ⇒ NumeralConfig n → n → Bool-parsePrintIsId nc n = either (const True) id-                        $ do xs ← convertTo   nc n-                             n' ← convertFrom nc xs-                             return $ n' ≡ n
roman-numerals.cabal view
@@ -1,11 +1,11 @@ name:          roman-numerals-version:       0.1.0.1+version:       0.3 cabal-version: >= 1.6 build-type:    Simple stability:     stable-author:        Roel van Dijk-maintainer:    vandijk.roel@gmail.com-copyright:     (c) 2009 Roel van Dijk+author:        Roel van Dijk <vandijk.roel@gmail.com>+maintainer:    Roel van Dijk <vandijk.roel@gmail.com>+copyright:     (c) 2009–2010 Roel van Dijk license:       BSD3 license-file:  LICENSE category:      Numerical, Parsing@@ -24,9 +24,11 @@  library   build-depends: base                 >= 3.0.3.1 && < 4.3-               , base-unicode-symbols >= 0.1.1   && < 0.2-               , mtl                  == 1.1.*-  exposed-modules: Text.RomanNumerals+               , base-unicode-symbols >= 0.1.1   && < 0.3+               , bytestring           >= 0.9.1   && < 0.10+               , monads-fd            >= 0.1     && < 0.2+  exposed-modules: Text.Numeral.Roman+  ghc-options: -Wall