packages feed

config-value 0.6.3.1 → 0.7.0.0

raw patch · 13 files changed

+444/−140 lines, 13 filesdep +config-valuedep ~basedep ~textPVP ok

version bump matches the API change (PVP)

Dependencies added: config-value

Dependency ranges changed: base, text

API changes (from Hackage documentation)

- Config: Floating :: a -> Integer -> Integer -> Value a
- Config: instance GHC.Exception.Exception Config.ParseError
+ Config: data Number
+ Config: instance GHC.Exception.Type.Exception Config.ParseError
+ Config: integerToNumber :: Integer -> Number
+ Config: numberToInteger :: Number -> Maybe Integer
+ Config: numberToRational :: Number -> Rational
+ Config: rationalToNumber :: Rational -> Number
+ Config.Number: MkNumber :: !Radix -> !Rational -> Number
+ Config.Number: Radix10 :: !Integer -> Radix
+ Config.Number: Radix16 :: !Integer -> Radix
+ Config.Number: Radix2 :: Radix
+ Config.Number: Radix8 :: Radix
+ Config.Number: [numberCoefficient] :: Number -> !Rational
+ Config.Number: [numberRadix] :: Number -> !Radix
+ Config.Number: data Number
+ Config.Number: data Radix
+ Config.Number: instance Data.Data.Data Config.Number.Number
+ Config.Number: instance Data.Data.Data Config.Number.Radix
+ Config.Number: instance GHC.Classes.Eq Config.Number.Number
+ Config.Number: instance GHC.Classes.Eq Config.Number.Radix
+ Config.Number: instance GHC.Classes.Ord Config.Number.Number
+ Config.Number: instance GHC.Classes.Ord Config.Number.Radix
+ Config.Number: instance GHC.Generics.Generic Config.Number.Number
+ Config.Number: instance GHC.Generics.Generic Config.Number.Radix
+ Config.Number: instance GHC.Read.Read Config.Number.Number
+ Config.Number: instance GHC.Read.Read Config.Number.Radix
+ Config.Number: instance GHC.Show.Show Config.Number.Number
+ Config.Number: instance GHC.Show.Show Config.Number.Radix
+ Config.Number: integerToNumber :: Integer -> Number
+ Config.Number: numberToInteger :: Number -> Maybe Integer
+ Config.Number: numberToRational :: Number -> Rational
+ Config.Number: radixToInt :: Radix -> Int
+ Config.Number: rationalToNumber :: Rational -> Number
- Config: Number :: a -> Int -> Integer -> Value a
+ Config: Number :: a -> Number -> Value a
- Config.Lens: number :: Applicative f => (Integer -> f Integer) -> Value a -> f (Value a)
+ Config.Lens: number :: Applicative f => (Number -> f Number) -> Value a -> f (Value a)

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+0.7.0.0+---+* Updated number representation to preserve fractional part+  and added new `Config.Number` module with operations on+  this new type.+ 0.6.3.1 --- * Build on GHC 8.4.1
config-value.cabal view
@@ -1,47 +1,64 @@+cabal-version:       2.2 name:                config-value-version:             0.6.3.1+version:             0.7.0.0 synopsis:            Simple, layout-based value language similar to YAML or JSON license:             MIT license-file:        LICENSE author:              Eric Mertens maintainer:          emertens@gmail.com-copyright:           2015-2016 Eric Mertens+copyright:           2015-2016,2019 Eric Mertens category:            Language build-type:          Simple-cabal-version:       >=1.10 homepage:            https://github.com/glguy/config-value bug-reports:         https://github.com/glguy/config-value/issues description:         This package implements a language similar to YAML or JSON but                      with fewer special cases and fewer dependencies. It emphasizes                      layout structure for sections and lists, and requires quotes                      around strings.-tested-with:         GHC==7.10.3, GHC==8.0.2, GHC==8.2.1+tested-with:         GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.1 -extra-source-files:    README.md-                       CHANGELOG.md-                       config-value.vim+extra-source-files:+  README.md+  CHANGELOG.md+  config-value.vim  library -  exposed-modules:     Config,-                       Config.Lens+  exposed-modules:+    Config+    Config.Lens+    Config.Number -  other-modules:       Config.Lexer,-                       Config.LexerUtils,-                       Config.Parser,-                       Config.Tokens,-                       Config.Pretty,-                       Config.Value+  other-modules:+    Config.Lexer+    Config.LexerUtils+    Config.Parser+    Config.NumberParser+    Config.Tokens+    Config.Pretty+    Config.Value -  build-depends:       base       >= 4.8     && < 4.12,-                       array      >= 0.4     && < 0.6,-                       pretty     >= 1.1.1.0 && < 1.2,-                       text       >= 1.2.0.4 && < 1.3+  build-depends:+    base       >= 4.8     && < 4.14,+    array      >= 0.4     && < 0.6,+    pretty     >= 1.1.1.0 && < 1.2,+    text       >= 1.2.0.4 && < 1.3 +  build-tool-depends:+    alex:alex   ^>= 3.2.4,+    happy:happy ^>= 1.19.12,+   hs-source-dirs:      src-  build-tools:         alex, happy   default-language:    Haskell2010  source-repository head   type: git   location: git://github.com/glguy/config-value.git++test-suite unit-tests+  type:                 exitcode-stdio-1.0+  main-is:              Main.hs+  hs-source-dirs:       test+  build-depends:        base, config-value, text+  default-language:     Haskell2010+  ghc-options:          -Wall
src/Config.hs view
@@ -250,10 +250,18 @@   , Atom(..)   , valueAnn +  -- * Numbers+  , Number+  , numberToInteger+  , numberToRational+  , integerToNumber+  , rationalToNumber+   -- * Errors   , ParseError(..)   ) where +import           Config.Number (Number, numberToInteger, numberToRational, integerToNumber, rationalToNumber) import           Config.Value  (Atom(..), Value(..), Section(..), valueAnn) import           Config.Parser (parseValue) import           Config.Pretty (pretty)@@ -296,16 +304,12 @@ explainToken token =   case token of     T.Error e     -> explainError e-    T.Floating{}  -> "parse error: unexpected floating-point literal"     T.Atom atom   -> "parse error: unexpected atom: `" ++ Text.unpack atom ++ "`"     T.String str  -> "parse error: unexpected string: " ++ show (Text.unpack str)     T.Bullet      -> "parse error: unexpected bullet '*'"     T.Comma       -> "parse error: unexpected comma ','"     T.Section s   -> "parse error: unexpected section: `" ++ Text.unpack s ++ "`"-    T.Number 2  n -> "parse error: unexpected number: " ++ showIntAtBase' "0b"  2 intToDigit n ""-    T.Number 8  n -> "parse error: unexpected number: " ++ showIntAtBase' "0o"  8 intToDigit n ""-    T.Number 16 n -> "parse error: unexpected number: " ++ showIntAtBase' "0x" 16 intToDigit n ""-    T.Number _  n -> "parse error: unexpected number: " ++ showIntAtBase' ""   10 intToDigit n ""+    T.Number{}    -> "parse error: unexpected number"     T.OpenList    -> "parse error: unexpected start of list '['"     T.CloseList   -> "parse error: unexpected end of list ']'"     T.OpenMap     -> "parse error: unexpected start of section '{'"
src/Config/Lens.hs view
@@ -10,8 +10,8 @@ module Config.Lens   ( key   , text-  , number   , atom+  , number   , list   , values   , sections@@ -19,6 +19,7 @@   , valuePlate   ) where +import Config.Number import Config.Value import Data.Text @@ -56,11 +57,10 @@ atom f (Atom a t) = Atom a <$> f t atom _ v          = pure v --- | Traversal for the 'Integer' contained inside the given--- 'Value' when it is a 'Number'.-number :: Applicative f => (Integer -> f Integer) -> Value a -> f (Value a)-number f (Number a b n) = Number a b <$> f n-number _ v              = pure v+-- | Traversal for the 'Number' contained inside the given 'Value'.+number :: Applicative f => (Number -> f Number) -> Value a -> f (Value a)+number f (Number a n) = Number a <$> f n+number _ v            = pure v  -- | Traversal for the ['Value'] contained inside the given -- 'Value' when it is a 'List'.@@ -93,9 +93,8 @@ ann :: Functor f => (a -> f a) -> Value a -> f (Value a) ann f v =   case v of-    Sections a x   -> (\a' -> Sections a' x  ) <$> f a-    Number   a x y -> (\a' -> Number   a' x y) <$> f a-    Floating a x y -> (\a' -> Floating a' x y) <$> f a-    Text     a x   -> (\a' -> Text     a' x  ) <$> f a-    Atom     a x   -> (\a' -> Atom     a' x  ) <$> f a-    List     a x   -> (\a' -> List     a' x  ) <$> f a+    Sections a x -> (\a' -> Sections a' x) <$> f a+    Number   a x -> (\a' -> Number   a' x) <$> f a+    Text     a x -> (\a' -> Text     a' x) <$> f a+    Atom     a x -> (\a' -> Atom     a' x) <$> f a+    List     a x -> (\a' -> List     a' x) <$> f a
src/Config/Lexer.x view
@@ -52,6 +52,7 @@ @atom           = $alpha [$alpha $digit $unidigit \. _ \-]*  @exponent       = [Ee] [\-\+]? @decimal+@hexexponent    = [Pp] [\-\+]? @decimal  config :- @@ -65,11 +66,11 @@ ","                     { token_ Comma                  } "]"                     { token_ CloseList              } "*"                     { token_ Bullet                 }-"-"? 0 [Xx] @hexadecimal{ token (number 2 16)           }-"-"? 0 [Oo] @octal      { token (number 2  8)           }-"-"? 0 [Bb] @binary     { token (number 2  2)           }-"-"? @decimal           { token (number 0 10)           }-"-"? @decimal ("." @decimal)? @exponent? { token floating }++"-"? 0 [Xx] @hexadecimal ("." @hexadecimal?)? @hexexponent? { token number }+"-"? 0 [Oo] @octal       ("." @octal      ?)?               { token number }+"-"? 0 [Bb] @binary      ("." @binary     ?)?               { token number }+"-"?        @decimal     ("." @decimal    ?)? @exponent?    { token number } @atom                   { token Atom                    } @atom $white_no_nl* :   { token section                 } \"                      { startString                   }
src/Config/LexerUtils.hs view
@@ -20,7 +20,6 @@   , token_   , section   , number-  , floating    -- * Final actions   , untermString@@ -28,14 +27,17 @@   , errorAction   ) where -import Data.Char            (GeneralCategory(..), generalCategory, digitToInt,-                             isAscii, isSpace, ord, isDigit)-import Data.Text            (Text)-import Data.Word            (Word8)-import Numeric              (readInt)-import qualified Data.Text      as Text+import           Control.Applicative+import           Data.Char (GeneralCategory(..), generalCategory, digitToInt,+                            isAscii, isSpace, ord, isDigit, isHexDigit)+import           Data.Text (Text)+import           Data.Word (Word8)+import           Numeric   (readInt, readHex)+import qualified Data.Text as Text -import Config.Tokens+import           Config.Tokens+import           Config.Number+import qualified Config.NumberParser  ------------------------------------------------------------------------ -- Custom Alex wrapper - these functions are used by generated code@@ -148,39 +150,9 @@ -- given base. This function expect the token to be -- legal for the given base. This is checked by Alex. number ::-  Int  {- ^ prefix length      -} ->-  Int  {- ^ base               -} ->   Text {- ^ sign-prefix-digits -} ->   Token-number prefixLen base str =-  case readInt (fromIntegral base) (const True) digitToInt str2 of-    [(n,"")] -> Number base (s*n)-    _        -> error "number: Lexer failure"-  where-  str2     = drop prefixLen str1-  (s,str1) = case Text.unpack str of-               '-':rest -> (-1, rest)-               rest     -> ( 1, rest)---- | Construct a 'Floating' token from a lexeme.-floating ::-  Text {- ^ sign-integer-[. decimal][e exponent] -} ->-  Token-floating str = Floating (s * read (x1++x2)) (x3-fromIntegral (length x2))--  where-  (s,str1) = case Text.unpack str of-               '-':rest -> (-1, rest)-               rest     -> ( 1, rest)-  (x1,str2) = span isDigit str1-  (x2,str3) = case str2 of-                '.':xs -> span isDigit xs-                _ -> ("", str2)-  x3        = case str3 of-                []        -> 0-                _e:'+':xs -> read xs-                _e:xs     -> read xs-+number = Number . Config.NumberParser.number . Text.unpack . Text.toUpper  -- | Process a section heading token section :: Text -> Token
+ src/Config/Number.hs view
@@ -0,0 +1,88 @@+{-# Language DeriveDataTypeable, DeriveGeneric #-}+{-|+Module      : Config.Number+Description : Scientific-notation numbers with explicit radix+Copyright   : (c) Eric Mertens, 2019+License     : ISC+Maintainer  : emertens@gmail.com++This module provides a representation of numbers in scientific+notation.+-}+module Config.Number+  ( Number(..)+  , Radix(..)+  , radixToInt+  , numberToRational+  , numberToInteger+  , integerToNumber+  , rationalToNumber+  ) where++import Data.Ratio (numerator, denominator)+import Data.Data (Data)+import GHC.Generics (Generic)++-- | Numbers are represented as base, coefficient, and exponent.+--+-- The most convenient way to get numbers into and out of this form+-- is to use one of: 'numberToRational', 'numberToInteger',+-- 'rationalToNumber', or 'integerToNumber'.+--+-- This representation is explicit about the radix and exponent+-- used to facilitate better pretty-printing. By using explicit+-- exponents extremely large numbers can be represented compactly.+-- Consider that it is easy to write `1e100000000` which would use+-- a significant amount of memory if realized as an 'Integer'. This+-- representation allows concerned programs to check bounds before+-- converting to a representation like 'Integer'.+data Number = MkNumber+  { numberRadix       :: !Radix+  , numberCoefficient :: !Rational+  }+  deriving (Eq, Ord, Read, Show, Data, Generic)++-- | Radix used for a number. Some radix modes support an+-- exponent.+data Radix+  = Radix2           -- ^ binary, base 2+  | Radix8           -- ^ octal, base 8+  | Radix10 !Integer -- ^ decimal, base 10, exponent base 10+  | Radix16 !Integer -- ^ hexdecimal, base 16, exponent base 2+  deriving (Eq, Ord, Read, Show, Data, Generic)++-- | Returns the radix as an integer ignoring any exponent.+radixToInt :: Radix -> Int+radixToInt r =+  case r of+    Radix2 {} ->  2+    Radix8 {} ->  8+    Radix10{} -> 10+    Radix16{} -> 16++-- | Convert a number to a 'Rational'. Warning: This can use a+-- lot of member in the case of very large exponent parts.+numberToRational :: Number -> Rational+numberToRational (MkNumber r c) =+  case r of+    Radix2    -> c+    Radix8    -> c+    Radix10 e -> c * 10 ^^ e+    Radix16 e -> c * 2  ^^ e++-- | Convert a number to a 'Integer'. Warning: This can use a+-- lot of member in the case of very large exponent parts.+numberToInteger :: Number -> Maybe Integer+numberToInteger n+  | denominator r == 1 = Just $! numerator r+  | otherwise          = Nothing+  where+    r = numberToRational n++-- | 'Integer' to a radix 10 'Number' with no exponent+integerToNumber :: Integer -> Number+integerToNumber = rationalToNumber . fromInteger++-- | 'Rational' to a radix 10 'Number' with no exponent+rationalToNumber :: Rational -> Number+rationalToNumber r = (MkNumber (Radix10 0) r)
+ src/Config/NumberParser.y view
@@ -0,0 +1,125 @@+{+{-# LANGUAGE Trustworthy #-}++module Config.NumberParser where++import Data.List (foldl')+import Config.Number++}++%tokentype+    { Char}+%token+'+' { '+' }+'-' { '-' }+'.' { '.' }+'0' { '0' }+'1' { '1' }+'2' { '2' }+'3' { '3' }+'4' { '4' }+'5' { '5' }+'6' { '6' }+'7' { '7' }+'8' { '8' }+'9' { '9' }+'A' { 'A' }+'B' { 'B' }+'C' { 'C' }+'D' { 'D' }+'E' { 'E' }+'F' { 'F' }+'O' { 'O' }+'P' { 'P' }+'X' { 'X' }++%name number++%%++number ::                               { Number        }+  : '-' unsigned_number                 { negNum $2     }+  |     unsigned_number                 {        $1     }++unsigned_number+  : '0' 'X' hexadecimal fracpart(hexadecimal) exppart('P')+                                        { mkNum (Radix16 $5) $3 $4 }+  |         decimal     fracpart(decimal    ) exppart('E')+                                        { mkNum (Radix10 $3) $1 $2 }+  | '0' 'O' octal       fracpart(octal      )+                                        { mkNum Radix8 $3 $4 }+  | '0' 'B' binary      fracpart(binary     )+                                        { mkNum Radix2 $3 $4 }++fracpart(p) ::                          { [Int]         }+  :                                     { []            }+  | '.'                                 { []            }+  | '.' p                               { $2            }++exppart(p) ::                           { Integer       }+  :                                     { 0             }+  | p expnum                            { $2            }+++expnum ::                               { Integer       }+  : '+' decimal                         {   toInt 10 $2 }+  | '-' decimal                         { - toInt 10 $2 }+  |     decimal                         {   toInt 10 $1 }++hexadecimal ::                          { [Int]         }+  :             hexdigit                { [$1]          }+  | hexadecimal hexdigit                { $2 : $1       }++decimal ::                              { [Int]         }+  :         decdigit                    { [$1]          }+  | decimal decdigit                    { $2 : $1       }++octal ::                                { [Int]         }+  :       octdigit                      { [$1]          }+  | octal octdigit                      { $2 : $1       }++binary ::                               { [Int]         }+  :        bindigit                     { [$1]          }+  | binary bindigit                     { $2 : $1       }++hexdigit :: { Int }+  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}+  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}+  | '8' { 8} | '9' { 9} | 'A' {10} | 'B' {11}+  | 'C' {12} | 'D' {13} | 'E' {14} | 'F' {15}++decdigit+  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}+  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}+  | '8' { 8} | '9' { 9}++octdigit :: { Int }+  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}+  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}++bindigit :: { Int }+  : '0' { 0} | '1' { 1}++{++mkNum :: Radix -> [Int] -> [Int] -> Number+mkNum radix coef frac =+  MkNumber radix (fromInteger (toInt base coef) + toFrac base frac)+  where+    base = radixToInt radix++negNum :: Number -> Number+negNum n = n { numberCoefficient = - numberCoefficient n }++toInt :: Int -> [Int] -> Integer+toInt base = foldl' (\acc i -> acc*base' + fromIntegral i) 0 . reverse+  where base' = fromIntegral base++toFrac :: Int -> [Int] -> Rational+toFrac base = foldl' (\acc i -> (fromIntegral i+acc)/base') 0+  where base' = fromIntegral base++happyError [] = error "Unexpected EOF"+happyError (c:_) = error ("Unexpected: "++[c])+}
src/Config/Parser.y view
@@ -15,7 +15,6 @@ STRING                          { Located _ T.String{}          } ATOM                            { Located _ T.Atom{}            } NUMBER                          { Located _ T.Number{}          }-FLOATING                        { Located _ T.Floating{}        } '*'                             { Located $$ T.Bullet            } '['                             { Located $$ T.OpenList          } ','                             { Located _ T.Comma             }@@ -43,7 +42,6 @@  simple ::                       { Value Position                }   : NUMBER                      { number   $1                   }-  | FLOATING                    { floating $1                   }   | STRING                      { text     $1                   }   | ATOM                        { atom     $1                   }   | '{' inlinesections '}'      { Sections $1 (reverse $2)      }@@ -88,16 +86,10 @@ {  -- | Convert number token to number value. This needs a custom--- function like this because there are two value matched from+-- function like this because there are multiple values matched from -- the constructor. number :: Located Token -> Value Position-number = \(Located a (T.Number base val)) -> Number a base val---- | Convert floating token to floating value. This needs a custom--- function like this because there are two value matched from--- the constructor.-floating :: Located Token -> Value Position-floating = \(Located a (T.Floating coef expo)) -> Floating a coef expo+number = \(Located a (T.Number n)) -> Number a n  section :: Located Token -> Value Position -> Section Position section = \(Located a (T.Section k)) v -> Section a k v
src/Config/Pretty.hs view
@@ -3,12 +3,14 @@  import           Data.Char (isPrint, isDigit,intToDigit) import           Data.List (mapAccumL)+import           Data.Ratio (numerator, denominator) import qualified Data.Text as Text import           Text.PrettyPrint import           Numeric(showIntAtBase) import           Prelude hiding ((<>)) -import Config.Value+import           Config.Value+import           Config.Number  -- | Pretty-print a 'Value' as shown in the example. -- Sections will nest complex values underneath with@@ -19,26 +21,38 @@   case value of     Sections _ [] -> text "{}"     Sections _ xs -> prettySections xs-    Number _ b n  -> prettyNum b n-    Floating _ c e-> prettyFloating c e+    Number _ n    -> prettyNumber n     Text _ t      -> prettyText (Text.unpack t)     Atom _ t      -> text (Text.unpack (atomName t))     List _ []     -> text "[]"     List _ xs     -> vcat [ char '*' <+> pretty x | x <- xs ] --prettyNum :: Int -> Integer -> Doc-prettyNum b n-  | b == 16   = pref <> text "0x" <> num-  | b ==  8   = pref <> text "0o" <> num-  | b ==  2   = pref <> text "0b" <> num-  | otherwise = integer n+prettyNumber :: Number -> Doc+prettyNumber (MkNumber r c) =+  case r of+    Radix16 e -> pref <> text "0x" <> num <> expPart 'p' e+    Radix10 e -> pref <>              num <> expPart 'e' e+    Radix8    -> pref <> text "0o" <> num+    Radix2    -> pref <> text "0b" <> num   where-  pref = if n < 0 then char '-' else empty-  num  = text (showIntAtBase (fromIntegral b) intToDigit (abs n) "")+    radix = radixToInt r+    pref = if c < 0 then char '-' else empty+    num  = text (showIntAtBase (fromIntegral radix) intToDigit whole "") -- XXX+           <> fracPart frac+    (whole,frac) = properFraction (abs c)+    expPart _ 0 = text ""+    expPart c i = text (c : show i)+    fracPart 0 = text ""+    fracPart i = text ('.' : showFrac radix frac) -prettyFloating :: Integer -> Integer -> Doc-prettyFloating c e = text (show c ++ "e" ++ show e)+showFrac :: Int -> Rational -> String+showFrac radix 0 = ""+showFrac radix x = show w ++ rest+  where+    (w,f) = properFraction (x * fromIntegral radix)+    rest+      | denominator f < denominator x = showFrac radix f+      | otherwise = ""  prettyText :: String -> Doc prettyText = doubleQuotes . cat . snd . mapAccumL ppChar True@@ -79,6 +93,3 @@ isBig (Sections _ (_:_)) = True isBig (List _ (_:_))     = True isBig _                  = False---
src/Config/Tokens.hs view
@@ -10,6 +10,7 @@   ) where  import Data.Text (Text)+import Config.Number  -- | A position in a text file data Position = Position@@ -37,8 +38,7 @@   | Atom Text   | Bullet   | Comma-  | Number Int Integer-  | Floating Integer Integer+  | Number Number   | OpenList   | CloseList   | OpenMap
src/Config/Value.hs view
@@ -1,7 +1,7 @@ {-# Language DeriveGeneric, DeriveTraversable, DeriveDataTypeable #-}  -- | This module provides the types used in this package for configuration.--- Visit "ConfigFile.Parser" to parse values of this type in a convenient+-- Visit "Config.Parser" to parse values of this type in a convenient -- layout based notation. module Config.Value   ( Section(..)@@ -15,11 +15,13 @@ import Data.String  (IsString(..)) import GHC.Generics (Generic, Generic1) +import Config.Number (Number)+ -- | A single section of a 'Value' -- -- Example: -----    * @my-key: my-value@ is @'Section' ('Atom' "my-key") ('Atom' "my-value")@+--    * @my-key: my-value@ is @'Section' _ ('Atom' _ "my-key") ('Atom' _ "my-value")@ data Section a = Section   { sectionAnn   :: a   , sectionName  :: Text@@ -43,31 +45,15 @@  -- | Sum type of the values supported by this language. ----- The first field of the 'Number' constructor is the based used in the concrete--- syntax of the configuration value.------ The 'Floating' constructor stores the coefficient and power-of-10 exponent used in--- the concrete syntax. This allows representing numbers that would--- otherwise overflow a 'Double'.--- -- 'Value' is parameterized over an annotation type indented to be used for--- file position or other application specific information.------ Examples:------    * @0xff@ is @'Number' 16 255@------    * @123@  is @'Number' 10 123@------    * @123e10@ is @'Floating' 123 10@---    * @123.45@ is @'Floating' 12345 (-2)@+-- file position or other application specific information. When no+-- annotations are needed, '()' is a fine choice. data Value a   = Sections a [Section a] -- ^ lists of key-value pairs-  | Number   a Int Integer -- ^ integer literal base (2, 8, 10, or 16) and integer value-  | Floating a Integer Integer -- ^ coef exponent: coef * 10 ^ exponent-  | Text     a Text -- ^ quoted strings-  | Atom     a Atom -- ^ unquoted strings-  | List     a [Value a] -- ^ lists+  | Number   a Number      -- ^ numbers+  | Text     a Text        -- ^ quoted strings+  | Atom     a Atom        -- ^ unquoted strings+  | List     a [Value a]   -- ^ lists   deriving ( Eq, Read, Show, Typeable, Data            , Functor, Foldable, Traversable            , Generic, Generic1@@ -77,9 +63,8 @@ valueAnn :: Value a -> a valueAnn v =   case v of-    Sections a _   -> a-    Number   a _ _ -> a-    Floating a _ _ -> a-    Text     a _   -> a-    Atom     a _   -> a-    List     a _   -> a+    Sections a _ -> a+    Number   a _ -> a+    Text     a _ -> a+    Atom     a _ -> a+    List     a _ -> a
+ test/Main.hs view
@@ -0,0 +1,104 @@+{-# Language OverloadedStrings #-}+{-|+Module      : Main+Description : Unit tests for config-schema+Copyright   : (c) Eric Mertens, 2017+License     : ISC+Maintainer  : emertens@gmail.com+-}+module Main (main) where++import           Config+import           Config.Number+import           Control.Monad (unless)+import           Data.Foldable+import           Data.Text (Text)+import qualified Data.Text as Text++parseTest ::+  Value () {- ^ expected value -} ->+  [Text]   {- ^ input lines    -} ->+  IO ()+parseTest expected txts =+  case parse (Text.unlines txts) of+    Left e -> fail (show e)+    Right v ->+      unless ((() <$ v) == expected) (fail (show (expected, () <$ v)))++number :: Number -> Value ()+number = Number ()++atom :: Atom -> Value ()+atom = Atom ()++list :: [Value ()] -> Value ()+list = List ()++text :: Text -> Value ()+text = Text ()++sections :: [(Text, Value ())] -> Value ()+sections xs = Sections () [Section () k v | (k,v) <- xs]++main :: IO ()+main = sequenceA_++  [ parseTest (number (MkNumber (Radix10 0) 42))     ["42"]+  , parseTest (number (MkNumber (Radix10 56) 42))    ["42e56"]+  , parseTest (number (MkNumber (Radix10 56) 42.34)) ["42.34e56"]+  , parseTest (number (MkNumber (Radix10 0) 42.34))  ["42.34"]+  , parseTest (number (MkNumber (Radix10 0) 42))     ["42."]+  , parseTest (number (MkNumber (Radix10 0) 42))     ["042"]++  , parseTest (number (MkNumber (Radix16 0) 42))     ["0x2a"]+  , parseTest (number (MkNumber (Radix16 56) 42))    ["0x2ap56"]+  , parseTest (number (MkNumber (Radix16 56) (0x2a + (0x34 / 16^(2::Int))))) ["0x2a.34p56"]+  , parseTest (number (MkNumber (Radix16 0)  (0x2a + (0x3f / 16^(2::Int))))) ["0x2a.3f"]+  , parseTest (number (MkNumber (Radix16 0) 42))     ["0x2a."]+  , parseTest (number (MkNumber (Radix16 0) 42))     ["0x02a"]++  , parseTest (number (MkNumber Radix2 42))     ["0b101010"]+  , parseTest (number (MkNumber Radix2 4))     ["0b0100"]+  , parseTest (number (MkNumber Radix2 4))     ["0b0100."]+  , parseTest (number (MkNumber Radix2 (4 + (22 / 2^(6::Int))))) ["0b100.010110"]++  , parseTest (number (MkNumber Radix8 55))     ["0o67"]+  , parseTest (number (MkNumber Radix8 55))     ["0o67."]+  , parseTest (number (MkNumber Radix8 55))     ["0o067"]+  , parseTest (number (MkNumber Radix8 (55 + (10 / 64)))) ["0o67.12"]++  , parseTest (atom "example") ["example"]+  , parseTest (atom "one-two") ["one-two"]+  , parseTest (atom "one-1") ["one-1"]++  , parseTest (list []) ["[ ]"]+  , parseTest (list []) ["[]"]+  , parseTest (list [atom "x"]) ["[x]"]+  , parseTest (list [atom "x"]) ["* x"]+  , parseTest (list [atom "x", atom "y", atom "z"]) ["[x, y, z]"]+  , parseTest (list [atom "x", atom "y", atom "z"])+        ["* x", "* y", "* z"]+  , parseTest (list [atom "x", list [atom "y", atom "z"]]) ["[x,[y,z]]"]+  , parseTest (list [atom "x", list [atom "y", atom "z"]]) ["* x", "* [y,z]"]+  , parseTest (list [atom "x", list [atom "y", atom "z"]]) ["* x", "* * y", "  * z"]++  , parseTest (text "string") ["\"string\""]+  , parseTest (text "\10string\1\2") ["\"\\x0ast\\&r\\     \\ing\\SOH\\^B\""]+  , parseTest (text "string") ["\"str\\", "     \\ing\""]++  , parseTest (sections []) ["{}"]+  , parseTest (sections [("x", atom "y")]) ["{x:y}"]+  , parseTest (sections [("x", atom "y")]) ["x:y"]+  , parseTest (sections [("x", atom "y"), ("z", atom "w")]) ["{x:y,z:w}"]+  , parseTest (sections [("x", sections [("y", atom "z")])]) ["x:y:z"]+  , parseTest (sections [("x", sections [("y", atom "z")])])+      ["x:"+      ," y:"+      ,"  z"]+  , parseTest (sections [("x", list [atom "y", atom "z"])])+      ["x: * y"+      ,"   * z"]+  , parseTest (list [sections [("x", atom "y")], sections [("z", atom "w")]])+      ["* x: y"+      ,"* z: w"]+  ]