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 +6/−0
- config-value.cabal +37/−20
- src/Config.hs +9/−5
- src/Config/Lens.hs +11/−12
- src/Config/Lexer.x +6/−5
- src/Config/LexerUtils.hs +11/−39
- src/Config/Number.hs +88/−0
- src/Config/NumberParser.y +125/−0
- src/Config/Parser.y +2/−10
- src/Config/Pretty.hs +28/−17
- src/Config/Tokens.hs +2/−2
- src/Config/Value.hs +15/−30
- test/Main.hs +104/−0
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"]+ ]