packages feed

config-value 0.7.0.1 → 0.8.3

raw patch · 13 files changed

Files

CHANGELOG.md view
@@ -1,3 +1,34 @@+0.8.3+---+* Add `prettyInline` for layout-free pretty-printing++0.8.2.1+---+* Fix pretty-printing bug with long string literals++0.8.2+---+* Add `+` and `-` to the set of layout-based list syntax bullets.+  All elements of the list are checked to see that a consistent+  bullet is used. Different bullets might be used to help make+  nested lists more understandable. `-` might be used to make things+  look more like YAML++0.8.1+---+* Allow underscores in number literals+  Copied from <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0076-numeric-underscores.rst#new-syntax-this-proposal>++  Underscores are allowed and ignored+  - in the *middle* of integer-parts of the literal syntax+  - between base-markers (0x, 0o, 0b) and number part+  - before the `eEpP` part of an exponent++0.8+---+* Allow atoms and section names to start with `@` or `$`+* Add `Config.Macro` module+ 0.7.0.1 --- 
README.md view
@@ -1,10 +1,12 @@ # config-value -[![Hackage](https://img.shields.io/hackage/v/config-value.svg)](https://hackage.haskell.org/package/config-value) [![Build Status](https://secure.travis-ci.org/glguy/config-value.svg)](http://travis-ci.org/glguy/config-value)+[![Hackage](https://img.shields.io/hackage/v/config-value.svg)](https://hackage.haskell.org/package/config-value) [![Build Status](https://api.travis-ci.com/glguy/config-value.svg)](http://travis-ci.com/glguy/config-value)  This package implements a simple, layout-based value definition language used for supplying configuration values to various applications. +Before starting to use config-value, you probably want to read the documentation for [config-schema](https://github.com/glguy/config-schema) to see the user-friendly way to wrap this library.+ Live Demo -------- @@ -19,6 +21,8 @@     configuration:       {} -- empty section +    inline-maps: {key1: value1, key2: value2}+     sections:      "glguy" @@ -28,14 +32,16 @@        so you can comment out otherwise valid        portions of your config     -}-    atoms      : yes+    atoms:       yes -    decimal    : -1234+    decimal:     -1234     hexadecimal: 0x1234-    octal      : 0o1234-    binary     : 0b1010-    floating   : 12.34e56+    octal:       0o1234+    binary:      0b1010 +    floats:      [1e2, 0x3p-5, 24.48]+    underscores: 1_000_000+ lists:    * sections: in-lists      next-section: still-in-list@@ -45,7 +51,10 @@      * "lists"    * 3 -unicode : "standard Haskell format strings (1 ≤ 2)\x2228(2 ≤ 3)"+unicode: "standard Haskell format strings (1 ≤ 2)x2228(2 ≤ 3)"++multiline: "haskell style\+           \string gaps" ```  Format
config-value.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.2 name:                config-value-version:             0.7.0.1+version:             0.8.3 synopsis:            Simple, layout-based value language similar to YAML or JSON license:             MIT license-file:        LICENSE@@ -15,7 +15,7 @@                      with fewer special cases and fewer dependencies. It emphasizes                      layout structure for sections and lists, and requires quotes                      around strings.-tested-with:         GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.1+tested-with:         GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.4, GHC==8.10.2, GHC==9.0.1  extra-source-files:   README.md@@ -28,6 +28,7 @@     Config     Config.Lens     Config.Number+    Config.Macro    other-modules:     Config.Lexer@@ -39,14 +40,15 @@     Config.Value    build-depends:-    base       >= 4.8     && < 4.14,+    base       >= 4.8     && < 4.18,     array      >= 0.4     && < 0.6,+    containers >= 0.5     && < 0.7,     pretty     >= 1.1.1.0 && < 1.2,-    text       >= 1.2.0.4 && < 1.3+    text       >= 1.2.0.4 && < 2.1,    build-tool-depends:     alex:alex   ^>= 3.2.4,-    happy:happy ^>= 1.19.12,+    happy:happy >= 1.19 && <1.21,    hs-source-dirs:      src   default-language:    Haskell2010
config-value.vim view
@@ -17,11 +17,15 @@ syn match   cvFloat             "-\=[0-9]\+\.[0-9]\+\([eE][-+]\=[0-9]\+\)\=\>" syn match   cvFloat             "-\=[0-9]\+[eE][-+]\=[0-9]\+\>" +syn match   cvVariable          "$[a-zA-Z0-9\._\-]*\>"+syn match   cvDirective         "@[a-zA-Z0-9\._\-]*\>" syn match   cvAtom              "\<[a-zA-Z][a-zA-Z0-9\._\-]*\>"  syn match   cvLineComment      "--.*$" syn region  cvBlockComment     start="{-" end="-}" contains=cvString,cvBlockComment +hi def link cvVariable                    Macro+hi def link cvDirective                   Include hi def link cvAtom                        Identifier hi def link cvDelimiter                   Delimiter @@ -35,3 +39,7 @@ hi def link cvComment                     Comment  let b:current_syntax = "config-value"++setlocal commentstring=--%s+setlocal comments=:--+let b:undo_ftplugin = "setl com< commentstring<"
src/Config.hs view
@@ -91,8 +91,8 @@ indented to the right of the key.  The lexical syntax for section names is identical to the lexical syntax-of /atoms/. Section names are nonempty sequences starting with an /alpha/-character followed by zero or more /alpha/, /digit/, /period/ (.),+of /atoms/. Section names are nonempty sequences starting with an /alpha/,+@$@ or @\@@ character followed by zero or more /alpha/, /digit/, /period/ (.), underscore (_), or dash (-).  Section lists can be nested.@@ -128,9 +128,11 @@  Example: @[1, 2, 3]@ -Layout list entries are started with a leading @*@. Each leading @*@ must occur-in the some column of the file. Lists can be nested by starting the new list-on a column to the right of the current list.+Layout list entries are started with a leading @*@, @+@, or @-@. Each leading bullet+must occur in the some column of the file. Lists can be nested by starting the new+list on a column to the right of the current list. A single list must use the same+bullet token for every element of the list. Nested lists can choose a different+bullet. This can help visually distinguish nested lists.  Layout based lists can not occur inside inline list syntax. Layout based section lists can occur inside layout based lists@@ -205,8 +207,9 @@ /Atoms/ are unquoted strings that are distinct from normal /text/ values. This type is intended to represent enumerations in a configuration file. -Atoms are nonempty sequences starting with an /alpha/ character followed by-zero or more /alpha/, /digit/, /period/ (.), underscore (_), or dash (-).+Atoms are nonempty sequences starting with an /alpha/, @$@, or @\@@ character+followed by zero or more /alpha/, /digit/, /period/ (.), underscore (_), or+dash (-).  Lexical syntax: @$alpha [$alpha $digit $unidigit \\. _ \\-]*@ @@ -243,6 +246,7 @@    -- * Pretty-printing   , pretty+  , prettyInline    -- * Types   , Section(..)@@ -264,14 +268,12 @@ import           Config.Number (Number, numberToInteger, numberToRational, integerToNumber, rationalToNumber) import           Config.Value  (Atom(..), Value(..), Section(..), valueAnn) import           Config.Parser (parseValue)-import           Config.Pretty (pretty)+import           Config.Pretty (pretty, prettyInline) import           Config.Lexer  (scanTokens) import           Config.Tokens (Error(..), Position(..), Located(..), layoutPass, Token) import qualified Config.Tokens as T  import           Control.Exception (Exception(..))-import           Numeric (showIntAtBase)-import           Data.Char (intToDigit) import           Data.Text (Text) import qualified Data.Text as Text @@ -306,7 +308,7 @@     T.Error e     -> explainError e     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.Bullet s    -> "parse error: unexpected bullet '" ++ Text.unpack s ++ "'"     T.Comma       -> "parse error: unexpected comma ','"     T.Section s   -> "parse error: unexpected section: `" ++ Text.unpack s ++ "`"     T.Number{}    -> "parse error: unexpected number"@@ -317,11 +319,6 @@     T.LayoutSep   -> "parse error: unexpected end of block"     T.LayoutEnd   -> "parse error: unexpected end of block"     T.EOF         -> "parse error: unexpected end of file"--showIntAtBase' :: (Show a, Integral a) => String -> a -> (Int -> Char) -> a -> ShowS-showIntAtBase' pfx base toDigit n-  | n < 0     = showChar '-' . showString pfx . showIntAtBase base toDigit (negate n)-  | otherwise =                showString pfx . showIntAtBase base toDigit n  explainError :: Error -> String explainError e =
src/Config/Lexer.x view
@@ -21,19 +21,20 @@  $asciialpha     = [A-Z a-z] $digit          = [0-9]-$octdigit       = [0-7]-$hexdigit       = [0-9a-fA-F]-$bindigit       = [0-1]+$octit          = [0-7]+$hexit          = [0-9a-fA-F]+$binit          = [0-1] $white_no_nl    = $white # \n-$charesc        = [abfnrtv\\\"']+$charesc        = [abfnrtv\\\"'&] $cntrl          = [A-Z@\[\\\]\^_] $alpha          = [$unilower $uniupper $asciialpha] +@spacer         = _* -@decimal        = $digit+-@octal          = $octdigit+-@binary         = $bindigit+-@hexadecimal    = $hexdigit++@decimal        = $digit (@spacer $digit)*+@octal          = $octit (@spacer $octit)*+@binary         = $binit (@spacer $binit)*+@hexadecimal    = $hexit (@spacer $hexit)*  -- Copied from Haskell 2010 @ascii          = \^ $cntrl@@ -44,15 +45,14 @@                 | SP  | DEL @escape         =   $charesc                 |   @ascii-                |   @decimal-                | o @octal-                | x @hexadecimal-                | &+                |   $digit++                | o $octit++                | x $hexit+ -@atom           = $alpha [$alpha $digit $unidigit \. _ \-]*+@atom           = [$alpha \$ \@] [$alpha $digit $unidigit \. _ \-]* -@exponent       = [Ee] [\-\+]? @decimal-@hexexponent    = [Pp] [\-\+]? @decimal+@exponent       = @spacer [Ee] [\-\+]? @decimal+@hexexponent    = @spacer [Pp] [\-\+]? @decimal  config :- @@ -65,12 +65,14 @@ "["                     { token_ OpenList               } ","                     { token_ Comma                  } "]"                     { token_ CloseList              }-"*"                     { token_ Bullet                 }+"*"                     { token Bullet                  }+"-"                     { token Bullet                  }+"+"                     { token Bullet                  } -"-"? 0 [Xx] @hexadecimal ("." @hexadecimal?)? @hexexponent? { token number }-"-"? 0 [Oo] @octal       ("." @octal      ?)?               { token number }-"-"? 0 [Bb] @binary      ("." @binary     ?)?               { token number }-"-"?        @decimal     ("." @decimal    ?)? @exponent?    { token number }+"-"? 0 [Xx] @spacer @hexadecimal ("." @hexadecimal?)? @hexexponent? { token number }+"-"? 0 [Oo] @spacer @octal       ("." @octal      ?)?               { token number }+"-"? 0 [Bb] @spacer @binary      ("." @binary     ?)?               { token number }+"-"?                @decimal     ("." @decimal    ?)? @exponent?    { token number } @atom                   { token Atom                    } @atom $white_no_nl* :   { token section                 } \"                      { startString                   }
src/Config/LexerUtils.hs view
@@ -27,16 +27,12 @@   , errorAction   ) where -import           Control.Applicative-import           Data.Char (GeneralCategory(..), generalCategory, digitToInt,-                            isAscii, isSpace, ord, isDigit, isHexDigit)+import           Data.Char (GeneralCategory(..), generalCategory, isAscii, isSpace, ord) import           Data.Text (Text) import           Data.Word (Word8)-import           Numeric   (readInt, readHex) import qualified Data.Text as Text  import           Config.Tokens-import           Config.Number import qualified Config.NumberParser  ------------------------------------------------------------------------@@ -152,7 +148,8 @@ number ::   Text {- ^ sign-prefix-digits -} ->   Token-number = Number . Config.NumberParser.number . Text.unpack . Text.toUpper+number = Number . Config.NumberParser.number+       . Text.unpack . Text.toUpper . Text.filter ('_' /=)  -- | Process a section heading token section :: Text -> Token
+ src/Config/Macro.hs view
@@ -0,0 +1,260 @@+{-# LANGUAGE Safe, OverloadedStrings, DeriveTraversable, RankNTypes #-}+{-|+Module      : Config.Macro+Description : Configuration pre-processor adding support for aliases and common sections+Copyright   : (c) Eric Mertens, 2020+License     : ISC+Maintainer  : emertens@gmail.com++This module provides assigns meaning to atoms and section names that start with @\@@+and @$@. It provides processing pass for configuration to use local variables and+inclusion to better structure configuration.++= Sigils++* @$@ starts a variable.+* @\@@ starts a directive.++Merge key-value mappings using @\@splice@.++Load external configuration with @\@load@.++= Variables++Variables are atoms that start with a @$@ sigil. Variables are defined by+setting a variable as a section name. This variable will remain in+scope for the remainder of the sections being defined.++Variables used in a value position will be replaced with their previously+defined values.++@+$example: 42+field1: $example+field2: [0, $example]+@++expands to++@+field1: 42+field2: [0, 42]+@++Later variable definitions will shadow earlier definitions.++@+{ $x: 1, $x: 2, k: $x }+@++expands to++@+{ k: 2 }+@++Scoping examples:++@+top1:+  a:  $x                     -- BAD: $x not defined yet+  $x: 42                     -- $x is now defined to be 42+  b:  $x                     -- OK: $x was defined above+  c:  {sub1: $x, sub2: [$x]} -- OK: $x in scope in subsections+                             -- note: $x now goes out of scope+top2: $x                     -- BAD: $x no longer in scope+@++Macros are expanded at their definition site. All variables are resolved before+adding the new variable into the environment. Variables are lexically scoped+rather than dynamically scoped.++Allowed:++@+$x: 1+$y: $x -- OK, y is now 1+@++Not allowed:++@+$y: $x -- BAD: $x was not in scope+$x: 1+z:  $y+@++= Sections splicing++One sections value can be spliced into another sections value using the @\@splice@+directive. It is an error to splice a value that is not a key-value sections.++@+$xy: { x: 0, y: 1 }+example:+  \@splice: $xy+  z: 2+@++expands to++@+example:+  x: 0+  y: 1+  z: 2+@++= File loading++The @\@load@ directive is intended including configuration from other sources.+'loadFileWithMacros' provides an interpretation of this directive that loads+other files. An arbitrary interpretation can be defined with 'expandMacros''++To load a value define a key-value mapping with a single @\@load@ key with a+value specifying the location to load from.++@+x: @load: "fourty-two.cfg"+@++could expand to++@+x: 42+@++-}+module Config.Macro (+  -- * Macro expansion primitives+  MacroError(..),+  expandMacros,+  expandMacros',++  -- * File loader with inclusion+  LoadFileError(..),+  FilePosition(..),+  loadFileWithMacros+  ) where++import Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.Text.IO as Text+import Control.Exception+import Config+import Data.Map (Map)+import Data.Typeable (Typeable)+import qualified Data.Map as Map++-- | Errors from macro expansion annotated with the 'valueAnn' from+-- the 'Value' nearest to the problem (typically a file position).+data MacroError a+  = UndeclaredVariable a Text -- ^ Variable used before its defintion+  | UnknownDirective a Text   -- ^ Unknown directive+  | BadSplice a               -- ^ Incorrect use of @\@splice@+  | BadLoad a                 -- ^ Incorrect use of @\@load@+  deriving+  (Eq, Read, Show, Functor, Foldable, Traversable)++instance (Typeable a, Show a) => Exception (MacroError a)++data Special = Plain | Variable Text | Splice | Load++processAtom :: a -> Text -> Either (MacroError a) Special+processAtom a txt =+  case Text.uncons txt of+    Just ('@',"splice") -> Right Splice+    Just ('@',"load"  ) -> Right Load+    Just ('@',t       ) -> Left (UnknownDirective a t)+    Just ('$',t       ) -> Right (Variable t)+    _                   -> Right Plain++-- | Expand macros in a configuration value.+--+-- @\@load@ not supported and results in a 'BadLoad' error.+expandMacros :: Value a -> Either (MacroError a) (Value a)+expandMacros = expandMacros' Left (Left . BadLoad . valueAnn) Map.empty++-- | Expand macros in a configuration value using a pre-populated environment.+expandMacros' ::+  Monad m =>+  (forall b. MacroError a -> m b) {- ^ failure                       -} ->+  (Value a -> m (Value a))        {- ^ @\@load@ implementation       -} ->+  Map Text (Value a)              {- ^ variable environment          -} ->+  Value a                         {- ^ value to expand               -} ->+  m (Value a)                     {- ^ expanded value                -}+expandMacros' failure load = go+  where+    proc a txt = either failure pure (processAtom a txt)++    go env v =+      case v of+        Number a x -> pure (Number a x)+        Text a x -> pure (Text a x)+        List a x -> List a <$> traverse (go env) x++        Sections _ [Section _ "@load" arg] -> load =<< go env arg+        Sections a x -> Sections a <$> elaborateSections env x++        Atom a x ->+          do x' <- proc a (atomName x)+             case x' of+               Plain -> pure (Atom a x)+               Splice -> failure (BadSplice a)+               Load -> failure (BadLoad a)+               Variable var ->+                 case Map.lookup var env of+                   Nothing -> failure (UndeclaredVariable a var)+                   Just y -> pure y++    elaborateSections _ [] = pure []+    elaborateSections env (Section a k v : xs) =+      do special <- proc a k+         v' <- go env v+         case special of+           Load -> failure (BadLoad a)+           Variable var -> elaborateSections (Map.insert var v' env) xs+           Plain -> (Section a k v' :) <$> elaborateSections env xs+           Splice ->+             case v' of+               Sections _ ys -> (ys++) <$> elaborateSections env xs+               _ -> failure (BadSplice a)++-- | A pair of filepath and position+data FilePosition = FilePosition FilePath Position+  deriving (Read, Show, Ord, Eq)++-- | Errors thrown by 'loadFileWithMacros'+data LoadFileError+  = LoadFileParseError FilePath ParseError -- ^ failure to parse a file+  | LoadFileMacroError (MacroError FilePosition) -- ^ failure to expand macros+  deriving (Eq, Read, Show)++instance Exception LoadFileError++-- | Load a configuration value from a given file path.+--+-- @\@load@ will compute included file path from the given function given the+-- load argument and current configuration file path.+--+-- Valid @\@load@ arguments are string literals use as arguments to+-- the path resolution function.+--+-- Throws `IOError` from file loads and `LoadFileError`+loadFileWithMacros ::+  (Text -> FilePath -> IO FilePath) {- ^ inclusion path resolution -} ->+  FilePath                          {- ^ starting file path -} ->+  IO (Value FilePosition)           {- ^ macro-expanded config value -}+loadFileWithMacros findPath = go+  where+    go path =+      do txt <- Text.readFile path+         v1 <- case parse txt of+                 Left e -> throwIO (LoadFileParseError path e)+                 Right v -> pure v+         let v2 = FilePosition path <$> v1+         let loadImpl pathVal =+               case pathVal of+                 Text _ str -> go =<< findPath str path+                 _ -> throwIO (LoadFileMacroError (BadLoad (valueAnn pathVal)))+         expandMacros' (throwIO . LoadFileMacroError) loadImpl Map.empty v2
src/Config/Number.hs view
@@ -1,4 +1,4 @@-{-# Language DeriveDataTypeable, DeriveGeneric #-}+{-# Language DeriveDataTypeable, DeriveGeneric, Safe #-} {-| Module      : Config.Number Description : Scientific-notation numbers with explicit radix@@ -61,17 +61,17 @@     Radix16{} -> 16  -- | Convert a number to a 'Rational'. Warning: This can use a--- lot of member in the case of very large exponent parts.+-- lot of memory 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+    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.+-- lot of memory in the case of very large exponent parts. numberToInteger :: Number -> Maybe Integer numberToInteger n   | denominator r == 1 = Just $! numerator r@@ -85,4 +85,4 @@  -- | 'Rational' to a radix 10 'Number' with no exponent rationalToNumber :: Rational -> Number-rationalToNumber r = (MkNumber (Radix10 0) r)+rationalToNumber = MkNumber (Radix10 0)
src/Config/Parser.y view
@@ -1,5 +1,5 @@ {-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE Trustworthy, OverloadedStrings #-}  module Config.Parser (parseValue) where @@ -15,11 +15,13 @@ STRING                          { Located _ T.String{}          } ATOM                            { Located _ T.Atom{}            } NUMBER                          { Located _ T.Number{}          }-'*'                             { Located $$ T.Bullet            }-'['                             { Located $$ T.OpenList          }+'*'                             { Located $$ (T.Bullet "*")     }+'+'                             { Located $$ (T.Bullet "+")     }+'-'                             { Located $$ (T.Bullet "-")     }+'['                             { Located $$ T.OpenList         } ','                             { Located _ T.Comma             } ']'                             { Located _ T.CloseList         }-'{'                             { Located $$ T.OpenMap           }+'{'                             { Located $$ T.OpenMap          } '}'                             { Located _ T.CloseMap          } SEP                             { Located _ T.LayoutSep         } END                             { Located _ T.LayoutEnd         }@@ -37,7 +39,9 @@  value ::                        { Value Position                }   : sections END                { sections $1                   }-  | '*' list END                { List $1 (reverse $2)          }+  | '*' list('*') END           { List $1 (reverse $2)          }+  | '-' list('-') END           { List $1 (reverse $2)          }+  | '+' list('+') END           { List $1 (reverse $2)          }   | simple                      { $1                            }  simple ::                       { Value Position                }@@ -70,9 +74,9 @@ section ::                      { Section Position              }   : SECTION value               { section $1 $2                 } -list ::                         { [Value Position]              }-  :              value          { [$1]                          }-  | list SEP '*' value          { $4 : $1                       }+list(blt) ::                    { [Value Position]              }+  :                   value     { [$1]                          }+  | list(blt) SEP blt value     { $4 : $1                       }  inlinelist ::                   { [Value Position]              }   :                             { []                            }
src/Config/Pretty.hs view
@@ -1,9 +1,10 @@+{-# Language Safe #-} -- | Pretty-printing implementation for 'Value'-module Config.Pretty (pretty) where+module Config.Pretty (pretty, prettyInline) where  import           Data.Char (isPrint, isDigit,intToDigit) import           Data.List (mapAccumL)-import           Data.Ratio (numerator, denominator)+import           Data.Ratio (denominator) import qualified Data.Text as Text import           Text.PrettyPrint import           Numeric(showIntAtBase)@@ -37,16 +38,17 @@   where     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)+    num  = text (showIntAtBase (fromIntegral radix) intToDigit whole "")+           <> fracPart+    (whole,frac) = properFraction (abs c) :: (Integer, Rational)     expPart _ 0 = text ""-    expPart c i = text (c : show i)-    fracPart 0 = text ""-    fracPart i = text ('.' : showFrac radix frac)+    expPart p i = text (p : show i)+    fracPart+      | 0 == frac = text ""+      | otherwise = text ('.' : showFrac radix frac)  showFrac :: Int -> Rational -> String-showFrac radix 0 = ""+showFrac _ 0 = "" showFrac radix x = intToDigit w : rest   where     (w,f) = properFraction (x * fromIntegral radix)@@ -55,7 +57,7 @@       | otherwise = ""  prettyText :: String -> Doc-prettyText = doubleQuotes . cat . snd . mapAccumL ppChar True+prettyText = doubleQuotes . hcat . snd . mapAccumL ppChar True    where ppChar s x           | isDigit x = (True, if not s then text "\\&" <> char x else char x)@@ -93,3 +95,15 @@ isBig (Sections _ (_:_)) = True isBig (List _ (_:_))     = True isBig _                  = False++-- | Pretty-printer that uses no layout for sections or lists.+prettyInline :: Value a -> Doc+prettyInline value =+  case value of+    Number _ n    -> prettyNumber n+    Text _ t      -> prettyText (Text.unpack t)+    Atom _ t      -> text (Text.unpack (atomName t))+    List _ xs     -> brackets (list (map prettyInline xs))+    Sections _ xs -> braces (list [text (Text.unpack k) <> colon <> prettyInline v | Section _ k v <- xs])+  where+    list = hcat . punctuate comma
src/Config/Tokens.hs view
@@ -1,3 +1,4 @@+{-# Language Safe #-} -- | This module provides the token type used in the lexer and -- parser and provides the extra pass to insert layout tokens. module Config.Tokens@@ -10,7 +11,7 @@   ) where  import Data.Text (Text)-import Config.Number+import Config.Number (Number)  -- | A position in a text file data Position = Position@@ -36,7 +37,7 @@   = Section Text   | String Text   | Atom Text-  | Bullet+  | Bullet Text   | Comma   | Number Number   | OpenList@@ -99,5 +100,5 @@ usesLayout :: Located Token -> Bool usesLayout t   | Section{} <- locThing t = True-  | Bullet    <- locThing t = True+  | Bullet{}  <- locThing t = True   | otherwise               = False
src/Config/Value.hs view
@@ -1,4 +1,4 @@-{-# Language DeriveGeneric, DeriveTraversable, DeriveDataTypeable #-}+{-# Language DeriveGeneric, DeriveTraversable, DeriveDataTypeable, Safe #-}  -- | This module provides the types used in this package for configuration. -- Visit "Config.Parser" to parse values of this type in a convenient@@ -21,7 +21,7 @@ -- -- Example: -----    * @my-key: my-value@ is @'Section' _ ('Atom' _ "my-key") ('Atom' _ "my-value")@+--    * @my-key: my-value@ is @'Section' _ "my-key" ('Atom' _ "my-value")@ data Section a = Section   { sectionAnn   :: a   , sectionName  :: Text