Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ghc-toml-parser for openSUSE:Factory checked in at 2023-12-05 17:03:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ghc-toml-parser (Old) and /work/SRC/openSUSE:Factory/.ghc-toml-parser.new.25432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ghc-toml-parser" Tue Dec 5 17:03:50 2023 rev:2 rq:1130924 version:1.3.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/ghc-toml-parser/ghc-toml-parser.changes 2023-11-23 21:43:08.072986124 +0100 +++ /work/SRC/openSUSE:Factory/.ghc-toml-parser.new.25432/ghc-toml-parser.changes 2023-12-05 17:04:13.919394105 +0100 @@ -1,0 +2,9 @@ +Tue Nov 28 18:44:21 UTC 2023 - Peter Simons <psim...@suse.com> + +- Update toml-parser to version 1.3.1.0. + ## 1.3.1.0 + + * Added `Toml.Semantics.Ordered` for preserving input TOML orderings + * Added support for pretty-printing multi-line strings + +------------------------------------------------------------------- Old: ---- toml-parser-1.3.0.0.tar.gz New: ---- toml-parser-1.3.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ghc-toml-parser.spec ++++++ --- /var/tmp/diff_new_pack.6PI80X/_old 2023-12-05 17:04:15.371447629 +0100 +++ /var/tmp/diff_new_pack.6PI80X/_new 2023-12-05 17:04:15.375447776 +0100 @@ -20,7 +20,7 @@ %global pkgver %{pkg_name}-%{version} %bcond_with tests Name: ghc-%{pkg_name} -Version: 1.3.0.0 +Version: 1.3.1.0 Release: 0 Summary: TOML 1.0.0 parser License: ISC ++++++ toml-parser-1.3.0.0.tar.gz -> toml-parser-1.3.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/ChangeLog.md new/toml-parser-1.3.1.0/ChangeLog.md --- old/toml-parser-1.3.0.0/ChangeLog.md 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/ChangeLog.md 2001-09-09 03:46:40.000000000 +0200 @@ -1,5 +1,10 @@ # Revision history for toml-parser +## 1.3.1.0 + +* Added `Toml.Semantics.Ordered` for preserving input TOML orderings +* Added support for pretty-printing multi-line strings + ## 1.3.0.0 -- 2023-07-16 * Make more structured error messages available in the low-level modules. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/README.lhs new/toml-parser-1.3.1.0/README.lhs --- old/toml-parser-1.3.0.0/README.lhs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/README.lhs 2001-09-09 03:46:40.000000000 +0200 @@ -41,18 +41,27 @@ to ensure that its code typechecks and stays in sync with the rest of the package. ```haskell -import Toml (parse, decode, Value(..)) -import Toml.FromValue (FromValue(fromValue), parseTableFromValue, reqKey, optKey) +import GHC.Generics (Generic) +import QuoteStr (quoteStr) +import Test.Hspec (Spec, hspec, it, shouldBe) +import Toml (parse, decode, encode, Value(..)) +import Toml.FromValue (Result(Success), FromValue(fromValue), parseTableFromValue, reqKey) import Toml.FromValue.Generic (genericParseTable) -import Toml.ToValue (ToValue(toValue), ToTable(toTable), defaultTableToValue) +import Toml.ToValue (ToValue(toValue), ToTable(toTable), defaultTableToValue, table, (.=)) import Toml.ToValue.Generic (genericToTable) -import GHC.Generics (Generic) -main = pure () + +main :: IO () +main = hspec (parses >> decodes >> encodes) ``` ### Using the raw parser -Consider this sample TOML text from the specification. +Consider this sample TOML text from the TOML specification. + +```haskell +fruitStr :: String +fruitStr = [quoteStr| +``` ```toml [[fruits]] @@ -76,52 +85,58 @@ name = "plantain" ``` +```haskell +|] +``` + Parsing using this package generates the following value -```haskell ignore ->>> parse fruitStr -Right (fromList [ - ("fruits",Array [ - Table (fromList [ - ("name",String "apple"), - ("physical",Table (fromList [ - ("color",String "red"), - ("shape",String "round")])), - ("varieties",Array [ - Table (fromList [("name",String "red delicious")]), - Table (fromList [("name",String "granny smith")])])]), - Table (fromList [ - ("name",String "banana"), - ("varieties",Array [ - Table (fromList [("name",String "plantain")])])])])]) -``` - -We can render this parsed value back to TOML text using `prettyToml fruitToml`. -In this case the input was already sorted, so the generated text will happen -to match almost exactly. +```haskell +parses :: Spec +parses = it "parses" $ + parse fruitStr + `shouldBe` + Right (table [ + ("fruits", Array [ + Table (table [ + ("name", String "apple"), + ("physical", Table (table [ + ("color", String "red"), + ("shape", String "round")])), + ("varieties", Array [ + Table (table [("name", String "red delicious")]), + Table (table [("name", String "granny smith")])])]), + Table (table [ + ("name", String "banana"), + ("varieties", Array [ + Table (table [("name", String "plantain")])])])])]) +``` ### Using decoding classes Here's an example of defining datatypes and deserializers for the TOML above. +The `FromValue` typeclass is used to encode each datatype into a TOML value. +Instances can be derived for simple record types. More complex examples can +be manually derived. ```haskell -newtype Fruits = Fruits [Fruit] - deriving (Eq, Show) +newtype Fruits = Fruits { fruits :: [Fruit] } + deriving (Eq, Show, Generic) -data Fruit = Fruit String (Maybe Physical) [Variety] - deriving (Eq, Show) +data Fruit = Fruit { name :: String, physical :: Maybe Physical, varieties :: [Variety] } + deriving (Eq, Show, Generic) -data Physical = Physical String String - deriving (Eq, Show) +data Physical = Physical { color :: String, shape :: String } + deriving (Eq, Show, Generic) newtype Variety = Variety String - deriving (Eq, Show) + deriving (Eq, Show, Generic) instance FromValue Fruits where - fromValue = parseTableFromValue (Fruits <$> reqKey "fruits") + fromValue = parseTableFromValue genericParseTable instance FromValue Fruit where - fromValue = parseTableFromValue (Fruit <$> reqKey "name" <*> optKey "physical" <*> reqKey "varieties") + fromValue = parseTableFromValue genericParseTable instance FromValue Physical where fromValue = parseTableFromValue (Physical <$> reqKey "color" <*> reqKey "shape") @@ -132,32 +147,67 @@ We can run this example on the original value to deserialize it into domain-specific datatypes. -```haskell ignore ->>> decode fruitStr :: Result Fruits -Success [] (Fruits [ - Fruit "apple" (Just (Physical "red" "round")) [Variety "red delicious", Variety "granny smith"], - Fruit "banana" Nothing [Variety "plantain"]]) -``` - -### Generics +```haskell +decodes :: Spec +decodes = it "decodes" $ + decode fruitStr + `shouldBe` + Success [] (Fruits [ + Fruit + "apple" + (Just (Physical "red" "round")) + [Variety "red delicious", Variety "granny smith"], + Fruit "banana" Nothing [Variety "plantain"]]) +``` + +### Using encoding classes + +The `ToValue` class is for all datatypes that can be encoded into TOML. +The more specialized `ToTable` class is for datatypes that encode into +tables and are thus elligible to be top-level types (all TOML documents +are tables at the top-level). -Code for generating and matching tables to records can be derived -using GHC.Generics. This will generate tables using the field names -as table keys. +Generics can be used to derive `ToTable` for simple record types. +Manually defined instances are available for the more complex cases. ```haskell -data ExampleRecord = ExampleRecord { - exString :: String, - exList :: [Int], - exOpt :: Maybe Bool} - deriving (Show, Generic, Eq) +instance ToValue Fruits where toValue = defaultTableToValue +instance ToValue Fruit where toValue = defaultTableToValue +instance ToValue Physical where toValue = defaultTableToValue +instance ToValue Variety where toValue = defaultTableToValue + +instance ToTable Fruits where toTable = genericToTable +instance ToTable Fruit where toTable = genericToTable +instance ToTable Physical where toTable x = table ["color" .= color x, "shape" .= shape x] +instance ToTable Variety where toTable (Variety x) = table ["name" .= x] + +encodes :: Spec +encodes = it "encodes" $ + show (encode (Fruits [Fruit + "apple" + (Just (Physical "red" "round")) + [Variety "red delicious", Variety "granny smith"]])) + `shouldBe` [quoteStr| + [[fruits]] + name = "apple" + + [fruits.physical] + color = "red" + shape = "round" -instance FromValue ExampleRecord where fromValue = parseTableFromValue genericParseTable -instance ToTable ExampleRecord where toTable = genericToTable -instance ToValue ExampleRecord where toValue = defaultTableToValue + [[fruits.varieties]] + name = "red delicious" + + [[fruits.varieties]] + name = "granny smith"|] ``` -### Larger Example +## More Examples A demonstration of using this package at a more realistic scale -can be found in [HieDemoSpec](test/HieDemoSpec.hs). +can be found in [HieDemoSpec](test/HieDemoSpec.hs). The various unit +test files demonstrate what you can do with this library and what +outputs you can expect. + +See the low-level operations used to build a TOML syntax highlighter +in [TomlHighlighter](test-drivers/highlighter/Main.hs). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/README.md new/toml-parser-1.3.1.0/README.md --- old/toml-parser-1.3.0.0/README.md 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/README.md 2001-09-09 03:46:40.000000000 +0200 @@ -41,18 +41,27 @@ to ensure that its code typechecks and stays in sync with the rest of the package. ```haskell -import Toml (parse, decode, Value(..)) -import Toml.FromValue (FromValue(fromValue), parseTableFromValue, reqKey, optKey) +import GHC.Generics (Generic) +import QuoteStr (quoteStr) +import Test.Hspec (Spec, hspec, it, shouldBe) +import Toml (parse, decode, encode, Value(..)) +import Toml.FromValue (Result(Success), FromValue(fromValue), parseTableFromValue, reqKey) import Toml.FromValue.Generic (genericParseTable) -import Toml.ToValue (ToValue(toValue), ToTable(toTable), defaultTableToValue) +import Toml.ToValue (ToValue(toValue), ToTable(toTable), defaultTableToValue, table, (.=)) import Toml.ToValue.Generic (genericToTable) -import GHC.Generics (Generic) -main = pure () + +main :: IO () +main = hspec (parses >> decodes >> encodes) ``` ### Using the raw parser -Consider this sample TOML text from the specification. +Consider this sample TOML text from the TOML specification. + +```haskell +fruitStr :: String +fruitStr = [quoteStr| +``` ```toml [[fruits]] @@ -76,52 +85,58 @@ name = "plantain" ``` +```haskell +|] +``` + Parsing using this package generates the following value -```haskell ignore ->>> parse fruitStr -Right (fromList [ - ("fruits",Array [ - Table (fromList [ - ("name",String "apple"), - ("physical",Table (fromList [ - ("color",String "red"), - ("shape",String "round")])), - ("varieties",Array [ - Table (fromList [("name",String "red delicious")]), - Table (fromList [("name",String "granny smith")])])]), - Table (fromList [ - ("name",String "banana"), - ("varieties",Array [ - Table (fromList [("name",String "plantain")])])])])]) -``` - -We can render this parsed value back to TOML text using `prettyToml fruitToml`. -In this case the input was already sorted, so the generated text will happen -to match almost exactly. +```haskell +parses :: Spec +parses = it "parses" $ + parse fruitStr + `shouldBe` + Right (table [ + ("fruits", Array [ + Table (table [ + ("name", String "apple"), + ("physical", Table (table [ + ("color", String "red"), + ("shape", String "round")])), + ("varieties", Array [ + Table (table [("name", String "red delicious")]), + Table (table [("name", String "granny smith")])])]), + Table (table [ + ("name", String "banana"), + ("varieties", Array [ + Table (table [("name", String "plantain")])])])])]) +``` ### Using decoding classes Here's an example of defining datatypes and deserializers for the TOML above. +The `FromValue` typeclass is used to encode each datatype into a TOML value. +Instances can be derived for simple record types. More complex examples can +be manually derived. ```haskell -newtype Fruits = Fruits [Fruit] - deriving (Eq, Show) +newtype Fruits = Fruits { fruits :: [Fruit] } + deriving (Eq, Show, Generic) -data Fruit = Fruit String (Maybe Physical) [Variety] - deriving (Eq, Show) +data Fruit = Fruit { name :: String, physical :: Maybe Physical, varieties :: [Variety] } + deriving (Eq, Show, Generic) -data Physical = Physical String String - deriving (Eq, Show) +data Physical = Physical { color :: String, shape :: String } + deriving (Eq, Show, Generic) newtype Variety = Variety String - deriving (Eq, Show) + deriving (Eq, Show, Generic) instance FromValue Fruits where - fromValue = parseTableFromValue (Fruits <$> reqKey "fruits") + fromValue = parseTableFromValue genericParseTable instance FromValue Fruit where - fromValue = parseTableFromValue (Fruit <$> reqKey "name" <*> optKey "physical" <*> reqKey "varieties") + fromValue = parseTableFromValue genericParseTable instance FromValue Physical where fromValue = parseTableFromValue (Physical <$> reqKey "color" <*> reqKey "shape") @@ -132,32 +147,67 @@ We can run this example on the original value to deserialize it into domain-specific datatypes. -```haskell ignore ->>> decode fruitStr :: Result Fruits -Success [] (Fruits [ - Fruit "apple" (Just (Physical "red" "round")) [Variety "red delicious", Variety "granny smith"], - Fruit "banana" Nothing [Variety "plantain"]]) -``` - -### Generics +```haskell +decodes :: Spec +decodes = it "decodes" $ + decode fruitStr + `shouldBe` + Success [] (Fruits [ + Fruit + "apple" + (Just (Physical "red" "round")) + [Variety "red delicious", Variety "granny smith"], + Fruit "banana" Nothing [Variety "plantain"]]) +``` + +### Using encoding classes + +The `ToValue` class is for all datatypes that can be encoded into TOML. +The more specialized `ToTable` class is for datatypes that encode into +tables and are thus elligible to be top-level types (all TOML documents +are tables at the top-level). -Code for generating and matching tables to records can be derived -using GHC.Generics. This will generate tables using the field names -as table keys. +Generics can be used to derive `ToTable` for simple record types. +Manually defined instances are available for the more complex cases. ```haskell -data ExampleRecord = ExampleRecord { - exString :: String, - exList :: [Int], - exOpt :: Maybe Bool} - deriving (Show, Generic, Eq) +instance ToValue Fruits where toValue = defaultTableToValue +instance ToValue Fruit where toValue = defaultTableToValue +instance ToValue Physical where toValue = defaultTableToValue +instance ToValue Variety where toValue = defaultTableToValue + +instance ToTable Fruits where toTable = genericToTable +instance ToTable Fruit where toTable = genericToTable +instance ToTable Physical where toTable x = table ["color" .= color x, "shape" .= shape x] +instance ToTable Variety where toTable (Variety x) = table ["name" .= x] + +encodes :: Spec +encodes = it "encodes" $ + show (encode (Fruits [Fruit + "apple" + (Just (Physical "red" "round")) + [Variety "red delicious", Variety "granny smith"]])) + `shouldBe` [quoteStr| + [[fruits]] + name = "apple" + + [fruits.physical] + color = "red" + shape = "round" -instance FromValue ExampleRecord where fromValue = parseTableFromValue genericParseTable -instance ToTable ExampleRecord where toTable = genericToTable -instance ToValue ExampleRecord where toValue = defaultTableToValue + [[fruits.varieties]] + name = "red delicious" + + [[fruits.varieties]] + name = "granny smith"|] ``` -### Larger Example +## More Examples A demonstration of using this package at a more realistic scale -can be found in [HieDemoSpec](test/HieDemoSpec.hs). +can be found in [HieDemoSpec](test/HieDemoSpec.hs). The various unit +test files demonstrate what you can do with this library and what +outputs you can expect. + +See the low-level operations used to build a TOML syntax highlighter +in [TomlHighlighter](test-drivers/highlighter/Main.hs). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/src/Toml/FromValue.hs new/toml-parser-1.3.1.0/src/Toml/FromValue.hs --- old/toml-parser-1.3.0.0/src/Toml/FromValue.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/src/Toml/FromValue.hs 2001-09-09 03:46:40.000000000 +0200 @@ -60,7 +60,6 @@ import Data.Ratio (Ratio) import Data.Sequence (Seq) import Data.Sequence qualified as Seq -import Data.String (IsString (fromString)) import Data.Text qualified import Data.Text.Lazy qualified import Data.Time (ZonedTime, LocalTime, Day, TimeOfDay) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/src/Toml/Lexer/Utils.hs new/toml-parser-1.3.1.0/src/Toml/Lexer/Utils.hs --- old/toml-parser-1.3.0.0/src/Toml/Lexer/Utils.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/src/Toml/Lexer/Utils.hs 2001-09-09 03:46:40.000000000 +0200 @@ -40,18 +40,19 @@ startLstr, endStr, unicodeEscape, + recommendEscape, mkError, ) where -import Data.Char (ord, chr, isAscii) +import Data.Char (ord, chr, isAscii, isControl) import Data.Foldable (asum) import Data.Time.Format (parseTimeM, defaultTimeLocale, ParseTime) import Numeric (readHex) - +import Text.Printf (printf) +import Toml.Lexer.Token (Token(..)) import Toml.Located (Located(..)) import Toml.Position (move, Position) -import Toml.Lexer.Token (Token(..)) -- | Type of actions associated with lexer patterns type Action = Located String -> Context -> Outcome @@ -115,6 +116,10 @@ | otherwise -> strFrag (Located p [chr n]) ctx _ -> error "unicodeEscape: panic" +recommendEscape :: Action +recommendEscape (Located p x) _ = + LexerError (Located p (printf "control characters must be escaped, use: \\u%04X" (ord (head x)))) + -- | Emit a token ignoring the current lexeme token_ :: Token -> Action token_ t x _ = EmitToken (t <$ x) @@ -167,4 +172,6 @@ mkError "" = "unexpected end-of-input" mkError ('\n':_) = "unexpected end-of-line" mkError ('\r':'\n':_) = "unexpected end-of-line" -mkError (x:_) = "unexpected " ++ show x \ No newline at end of file +mkError (x:_) + | isControl x = "control characters prohibited" + | otherwise = "unexpected " ++ show x \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/src/Toml/Lexer.x new/toml-parser-1.3.1.0/src/Toml/Lexer.x --- old/toml-parser-1.3.0.0/src/Toml/Lexer.x 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/src/Toml/Lexer.x 2001-09-09 03:46:40.000000000 +0200 @@ -37,6 +37,7 @@ $hexdig = [ $digit A-F a-f ] $basic_unescaped = [ $wschar \x21 \x23-\x5B \x5D-\x7E $non_ascii ] $comment_start_symbol = \# +$control = [\x00-\x1F \x7F] @barekey = [0-9 A-Z a-z \- _]+ @@ -89,6 +90,7 @@ toml :- + <val> { @bad_dec_int { failure "leading zero prohibited" } @@ -170,6 +172,7 @@ \\ b { strFrag . ("\b" <$) } \\ \\ { strFrag . ("\\" <$) } \\ \" { strFrag . ("\"" <$) } + $control # [\t\r\n] { recommendEscape } } { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/src/Toml/Pretty.hs new/toml-parser-1.3.1.0/src/Toml/Pretty.hs --- old/toml-parser-1.3.0.0/src/Toml/Pretty.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/src/Toml/Pretty.hs 2001-09-09 03:46:40.000000000 +0200 @@ -36,12 +36,13 @@ -- * Pretty errors prettySemanticError, prettyMatchMessage, + prettyLocated, ) where import Data.Char (ord, isAsciiLower, isAsciiUpper, isDigit, isPrint) import Data.Foldable (fold) import Data.List (partition, sortOn) -import Data.List.NonEmpty (NonEmpty((:|))) +import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty qualified as NonEmpty import Data.Map qualified as Map import Data.String (fromString) @@ -51,7 +52,9 @@ import Text.Printf (printf) import Toml.FromValue.Matcher (MatchMessage(..), Scope (..)) import Toml.Lexer (Token(..)) +import Toml.Located (Located(..)) import Toml.Parser.Types (SectionKind(..)) +import Toml.Position (Position(..)) import Toml.Semantics (SemanticError (..), SemanticErrorKind (..)) import Toml.Value (Value(..), Table) @@ -102,6 +105,25 @@ | x <= '\xffff' -> printf "\\u%04X%s" (ord x) (go xs) | otherwise -> printf "\\U%08X%s" (ord x) (go xs) +-- | Quote a string using basic string literal syntax. +quoteMlString :: String -> String +quoteMlString = ("\"\"\"\n"++) . go + where + go = \case + "" -> "\"\"\"" -- terminator + '"' : '"' : '"' : xs -> "\"\"\\\"" ++ go xs + '\\' : xs -> '\\' : '\\' : go xs + '\b' : xs -> '\\' : 'b' : go xs + '\f' : xs -> '\\' : 'f' : go xs + '\t' : xs -> '\\' : 't' : go xs + '\n' : xs -> '\n' : go xs + '\r' : '\n' : xs -> '\r' : '\n' : go xs + '\r' : xs -> '\\' : 'r' : go xs + x : xs + | isPrint x -> x : go xs + | x <= '\xffff' -> printf "\\u%04X%s" (ord x) (go xs) + | otherwise -> printf "\\U%08X%s" (ord x) (go xs) + -- | Pretty-print a section heading. The result is annotated as a 'TableClass'. prettySectionKind :: SectionKind -> NonEmpty String -> TomlDoc prettySectionKind TableKind key = @@ -155,7 +177,7 @@ Table t -> lbrace <> concatWith (surround ", ") [prettyAssignment k v | (k,v) <- Map.assocs t] <> rbrace Bool True -> annotate BoolClass "true" Bool False -> annotate BoolClass "false" - String str -> annotate StringClass (fromString (quoteString str)) + String str -> prettySmartString str TimeOfDay tod -> annotate DateClass (fromString (formatTime defaultTimeLocale "%H:%M:%S%Q" tod)) ZonedTime zt | timeZoneMinutes (zonedTimeZone zt) == 0 -> @@ -164,7 +186,38 @@ LocalTime lt -> annotate DateClass (fromString (formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S%Q" lt)) Day d -> annotate DateClass (fromString (formatTime defaultTimeLocale "%Y-%m-%d" d)) --- | Predicate for values that should be completely rendered on the +prettySmartString :: String -> TomlDoc +prettySmartString str + | '\n' `elem` str = + column \i -> + pageWidth \case + AvailablePerLine n _ | length str > n - i -> + prettyMlString str + _ -> prettyString str + | otherwise = prettyString str + +prettyMlString :: String -> TomlDoc +prettyMlString str = annotate StringClass (column \i -> hang (-i) (fromString (quoteMlString str))) + +prettyString :: String -> TomlDoc +prettyString str = annotate StringClass (fromString (quoteString str)) + +-- | Predicate for values that CAN rendered on the +-- righthand-side of an @=@. +isSimple :: Value -> Bool +isSimple = \case + Integer _ -> True + Float _ -> True + Bool _ -> True + String _ -> True + TimeOfDay _ -> True + ZonedTime _ -> True + LocalTime _ -> True + Day _ -> True + Table x -> isSingularTable x -- differs from isAlwaysSimple + Array x -> null x || not (all isTable x) + +-- | Predicate for values that can be MUST rendered on the -- righthand-side of an @=@. isAlwaysSimple :: Value -> Bool isAlwaysSimple = \case @@ -176,7 +229,7 @@ ZonedTime _ -> True LocalTime _ -> True Day _ -> True - Table x -> isSingularTable x + Table _ -> False -- differs from isSimple Array x -> null x || not (all isTable x) -- | Predicate for table values. @@ -188,7 +241,7 @@ -- These can be collapsed using dotted-key notation on the lefthand-side -- of a @=@. isSingularTable :: Table -> Bool -isSingularTable (Map.elems -> [v]) = isAlwaysSimple v +isSingularTable (Map.elems -> [v]) = isSimple v isSingularTable _ = False -- | Render a complete TOML document using top-level table and array of @@ -259,31 +312,31 @@ NoProjection -> id KeyProjection f -> sortOn (f prefix . fst) - (simple, sections) = partition (isAlwaysSimple . snd) (order (Map.assocs t)) + kvs = order (Map.assocs t) + + -- this table will require no subsequent tables to be defined + simpleToml = all isSimple t + + (simple, sections) = partition (isAlwaysSimple . snd) kvs topLines = [fold topElts | let topElts = headers ++ assignments, not (null topElts)] headers = case NonEmpty.nonEmpty prefix of - Just key | not (null simple) || null sections || kind == ArrayTableKind -> + Just key | simpleToml || not (null simple) || null sections || kind == ArrayTableKind -> [prettySectionKind kind key <> hardline] _ -> [] - assignments = [prettyAssignment k v <> hardline | (k,v) <- simple] + assignments = [prettyAssignment k v <> hardline | (k,v) <- if simpleToml then kvs else simple] - subtables = [prettySection (prefix `snoc` k) v | (k,v) <- sections] + subtables = [prettySection (prefix ++ [k]) v | not simpleToml, (k,v) <- sections] prettySection key (Table tab) = - prettyToml_ mbKeyProj TableKind (NonEmpty.toList key) tab + prettyToml_ mbKeyProj TableKind key tab prettySection key (Array a) = - vcat [prettyToml_ mbKeyProj ArrayTableKind (NonEmpty.toList key) tab | Table tab <- a] + vcat [prettyToml_ mbKeyProj ArrayTableKind key tab | Table tab <- a] prettySection _ _ = error "prettySection applied to simple value" --- | Create a 'NonEmpty' with a given prefix and last element. -snoc :: [a] -> a -> NonEmpty a -snoc [] y = y :| [] -snoc (x : xs) y = x :| xs ++ [y] - -- | Render a semantic TOML error in a human-readable string. -- -- @since 1.3.0.0 @@ -304,3 +357,6 @@ where f (ScopeIndex i) = ('[' :) . shows i . (']':) f (ScopeKey key) = ('.' :) . shows (prettySimpleKey key) + +prettyLocated :: Located String -> String +prettyLocated (Located p s) = printf "%d:%d: %s" (posLine p) (posColumn p) s diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/src/Toml/Semantics/Ordered.hs new/toml-parser-1.3.1.0/src/Toml/Semantics/Ordered.hs --- old/toml-parser-1.3.0.0/src/Toml/Semantics/Ordered.hs 1970-01-01 01:00:00.000000000 +0100 +++ new/toml-parser-1.3.1.0/src/Toml/Semantics/Ordered.hs 2001-09-09 03:46:40.000000000 +0200 @@ -0,0 +1,113 @@ +{-| +Module : Toml.Semantics.Ordered +Description : Tool for extracting an ordering from an existing TOML file +Copyright : (c) Eric Mertens, 2023 +License : ISC +Maintainer : emert...@gmail.com + +This module can help build a key ordering projection given an existing +TOML file. This could be useful for applying a transformation to a TOML +file before pretty-printing it back in something very close to the +original order. + +When using the computed order, table keys will be remembered in the order +they appeared in the source file. Any key additional keys added to the +tables will be ordered alphabetically after all the known keys. + +@ +demo = + do txt <- 'readFile' \"demo.toml\" + let Right exprs = 'Toml.Parser.parseRawToml' txt + to = 'extractTableOrder' exprs + Right toml = 'Toml.Semantics.semantics' exprs + projection = 'projectKey' to + 'print' ('Toml.Pretty.prettyTomlOrdered' projection toml) +@ + +@since 1.3.1.0 + +-} +module Toml.Semantics.Ordered ( + TableOrder, + extractTableOrder, + projectKey, + ProjectedKey, + debugTableOrder, + ) where + +import Data.Foldable (foldl', toList) +import Data.List (sortOn) +import Data.Map (Map) +import Data.Map qualified as Map +import Toml.Located (Located(locThing)) +import Toml.Parser.Types (Expr(..), Key, Val(ValTable, ValArray)) + +-- | Summary of the order of the keys in a TOML document. +newtype TableOrder = TO (Map String KeyOrder) + +data KeyOrder = KeyOrder !Int TableOrder + +newtype ProjectedKey = PK (Either Int String) + deriving (Eq, Ord) + +-- | Generate a projection function for use with 'Toml.Pretty.prettyTomlOrdered' +projectKey :: + TableOrder {- ^ table order -} -> + [String] {- ^ table path -} -> + String {- ^ key -} -> + ProjectedKey {- ^ type suitable for ordering table keys -} +projectKey (TO to) [] = \k -> + case Map.lookup k to of + Just (KeyOrder i _) -> PK (Left i) + Nothing -> PK (Right k) +projectKey (TO to) (p:ps) = + case Map.lookup p to of + Just (KeyOrder _ to') -> projectKey to' ps + Nothing -> PK . Right + +emptyOrder :: TableOrder +emptyOrder = TO Map.empty + +-- | Extract a 'TableOrder' from the output of 'Toml.Parser.parseRawToml' +-- to be later used with 'projectKey'. +extractTableOrder :: [Expr] -> TableOrder +extractTableOrder = snd . foldl' addExpr ([], emptyOrder) + +addExpr :: ([String], TableOrder) -> Expr -> ([String], TableOrder) +addExpr (prefix, to) = \case + TableExpr k -> let k' = keyPath k in (k', addKey to k') + ArrayTableExpr k -> let k' = keyPath k in (k', addKey to k') + KeyValExpr k v -> (prefix, addVal prefix (addKey to (prefix ++ keyPath k)) v) + +addVal :: [String] -> TableOrder -> Val -> TableOrder +addVal prefix to = \case + ValArray xs -> foldl' (addVal prefix) to xs + ValTable kvs -> foldl' (\acc (k,v) -> + let k' = prefix ++ keyPath k in + addVal k' (addKey acc k') v) to kvs + _ -> to + +addKey :: TableOrder -> [String] -> TableOrder +addKey to [] = to +addKey (TO to) (x:xs) = TO (Map.alter f x to) + where + f Nothing = Just (KeyOrder (Map.size to) (addKey emptyOrder xs)) + f (Just (KeyOrder i m)) = Just (KeyOrder i (addKey m xs)) + +keyPath :: Key -> [String] +keyPath = map locThing . toList + +-- | Render a white-space nested representation of the key ordering extracted +-- by 'extractTableOrder'. This is provided for debugging and understandability. +debugTableOrder :: TableOrder -> String +debugTableOrder to = unlines (go 0 to []) + where + go i (TO m) z = + foldr (go1 i) z + (sortOn p (Map.assocs m)) + + go1 i (k, KeyOrder _ v) z = + (replicate (4*i) ' ' ++ k) : + go (i+1) v z + + p (_, KeyOrder i _) = i diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/src/Toml.hs new/toml-parser-1.3.1.0/src/Toml.hs --- old/toml-parser-1.3.0.0/src/Toml.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/src/Toml.hs 2001-09-09 03:46:40.000000000 +0200 @@ -32,13 +32,10 @@ Result(..), ) where -import Text.Printf (printf) import Toml.FromValue (FromValue (fromValue), Result(..)) import Toml.FromValue.Matcher (runMatcher) -import Toml.Located (Located(Located)) import Toml.Parser (parseRawToml) -import Toml.Position (Position(posColumn, posLine)) -import Toml.Pretty (TomlDoc, DocClass(..), prettyToml, prettySemanticError, prettyMatchMessage) +import Toml.Pretty (TomlDoc, DocClass(..), prettyToml, prettySemanticError, prettyMatchMessage, prettyLocated) import Toml.Semantics (semantics) import Toml.ToValue (ToTable (toTable)) import Toml.Value (Table, Value(..)) @@ -47,11 +44,10 @@ parse :: String -> Either String Table parse str = case parseRawToml str of - Left (Located p e) -> Left (printf "%d:%d: %s" (posLine p) (posColumn p) e) + Left e -> Left (prettyLocated e) Right exprs -> case semantics exprs of - Left (Located p e) -> - Left (printf "%d:%d: %s" (posLine p) (posColumn p) (prettySemanticError e)) + Left e -> Left (prettyLocated (prettySemanticError <$> e)) Right tab -> Right tab -- | Use the 'FromValue' instance to decode a value from a TOML string. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/test/DecodeSpec.hs new/toml-parser-1.3.1.0/test/DecodeSpec.hs --- old/toml-parser-1.3.0.0/test/DecodeSpec.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/test/DecodeSpec.hs 2001-09-09 03:46:40.000000000 +0200 @@ -1,13 +1,12 @@ {-# Language DuplicateRecordFields #-} module DecodeSpec (spec) where -import Data.Map qualified as Map import Data.Maybe (fromMaybe) import GHC.Generics (Generic) import QuoteStr (quoteStr) import Test.Hspec (it, shouldBe, Spec) -import Toml (decode, Result(Success), encode) -import Toml.FromValue (FromValue(..), runParseTable, reqKey, optKey) +import Toml (decode, Result, encode) +import Toml.FromValue (FromValue(..), reqKey, optKey) import Toml.FromValue.Generic (genericParseTable) import Toml.ToValue (ToTable(..), ToValue(toValue), table, (.=), defaultTableToValue) import Toml.ToValue.Generic (genericToTable) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/test/FromValueSpec.hs new/toml-parser-1.3.1.0/test/FromValueSpec.hs --- old/toml-parser-1.3.0.0/test/FromValueSpec.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/test/FromValueSpec.hs 2001-09-09 03:46:40.000000000 +0200 @@ -12,7 +12,7 @@ import Control.Monad (when) import Test.Hspec (it, shouldBe, Spec) import Toml (Result(..), Value(..)) -import Toml.FromValue (Result(..), FromValue(fromValue), optKey, parseTableFromValue, reqKey, warnTable, pickKey, runParseTable) +import Toml.FromValue (FromValue(fromValue), optKey, reqKey, warnTable, pickKey, runParseTable) import Toml.FromValue.Matcher (Matcher, runMatcher) import Toml.FromValue.ParseTable (KeyAlt(..)) import Toml.Pretty (prettyMatchMessage) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/test/HieDemoSpec.hs new/toml-parser-1.3.1.0/test/HieDemoSpec.hs --- old/toml-parser-1.3.0.0/test/HieDemoSpec.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/test/HieDemoSpec.hs 2001-09-09 03:46:40.000000000 +0200 @@ -15,11 +15,10 @@ -} module HieDemoSpec where -import Control.Applicative (optional) import GHC.Generics ( Generic ) import QuoteStr (quoteStr) import Test.Hspec (Spec, it, shouldBe) -import Toml (Value(Table, Array), Table, Result(..), decode) +import Toml (Value(Table, Array), Table, decode) import Toml.FromValue import Toml.FromValue.Generic (genericParseTable) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/test/LexerSpec.hs new/toml-parser-1.3.1.0/test/LexerSpec.hs --- old/toml-parser-1.3.0.0/test/LexerSpec.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/test/LexerSpec.hs 2001-09-09 03:46:40.000000000 +0200 @@ -9,7 +9,17 @@ do it "handles special cased control character" $ parse "x = '\SOH'" `shouldBe` - Left "1:6: lexical error: unexpected '\\SOH'" + Left "1:6: lexical error: control characters prohibited" + + it "recommends escapes for control characters (1)" $ + parse "x = \"\SOH\"" + `shouldBe` + Left "1:6: lexical error: control characters must be escaped, use: \\u0001" + + it "recommends escapes for control characters (2)" $ + parse "x = \"\DEL\"" + `shouldBe` + Left "1:6: lexical error: control characters must be escaped, use: \\u007F" -- These seem boring, but they provide test coverage of an error case in the state machine it "handles unexpected '}'" $ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/test/PrettySpec.hs new/toml-parser-1.3.1.0/test/PrettySpec.hs --- old/toml-parser-1.3.0.0/test/PrettySpec.hs 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/test/PrettySpec.hs 2001-09-09 03:46:40.000000000 +0200 @@ -29,9 +29,10 @@ it "renders empty tables" $ fmap tomlString (parse "x.y.z={}\nz.y.w=false") `shouldBe` Right [quoteStr| - z.y.w = false + [x.y.z] - [x.y.z]|] + [z] + y.w = false|] it "renders empty tables in array of tables" $ fmap tomlString (parse "ex=[{},{},{a=9}]") @@ -59,6 +60,24 @@ `shouldBe` Right [quoteStr| a = "\\\b\t\r\n\f\"\u007F\U0001000C"|] + it "renders multiline strings" $ + fmap tomlString (parse [quoteStr| + Everything-I-Touch = "Everything I touch\nwith tenderness, alas,\npricks like a bramble." + Two-More = [ + "The west wind whispered,\nAnd touched the eyelids of spring:\nHer eyes, Primroses.", + "Plum flower temple:\nVoices rise\nFrom the foothills", + ]|]) + `shouldBe` Right [quoteStr| + Everything-I-Touch = """ + Everything I touch + with tenderness, alas, + pricks like a bramble.""" + Two-More = [ """ + The west wind whispered, + And touched the eyelids of spring: + Her eyes, Primroses.""" + , "Plum flower temple:\nVoices rise\nFrom the foothills" ]|] + it "renders floats" $ fmap tomlString (parse "a=0.0\nb=-0.1\nc=0.1\nd=3.141592653589793\ne=4e123") `shouldBe` Right [quoteStr| @@ -106,3 +125,15 @@ `shouldBe` Right [quoteStr| x = [ [ {a = "this is a longer example", b = "and it will linewrap"} , {c = "all on its own"} ] ]|] + + it "factors out unique table prefixes in leaf tables" $ + fmap tomlString (parse [quoteStr| + [x] + i = 1 + p.q = "a" + y.z = "c"|]) + `shouldBe` Right [quoteStr| + [x] + i = 1 + p.q = "a" + y.z = "c"|] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toml-parser-1.3.0.0/toml-parser.cabal new/toml-parser-1.3.1.0/toml-parser.cabal --- old/toml-parser-1.3.0.0/toml-parser.cabal 2001-09-09 03:46:40.000000000 +0200 +++ new/toml-parser-1.3.1.0/toml-parser.cabal 2001-09-09 03:46:40.000000000 +0200 @@ -1,6 +1,6 @@ cabal-version: 3.0 name: toml-parser -version: 1.3.0.0 +version: 1.3.1.0 synopsis: TOML 1.0.0 parser description: TOML parser using generated lexers and parsers with @@ -13,7 +13,7 @@ copyright: 2023 Eric Mertens category: Text build-type: Simple -tested-with: GHC == {8.10.7, 9.0.2, 9.2.8, 9.4.5, 9.6.2} +tested-with: GHC == {8.10.7, 9.0.2, 9.2.8, 9.4.7, 9.6.3} extra-doc-files: ChangeLog.md @@ -60,6 +60,7 @@ Toml.Position Toml.Pretty Toml.Semantics + Toml.Semantics.Ordered Toml.ToValue Toml.ToValue.Generic Toml.Value @@ -68,8 +69,8 @@ Toml.Parser.Utils build-depends: array ^>= 0.5, - base ^>= {4.14, 4.15, 4.16, 4.17, 4.18}, - containers ^>= {0.5, 0.6}, + base ^>= {4.14, 4.15, 4.16, 4.17, 4.18, 4.19}, + containers ^>= {0.5, 0.6, 0.7}, prettyprinter ^>= 1.7, text >= 0.2 && < 3, time ^>= {1.9, 1.10, 1.11, 1.12}, @@ -91,7 +92,7 @@ base, containers, hspec ^>= {2.10, 2.11}, - template-haskell ^>= {2.16, 2.17, 2.18, 2.19, 2.20}, + template-haskell ^>= {2.16, 2.17, 2.18, 2.19, 2.20, 2.21}, time, toml-parser, other-modules: @@ -108,9 +109,18 @@ import: extensions type: exitcode-stdio-1.0 main-is: README.lhs - ghc-options: -pgmL markdown-unlit + ghc-options: -pgmL markdown-unlit -optL "haskell toml" + default-extensions: + QuasiQuotes + other-modules: + QuoteStr + hs-source-dirs: + . + test build-depends: base, toml-parser, + hspec ^>= {2.10, 2.11}, + template-haskell ^>= {2.16, 2.17, 2.18, 2.19, 2.20, 2.21}, build-tool-depends: markdown-unlit:markdown-unlit ^>= {0.5.1, 0.6.0},